0001: /*******************************************************************************
0002: * Copyright (c) 2000, 2007 IBM Corporation and others.
0003: * All rights reserved. This program and the accompanying materials
0004: * are made available under the terms of the Eclipse Public License v1.0
0005: * which accompanies this distribution, and is available at
0006: * http://www.eclipse.org/legal/epl-v10.html
0007: *
0008: * Contributors:
0009: * IBM Corporation - initial API and implementation
0010: *******************************************************************************/package org.eclipse.jdt.internal.core.search.matching;
0011:
0012: import org.eclipse.core.runtime.*;
0013: import org.eclipse.jdt.core.*;
0014: import org.eclipse.jdt.core.compiler.*;
0015: import org.eclipse.jdt.core.search.*;
0016: import org.eclipse.jdt.internal.compiler.ast.*;
0017: import org.eclipse.jdt.internal.compiler.lookup.*;
0018: import org.eclipse.jdt.internal.core.search.indexing.IIndexConstants;
0019:
0020: public abstract class PatternLocator implements IIndexConstants {
0021:
0022: // store pattern info
0023: protected int matchMode;
0024: protected boolean isCaseSensitive;
0025: protected boolean isCamelCase;
0026: protected boolean isEquivalentMatch;
0027: protected boolean isErasureMatch;
0028: protected boolean mustResolve;
0029: protected boolean mayBeGeneric;
0030:
0031: // match to report
0032: SearchMatch match = null;
0033:
0034: /* match levels */
0035: public static final int IMPOSSIBLE_MATCH = 0;
0036: public static final int INACCURATE_MATCH = 1;
0037: public static final int POSSIBLE_MATCH = 2;
0038: public static final int ACCURATE_MATCH = 3;
0039: public static final int ERASURE_MATCH = 4;
0040:
0041: // Possible rule match flavors
0042: // see bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=79866
0043: public static final int EXACT_FLAVOR = 0x0010;
0044: public static final int PREFIX_FLAVOR = 0x0020;
0045: public static final int PATTERN_FLAVOR = 0x0040;
0046: public static final int REGEXP_FLAVOR = 0x0080;
0047: public static final int CAMELCASE_FLAVOR = 0x0100;
0048: public static final int SUPER_INVOCATION_FLAVOR = 0x0200;
0049: public static final int SUB_INVOCATION_FLAVOR = 0x0400;
0050: public static final int OVERRIDDEN_METHOD_FLAVOR = 0x0800;
0051: public static final int MATCH_LEVEL_MASK = 0x0F;
0052: public static final int FLAVORS_MASK = ~MATCH_LEVEL_MASK;
0053:
0054: /* match container */
0055: public static final int COMPILATION_UNIT_CONTAINER = 1;
0056: public static final int CLASS_CONTAINER = 2;
0057: public static final int METHOD_CONTAINER = 4;
0058: public static final int FIELD_CONTAINER = 8;
0059: public static final int ALL_CONTAINER = COMPILATION_UNIT_CONTAINER
0060: | CLASS_CONTAINER | METHOD_CONTAINER | FIELD_CONTAINER;
0061:
0062: /* match rule */
0063: public static final int RAW_MASK = SearchPattern.R_EQUIVALENT_MATCH
0064: | SearchPattern.R_ERASURE_MATCH;
0065: public static final int RULE_MASK = RAW_MASK; // no other values for the while...
0066:
0067: public static PatternLocator patternLocator(SearchPattern pattern) {
0068: switch (((InternalSearchPattern) pattern).kind) {
0069: case IIndexConstants.PKG_REF_PATTERN:
0070: return new PackageReferenceLocator(
0071: (PackageReferencePattern) pattern);
0072: case IIndexConstants.PKG_DECL_PATTERN:
0073: return new PackageDeclarationLocator(
0074: (PackageDeclarationPattern) pattern);
0075: case IIndexConstants.TYPE_REF_PATTERN:
0076: return new TypeReferenceLocator(
0077: (TypeReferencePattern) pattern);
0078: case IIndexConstants.TYPE_DECL_PATTERN:
0079: return new TypeDeclarationLocator(
0080: (TypeDeclarationPattern) pattern);
0081: case IIndexConstants.SUPER_REF_PATTERN:
0082: return new SuperTypeReferenceLocator(
0083: (SuperTypeReferencePattern) pattern);
0084: case IIndexConstants.CONSTRUCTOR_PATTERN:
0085: return new ConstructorLocator((ConstructorPattern) pattern);
0086: case IIndexConstants.FIELD_PATTERN:
0087: return new FieldLocator((FieldPattern) pattern);
0088: case IIndexConstants.METHOD_PATTERN:
0089: return new MethodLocator((MethodPattern) pattern);
0090: case IIndexConstants.OR_PATTERN:
0091: return new OrLocator((OrPattern) pattern);
0092: case IIndexConstants.AND_PATTERN:
0093: return new AndLocator((AndPattern) pattern);
0094: case IIndexConstants.LOCAL_VAR_PATTERN:
0095: return new LocalVariableLocator(
0096: (LocalVariablePattern) pattern);
0097: case IIndexConstants.TYPE_PARAM_PATTERN:
0098: return new TypeParameterLocator(
0099: (TypeParameterPattern) pattern);
0100: }
0101: return null;
0102: }
0103:
0104: public static char[] qualifiedPattern(char[] simpleNamePattern,
0105: char[] qualificationPattern) {
0106: // NOTE: if case insensitive search then simpleNamePattern & qualificationPattern are assumed to be lowercase
0107: if (simpleNamePattern == null) {
0108: if (qualificationPattern == null)
0109: return null;
0110: return CharOperation.concat(qualificationPattern, ONE_STAR,
0111: '.');
0112: } else {
0113: return qualificationPattern == null ? CharOperation.concat(
0114: ONE_STAR, simpleNamePattern) : CharOperation
0115: .concat(qualificationPattern, simpleNamePattern,
0116: '.');
0117: }
0118: }
0119:
0120: public static char[] qualifiedSourceName(TypeBinding binding) {
0121: if (binding instanceof ReferenceBinding) {
0122: ReferenceBinding type = (ReferenceBinding) binding;
0123: if (type.isLocalType())
0124: return type.isMemberType() ? CharOperation.concat(
0125: qualifiedSourceName(type.enclosingType()), type
0126: .sourceName(), '.') : CharOperation
0127: .concat(qualifiedSourceName(type
0128: .enclosingType()), new char[] { '.',
0129: '1', '.' }, type.sourceName());
0130: }
0131: return binding != null ? binding.qualifiedSourceName() : null;
0132: }
0133:
0134: public PatternLocator(SearchPattern pattern) {
0135: int matchRule = pattern.getMatchRule();
0136: this .isCaseSensitive = (matchRule & SearchPattern.R_CASE_SENSITIVE) != 0;
0137: this .isCamelCase = (matchRule & SearchPattern.R_CAMEL_CASE_MATCH) != 0;
0138: this .isErasureMatch = (matchRule & SearchPattern.R_ERASURE_MATCH) != 0;
0139: this .isEquivalentMatch = (matchRule & SearchPattern.R_EQUIVALENT_MATCH) != 0;
0140: this .matchMode = matchRule & JavaSearchPattern.MATCH_MODE_MASK;
0141: this .mustResolve = ((InternalSearchPattern) pattern).mustResolve;
0142: }
0143:
0144: /*
0145: * Clear caches
0146: */
0147: protected void clear() {
0148: // nothing to clear by default
0149: }
0150:
0151: /* (non-Javadoc)
0152: * Modify PatternLocator.qualifiedPattern behavior:
0153: * do not add star before simple name pattern when qualification pattern is null.
0154: * This avoid to match p.X when pattern is only X...
0155: */
0156: protected char[] getQualifiedPattern(char[] simpleNamePattern,
0157: char[] qualificationPattern) {
0158: // NOTE: if case insensitive search then simpleNamePattern & qualificationPattern are assumed to be lowercase
0159: if (simpleNamePattern == null) {
0160: if (qualificationPattern == null)
0161: return null;
0162: return CharOperation.concat(qualificationPattern, ONE_STAR,
0163: '.');
0164: } else if (qualificationPattern == null) {
0165: return simpleNamePattern;
0166: } else {
0167: return CharOperation.concat(qualificationPattern,
0168: simpleNamePattern, '.');
0169: }
0170: }
0171:
0172: /* (non-Javadoc)
0173: * Modify PatternLocator.qualifiedSourceName behavior:
0174: * also concatene enclosing type name when type is a only a member type.
0175: */
0176: protected char[] getQualifiedSourceName(TypeBinding binding) {
0177: TypeBinding type = binding instanceof ArrayBinding ? ((ArrayBinding) binding).leafComponentType
0178: : binding;
0179: if (type instanceof ReferenceBinding) {
0180: if (type.isLocalType()) {
0181: return CharOperation.concat(qualifiedSourceName(type
0182: .enclosingType()),
0183: new char[] { '.', '1', '.' }, binding
0184: .sourceName());
0185: } else if (type.isMemberType()) {
0186: return CharOperation.concat(qualifiedSourceName(type
0187: .enclosingType()), binding.sourceName(), '.');
0188: }
0189: }
0190: return binding != null ? binding.qualifiedSourceName() : null;
0191: }
0192:
0193: /*
0194: * Get binding of type argument from a class unit scope and its index position.
0195: * Cache is lazy initialized and if no binding is found, then store a problem binding
0196: * to avoid making research twice...
0197: */
0198: protected TypeBinding getTypeNameBinding(int index) {
0199: return null;
0200: }
0201:
0202: /**
0203: * Initializes this search pattern so that polymorphic search can be performed.
0204: */
0205: public void initializePolymorphicSearch(MatchLocator locator) {
0206: // default is to do nothing
0207: }
0208:
0209: public int match(Annotation node, MatchingNodeSet nodeSet) {
0210: // each subtype should override if needed
0211: return IMPOSSIBLE_MATCH;
0212: }
0213:
0214: /**
0215: * Check if the given ast node syntactically matches this pattern.
0216: * If it does, add it to the match set.
0217: * Returns the match level.
0218: */
0219: public int match(ASTNode node, MatchingNodeSet nodeSet) { // needed for some generic nodes
0220: // each subtype should override if needed
0221: return IMPOSSIBLE_MATCH;
0222: }
0223:
0224: public int match(ConstructorDeclaration node,
0225: MatchingNodeSet nodeSet) {
0226: // each subtype should override if needed
0227: return IMPOSSIBLE_MATCH;
0228: }
0229:
0230: public int match(Expression node, MatchingNodeSet nodeSet) {
0231: // each subtype should override if needed
0232: return IMPOSSIBLE_MATCH;
0233: }
0234:
0235: public int match(FieldDeclaration node, MatchingNodeSet nodeSet) {
0236: // each subtype should override if needed
0237: return IMPOSSIBLE_MATCH;
0238: }
0239:
0240: public int match(LocalDeclaration node, MatchingNodeSet nodeSet) {
0241: // each subtype should override if needed
0242: return IMPOSSIBLE_MATCH;
0243: }
0244:
0245: public int match(MethodDeclaration node, MatchingNodeSet nodeSet) {
0246: // each subtype should override if needed
0247: return IMPOSSIBLE_MATCH;
0248: }
0249:
0250: public int match(MemberValuePair node, MatchingNodeSet nodeSet) {
0251: // each subtype should override if needed
0252: return IMPOSSIBLE_MATCH;
0253: }
0254:
0255: public int match(MessageSend node, MatchingNodeSet nodeSet) {
0256: // each subtype should override if needed
0257: return IMPOSSIBLE_MATCH;
0258: }
0259:
0260: public int match(Reference node, MatchingNodeSet nodeSet) {
0261: // each subtype should override if needed
0262: return IMPOSSIBLE_MATCH;
0263: }
0264:
0265: public int match(TypeDeclaration node, MatchingNodeSet nodeSet) {
0266: // each subtype should override if needed
0267: return IMPOSSIBLE_MATCH;
0268: }
0269:
0270: public int match(TypeParameter node, MatchingNodeSet nodeSet) {
0271: // each subtype should override if needed
0272: return IMPOSSIBLE_MATCH;
0273: }
0274:
0275: public int match(TypeReference node, MatchingNodeSet nodeSet) {
0276: // each subtype should override if needed
0277: return IMPOSSIBLE_MATCH;
0278: }
0279:
0280: /**
0281: * Returns the type(s) of container for this pattern.
0282: * It is a bit combination of types, denoting compilation unit, class declarations, field declarations or method declarations.
0283: */
0284: protected int matchContainer() {
0285: // override if the pattern can be more specific
0286: return ALL_CONTAINER;
0287: }
0288:
0289: /**
0290: * Returns whether the given name matches the given pattern.
0291: */
0292: protected boolean matchesName(char[] pattern, char[] name) {
0293: if (pattern == null)
0294: return true; // null is as if it was "*"
0295: if (name == null)
0296: return false; // cannot match null name
0297: return matchNameValue(pattern, name) != IMPOSSIBLE_MATCH;
0298: }
0299:
0300: /**
0301: * Return how the given name matches the given pattern.
0302: * @see "https://bugs.eclipse.org/bugs/show_bug.cgi?id=79866"
0303: *
0304: * @param pattern
0305: * @param name
0306: * @return Possible values are:
0307: * <ul>
0308: * <li> {@link #ACCURATE_MATCH}</li>
0309: * <li> {@link #IMPOSSIBLE_MATCH}</li>
0310: * <li> {@link #POSSIBLE_MATCH} which may be flavored with following values:
0311: * <ul>
0312: * <li>{@link #EXACT_FLAVOR}: Given name is equals to pattern</li>
0313: * <li>{@link #PREFIX_FLAVOR}: Given name prefix equals to pattern</li>
0314: * <li>{@link #CAMELCASE_FLAVOR}: Given name matches pattern as Camel Case</li>
0315: * <li>{@link #PATTERN_FLAVOR}: Given name matches pattern as Pattern (ie. using '*' and '?' characters)</li>
0316: * </ul>
0317: * </li>
0318: * </ul>
0319: */
0320: protected int matchNameValue(char[] pattern, char[] name) {
0321: if (pattern == null)
0322: return ACCURATE_MATCH; // null is as if it was "*"
0323: if (name == null)
0324: return IMPOSSIBLE_MATCH; // cannot match null name
0325: if (name.length == 0) { // empty name
0326: if (pattern.length == 0) { // can only matches empty pattern
0327: return ACCURATE_MATCH;
0328: }
0329: return IMPOSSIBLE_MATCH;
0330: } else if (pattern.length == 0) {
0331: return IMPOSSIBLE_MATCH; // need to have both name and pattern length==0 to be accurate
0332: }
0333: boolean matchFirstChar = !this .isCaseSensitive
0334: || pattern[0] == name[0];
0335: boolean sameLength = pattern.length == name.length;
0336: boolean canBePrefix = name.length >= pattern.length;
0337: if (this .isCamelCase) {
0338: if (matchFirstChar
0339: && CharOperation
0340: .camelCaseMatch(
0341: pattern,
0342: name,
0343: (this .matchMode & SearchPattern.R_PREFIX_MATCH) != 0)) {
0344: return POSSIBLE_MATCH;
0345: }
0346: if (this .isCaseSensitive)
0347: return IMPOSSIBLE_MATCH;
0348: }
0349: switch (this .matchMode) {
0350: case SearchPattern.R_EXACT_MATCH:
0351: if (sameLength
0352: && matchFirstChar
0353: && CharOperation.equals(pattern, name,
0354: this .isCaseSensitive)) {
0355: return POSSIBLE_MATCH | EXACT_FLAVOR;
0356: }
0357: break;
0358: case SearchPattern.R_PREFIX_MATCH:
0359: if (canBePrefix
0360: && matchFirstChar
0361: && CharOperation.prefixEquals(pattern, name,
0362: this .isCaseSensitive)) {
0363: return POSSIBLE_MATCH;
0364: }
0365: break;
0366: case SearchPattern.R_PATTERN_MATCH:
0367: // TODO_PERFS (frederic) Not sure this lowercase is necessary
0368: if (!this .isCaseSensitive) {
0369: pattern = CharOperation.toLowerCase(pattern);
0370: }
0371: if (CharOperation
0372: .match(pattern, name, this .isCaseSensitive)) {
0373: return POSSIBLE_MATCH;
0374: }
0375: break;
0376: case SearchPattern.R_REGEXP_MATCH:
0377: // TODO (frederic) implement regular expression match
0378: break;
0379: }
0380: return IMPOSSIBLE_MATCH;
0381: }
0382:
0383: /**
0384: * Returns whether the given type reference matches the given pattern.
0385: */
0386: protected boolean matchesTypeReference(char[] pattern,
0387: TypeReference type) {
0388: if (pattern == null)
0389: return true; // null is as if it was "*"
0390: if (type == null)
0391: return true; // treat as an inexact match
0392:
0393: char[][] compoundName = type.getTypeName();
0394: char[] simpleName = compoundName[compoundName.length - 1];
0395: int dimensions = type.dimensions() * 2;
0396: if (dimensions > 0) {
0397: int length = simpleName.length;
0398: char[] result = new char[length + dimensions];
0399: System.arraycopy(simpleName, 0, result, 0, length);
0400: for (int i = length, l = result.length; i < l;) {
0401: result[i++] = '[';
0402: result[i++] = ']';
0403: }
0404: simpleName = result;
0405: }
0406:
0407: return matchesName(pattern, simpleName);
0408: }
0409:
0410: /**
0411: * Returns the match level for the given importRef.
0412: */
0413: protected int matchLevel(ImportReference importRef) {
0414: // override if interested in import references which are caught by the generic version of match(ASTNode, MatchingNodeSet)
0415: return IMPOSSIBLE_MATCH;
0416: }
0417:
0418: /**
0419: * Reports the match of the given import reference if the resolveLevel is high enough.
0420: */
0421: protected void matchLevelAndReportImportRef(
0422: ImportReference importRef, Binding binding,
0423: MatchLocator locator) throws CoreException {
0424: int level = resolveLevel(binding);
0425: if (level >= INACCURATE_MATCH) {
0426: matchReportImportRef(importRef, binding, locator
0427: .createImportHandle(importRef),
0428: level == ACCURATE_MATCH ? SearchMatch.A_ACCURATE
0429: : SearchMatch.A_INACCURATE, locator);
0430: }
0431: }
0432:
0433: /**
0434: * Reports the match of the given import reference.
0435: */
0436: protected void matchReportImportRef(ImportReference importRef,
0437: Binding binding, IJavaElement element, int accuracy,
0438: MatchLocator locator) throws CoreException {
0439: if (locator.encloses(element)) {
0440: // default is to report a match as a regular ref.
0441: this .matchReportReference(importRef, element,
0442: null/*no binding*/, accuracy, locator);
0443: }
0444: }
0445:
0446: /**
0447: * Reports the match of the given reference.
0448: */
0449: protected void matchReportReference(ASTNode reference,
0450: IJavaElement element, Binding elementBinding, int accuracy,
0451: MatchLocator locator) throws CoreException {
0452: match = null;
0453: int referenceType = referenceType();
0454: int offset = reference.sourceStart;
0455: switch (referenceType) {
0456: case IJavaElement.PACKAGE_FRAGMENT:
0457: match = locator
0458: .newPackageReferenceMatch(element, accuracy,
0459: offset, reference.sourceEnd - offset + 1,
0460: reference);
0461: break;
0462: case IJavaElement.TYPE:
0463: match = locator.newTypeReferenceMatch(element,
0464: elementBinding, accuracy, offset,
0465: reference.sourceEnd - offset + 1, reference);
0466: break;
0467: case IJavaElement.FIELD:
0468: match = locator.newFieldReferenceMatch(element,
0469: elementBinding, accuracy, offset,
0470: reference.sourceEnd - offset + 1, reference);
0471: break;
0472: case IJavaElement.LOCAL_VARIABLE:
0473: match = locator.newLocalVariableReferenceMatch(element,
0474: accuracy, offset, reference.sourceEnd - offset + 1,
0475: reference);
0476: break;
0477: case IJavaElement.TYPE_PARAMETER:
0478: match = locator.newTypeParameterReferenceMatch(element,
0479: accuracy, offset, reference.sourceEnd - offset + 1,
0480: reference);
0481: break;
0482: }
0483: if (match != null) {
0484: locator.report(match);
0485: }
0486: }
0487:
0488: /**
0489: * Reports the match of the given reference. Also provide a local element to eventually report in match.
0490: */
0491: protected void matchReportReference(ASTNode reference,
0492: IJavaElement element, IJavaElement localElement,
0493: IJavaElement[] otherElements, Binding elementBinding,
0494: int accuracy, MatchLocator locator) throws CoreException {
0495: matchReportReference(reference, element, elementBinding,
0496: accuracy, locator);
0497: }
0498:
0499: /**
0500: * Reports the match of the given reference. Also provide a scope to look for potential other elements.
0501: */
0502: protected void matchReportReference(ASTNode reference,
0503: IJavaElement element, Binding elementBinding, Scope scope,
0504: int accuracy, MatchLocator locator) throws CoreException {
0505: matchReportReference(reference, element, elementBinding,
0506: accuracy, locator);
0507: }
0508:
0509: public SearchMatch newDeclarationMatch(ASTNode reference,
0510: IJavaElement element, Binding elementBinding, int accuracy,
0511: int length, MatchLocator locator) {
0512: return locator.newDeclarationMatch(element, elementBinding,
0513: accuracy, reference.sourceStart, length);
0514: }
0515:
0516: protected int referenceType() {
0517: return 0; // defaults to unknown (a generic JavaSearchMatch will be created)
0518: }
0519:
0520: /**
0521: * Finds out whether the given ast node matches this search pattern.
0522: * Returns IMPOSSIBLE_MATCH if it doesn't.
0523: * Returns INACCURATE_MATCH if it potentially matches this search pattern (ie.
0524: * it has already been resolved but resolving failed.)
0525: * Returns ACCURATE_MATCH if it matches exactly this search pattern (ie.
0526: * it doesn't need to be resolved or it has already been resolved.)
0527: */
0528: public int resolveLevel(ASTNode possibleMatchingNode) {
0529: // only called with nodes which were possible matches to the call to matchLevel
0530: // need to do instance of checks to find out exact type of ASTNode
0531: return IMPOSSIBLE_MATCH;
0532: }
0533:
0534: /*
0535: * Update pattern locator match for parameterized top level types.
0536: * Set match raw flag and recurse to enclosing types if any...
0537: */
0538: protected void updateMatch(
0539: ParameterizedTypeBinding parameterizedBinding,
0540: char[][][] patternTypeArguments, MatchLocator locator) {
0541: // Only possible if locator has an unit scope.
0542: if (locator.unitScope != null) {
0543: updateMatch(parameterizedBinding, patternTypeArguments,
0544: false, 0, locator);
0545: }
0546: }
0547:
0548: protected void updateMatch(
0549: ParameterizedTypeBinding parameterizedBinding,
0550: char[][][] patternTypeArguments,
0551: boolean patternHasTypeParameters, int depth,
0552: MatchLocator locator) {
0553: // Only possible if locator has an unit scope.
0554: if (locator.unitScope == null)
0555: return;
0556:
0557: // Set match raw flag
0558: boolean endPattern = patternTypeArguments == null ? true
0559: : depth >= patternTypeArguments.length;
0560: TypeBinding[] argumentsBindings = parameterizedBinding.arguments;
0561: boolean isRaw = parameterizedBinding.isRawType()
0562: || (argumentsBindings == null && parameterizedBinding
0563: .genericType().isGenericType());
0564: if (isRaw && !match.isRaw()) {
0565: match.setRaw(isRaw);
0566: }
0567:
0568: // Update match
0569: if (!endPattern && patternTypeArguments != null) {
0570: // verify if this is a reference to the generic type itself
0571: if (!isRaw && patternHasTypeParameters
0572: && argumentsBindings != null) {
0573: boolean needUpdate = false;
0574: TypeVariableBinding[] typeVariables = parameterizedBinding
0575: .genericType().typeVariables();
0576: for (int i = 0, l = argumentsBindings.length; i < l; i++) {
0577: if (argumentsBindings[i] != typeVariables[i]) {
0578: needUpdate = true;
0579: break;
0580: }
0581: }
0582: if (needUpdate) {
0583: char[][] patternArguments = patternTypeArguments[depth];
0584: updateMatch(argumentsBindings, locator,
0585: patternArguments, patternHasTypeParameters);
0586: }
0587: } else {
0588: char[][] patternArguments = patternTypeArguments[depth];
0589: updateMatch(argumentsBindings, locator,
0590: patternArguments, patternHasTypeParameters);
0591: }
0592: }
0593:
0594: // Recurse
0595: TypeBinding enclosingType = parameterizedBinding
0596: .enclosingType();
0597: if (enclosingType != null
0598: && (enclosingType.isParameterizedType() || enclosingType
0599: .isRawType())) {
0600: updateMatch((ParameterizedTypeBinding) enclosingType,
0601: patternTypeArguments, patternHasTypeParameters,
0602: depth + 1, locator);
0603: }
0604: }
0605:
0606: /*
0607: * Update pattern locator match comparing type arguments with pattern ones.
0608: * Try to resolve pattern and look for compatibility with type arguments
0609: * to set match rule.
0610: */
0611: protected void updateMatch(TypeBinding[] argumentsBinding,
0612: MatchLocator locator, char[][] patternArguments,
0613: boolean hasTypeParameters) {
0614: // Only possible if locator has an unit scope.
0615: if (locator.unitScope == null)
0616: return;
0617:
0618: // First compare lengthes
0619: int patternTypeArgsLength = patternArguments == null ? 0
0620: : patternArguments.length;
0621: int typeArgumentsLength = argumentsBinding == null ? 0
0622: : argumentsBinding.length;
0623:
0624: // Initialize match rule
0625: int matchRule = match.getRule();
0626: if (match.isRaw()) {
0627: if (patternTypeArgsLength != 0) {
0628: matchRule &= ~SearchPattern.R_FULL_MATCH;
0629: }
0630: }
0631: if (hasTypeParameters) {
0632: matchRule = SearchPattern.R_ERASURE_MATCH;
0633: }
0634:
0635: // Compare arguments lengthes
0636: if (patternTypeArgsLength == typeArgumentsLength) {
0637: if (!match.isRaw() && hasTypeParameters) {
0638: // generic patterns are always not compatible match
0639: match.setRule(SearchPattern.R_ERASURE_MATCH);
0640: return;
0641: }
0642: } else {
0643: if (patternTypeArgsLength == 0) {
0644: if (!match.isRaw() || hasTypeParameters) {
0645: match.setRule(matchRule
0646: & ~SearchPattern.R_FULL_MATCH);
0647: }
0648: } else if (typeArgumentsLength == 0) {
0649: // raw binding is always compatible
0650: match.setRule(matchRule & ~SearchPattern.R_FULL_MATCH);
0651: } else {
0652: match.setRule(0); // impossible match
0653: }
0654: return;
0655: }
0656: if (argumentsBinding == null || patternArguments == null) {
0657: match.setRule(matchRule);
0658: return;
0659: }
0660:
0661: // Compare binding for each type argument only if pattern is not erasure only and at first level
0662: if (!hasTypeParameters && !match.isRaw()
0663: && (match.isEquivalent() || match.isExact())) {
0664: for (int i = 0; i < typeArgumentsLength; i++) {
0665: // Get parameterized type argument binding
0666: TypeBinding argumentBinding = argumentsBinding[i];
0667: if (argumentBinding instanceof CaptureBinding) {
0668: WildcardBinding capturedWildcard = ((CaptureBinding) argumentBinding).wildcard;
0669: if (capturedWildcard != null)
0670: argumentBinding = capturedWildcard;
0671: }
0672: // Get binding for pattern argument
0673: char[] patternTypeArgument = patternArguments[i];
0674: char patternWildcard = patternTypeArgument[0];
0675: char[] patternTypeName = patternTypeArgument;
0676: int patternWildcardKind = -1;
0677: switch (patternWildcard) {
0678: case Signature.C_STAR:
0679: if (argumentBinding.isWildcard()) {
0680: WildcardBinding wildcardBinding = (WildcardBinding) argumentBinding;
0681: if (wildcardBinding.boundKind == Wildcard.UNBOUND)
0682: continue;
0683: }
0684: matchRule &= ~SearchPattern.R_FULL_MATCH;
0685: continue; // unbound parameter always match
0686: case Signature.C_EXTENDS:
0687: patternWildcardKind = Wildcard.EXTENDS;
0688: patternTypeName = CharOperation.subarray(
0689: patternTypeArgument, 1,
0690: patternTypeArgument.length);
0691: break;
0692: case Signature.C_SUPER:
0693: patternWildcardKind = Wildcard.SUPER;
0694: patternTypeName = CharOperation.subarray(
0695: patternTypeArgument, 1,
0696: patternTypeArgument.length);
0697: default:
0698: break;
0699: }
0700: patternTypeName = Signature
0701: .toCharArray(patternTypeName);
0702: TypeBinding patternBinding = locator.getType(
0703: patternTypeArgument, patternTypeName);
0704:
0705: // If have no binding for pattern arg, then we won't be able to refine accuracy
0706: if (patternBinding == null) {
0707: if (argumentBinding.isWildcard()) {
0708: WildcardBinding wildcardBinding = (WildcardBinding) argumentBinding;
0709: if (wildcardBinding.boundKind == Wildcard.UNBOUND) {
0710: matchRule &= ~SearchPattern.R_FULL_MATCH;
0711: } else {
0712: match
0713: .setRule(SearchPattern.R_ERASURE_MATCH);
0714: return;
0715: }
0716: }
0717: continue;
0718: }
0719:
0720: // Verify tha pattern binding is compatible with match type argument binding
0721: switch (patternWildcard) {
0722: case Signature.C_STAR: // UNBOUND pattern
0723: // unbound always match => skip to next argument
0724: matchRule &= ~SearchPattern.R_FULL_MATCH;
0725: continue;
0726: case Signature.C_EXTENDS: // EXTENDS pattern
0727: if (argumentBinding.isWildcard()) { // argument is a wildcard
0728: WildcardBinding wildcardBinding = (WildcardBinding) argumentBinding;
0729: // It's ok if wildcards are identical
0730: if (wildcardBinding.boundKind == patternWildcardKind
0731: && wildcardBinding.bound == patternBinding) {
0732: continue;
0733: }
0734: // Look for wildcard compatibility
0735: switch (wildcardBinding.boundKind) {
0736: case Wildcard.EXTENDS:
0737: if (wildcardBinding.bound == null
0738: || wildcardBinding.bound
0739: .isCompatibleWith(patternBinding)) {
0740: // valid when arg extends a subclass of pattern
0741: matchRule &= ~SearchPattern.R_FULL_MATCH;
0742: continue;
0743: }
0744: break;
0745: case Wildcard.SUPER:
0746: break;
0747: case Wildcard.UNBOUND:
0748: matchRule &= ~SearchPattern.R_FULL_MATCH;
0749: continue;
0750: }
0751: } else if (argumentBinding
0752: .isCompatibleWith(patternBinding)) {
0753: // valid when arg is a subclass of pattern
0754: matchRule &= ~SearchPattern.R_FULL_MATCH;
0755: continue;
0756: }
0757: break;
0758: case Signature.C_SUPER: // SUPER pattern
0759: if (argumentBinding.isWildcard()) { // argument is a wildcard
0760: WildcardBinding wildcardBinding = (WildcardBinding) argumentBinding;
0761: // It's ok if wildcards are identical
0762: if (wildcardBinding.boundKind == patternWildcardKind
0763: && wildcardBinding.bound == patternBinding) {
0764: continue;
0765: }
0766: // Look for wildcard compatibility
0767: switch (wildcardBinding.boundKind) {
0768: case Wildcard.EXTENDS:
0769: break;
0770: case Wildcard.SUPER:
0771: if (wildcardBinding.bound == null
0772: || patternBinding
0773: .isCompatibleWith(wildcardBinding.bound)) {
0774: // valid only when arg super a superclass of pattern
0775: matchRule &= ~SearchPattern.R_FULL_MATCH;
0776: continue;
0777: }
0778: break;
0779: case Wildcard.UNBOUND:
0780: matchRule &= ~SearchPattern.R_FULL_MATCH;
0781: continue;
0782: }
0783: } else if (patternBinding
0784: .isCompatibleWith(argumentBinding)) {
0785: // valid only when arg is a superclass of pattern
0786: matchRule &= ~SearchPattern.R_FULL_MATCH;
0787: continue;
0788: }
0789: break;
0790: default:
0791: if (argumentBinding.isWildcard()) {
0792: WildcardBinding wildcardBinding = (WildcardBinding) argumentBinding;
0793: switch (wildcardBinding.boundKind) {
0794: case Wildcard.EXTENDS:
0795: if (wildcardBinding.bound == null
0796: || patternBinding
0797: .isCompatibleWith(wildcardBinding.bound)) {
0798: // valid only when arg extends a superclass of pattern
0799: matchRule &= ~SearchPattern.R_FULL_MATCH;
0800: continue;
0801: }
0802: break;
0803: case Wildcard.SUPER:
0804: if (wildcardBinding.bound == null
0805: || wildcardBinding.bound
0806: .isCompatibleWith(patternBinding)) {
0807: // valid only when arg super a subclass of pattern
0808: matchRule &= ~SearchPattern.R_FULL_MATCH;
0809: continue;
0810: }
0811: break;
0812: case Wildcard.UNBOUND:
0813: matchRule &= ~SearchPattern.R_FULL_MATCH;
0814: continue;
0815: }
0816: } else if (argumentBinding == patternBinding)
0817: // valid only when arg is equals to pattern
0818: continue;
0819: break;
0820: }
0821:
0822: // Argument does not match => erasure match will be the only possible one
0823: match.setRule(SearchPattern.R_ERASURE_MATCH);
0824: return;
0825: }
0826: }
0827:
0828: // Set match rule
0829: match.setRule(matchRule);
0830: }
0831:
0832: /**
0833: * Finds out whether the given binding matches this search pattern.
0834: * Returns ACCURATE_MATCH if it does.
0835: * Returns INACCURATE_MATCH if resolve failed but match is still possible.
0836: * Returns IMPOSSIBLE_MATCH otherwise.
0837: * Default is to return INACCURATE_MATCH.
0838: */
0839: public int resolveLevel(Binding binding) {
0840: // override if the pattern can match the binding
0841: return INACCURATE_MATCH;
0842: }
0843:
0844: /**
0845: * Returns whether the given type binding matches the given simple name pattern
0846: * and qualification pattern.
0847: * Note that from since 3.1, this method resolve to accurate member or local types
0848: * even if they are not fully qualified (ie. X.Member instead of p.X.Member).
0849: * Returns ACCURATE_MATCH if it does.
0850: * Returns INACCURATE_MATCH if resolve failed.
0851: * Returns IMPOSSIBLE_MATCH if it doesn't.
0852: */
0853: protected int resolveLevelForType(char[] simpleNamePattern,
0854: char[] qualificationPattern, TypeBinding binding) {
0855: // return resolveLevelForType(qualifiedPattern(simpleNamePattern, qualificationPattern), type);
0856: char[] qualifiedPattern = getQualifiedPattern(
0857: simpleNamePattern, qualificationPattern);
0858: int level = resolveLevelForType(qualifiedPattern, binding);
0859: if (level == ACCURATE_MATCH || binding == null)
0860: return level;
0861: TypeBinding type = binding instanceof ArrayBinding ? ((ArrayBinding) binding).leafComponentType
0862: : binding;
0863: char[] sourceName = null;
0864: if (type.isMemberType() || type.isLocalType()) {
0865: if (qualificationPattern != null) {
0866: sourceName = getQualifiedSourceName(binding);
0867: } else {
0868: sourceName = binding.sourceName();
0869: }
0870: } else if (qualificationPattern == null) {
0871: sourceName = getQualifiedSourceName(binding);
0872: }
0873: if (sourceName == null)
0874: return IMPOSSIBLE_MATCH;
0875: if ((this .matchMode & SearchPattern.R_PREFIX_MATCH) != 0) {
0876: if (CharOperation.prefixEquals(qualifiedPattern,
0877: sourceName, this .isCaseSensitive)) {
0878: return ACCURATE_MATCH;
0879: }
0880: }
0881: if (this .isCamelCase) {
0882: if ((qualifiedPattern.length > 0 && sourceName.length > 0 && qualifiedPattern[0] == sourceName[0])) {
0883: if (CharOperation
0884: .camelCaseMatch(
0885: qualifiedPattern,
0886: sourceName,
0887: (this .matchMode & SearchPattern.R_PREFIX_MATCH) != 0)) {
0888: return ACCURATE_MATCH;
0889: }
0890: }
0891: if (!this .isCaseSensitive
0892: && this .matchMode == SearchPattern.R_EXACT_MATCH) {
0893: boolean matchPattern = CharOperation.equals(
0894: qualifiedPattern, sourceName, false);
0895: return matchPattern ? ACCURATE_MATCH : IMPOSSIBLE_MATCH;
0896: }
0897: }
0898: boolean matchPattern = CharOperation.match(qualifiedPattern,
0899: sourceName, this .isCaseSensitive);
0900: return matchPattern ? ACCURATE_MATCH : IMPOSSIBLE_MATCH;
0901:
0902: }
0903:
0904: /**
0905: * Returns whether the given type binding matches the given qualified pattern.
0906: * Returns ACCURATE_MATCH if it does.
0907: * Returns INACCURATE_MATCH if resolve failed.
0908: * Returns IMPOSSIBLE_MATCH if it doesn't.
0909: */
0910: protected int resolveLevelForType(char[] qualifiedPattern,
0911: TypeBinding type) {
0912: if (qualifiedPattern == null)
0913: return ACCURATE_MATCH;
0914: if (type == null)
0915: return INACCURATE_MATCH;
0916:
0917: // Type variable cannot be specified through pattern => this kind of binding cannot match it (see bug 79803)
0918: if (type.isTypeVariable())
0919: return IMPOSSIBLE_MATCH;
0920:
0921: // NOTE: if case insensitive search then qualifiedPattern is assumed to be lowercase
0922:
0923: char[] qualifiedPackageName = type.qualifiedPackageName();
0924: char[] qualifiedSourceName = qualifiedSourceName(type);
0925: char[] fullyQualifiedTypeName = qualifiedPackageName.length == 0 ? qualifiedSourceName
0926: : CharOperation.concat(qualifiedPackageName,
0927: qualifiedSourceName, '.');
0928: return CharOperation.match(qualifiedPattern,
0929: fullyQualifiedTypeName, this .isCaseSensitive) ? ACCURATE_MATCH
0930: : IMPOSSIBLE_MATCH;
0931: }
0932:
0933: /* (non-Javadoc)
0934: * Resolve level for type with a given binding with all pattern information.
0935: */
0936: protected int resolveLevelForType(char[] simpleNamePattern,
0937: char[] qualificationPattern,
0938: char[][][] patternTypeArguments, int depth, TypeBinding type) {
0939: // standard search with no generic additional information must succeed
0940: int level = resolveLevelForType(simpleNamePattern,
0941: qualificationPattern, type);
0942: if (level == IMPOSSIBLE_MATCH)
0943: return IMPOSSIBLE_MATCH;
0944: if (type == null || patternTypeArguments == null
0945: || patternTypeArguments.length == 0
0946: || depth >= patternTypeArguments.length) {
0947: return level;
0948: }
0949:
0950: // if pattern is erasure match (see bug 79790), commute impossible to erasure
0951: int impossible = this .isErasureMatch ? ERASURE_MATCH
0952: : IMPOSSIBLE_MATCH;
0953:
0954: // pattern has type parameter(s) or type argument(s)
0955: if (type.isGenericType()) {
0956: // Binding is generic, get its type variable(s)
0957: TypeVariableBinding[] typeVariables = null;
0958: if (type instanceof SourceTypeBinding) {
0959: SourceTypeBinding sourceTypeBinding = (SourceTypeBinding) type;
0960: typeVariables = sourceTypeBinding.typeVariables;
0961: } else if (type instanceof BinaryTypeBinding) {
0962: BinaryTypeBinding binaryTypeBinding = (BinaryTypeBinding) type;
0963: if (this .mustResolve)
0964: typeVariables = binaryTypeBinding.typeVariables(); // TODO (frederic) verify performance
0965: }
0966: if (patternTypeArguments[depth] != null
0967: && patternTypeArguments[depth].length > 0
0968: && typeVariables != null
0969: && typeVariables.length > 0) {
0970: if (typeVariables.length != patternTypeArguments[depth].length)
0971: return IMPOSSIBLE_MATCH;
0972: }
0973: // TODO (frederic) do we need to verify each parameter?
0974: return level; // we can't do better
0975: } else if (type.isRawType()) {
0976: return level; // raw type always match
0977: } else {
0978: TypeBinding leafType = type.leafComponentType();
0979: if (!leafType.isParameterizedType()) {
0980: // Standard types (ie. neither generic nor parameterized nor raw types)
0981: // cannot match pattern with type parameters or arguments
0982: return (patternTypeArguments[depth] == null || patternTypeArguments[depth].length == 0) ? level
0983: : IMPOSSIBLE_MATCH;
0984: }
0985: ParameterizedTypeBinding paramTypeBinding = (ParameterizedTypeBinding) leafType;
0986:
0987: // Compare arguments only if there ones on both sides
0988: if (patternTypeArguments[depth] != null
0989: && patternTypeArguments[depth].length > 0
0990: && paramTypeBinding.arguments != null
0991: && paramTypeBinding.arguments.length > 0) {
0992:
0993: // type parameters length must match at least specified type names length
0994: int length = patternTypeArguments[depth].length;
0995: if (paramTypeBinding.arguments.length != length)
0996: return IMPOSSIBLE_MATCH;
0997:
0998: // verify each pattern type parameter
0999: nextTypeArgument: for (int i = 0; i < length; i++) {
1000: char[] patternTypeArgument = patternTypeArguments[depth][i];
1001: TypeBinding argTypeBinding = paramTypeBinding.arguments[i];
1002: // get corresponding pattern wildcard
1003: switch (patternTypeArgument[0]) {
1004: case Signature.C_STAR: // unbound parameter always match
1005: case Signature.C_SUPER: // needs pattern type parameter binding
1006: // skip to next type argument as it will be resolved later
1007: continue nextTypeArgument;
1008: case Signature.C_EXTENDS:
1009: // remove wildcard from patter type argument
1010: patternTypeArgument = CharOperation.subarray(
1011: patternTypeArgument, 1,
1012: patternTypeArgument.length);
1013: default:
1014: // no wildcard
1015: break;
1016: }
1017: // get pattern type argument from its signature
1018: patternTypeArgument = Signature
1019: .toCharArray(patternTypeArgument);
1020: if (!this .isCaseSensitive)
1021: patternTypeArgument = CharOperation
1022: .toLowerCase(patternTypeArgument);
1023: boolean patternTypeArgHasAnyChars = CharOperation
1024: .contains(new char[] { '*', '?' },
1025: patternTypeArgument);
1026:
1027: // Verify that names match...
1028: // ...special case for wildcard
1029: if (argTypeBinding instanceof CaptureBinding) {
1030: WildcardBinding capturedWildcard = ((CaptureBinding) argTypeBinding).wildcard;
1031: if (capturedWildcard != null)
1032: argTypeBinding = capturedWildcard;
1033: }
1034: if (argTypeBinding.isWildcard()) {
1035: WildcardBinding wildcardBinding = (WildcardBinding) argTypeBinding;
1036: switch (wildcardBinding.boundKind) {
1037: case Wildcard.EXTENDS:
1038: // Invalid if type argument is not exact
1039: if (patternTypeArgHasAnyChars)
1040: return impossible;
1041: case Wildcard.UNBOUND:
1042: // there's no bound name to match => valid
1043: continue nextTypeArgument;
1044: }
1045: // Look if bound name match pattern type argument
1046: ReferenceBinding boundBinding = (ReferenceBinding) wildcardBinding.bound;
1047: if (CharOperation.match(patternTypeArgument,
1048: boundBinding.shortReadableName(),
1049: this .isCaseSensitive)
1050: || CharOperation.match(
1051: patternTypeArgument,
1052: boundBinding.readableName(),
1053: this .isCaseSensitive)) {
1054: // found name in hierarchy => match
1055: continue nextTypeArgument;
1056: }
1057:
1058: // If pattern is not exact then match fails
1059: if (patternTypeArgHasAnyChars)
1060: return impossible;
1061:
1062: // Look for bound name in type argument superclasses
1063: boundBinding = boundBinding.super class();
1064: while (boundBinding != null) {
1065: if (CharOperation.equals(
1066: patternTypeArgument, boundBinding
1067: .shortReadableName(),
1068: this .isCaseSensitive)
1069: || CharOperation
1070: .equals(
1071: patternTypeArgument,
1072: boundBinding
1073: .readableName(),
1074: this .isCaseSensitive)) {
1075: // found name in hierarchy => match
1076: continue nextTypeArgument;
1077: } else if (boundBinding.isLocalType()
1078: || boundBinding.isMemberType()) {
1079: // for local or member type, verify also source name (bug 81084)
1080: if (CharOperation.match(
1081: patternTypeArgument,
1082: boundBinding.sourceName(),
1083: this .isCaseSensitive))
1084: continue nextTypeArgument;
1085: }
1086: boundBinding = boundBinding.super class();
1087: }
1088: return impossible;
1089: }
1090:
1091: // See if names match
1092: if (CharOperation.match(patternTypeArgument,
1093: argTypeBinding.shortReadableName(),
1094: this .isCaseSensitive)
1095: || CharOperation.match(patternTypeArgument,
1096: argTypeBinding.readableName(),
1097: this .isCaseSensitive)) {
1098: continue nextTypeArgument;
1099: } else if (argTypeBinding.isLocalType()
1100: || argTypeBinding.isMemberType()) {
1101: // for local or member type, verify also source name (bug 81084)
1102: if (CharOperation.match(patternTypeArgument,
1103: argTypeBinding.sourceName(),
1104: this .isCaseSensitive))
1105: continue nextTypeArgument;
1106: }
1107:
1108: // If pattern is not exact then match fails
1109: if (patternTypeArgHasAnyChars)
1110: return impossible;
1111:
1112: // Scan hierarchy
1113: TypeBinding leafTypeBinding = argTypeBinding
1114: .leafComponentType();
1115: if (leafTypeBinding.isBaseType())
1116: return impossible;
1117: ReferenceBinding refBinding = ((ReferenceBinding) leafTypeBinding)
1118: .super class();
1119: while (refBinding != null) {
1120: if (CharOperation.equals(patternTypeArgument,
1121: refBinding.shortReadableName(),
1122: this .isCaseSensitive)
1123: || CharOperation.equals(
1124: patternTypeArgument, refBinding
1125: .readableName(),
1126: this .isCaseSensitive)) {
1127: // found name in hierarchy => match
1128: continue nextTypeArgument;
1129: } else if (refBinding.isLocalType()
1130: || refBinding.isMemberType()) {
1131: // for local or member type, verify also source name (bug 81084)
1132: if (CharOperation.match(
1133: patternTypeArgument, refBinding
1134: .sourceName(),
1135: this .isCaseSensitive))
1136: continue nextTypeArgument;
1137: }
1138: refBinding = refBinding.super class();
1139: }
1140: return impossible;
1141: }
1142: }
1143:
1144: // Recurse on enclosing type
1145: TypeBinding enclosingType = paramTypeBinding
1146: .enclosingType();
1147: if (enclosingType != null
1148: && enclosingType.isParameterizedType()
1149: && depth < patternTypeArguments.length
1150: && qualificationPattern != null) {
1151: int lastDot = CharOperation.lastIndexOf('.',
1152: qualificationPattern);
1153: char[] enclosingQualificationPattern = lastDot == -1 ? null
1154: : CharOperation.subarray(qualificationPattern,
1155: 0, lastDot);
1156: char[] enclosingSimpleNamePattern = lastDot == -1 ? qualificationPattern
1157: : CharOperation.subarray(qualificationPattern,
1158: lastDot + 1,
1159: qualificationPattern.length);
1160: int enclosingLevel = resolveLevelForType(
1161: enclosingSimpleNamePattern,
1162: enclosingQualificationPattern,
1163: patternTypeArguments, depth + 1, enclosingType);
1164: if (enclosingLevel == impossible)
1165: return impossible;
1166: if (enclosingLevel == IMPOSSIBLE_MATCH)
1167: return IMPOSSIBLE_MATCH;
1168: }
1169: return level;
1170: }
1171: }
1172:
1173: public String toString() {
1174: return "SearchPattern"; //$NON-NLS-1$
1175: }
1176: }
|