/******************************************************************************* * Copyright (c) 2000, 2010 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.wst.jsdt.internal.core.search.matching; import java.util.HashMap; import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.CoreException; import org.eclipse.wst.jsdt.core.Flags; import org.eclipse.wst.jsdt.core.IFunction; import org.eclipse.wst.jsdt.core.IJavaScriptElement; import org.eclipse.wst.jsdt.core.IType; import org.eclipse.wst.jsdt.core.JavaScriptModelException; import org.eclipse.wst.jsdt.core.Signature; import org.eclipse.wst.jsdt.core.compiler.CharOperation; import org.eclipse.wst.jsdt.core.infer.InferredMethod; import org.eclipse.wst.jsdt.core.search.MethodDeclarationMatch; import org.eclipse.wst.jsdt.core.search.SearchMatch; import org.eclipse.wst.jsdt.core.search.SearchPattern; import org.eclipse.wst.jsdt.internal.compiler.ast.ASTNode; import org.eclipse.wst.jsdt.internal.compiler.ast.AbstractMethodDeclaration; import org.eclipse.wst.jsdt.internal.compiler.ast.Argument; import org.eclipse.wst.jsdt.internal.compiler.ast.ImportReference; import org.eclipse.wst.jsdt.internal.compiler.ast.MessageSend; import org.eclipse.wst.jsdt.internal.compiler.ast.MethodDeclaration; import org.eclipse.wst.jsdt.internal.compiler.ast.TypeDeclaration; import org.eclipse.wst.jsdt.internal.compiler.env.IBinaryType; import org.eclipse.wst.jsdt.internal.compiler.lookup.Binding; import org.eclipse.wst.jsdt.internal.compiler.lookup.ClassScope; import org.eclipse.wst.jsdt.internal.compiler.lookup.MethodBinding; import org.eclipse.wst.jsdt.internal.compiler.lookup.ReferenceBinding; import org.eclipse.wst.jsdt.internal.compiler.lookup.SourceTypeBinding; import org.eclipse.wst.jsdt.internal.compiler.lookup.TypeBinding; import org.eclipse.wst.jsdt.internal.compiler.lookup.TypeConstants; import org.eclipse.wst.jsdt.internal.compiler.util.SimpleSet; import org.eclipse.wst.jsdt.internal.core.JavaElement; import org.eclipse.wst.jsdt.internal.core.search.BasicSearchEngine; public class MethodLocator extends PatternLocator { protected MethodPattern pattern; protected boolean isDeclarationOfReferencedMethodsPattern; //extra reference info public char[][][] allSuperDeclaringTypeNames; //method declarations which parameters verification fail private HashMap methodDeclarationsWithInvalidParam = new HashMap(); public MethodLocator(MethodPattern pattern) { super(pattern); this.pattern = pattern; this.isDeclarationOfReferencedMethodsPattern = this.pattern instanceof DeclarationOfReferencedMethodsPattern; } /* * Clear caches */ protected void clear() { this.methodDeclarationsWithInvalidParam = new HashMap(); } public void initializePolymorphicSearch(MatchLocator locator) { long start = 0; if (BasicSearchEngine.VERBOSE) { start = System.currentTimeMillis(); } try { this.allSuperDeclaringTypeNames = new SuperTypeNamesCollector( this.pattern, this.pattern.declaringSimpleName, this.pattern.declaringQualification, locator, this.pattern.declaringType, locator.progressMonitor).collect(); } catch (JavaScriptModelException e) { // inaccurate matches will be found } if (BasicSearchEngine.VERBOSE) { System.out.println("Time to initialize polymorphic search: "+(System.currentTimeMillis()-start)); //$NON-NLS-1$ } } /* * Return whether a type name is in pattern all super declaring types names. */ private boolean isTypeInSuperDeclaringTypeNames(char[][] typeName) { if (allSuperDeclaringTypeNames == null) return false; int length = allSuperDeclaringTypeNames.length; for (int i= 0; i<length; i++) { if (CharOperation.equals(allSuperDeclaringTypeNames[i], typeName)) { return true; } } return false; } /** * Returns whether the code gen will use an invoke virtual for * this message send or not. */ protected boolean isVirtualInvoke(MethodBinding method, MessageSend messageSend) { return !method.isStatic() && !method.isPrivate() && !messageSend.isSuperAccess(); } public int match(ASTNode node, MatchingNodeSet nodeSet) { int declarationsLevel = IMPOSSIBLE_MATCH; return nodeSet.addMatch(node, declarationsLevel); } //public int match(ConstructorDeclaration node, MatchingNodeSet nodeSet) - SKIP IT //public int match(Expression node, MatchingNodeSet nodeSet) - SKIP IT //public int match(FieldDeclaration node, MatchingNodeSet nodeSet) - SKIP IT public int match(MethodDeclaration node, MatchingNodeSet nodeSet) { if (!this.pattern.findDeclarations) return IMPOSSIBLE_MATCH; // Verify method name if (!matchesName(this.pattern.selector, node.getSafeName())) return IMPOSSIBLE_MATCH; // Verify parameters types boolean resolve = ((InternalSearchPattern)this.pattern).mustResolve; if (this.pattern.parameterSimpleNames != null) { int length = this.pattern.parameterSimpleNames.length; ASTNode[] args = node.arguments; int argsLength = args == null ? 0 : args.length; if (length != argsLength) return IMPOSSIBLE_MATCH; for (int i = 0; i < argsLength; i++) { if (args != null && !matchesTypeReference(this.pattern.parameterSimpleNames[i], ((Argument) args[i]).type)) { // Do not return as impossible when source level is at least 1.5 if (this.mayBeGeneric) { if (!((InternalSearchPattern)this.pattern).mustResolve) { // Set resolution flag on node set in case of types was inferred in parameterized types from generic ones... // (see bugs https://bugs.eclipse.org/bugs/show_bug.cgi?id=79990, 96761, 96763) nodeSet.mustResolve = true; resolve = true; } this.methodDeclarationsWithInvalidParam.put(node, null); } else { return IMPOSSIBLE_MATCH; } } } } // Verify type arguments (do not reject if pattern has no argument as it can be an erasure match) if (this.pattern.hasMethodArguments()) { return IMPOSSIBLE_MATCH; } // Method declaration may match pattern return nodeSet.addMatch(node, resolve ? POSSIBLE_MATCH : ACCURATE_MATCH); } public int match(MessageSend node, MatchingNodeSet nodeSet) { if (!this.pattern.findReferences) return IMPOSSIBLE_MATCH; if (!matchesName(this.pattern.selector, node.selector)) return IMPOSSIBLE_MATCH; if (this.pattern.parameterSimpleNames != null && (!this.pattern.varargs || ((node.bits & ASTNode.InsideJavadoc) != 0))) { int length = this.pattern.parameterSimpleNames.length; ASTNode[] args = node.arguments; int argsLength = args == null ? 0 : args.length; if (length != argsLength) return IMPOSSIBLE_MATCH; } return nodeSet.addMatch(node, ((InternalSearchPattern)this.pattern).mustResolve ? POSSIBLE_MATCH : ACCURATE_MATCH); } //public int match(TypeDeclaration node, MatchingNodeSet nodeSet) - SKIP IT //public int match(TypeReference node, MatchingNodeSet nodeSet) - SKIP IT protected int matchContainer() { if (this.pattern.findReferences) { // need to look almost everywhere to find in javadocs and static import return ALL_CONTAINER; } return pattern.isFunction ? COMPILATION_UNIT_CONTAINER : CLASS_CONTAINER; } /* (non-Javadoc) * @see org.eclipse.wst.jsdt.internal.core.search.matching.PatternLocator#matchLevelAndReportImportRef(org.eclipse.wst.jsdt.internal.compiler.ast.ImportReference, org.eclipse.wst.jsdt.internal.compiler.lookup.Binding, org.eclipse.wst.jsdt.internal.core.search.matching.MatchLocator) * Accept to report match of static field on static import */ protected void matchLevelAndReportImportRef(ImportReference importRef, Binding binding, MatchLocator locator) throws CoreException { } protected int matchMethod(MethodBinding method, boolean skipImpossibleArg) { if (!matchesName(this.pattern.selector, method.selector)) return IMPOSSIBLE_MATCH; int level = ACCURATE_MATCH; // look at return type only if declaring type is not specified if (this.pattern.declaringSimpleName == null) { // TODO (frederic) use this call to refine accuracy on return type // int newLevel = resolveLevelForType(this.pattern.returnSimpleName, this.pattern.returnQualification, this.pattern.returnTypeArguments, 0, method.returnType); int newLevel = resolveLevelForType(this.pattern.returnSimpleName, this.pattern.returnQualification, method.returnType); if (level > newLevel) { if (newLevel == IMPOSSIBLE_MATCH) return IMPOSSIBLE_MATCH; level = newLevel; // can only be downgraded } } // parameter types int parameterCount = this.pattern.parameterSimpleNames == null ? -1 : this.pattern.parameterSimpleNames.length; if (parameterCount > -1) { // global verification // if (method.parameters == null) return INACCURATE_MATCH; // if (parameterCount != method.parameters.length) return IMPOSSIBLE_MATCH; // if (!method.isValidBinding() && ((ProblemMethodBinding)method).problemId() == ProblemReasons.Ambiguous) { // // return inaccurate match for ambiguous call (bug 80890) // return INACCURATE_MATCH; // } // // // verify each parameter // for (int i = 0; i < parameterCount; i++) { // TypeBinding argType = method.parameters[i]; // int newLevel = IMPOSSIBLE_MATCH; // if (argType.isMemberType()) { // // only compare source name for member type (bug 41018) // newLevel = CharOperation.match(this.pattern.parameterSimpleNames[i], argType.sourceName(), this.isCaseSensitive) // ? ACCURATE_MATCH // : IMPOSSIBLE_MATCH; // } else { // // TODO (frederic) use this call to refine accuracy on parameter types //// newLevel = resolveLevelForType(this.pattern.parameterSimpleNames[i], this.pattern.parameterQualifications[i], this.pattern.parametersTypeArguments[i], 0, argType); // newLevel = resolveLevelForType(this.pattern.parameterSimpleNames[i], this.pattern.parameterQualifications[i], argType); // } // if (level > newLevel) { // if (newLevel == IMPOSSIBLE_MATCH) { // if (skipImpossibleArg) { // // Do not consider match as impossible while finding declarations and source level >= 1.5 // // (see bugs https://bugs.eclipse.org/bugs/show_bug.cgi?id=79990, 96761, 96763) // newLevel = level; // } else { // return IMPOSSIBLE_MATCH; // } // } // level = newLevel; // can only be downgraded // } // } } return level; } private boolean matchOverriddenMethod(ReferenceBinding type, MethodBinding method, MethodBinding matchMethod) { if (type == null || this.pattern.selector == null) return false; // matches superclass if (!CharOperation.equals(type.compoundName, TypeConstants.JAVA_LANG_OBJECT)) { ReferenceBinding superClass = type.superclass(); if (matchOverriddenMethod(superClass, method, matchMethod)) { return true; } } return false; } /** * @see org.eclipse.wst.jsdt.internal.core.search.matching.PatternLocator#matchReportReference(org.eclipse.wst.jsdt.internal.compiler.ast.ASTNode, org.eclipse.wst.jsdt.core.IJavaScriptElement, Binding, int, org.eclipse.wst.jsdt.internal.core.search.matching.MatchLocator) */ protected void matchReportReference(ASTNode reference, IJavaScriptElement element, Binding elementBinding, int accuracy, MatchLocator locator) throws CoreException { MethodBinding methodBinding = (reference instanceof MessageSend) ? ((MessageSend)reference).binding: ((elementBinding instanceof MethodBinding) ? (MethodBinding) elementBinding : null); if (this.isDeclarationOfReferencedMethodsPattern) { if (methodBinding == null) return; // need exact match to be able to open on type ref if (accuracy != SearchMatch.A_ACCURATE) return; // element that references the method must be included in the enclosing element DeclarationOfReferencedMethodsPattern declPattern = (DeclarationOfReferencedMethodsPattern) this.pattern; while (element != null && !declPattern.enclosingElement.equals(element)) element = element.getParent(); if (element != null) { reportDeclaration(methodBinding, locator, declPattern.knownMethods); } } else { match = locator.newMethodReferenceMatch(element, elementBinding, accuracy, -1, -1, false /*not constructor*/, reference); if (this.pattern.findReferences && reference instanceof MessageSend) { IJavaScriptElement focus = ((InternalSearchPattern) this.pattern).focus; // verify closest match if pattern was bound // (see bug 70827) if (focus != null && focus.getElementType() == IJavaScriptElement.METHOD) { if (methodBinding != null) { boolean isPrivate = Flags.isPrivate(((IFunction) focus).getFlags()); if (isPrivate && !CharOperation.equals(methodBinding.declaringClass.sourceName, focus.getParent().getElementName().toCharArray())) { return; // finally the match was not possible } } } matchReportReference((MessageSend)reference, locator, ((MessageSend)reference).binding); } else { int offset = reference.sourceStart; int length = reference.sourceEnd - offset + 1; match.setOffset(offset); match.setLength(length); locator.report(match); } } } void matchReportReference(MessageSend messageSend, MatchLocator locator, MethodBinding methodBinding) throws CoreException { if (this.pattern.hasMethodArguments()) { // binding has no type params, compatible erasure if pattern does match.setRule(SearchPattern.R_ERASURE_MATCH); } // See whether it is necessary to report or not if (match.getRule() == 0) return; // impossible match boolean report = (this.isErasureMatch && match.isErasure()) || (this.isEquivalentMatch && match.isEquivalent()) || match.isExact(); if (!report) return; // Report match int offset = (int) (messageSend.nameSourcePosition >>> 32); match.setOffset(offset); match.setLength(messageSend.sourceEnd - offset + 1); locator.report(match); } /* * Return whether method parameters are equals to pattern ones. */ private boolean methodParametersEqualsPattern(MethodBinding method) { TypeBinding[] methodParameters = method.parameters; int length = methodParameters.length; if (length != this.pattern.parameterSimpleNames.length) return false; for (int i = 0; i < length; i++) { char[] paramQualifiedName = qualifiedPattern(this.pattern.parameterSimpleNames[i], this.pattern.parameterQualifications[i]); if (!CharOperation.match(paramQualifiedName, methodParameters[i].readableName(), this.isCaseSensitive)) { return false; } } return true; } public SearchMatch newDeclarationMatch(ASTNode reference, IJavaScriptElement element, Binding elementBinding, int accuracy, int length, MatchLocator locator) { if (elementBinding != null) { MethodBinding methodBinding = (MethodBinding) elementBinding; // If method parameters verification was not valid, then try to see if method arguments can match a method in hierarchy if (this.methodDeclarationsWithInvalidParam.containsKey(reference)) { // First see if this reference has already been resolved => report match if validated Boolean report = (Boolean) this.methodDeclarationsWithInvalidParam.get(reference); if (report != null) { if (report.booleanValue()) { return super.newDeclarationMatch(reference, element, elementBinding, accuracy, length, locator); } return null; } if (matchOverriddenMethod(methodBinding.declaringClass, methodBinding, null)) { this.methodDeclarationsWithInvalidParam.put(reference, Boolean.TRUE); return super.newDeclarationMatch(reference, element, elementBinding, accuracy, length, locator); } if (isTypeInSuperDeclaringTypeNames(methodBinding.declaringClass.compoundName)) { MethodBinding patternBinding = locator.getMethodBinding(this.pattern); if (patternBinding != null) { if (!matchOverriddenMethod(patternBinding.declaringClass, patternBinding, methodBinding)) { this.methodDeclarationsWithInvalidParam.put(reference, Boolean.FALSE); return null; } } this.methodDeclarationsWithInvalidParam.put(reference, Boolean.TRUE); return super.newDeclarationMatch(reference, element, elementBinding, accuracy, length, locator); } this.methodDeclarationsWithInvalidParam.put(reference, Boolean.FALSE); return null; } } return super.newDeclarationMatch(reference, element, elementBinding, accuracy, length, locator); } protected int referenceType() { return IJavaScriptElement.METHOD; } protected void reportDeclaration(MethodBinding methodBinding, MatchLocator locator, SimpleSet knownMethods) throws CoreException { ReferenceBinding declaringClass = methodBinding.declaringClass; IType type = locator.lookupType(declaringClass); if (type == null) return; // case of a secondary type char[] bindingSelector = methodBinding.selector; boolean isBinary = type.isBinary(); IFunction method = null; TypeBinding[] parameters = methodBinding.original().parameters; int parameterLength = parameters.length; // if (isBinary) { // char[][] parameterTypes = new char[parameterLength][]; // for (int i = 0; i<parameterLength; i++) { // char[] typeName = parameters[i].qualifiedSourceName(); // for (int j=0, dim=parameters[i].dimensions(); j<dim; j++) { // typeName = CharOperation.concat(typeName, new char[] {'[', ']'}); // } // parameterTypes[i] = typeName; // } // method = locator.createBinaryMethodHandle(type, methodBinding.selector, parameterTypes); // } else { String[] parameterTypes = new String[parameterLength]; for (int i = 0; i < parameterLength; i++) { char[] typeName = parameters[i].shortReadableName(); if (parameters[i].isMemberType()) { typeName = CharOperation.subarray(typeName, CharOperation.indexOf('.', typeName)+1, typeName.length); } parameterTypes[i] = Signature.createTypeSignature(typeName, false); } method = type.getFunction(new String(bindingSelector), parameterTypes); // } if (method == null || knownMethods.addIfNotIncluded(method) == null) return; IResource resource = type.getResource(); IBinaryType info = null; if (isBinary) { if (resource == null) resource = type.getJavaScriptProject().getProject(); info = locator.getBinaryInfo((org.eclipse.wst.jsdt.internal.core.ClassFile)type.getClassFile(), resource); locator.reportBinaryMemberDeclaration(resource, method, methodBinding, info, SearchMatch.A_ACCURATE); } else { ClassScope scope = (ClassScope)((SourceTypeBinding) declaringClass).scope; if (scope != null) { TypeDeclaration typeDecl = scope.referenceContext; AbstractMethodDeclaration methodDecl = null; AbstractMethodDeclaration[] methodDecls = typeDecl.methods; for (int i = 0, length = methodDecls.length; i < length; i++) { if (CharOperation.equals(bindingSelector, methodDecls[i].selector)) { methodDecl = methodDecls[i]; break; } } if (methodDecl != null) { int offset = methodDecl.sourceStart; Binding binding = methodDecl.binding; if (binding != null) method = (IFunction) ((JavaElement) method).resolved(binding); match = new MethodDeclarationMatch(method, SearchMatch.A_ACCURATE, offset, methodDecl.sourceEnd-offset+1, locator.getParticipant(), resource); locator.report(match); } } } } public int resolveLevel(ASTNode possibleMatchingNode) { if (this.pattern.findReferences) { if (possibleMatchingNode instanceof MessageSend) { return resolveLevel((MessageSend) possibleMatchingNode); } } if (this.pattern.findDeclarations) { if (possibleMatchingNode instanceof MethodDeclaration) { return resolveLevel(((MethodDeclaration) possibleMatchingNode).binding); } else if (possibleMatchingNode instanceof InferredMethod) return resolveLevel(((InferredMethod) possibleMatchingNode).methodBinding); } return IMPOSSIBLE_MATCH; } public int resolveLevel(Binding binding) { if (binding == null) return INACCURATE_MATCH; if (!(binding instanceof MethodBinding)) return IMPOSSIBLE_MATCH; MethodBinding method = (MethodBinding) binding; boolean skipVerif = this.pattern.findDeclarations && this.mayBeGeneric; int methodLevel = matchMethod(method, skipVerif); if (methodLevel == IMPOSSIBLE_MATCH) { if (method != method.original()) methodLevel = matchMethod(method.original(), skipVerif); if (methodLevel == IMPOSSIBLE_MATCH) { return IMPOSSIBLE_MATCH; } else { method = method.original(); } } // declaring type char[] qualifiedPattern = qualifiedPattern(this.pattern.declaringSimpleName, this.pattern.declaringQualification); if (qualifiedPattern == null) return methodLevel; // since any declaring class will do boolean subType = !method.isStatic() && !method.isPrivate(); if (subType && this.pattern.declaringQualification != null && method.declaringClass != null && method.declaringClass.fPackage != null) { subType = CharOperation.compareWith(this.pattern.declaringQualification, method.declaringClass.fPackage.shortReadableName()) == 0; } int declaringLevel = subType ? resolveLevelAsSubtype(qualifiedPattern, method.declaringClass, null) : resolveLevelForType(qualifiedPattern, method.declaringClass); return methodLevel > declaringLevel ? declaringLevel : methodLevel; // return the weaker match } protected int resolveLevel(MessageSend messageSend) { MethodBinding method = messageSend.binding; if (method == null) { return INACCURATE_MATCH; } if (messageSend.resolvedType == null) { // Closest match may have different argument numbers when ProblemReason is NotFound // see MessageSend#resolveType(BlockScope) // see bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=97322 int argLength = messageSend.arguments == null ? 0 : messageSend.arguments.length; if (pattern.parameterSimpleNames == null || argLength == pattern.parameterSimpleNames.length) { return INACCURATE_MATCH; } return IMPOSSIBLE_MATCH; } int methodLevel = matchMethod(method, false); if (methodLevel == IMPOSSIBLE_MATCH) { if (method != method.original()) methodLevel = matchMethod(method.original(), false); if (methodLevel == IMPOSSIBLE_MATCH) return IMPOSSIBLE_MATCH; method = method.original(); } // receiver type char[] qualifiedPattern = qualifiedPattern(this.pattern.declaringSimpleName, this.pattern.declaringQualification); if (qualifiedPattern == null) return methodLevel; // since any declaring class will do int declaringLevel; if (isVirtualInvoke(method, messageSend) && (messageSend.actualReceiverType instanceof ReferenceBinding)) { ReferenceBinding methodReceiverType = (ReferenceBinding) messageSend.actualReceiverType; declaringLevel = resolveLevelAsSubtype(qualifiedPattern, methodReceiverType, method.parameters); if (declaringLevel == IMPOSSIBLE_MATCH) { if (method.declaringClass == null || this.allSuperDeclaringTypeNames == null) { declaringLevel = INACCURATE_MATCH; } else { char[][] compoundName = methodReceiverType.compoundName; for (int i = 0, max = this.allSuperDeclaringTypeNames.length; i < max; i++) { if (CharOperation.equals(this.allSuperDeclaringTypeNames[i], compoundName)) { return methodLevel // since this is an ACCURATE_MATCH so return the possibly weaker match | SUPER_INVOCATION_FLAVOR; // this is an overridden method => add flavor to returned level } } /* Do not return interfaces potential matches * @see "https://bugs.eclipse.org/bugs/show_bug.cgi?id=157814#c8" if (methodReceiverType.isInterface()) { // all methods interface with same name and parameters are potential matches // see bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=156491 return INACCURATE_MATCH | POLYMORPHIC_FLAVOR; } */ } } if ((declaringLevel & FLAVORS_MASK) != 0) { // level got some flavors => return it return declaringLevel; } } else { declaringLevel = resolveLevelForType(qualifiedPattern, method.declaringClass); } return methodLevel > declaringLevel ? declaringLevel : methodLevel; // return the weaker match } /** * Returns whether the given reference type binding matches or is a subtype of a type * that matches the given qualified pattern. * Returns ACCURATE_MATCH if it does. * Returns INACCURATE_MATCH if resolve fails * Returns IMPOSSIBLE_MATCH if it doesn't. */ protected int resolveLevelAsSubtype(char[] qualifiedPattern, ReferenceBinding type, TypeBinding[] argumentTypes) { if (type == null) return INACCURATE_MATCH; int level = resolveLevelForType(qualifiedPattern, type); if (level != IMPOSSIBLE_MATCH) { level |= OVERRIDDEN_METHOD_FLAVOR; return level; } // matches superclass if (!CharOperation.equals(type.compoundName, TypeConstants.JAVA_LANG_OBJECT)) { level = resolveLevelAsSubtype(qualifiedPattern, type.superclass(), argumentTypes); if (level != IMPOSSIBLE_MATCH) { if (argumentTypes != null) { // need to verify if method may be overridden MethodBinding[] methods = type.getMethods(this.pattern.selector); for (int i=0, length=methods.length; i<length; i++) { MethodBinding method = methods[i]; TypeBinding[] parameters = method.parameters; if (argumentTypes.length == parameters.length) { boolean found = true; for (int j=0,l=parameters.length; j<l; j++) { if (parameters[j] != argumentTypes[j]) { found = false; break; } } if (found) { // one method match in hierarchy if ((level & OVERRIDDEN_METHOD_FLAVOR) != 0) { // this method is already overridden on a super class, current match is impossible return IMPOSSIBLE_MATCH; } if (!method.isAbstract()) { // store the fact that the method is overridden level |= OVERRIDDEN_METHOD_FLAVOR; } } } } } return level | SUB_INVOCATION_FLAVOR; // add flavor to returned level } } return INACCURATE_MATCH; } public String toString() { return "Locator for " + this.pattern.toString(); //$NON-NLS-1$ } public int match(InferredMethod inferredMethod, MatchingNodeSet nodeSet) { if (!this.pattern.findDeclarations) return IMPOSSIBLE_MATCH; // Verify method name if (!matchesName(this.pattern.selector, inferredMethod.name)) return IMPOSSIBLE_MATCH; // Verify parameters types boolean resolve = ((InternalSearchPattern)this.pattern).mustResolve; if (this.pattern.parameterSimpleNames != null) { int length = this.pattern.parameterSimpleNames.length; ASTNode[] args = ((AbstractMethodDeclaration)inferredMethod.getFunctionDeclaration()).arguments; int argsLength = args == null ? 0 : args.length; if (length != argsLength) return IMPOSSIBLE_MATCH; for (int i = 0; i < argsLength; i++) { if (args != null && !matchesTypeReference(this.pattern.parameterSimpleNames[i], ((Argument) args[i]).type)) { // Do not return as impossible when source level is at least 1.5 if (this.mayBeGeneric) { if (!((InternalSearchPattern)this.pattern).mustResolve) { // Set resolution flag on node set in case of types was inferred in parameterized types from generic ones... // (see bugs https://bugs.eclipse.org/bugs/show_bug.cgi?id=79990, 96761, 96763) nodeSet.mustResolve = true; resolve = true; } this.methodDeclarationsWithInvalidParam.put((AbstractMethodDeclaration)inferredMethod.getFunctionDeclaration(), null); } else { return IMPOSSIBLE_MATCH; } } } } // Verify type arguments (do not reject if pattern has no argument as it can be an erasure match) // if (this.pattern.hasMethodArguments()) { // if (node.typeParameters == null || node.typeParameters.length != this.pattern.methodArguments.length) return IMPOSSIBLE_MATCH; // } // Method declaration may match pattern return nodeSet.addMatch(inferredMethod , resolve ? POSSIBLE_MATCH : ACCURATE_MATCH); } }