0001: /*
0002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
0003: *
0004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
0005: *
0006: * The contents of this file are subject to the terms of either the GNU
0007: * General Public License Version 2 only ("GPL") or the Common
0008: * Development and Distribution License("CDDL") (collectively, the
0009: * "License"). You may not use this file except in compliance with the
0010: * License. You can obtain a copy of the License at
0011: * http://www.netbeans.org/cddl-gplv2.html
0012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
0013: * specific language governing permissions and limitations under the
0014: * License. When distributing the software, include this License Header
0015: * Notice in each file and include the License file at
0016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
0017: * particular file as subject to the "Classpath" exception as provided
0018: * by Sun in the GPL Version 2 section of the License file that
0019: * accompanied this code. If applicable, add the following below the
0020: * License Header, with the fields enclosed by brackets [] replaced by
0021: * your own identifying information:
0022: * "Portions Copyrighted [year] [name of copyright owner]"
0023: *
0024: * Contributor(s):
0025: *
0026: * The Original Software is NetBeans. The Initial Developer of the Original
0027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
0028: * Microsystems, Inc. All Rights Reserved.
0029: *
0030: * If you wish your version of this file to be governed by only the CDDL
0031: * or only the GPL Version 2, indicate your decision by adding
0032: * "[Contributor] elects to include this software in this distribution
0033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
0034: * single choice of license, a recipient has the option to distribute
0035: * your version of this file under either the CDDL, the GPL Version 2 or
0036: * to extend the choice of license to its licensees as provided above.
0037: * However, if you add GPL Version 2 code and therefore, elected the GPL
0038: * Version 2 license, then the option applies only if the new code is
0039: * made subject to such option by the copyright holder.
0040: */
0041:
0042: package org.netbeans.modules.uml.core.reverseengineering.reframework;
0043:
0044: import java.util.List;
0045: import org.netbeans.modules.uml.common.generics.ETPairT;
0046: import org.netbeans.modules.uml.core.coreapplication.ICoreProduct;
0047: import org.netbeans.modules.uml.core.metamodel.core.foundation.BaseElement;
0048: import org.netbeans.modules.uml.core.metamodel.core.foundation.IDependency;
0049: import org.netbeans.modules.uml.core.metamodel.core.foundation.IElement;
0050: import org.netbeans.modules.uml.core.metamodel.core.foundation.IMultiplicity;
0051: import org.netbeans.modules.uml.core.metamodel.core.foundation.IMultiplicityRange;
0052: import org.netbeans.modules.uml.core.metamodel.core.foundation.INamedElement;
0053: import org.netbeans.modules.uml.core.metamodel.core.foundation.INamespace;
0054: import org.netbeans.modules.uml.core.metamodel.infrastructure.coreinfrastructure.IAttribute;
0055: import org.netbeans.modules.uml.core.metamodel.infrastructure.coreinfrastructure.IBehavioralFeature;
0056: import org.netbeans.modules.uml.core.metamodel.infrastructure.coreinfrastructure.IClassifier;
0057: import org.netbeans.modules.uml.core.metamodel.infrastructure.coreinfrastructure.INavigableEnd;
0058: import org.netbeans.modules.uml.core.metamodel.infrastructure.coreinfrastructure.IOperation;
0059: import org.netbeans.modules.uml.core.metamodel.infrastructure.coreinfrastructure.IParameter;
0060: import org.netbeans.modules.uml.core.metamodel.structure.IProject;
0061: import org.netbeans.modules.uml.core.metamodel.structure.ISourceFileArtifact;
0062: import org.netbeans.modules.uml.core.reverseengineering.parsingfacilities.ClassLoaderListener;
0063: import org.netbeans.modules.uml.core.reverseengineering.parsingfacilities.IClassLoaderListener;
0064: import org.netbeans.modules.uml.core.reverseengineering.parsingfacilities.IUMLParser;
0065: import org.netbeans.modules.uml.core.reverseengineering.parsingfacilities.IUMLParserEventDispatcher;
0066: import org.netbeans.modules.uml.core.reverseengineering.reframework.parsingframework.IErrorEvent;
0067: import org.netbeans.modules.uml.core.reverseengineering.reframework.parsingframework.IFacility;
0068: import org.netbeans.modules.uml.core.reverseengineering.reframework.parsingframework.IFacilityManager;
0069: import org.netbeans.modules.uml.core.reverseengineering.reframework.parsingframework.ILanguage;
0070: import org.netbeans.modules.uml.core.reverseengineering.reframework.parsingframework.ITokenDescriptor;
0071: import org.netbeans.modules.uml.core.reverseengineering.reframework.parsingframework.ProcessTypeKind;
0072: import org.netbeans.modules.uml.core.roundtripframework.codegeneration.IParseInformationCache;
0073: import org.netbeans.modules.uml.core.support.umlsupport.ProductRetriever;
0074: import org.netbeans.modules.uml.core.support.umlsupport.StringUtilities;
0075: import org.netbeans.modules.uml.core.support.umlsupport.XMLManip;
0076: import org.netbeans.modules.uml.core.support.umlutils.ETList;
0077: import org.netbeans.modules.uml.core.support.umlutils.ElementLocator;
0078: import org.netbeans.modules.uml.ui.products.ad.applicationcore.IADProduct;
0079:
0080: /**
0081: */
0082: public class UMLParserUtilities {
0083: public static final String PACKAGE_SEPARATOR = "::";
0084:
0085: // Element range specification
0086: public static final int ER_ENTIRE_ELEMENT = 0;
0087: public static final int ER_ELEMENT_HEAD = 1;
0088:
0089: /**
0090: * Retrieves an Element's Token Descriptors which contains file position
0091: * information for the Element.
0092: *
0093: * @param pElement
0094: * @param ppTokenDescriptors
0095: *
0096: * @return
0097: */
0098: public static ETList<ITokenDescriptor> getElementTokenDescriptors(
0099: IElement element) {
0100: ISourceFileArtifact sfa = getSourceFileArtifact(element);
0101: if (sfa != null) {
0102: IParseInformationCache pcache = getParseInformationCache();
0103: if (pcache != null) {
0104: IFileInformation fi = pcache.getParseInformation(sfa);
0105: if (fi != null)
0106: return findElementTokenDescriptors(element, fi);
0107: }
0108: }
0109: return null;
0110: }
0111:
0112: /**
0113: * returns the line number that the given element @a pElement starts on
0114: *
0115: * @param pElement[in] the element whose starting line number you want
0116: * @param pLineNumber[out] the line number
0117: *
0118: * @return HRESULT
0119: */
0120: public static int getElementStartLineNumber(IElement el) {
0121: ETList<ITokenDescriptor> toks = getElementTokenDescriptors(el);
0122: int line = 0;
0123: if (toks != null) {
0124: for (int i = 0, count = toks.size(); i < count; ++i) {
0125: ITokenDescriptor t = toks.get(i);
0126: if ("StartPosition".equals(t.getType()))
0127: line = t.getLine();
0128: }
0129: }
0130: return line;
0131: }
0132:
0133: /**
0134: * gets the source file name of a given element
0135: *
0136: * @param pElement[in] the element whose source file name you want
0137: * @param fileName[out] the name of the source file that contains the element.
0138: *
0139: * @return HRESULT
0140: */
0141: public static String getElementSourceFileName(IElement el) {
0142: ISourceFileArtifact art = getSourceFileArtifact(el);
0143: return art != null ? art.getSourceFile() : null;
0144: }
0145:
0146: /**
0147: * returns a UML Parser
0148: *
0149: * @param ppParser[out] a UML Parser object
0150: *
0151: * @return HRESULT
0152: */
0153: public static IUMLParser getUMLParser() {
0154: ICoreProduct product = ProductRetriever.retrieveProduct();
0155: if (product != null) {
0156: IFacilityManager fman = product.getFacilityManager();
0157: // AZTEC: TODO: Might we need to give the fully-qualified class name
0158: // here? It'd be preferable to look that up.
0159: IFacility fac = fman.retrieveFacility("Parsing.UMLParser");
0160: if (fac instanceof IUMLParser)
0161: return (IUMLParser) fac;
0162: }
0163: return null;
0164: }
0165:
0166: /**
0167: * Parses a Source File Artifact and returns the parse information.
0168: *
0169: * @param pSourceFileArtifact[in] the source file artifact to parse
0170: * @param ppResults[out] the parse information for the source file artifact
0171: *
0172: * @return HRESULT
0173: */
0174: public static IFileInformation parseSourceFileArtifact(
0175: ISourceFileArtifact art) {
0176: // Slurp the whole thing in and pray that we have enough memory.
0177: String source = art.getSourceCode();
0178:
0179: if (source != null && source.length() > 0) {
0180: ILanguage lan = art.getLanguage();
0181: if (lan != null)
0182: return parseText(source, lan.getName(), false, null,
0183: null, null);
0184: }
0185: return null;
0186: }
0187:
0188: /**
0189: * returns the start position of the ParserData object passed in. If the parser data object
0190: * has a comment, then the comment is considered the start position. If no comment is found,
0191: * the parser data's StartPosition token descriptor's position is considered the start position
0192: *
0193: * @param pParserData[in] could be an attribute, an operation, a class, etc.
0194: * @param pStartPosition[out] the start position of @a pParserData
0195: *
0196: * @return HRESULT
0197: */
0198: public static long getStartPosition(IParserData pdata) {
0199: ITokenDescriptor desc = pdata.getTokenDescriptor("Comment");
0200: if (desc == null)
0201: desc = pdata.getTokenDescriptor("StartPosition");
0202: return desc != null ? desc.getPosition() : -1;
0203: }
0204:
0205: /**
0206: * Returns an IParserData's EndPosition.
0207: *
0208: * @param pParserData[in] the parser data derived class whose end position you want.
0209: * Could be an IREClass, an IREOperation, etc.
0210: * @param pEndPosition[out] the parser data's end position (if it has one).
0211: *
0212: * @return HRESULT
0213: */
0214: public static long getEndPosition(IParserData pdata) {
0215: return getTokenDescriptorEndPosition(pdata, "EndPosition");
0216: }
0217:
0218: /**
0219: * Returns the sum of the file offset and length of a TokenDescriptor named @a
0220: * tokenDescriptorName
0221: *
0222: * @param pParserData[in] the ParserData object that you want token descriptors for
0223: * @param tokenDescriptorName[in] the name of the TokenDescriptor (e.g. StartPosition, EndPosition)
0224: * @param pPosition[out] the file offset of the TokenDescriptor's end position
0225: *
0226: * @return HRESULT
0227: */
0228: public static long getTokenDescriptorEndPosition(IParserData pdata,
0229: String tag) {
0230: ITokenDescriptor desc = pdata.getTokenDescriptor(tag);
0231: if (desc != null) {
0232: long pos = desc.getPosition();
0233: if (pos != -1)
0234: pos += desc.getLength();
0235: return pos;
0236: }
0237: return -1;
0238: }
0239:
0240: /**
0241: * Finds the IOperation that matches the IREOperation @a pREOperation
0242: *
0243: * @param pClassifier[in] the classifier that owns the IOperation
0244: * @param pREOperation[in] the IREOperation you're searching for.
0245: * @param ppOperation[out] the IOperation that matches @a pREOperation (if it exists)
0246: *
0247: * @return HRESULT
0248: */
0249: public static IOperation findMatchingOperation(IClassifier c,
0250: IREOperation reo) {
0251: ETList<IOperation> ops = c.getOperations();
0252: if (ops != null) {
0253: for (int i = 0, count = ops.size(); i < count; ++i) {
0254: IOperation op = ops.get(i);
0255: if (isMatchingOperation(op, reo))
0256: return op;
0257: }
0258: }
0259: return null;
0260: }
0261:
0262: /**
0263: * returns the IAttribute that matches the IREAttribute @a pREAttribute
0264: *
0265: * @param pClassifier[in] the classifier that owns the attribute
0266: * @param pREAttribute[in] the IREAttribute that you're searching for
0267: * @param ppAttribute[out] the IAttribute that matches @a pREAttribute (if it exists)
0268: *
0269: * @return HRESULT
0270: */
0271: public static IAttribute findMatchingAttribute(IClassifier c,
0272: IREAttribute rat) {
0273: String name = rat.getName();
0274: IAttribute att = c.getAttributeByName(name);
0275: if (att == null) {
0276: ETList<INavigableEnd> ends = c.getOutboundNavigableEnds();
0277: if (ends != null) {
0278: for (int i = 0, count = ends.size(); i < count; ++i) {
0279: INavigableEnd end = ends.get(i);
0280: if (end == null)
0281: continue;
0282:
0283: if (end instanceof IAttribute
0284: && name.equals(end.getName())) {
0285: att = (IAttribute) end;
0286: break;
0287: }
0288: }
0289: }
0290: }
0291: return att;
0292: }
0293:
0294: /**
0295: * returns the number of non-return type parameters for @a pOperation
0296: *
0297: * @param pOperation[in] the operation whose parameter count you want
0298: * @param pNumParameters[out] the number of parameters
0299: *
0300: * @return HRESULT
0301: */
0302: public static int getParameterCount(IOperation op) {
0303: ETList<IParameter> pars = op.getParameters();
0304: if (pars == null)
0305: return 0;
0306:
0307: int parcount = 0;
0308: for (int i = 0, count = pars.size(); i < count; ++i)
0309: if (pars.get(i).getDirection() != BaseElement.PDK_RESULT)
0310: parcount++;
0311: return parcount;
0312: }
0313:
0314: /**
0315: * Returns the Nth parameter for the given operation.
0316: *
0317: * @param pOperation[in] the operation whose Nth parameter you want.
0318: * @param index[in] this is "N" (starts from 0).
0319: * @param ppParameter[out] the Nth parameter
0320: *
0321: * @return HRESULT
0322: */
0323: public static IParameter getNthParameter(IOperation op, int n) {
0324: ETList<IParameter> pars = op.getParameters();
0325: if (pars == null)
0326: return null;
0327:
0328: int parcount = 0;
0329: for (int i = 0, count = pars.size(); i < count; ++i) {
0330: IParameter par = pars.get(i);
0331: if (par.getDirection() != BaseElement.PDK_RESULT
0332: && parcount++ == n)
0333: return par;
0334: }
0335: return null;
0336: }
0337:
0338: /**
0339: * returns the number of non-return type parameters for @a pREOperation
0340: *
0341: * @param pREOperation[in] the operation whose parameter count you want
0342: * @param pNumREParameters[out] the number of parameters
0343: *
0344: * @return HRESULT
0345: */
0346: public static int getParameterCount(IREOperation op) {
0347: ETList<IREParameter> pars = op.getParameters();
0348: if (pars == null)
0349: return 0;
0350:
0351: int parcount = 0;
0352: for (int i = 0, count = pars.size(); i < count; ++i)
0353: if (pars.get(i).getKind() != BaseElement.PDK_RESULT)
0354: parcount++;
0355: return parcount;
0356: }
0357:
0358: /**
0359: * Returns the Nth parameter for the given operation.
0360: *
0361: * @param pREOperation[in] the operation whose Nth parameter you want.
0362: * @param index[in] this is "N"
0363: * @param ppREParameter[out] the Nth parameter
0364: *
0365: * @return HRESULT
0366: */
0367: public static IREParameter getNthParameter(IREOperation op, int n) {
0368: ETList<IREParameter> pars = op.getParameters();
0369: if (pars == null)
0370: return null;
0371:
0372: int parcount = 0;
0373: for (int i = 0, count = pars.size(); i < count; ++i) {
0374: IREParameter par = pars.get(i);
0375: if (par.getKind() != BaseElement.PDK_RESULT
0376: && parcount++ == n)
0377: return par;
0378: }
0379: return null;
0380: }
0381:
0382: public static SourceCodeRange getElementRange(IParserData pdata,
0383: int elementRange) {
0384: SourceCodeRange range = new SourceCodeRange();
0385: range.begin = getStartPosition(pdata);
0386:
0387: String endTag = getTokenDescriptorEndTag(pdata, elementRange);
0388: if (endTag != null)
0389: range.end = getTokenDescriptorEndPosition(pdata, endTag);
0390: return range;
0391: }
0392:
0393: /**
0394: * Returns the token descriptor that points to the "end" of @a
0395: * pParserData. What "end" means is determined by @a elementRange and
0396: * by what kind of object @a pParserData points to.
0397: *
0398: * @param pParserData[in] the IParserData object that you want the
0399: * TokenDescriptor end tag for.
0400: *
0401: * @param elementRangeSpec[in] what tag is desired (see enum in header file)
0402: * @param endTag[out] the name of the "end tag". This string can be
0403: * passed to IParserData::GetTokenDescriptor()
0404: *
0405: * @return HRESULT
0406: */
0407: private static String getTokenDescriptorEndTag(IParserData pdata,
0408: int range) {
0409: if (range == ER_ELEMENT_HEAD) {
0410: // For a classifier, the end tag for a head is different than a method.
0411: // ER_ELEMENT_HEAD is not relevant to IAttributes
0412: if (pdata instanceof IREOperation)
0413: return "OpHeadEndPosition";
0414: else if (pdata instanceof IREClass)
0415: return "ClassHeadEndPosition";
0416: }
0417: return "EndPosition";
0418: }
0419:
0420: private static IFileInformation parseText(String text,
0421: String language, boolean parseFragment,
0422: ETList<IREOperation> topLevelOps,
0423: ETList<IREAttribute> topLevelAttribs,
0424: ETList<IErrorEvent> errors) {
0425: IFileInformation fileInf = null;
0426: int parseKind = parseFragment ? ProcessTypeKind.PTK_PROCESS_FRAGMENT
0427: : ProcessTypeKind.PTK_PROCESS_FILE;
0428: IUMLParser parser = getUMLParser();
0429: if (parser != null) {
0430: IUMLParserEventDispatcher disp = parser
0431: .getUMLParserDispatcher();
0432: if (disp != null) {
0433: IClassLoaderListener list = new ClassLoaderListener();
0434: disp.registerForUMLParserEvents(list, null);
0435: if (parseFragment)
0436: disp.registerForUMLParserAtomicEvents(null, null);
0437: parser.processStreamByType(text, language, parseKind);
0438: disp.revokeUMLParserSink(list);
0439:
0440: fileInf = list.getFileInformation();
0441: if (parseFragment) {
0442: disp.revokeUMLParserAtomicSink(list);
0443: if (topLevelAttribs != null)
0444: topLevelAttribs.addAll(list
0445: .getTopLevelAttributes());
0446: if (topLevelOps != null)
0447: topLevelOps
0448: .addAll(list.getTopLevelOperations());
0449: }
0450:
0451: // If caller is interested in errors, return them.
0452: if (errors != null)
0453: errors.addAll(fileInf.getErrors());
0454: }
0455: }
0456: return fileInf;
0457: }
0458:
0459: /**
0460: * Parses a source file and stores the results in @a pResults
0461: *
0462: * @param fileName[in] the source file to parse
0463: * @param pResults[out] the results of the parse operation
0464: *
0465: * @return HRESULT
0466: */
0467: public static IFileInformation parseFile(String filename) {
0468: IUMLParser parser = getUMLParser();
0469: if (parser != null) {
0470: IUMLParserEventDispatcher disp = parser
0471: .getUMLParserDispatcher();
0472: if (disp != null) {
0473: IClassLoaderListener list = new ClassLoaderListener();
0474: disp.registerForUMLParserEvents(list, filename);
0475: parser.processStreamFromFile(filename);
0476: return list.getFileInformation();
0477: }
0478: }
0479: return null;
0480: }
0481:
0482: /**
0483: * Returns a collection of ITokenDescriptor objects for a particular Element
0484: *
0485: * @param pElement[in] the element whose token descriptors you want
0486: * @param results[in] a list of parse results. This list indirectly contains
0487: * TokenDescriptors for all parsed items (e.g. attributes,
0488: * classes, etc.).
0489: * @param ppTokenDescriptors[out] a collection of TokenDescriptor objects
0490: *
0491: * @return HRESULT
0492: */
0493: private static ETList<ITokenDescriptor> findElementTokenDescriptors(
0494: IElement el, IFileInformation fi) {
0495: IClassifier c = getClassifierFromElement(el);
0496: if (c != null) {
0497: IREClass rec = getREClassFromClassifier(c, fi);
0498: if (rec != null)
0499: return findElementTokenDescriptors(el, rec);
0500: }
0501: return null;
0502: }
0503:
0504: /**
0505: * Returns a collection of ITokenDescriptor objects for a particular Element
0506: *
0507: * @param pElement[in] the element whose token descriptors you want
0508: * @param pOwningClass[in] the REClass object that owns or matches @a pElement
0509: * @param ppTokenDescriptors[out] a collection of TokenDescriptor objects
0510: *
0511: * @return HRESULT
0512: */
0513: private static ETList<ITokenDescriptor> findElementTokenDescriptors(
0514: IElement el, IREClass owningClass) {
0515: IParserData pdata = null;
0516: if (el instanceof IClassifier)
0517: pdata = owningClass;
0518: else if (el instanceof IAttribute
0519: && (pdata = findMatchingREAttribute(owningClass,
0520: (IAttribute) el)) != null)
0521: ;
0522: else if (el instanceof IOperation
0523: && (pdata = findMatchingREOperation(owningClass,
0524: (IOperation) el)) != null)
0525: ;
0526: else if (el instanceof IParameter) {
0527: // Get the parameter's owning operation
0528: IBehavioralFeature owningF = ((IParameter) el)
0529: .getBehavioralFeature();
0530: if (owningF instanceof IOperation)
0531: pdata = findMatchingREOperation(owningClass,
0532: (IOperation) owningF);
0533: }
0534:
0535: return pdata != null ? pdata.getTokenDescriptors() : null;
0536: }
0537:
0538: /**
0539: * given an IClassifier, this method returns the IREClass object that matches the IClassifier
0540: *
0541: * @param pClassifier
0542: * @param ppREClass
0543: *
0544: * @warning Does not handle namespaces yet
0545: * @return HRESULT
0546: */
0547: public static IREClass getREClassFromClassifier(IClassifier c,
0548: IFileInformation fi) {
0549: String cp = getNestedClassPath(c);
0550: return getREClassFromClassPath(cp, fi);
0551: }
0552:
0553: /**
0554: * Given a Class Name or Class Path (e.g. A::B::C), this operation
0555: * looks up an IREClass by that name in the IFileInformation
0556: * collection.
0557: *
0558: * @param classPathBSTR[]
0559: * @param pResults[]
0560: * @param ppREClass[]
0561: *
0562: * @return
0563: */
0564: private static IREClass getREClassFromClassPath(String cp,
0565: IFileInformation fi) {
0566: ETPairT<String, String> sp = StringUtilities.removeToken(cp,
0567: PACKAGE_SEPARATOR);
0568: String outerCN = sp.getParamOne();
0569: cp = sp.getParamTwo();
0570:
0571: // Get the number of top level classes
0572: int numClasses = fi.getTotalClasses();
0573:
0574: // If any classes were found...
0575: if (numClasses > 0) {
0576: if (outerCN != null && outerCN.length() > 0) {
0577: for (int i = 0; i < numClasses; ++i) {
0578: IREClass rec = fi.getClass(i);
0579: if (rec != null) {
0580: String recName = rec.getName();
0581: if (outerCN.equals(recName)) {
0582: return cp.length() == 0 ? rec
0583: : findMatchingREClass(rec, cp);
0584: }
0585: }
0586: }
0587: }
0588: }
0589: return null;
0590: }
0591:
0592: /**
0593: * Searches @a pREClass for an REOperation that matches @a pOperation and returns it if found.
0594: *
0595: * @param pREClass[in] the class that is being searched
0596: * @param pOperation[in] the operation used to find the matching REOperation
0597: * @param ppREOperation[out] the matching operation (if found).
0598: *
0599: * @return HRESULT
0600: */
0601: public static IREOperation findMatchingREOperation(IREClass c,
0602: IOperation op) {
0603: ETList<IREOperation> ops = c.getOperations();
0604: if (ops != null) {
0605: for (int i = 0, count = ops.size(); i < count; ++i) {
0606: IREOperation reop = ops.get(i);
0607: if (reop == null)
0608: continue;
0609:
0610: if (isMatchingOperation(op, reop))
0611: return reop;
0612: }
0613: }
0614: return null;
0615: }
0616:
0617: /**
0618: * determines if two operations (an IOperation and an IREOperation) match
0619: *
0620: * @param pOperation[in] the IOperation object
0621: * @param pOperationRE[in] the IREOperation object
0622: * @param isMatching[out]
0623: * - true if they match
0624: * - false if they don't match
0625: *
0626: * @return
0627: */
0628: private static boolean isMatchingOperation(IOperation op,
0629: IREOperation reo) {
0630: String name = op.getName();
0631: if (name == null || !name.equals(reo.getName()))
0632: return false;
0633:
0634: ETList<IParameter> pars = op.getParameters();
0635: ETList<IREParameter> repars = reo.getParameters();
0636:
0637: // if (op.getReturnType().getName().equals("")) {
0638: // for (int i = 0, count = pars.size(); i < count; ++i)
0639: // {
0640: // if (pars.get(i).getDirection() == BaseElement.PDK_RESULT) {
0641: // String returnType = XMLManip.getAttributeValue(pars.get(i).getDOM4JNode(), "type");
0642: // if (returnType.indexOf(".") != -1)
0643: // returnType = returnType.substring(returnType.lastIndexOf(".")+1) ;
0644: //
0645: // op.setReturnType2(returnType);
0646: // }
0647: // }
0648: //
0649: // }
0650:
0651: if ((pars == null || pars.size() == 0)
0652: && (repars == null || repars.size() == 0))
0653: return true;
0654:
0655: if ((pars == null) != (repars == null)
0656: || pars.size() != repars.size())
0657: return false;
0658:
0659: for (int i = 0, count = pars.size(); i < count; ++i) {
0660: // if (pars.get(i).getDirection() == BaseElement.PDK_RESULT) continue;
0661: if (!isMatchingParameter(op, pars.get(i), repars.get(i),
0662: false))
0663: return false;
0664: }
0665: return true;
0666: }
0667:
0668: private static boolean matchTemplateParameter(String p1, String p2) {
0669:
0670: // looking for a pattern like
0671: // p1-> MyClass <MyTemplate>
0672: // p2-> MyClass
0673:
0674: int i = p1.indexOf("<");
0675: if (i < 0)
0676: return false;
0677:
0678: String tmp1 = p1.substring(0, i);
0679: tmp1 = tmp1.trim();
0680:
0681: int j = p2.indexOf("<");
0682: String tmp2 = p2;
0683: if (j > 0) {
0684: tmp2 = p2.substring(0, j);
0685: tmp2 = tmp2.trim();
0686: }
0687:
0688: if (tmp1.equals(tmp2))
0689: return true;
0690:
0691: return false;
0692:
0693: }
0694:
0695: protected static boolean matchCollectionParameter(
0696: IParameter parameter, IREParameter parameterRE) {
0697: boolean retVal = false;
0698:
0699: if (parameterRE.isTemplateType() == true) {
0700: ILanguage lang = parameter.getLanguages().get(0);
0701: if (parameterRE.isCollectionType(lang) == true) {
0702: CollectionInformation info = parameterRE
0703: .getCollectionTypeInfo();
0704: String paramType = parameter.getTypeName();
0705: String reParamType = info.getTypeName();
0706:
0707: if (paramType.equals(reParamType) == true) {
0708: retVal = true;
0709:
0710: IMultiplicity mult = parameter.getMultiplicity();
0711: if (info.getNumberOfRanges() == mult
0712: .getRangeCount()) {
0713: List<IMultiplicityRange> ranges = mult
0714: .getRanges();
0715: for (int index = 0; index < info
0716: .getNumberOfRanges(); index++) {
0717: String paramColType = ranges.get(index)
0718: .getCollectionType(false);
0719: String reColType = info
0720: .getCollectionForRange(index);
0721:
0722: if (paramColType.equals(reColType) == false) {
0723: retVal = false;
0724: break;
0725: }
0726: }
0727: }
0728: }
0729: }
0730: }
0731:
0732: return retVal;
0733: }
0734:
0735: /**
0736: * Determines if the IParameter @a pParameter matches the IREParameter
0737: * @a pParameterRE. The method used for detecting a match depends on
0738: * the value of @a strictMatch as follows:
0739: *
0740: * strictMatch == VARIANT_TRUE - @a pParameterRE's name must be identical
0741: * to @a pParameter's name
0742: *
0743: * strictMatch == VARIANT_FALSE - If pParameterRE's name is not identical
0744: * to @a pParameter's name, then a match is
0745: * detected if the operation that pParameter
0746: * belongs to is a setter.
0747: *
0748: * @param pOperation[in] operation that owns pParameter
0749: * @param pParameter[in] the IParameter to test for a match
0750: * @param pParameterRE[in] the IREParameter to test for a match.
0751: * @param strictMatch[in] VARIANT_TRUE if a strict match should be performed.
0752: */
0753: protected static boolean isMatchingParameter(IOperation operation,
0754: IParameter parameter, IREParameter parameterRE,
0755: boolean strictMatch) {
0756: boolean isMatching = false;
0757:
0758: IClassifier type = parameter.getType();
0759: String qualifiedParameterType = null;
0760:
0761: if (type != null) {
0762: qualifiedParameterType = type.getFullyQualifiedName(false);
0763: } else {
0764: String tmp = parameter.getTypeID();
0765: tmp = tmp.replace(".", "::");
0766: parameter.setType2(tmp);
0767: qualifiedParameterType = tmp;
0768: }
0769:
0770: String parameterType = parameter.getTypeName();
0771: String parameterTypeRE = parameterRE.getType();
0772:
0773: // If the parameters' type names are the same, that's enough to
0774: // satisfy a "strict" match. See if the qualified name or the
0775: // un-qualified name match the REParameter's name
0776: if (parameterType.equals(parameterTypeRE)
0777: || qualifiedParameterType.equals(parameterTypeRE)) {
0778: isMatching = true;
0779: } else if (matchTemplateParameter(parameterType,
0780: parameterTypeRE)) {
0781: isMatching = true;
0782: } else if (matchCollectionParameter(parameter, parameterRE) == true) {
0783: isMatching = true;
0784: } else {
0785: int pos = parameterTypeRE.lastIndexOf("::");
0786:
0787: if (pos != -1) {
0788: String name = parameterTypeRE.substring(pos + 1);
0789:
0790: if (name.equals(parameterType)) {
0791: isMatching = true;
0792: }
0793: }
0794:
0795: if (!isMatching) {
0796: // The parameters' type names were different. If the caller has
0797: // requested a non-strict match try to make the non-strict
0798: // match, otherwise, these parameters don't match.
0799: if (!strictMatch) {
0800: // Non-strict matching is, at this time, only relevent for
0801: // setters. If we make it to this point in the code and
0802: // pOperation is a setter, we consider it to be a matching
0803: // parameter.
0804: isMatching = isSetter(operation);
0805: }
0806: }
0807: }
0808:
0809: return isMatching;
0810: }
0811:
0812: /**
0813: * Determines if @a pOperation is a setter operation.
0814: */
0815: public static boolean isSetter(IOperation operation) {
0816: IAttribute attribute = getSetterAttribute(operation);
0817:
0818: return (attribute == null) ? false : true;
0819: }
0820:
0821: public static IAttribute getSetterAttribute(IOperation operation) {
0822: ETList<IDependency> supplierDependencies = operation
0823: .getSupplierDependencies();
0824:
0825: if (supplierDependencies.getCount() > 0) {
0826: IDependency supplierDependency = supplierDependencies
0827: .item(0);
0828:
0829: if (supplierDependency != null) {
0830: INamedElement clientElement = supplierDependency
0831: .getClient();
0832:
0833: if (clientElement instanceof IAttribute)
0834: return (IAttribute) clientElement;
0835: }
0836: }
0837: return null;
0838: }
0839:
0840: /**
0841: * Searches @a pREClass for an REAttribute that matches @a pAttribute and returns it if found.
0842: *
0843: * @param pREClass[in] the class that is being searched
0844: * @param pAttribute[in] the attribute used to find the matching REAttribute
0845: * @param ppREAttribute[out] the matching attribute (if found).
0846: *
0847: * @return HRESULT
0848: */
0849: private static IREAttribute findMatchingREAttribute(IREClass c,
0850: IAttribute attr) {
0851: String name = attr.getName();
0852: ETList<IREAttribute> ratts = c.getAttributes();
0853: if (ratts != null) {
0854: for (int i = 0, count = ratts.size(); i < count; ++i) {
0855: IREAttribute rat = ratts.get(i);
0856: if (rat == null)
0857: continue;
0858:
0859: if (name != null && name.equals(rat.getName()))
0860: return rat;
0861: }
0862: }
0863: return null;
0864: }
0865:
0866: /**
0867: * Finds the REClass object (nested or not) refered to by nestedClassPath
0868: *
0869: * @param pSearchOrigin[in] which class the search should start from.
0870: * If this class does not match @a nestedClassPath,
0871: * its inner classes are searched recursively for
0872: * @a nestedClassPath.
0873: *
0874: * @param nestedClassPath[] @param ppREClass[]
0875: *
0876: * @return HRESULT
0877: */
0878: private static IREClass findMatchingREClass(IREClass searchOrigin,
0879: String nestedClassPath) {
0880: if (nestedClassPath == null || nestedClassPath.length() == 0)
0881: return searchOrigin;
0882: else {
0883: ETPairT<String, String> tokS = StringUtilities.removeToken(
0884: nestedClassPath, PACKAGE_SEPARATOR);
0885: String className = tokS.getParamOne();
0886: nestedClassPath = tokS.getParamTwo();
0887:
0888: if (className != null && className.length() > 0) {
0889: ETList<IREClass> recls = searchOrigin
0890: .getAllInnerClasses();
0891: if (recls != null) {
0892: for (int i = 0, count = recls.size(); i < count; ++i) {
0893: IREClass rec = recls.get(i);
0894: if (rec == null)
0895: continue;
0896:
0897: if (className.equals(rec.getName()))
0898: return findMatchingREClass(rec,
0899: nestedClassPath);
0900: }
0901: }
0902: }
0903: }
0904: return null;
0905: }
0906:
0907: /**
0908: * returns the portion of the classifier's fully qualified name that is made up
0909: * of classifiers.
0910: *
0911: * For example:
0912: *
0913: * Let's say that class ClassC is in the following hierarchy:
0914: *
0915: * PackageA::PackageB::ClassA::ClassB::ClassC
0916: *
0917: * This operation would return:
0918: *
0919: * ClassA::ClassB::ClassC
0920: *
0921: * @param pClassifier[in] the classifier whose nested class path you want
0922: * @param nestedClassPath[out] the portion of the classifier's fully qualified
0923: * name that is made up of classifiers.
0924: *
0925: * @return HRESULT
0926: */
0927: private static String getNestedClassPath(IClassifier c) {
0928: INamespace current = c;
0929: StringBuffer path = new StringBuffer();
0930: while (current instanceof IClassifier) {
0931: // if we're looking at a class (as opposed to a package)
0932: String name = current.getName();
0933: if (path.length() > 0) {
0934: // prepend the ::
0935: path.insert(0, PACKAGE_SEPARATOR);
0936: }
0937:
0938: // prepend the name
0939: path.insert(0, name);
0940:
0941: IElement owner = current.getOwner();
0942: if (owner instanceof INamespace)
0943: current = (INamespace) owner;
0944: }
0945: return path.toString();
0946: }
0947:
0948: /**
0949: * If @a pElement is a Classifier, this method returns pElement (casted to a pClassifier).
0950: * Otherwise, the Classifier that owns @a pElement is returned.
0951: *
0952: * @param pElement[in] the element whose Classifier you want.
0953: * @param ppClassifier[out] the Classifier
0954: *
0955: * @return HRESULT
0956: */
0957: public static IClassifier getClassifierFromElement(IElement el) {
0958: if (el instanceof IClassifier)
0959: return (IClassifier) el;
0960: else if (el instanceof INavigableEnd)
0961: return ((INavigableEnd) el).getReferencingClassifier();
0962: else {
0963: IElement owner = el.getOwner();
0964: if (owner != null) {
0965: if (owner instanceof IClassifier)
0966: return (IClassifier) owner;
0967:
0968: if (owner instanceof IOperation) {
0969: IElement grandOwner = owner.getOwner();
0970: if (grandOwner instanceof IClassifier)
0971: return (IClassifier) grandOwner;
0972: }
0973: }
0974: }
0975: return null;
0976: }
0977:
0978: /**
0979: * Returns the GLOBAL ParserInformationCache object
0980: *
0981: * @param ppParseInformationCache[out] the GLOBAL ParserInformationCache
0982: *
0983: * @return HRESULT
0984: */
0985: public static IParseInformationCache getParseInformationCache() {
0986: IADProduct product = getADProduct();
0987: return product != null ? product.getParseInformationCache()
0988: : null;
0989: }
0990:
0991: /**
0992: * returns the one and only IADProduct object
0993: *
0994: * @param ppADProduct[out] the IADProduct object
0995: *
0996: * @return HRESULT
0997: */
0998: public static IADProduct getADProduct() {
0999: return (IADProduct) ProductRetriever.retrieveProduct();
1000: }
1001:
1002: /**
1003: * Returns an element's source file artifact. If the element has
1004: * multiple source files, only the first is returned.
1005: *
1006: * @param pElement[]
1007: * @param ppSourceFileArtifact[]
1008: *
1009: * @return HRESULT
1010: */
1011: public static ISourceFileArtifact getSourceFileArtifact(IElement el) {
1012: ETList<IElement> els = el.getSourceFiles();
1013: if (els != null && els.size() > 0) {
1014: IElement first = els.get(0);
1015: if (first instanceof ISourceFileArtifact)
1016: return (ISourceFileArtifact) first;
1017: }
1018: return null;
1019: }
1020:
1021: public static class SourceCodeRange {
1022: public long begin = -1, end = -1;
1023:
1024: public boolean isValid() {
1025: return begin != -1 && end != -1;
1026: }
1027: }
1028: }
|