| /******************************************************************************* |
| * Copyright (c) 2000, 2005 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.jdt.internal.core.search.matching; |
| |
| import java.util.HashMap; |
| |
| import org.eclipse.core.resources.IResource; |
| import org.eclipse.core.runtime.*; |
| import org.eclipse.jdt.core.*; |
| import org.eclipse.jdt.core.compiler.CharOperation; |
| import org.eclipse.jdt.core.search.*; |
| import org.eclipse.jdt.internal.compiler.ast.*; |
| import org.eclipse.jdt.internal.compiler.env.IBinaryType; |
| import org.eclipse.jdt.internal.compiler.lookup.*; |
| import org.eclipse.jdt.internal.core.JavaElement; |
| import org.eclipse.jdt.internal.core.util.SimpleSet; |
| |
| 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) { |
| try { |
| this.allSuperDeclaringTypeNames = |
| new SuperTypeNamesCollector( |
| this.pattern, |
| this.pattern.declaringSimpleName, |
| this.pattern.declaringQualification, |
| locator, |
| this.pattern.declaringType, |
| locator.progressMonitor).collect(); |
| } catch (JavaModelException e) { |
| // inaccurate matches will be found |
| } |
| } |
| /* |
| * Return whether a type name is in pattern all super declaring types names. |
| */ |
| 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; |
| if (this.pattern.findReferences) { |
| if (node instanceof ImportReference) { |
| // With static import, we can have static method reference in import reference |
| ImportReference importRef = (ImportReference) node; |
| int length = importRef.tokens.length-1; |
| if (importRef.isStatic() && !importRef.onDemand && matchesName(this.pattern.selector, importRef.tokens[length])) { |
| char[][] compoundName = new char[length][]; |
| System.arraycopy(importRef.tokens, 0, compoundName, 0, length); |
| char[] declaringType = CharOperation.concat(pattern.declaringQualification, pattern.declaringSimpleName, '.'); |
| if (matchesName(declaringType, CharOperation.concatWith(compoundName, '.'))) { |
| declarationsLevel = ((InternalSearchPattern)this.pattern).mustResolve ? POSSIBLE_MATCH : ACCURATE_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.selector)) 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 (!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()) { |
| if (node.typeParameters == null || node.typeParameters.length != this.pattern.methodArguments.length) return IMPOSSIBLE_MATCH; |
| } |
| |
| // Method declaration may match pattern |
| return nodeSet.addMatch(node, resolve ? POSSIBLE_MATCH : ACCURATE_MATCH); |
| } |
| public int match(MemberValuePair node, MatchingNodeSet nodeSet) { |
| if (!this.pattern.findReferences) return IMPOSSIBLE_MATCH; |
| |
| if (!matchesName(this.pattern.selector, node.name)) return IMPOSSIBLE_MATCH; |
| |
| return nodeSet.addMatch(node, ((InternalSearchPattern)this.pattern).mustResolve ? 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.shouldCountParameter() || ((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(Reference node, MatchingNodeSet nodeSet) - SKIP IT |
| public int match(Annotation node, MatchingNodeSet nodeSet) { |
| if (!this.pattern.findReferences) return IMPOSSIBLE_MATCH; |
| MemberValuePair[] pairs = node.memberValuePairs(); |
| if (pairs == null || pairs.length == 0) return IMPOSSIBLE_MATCH; |
| |
| int length = pairs.length; |
| MemberValuePair pair = null; |
| for (int i=0; i<length; i++) { |
| pair = node.memberValuePairs()[i]; |
| if (matchesName(this.pattern.selector, pair.name)) { |
| ASTNode possibleNode = (node instanceof SingleMemberAnnotation) ? (ASTNode) node : pair; |
| return nodeSet.addMatch(possibleNode, ((InternalSearchPattern)this.pattern).mustResolve ? POSSIBLE_MATCH : ACCURATE_MATCH); |
| } |
| } |
| return IMPOSSIBLE_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 CLASS_CONTAINER; |
| } |
| /* (non-Javadoc) |
| * @see org.eclipse.jdt.internal.core.search.matching.PatternLocator#matchLevelAndReportImportRef(org.eclipse.jdt.internal.compiler.ast.ImportReference, org.eclipse.jdt.internal.compiler.lookup.Binding, org.eclipse.jdt.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 { |
| if (importRef.isStatic() && binding instanceof MethodBinding) { |
| super.matchLevelAndReportImportRef(importRef, binding, locator); |
| } |
| } |
| 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) return false; |
| |
| // matches superclass |
| if (!type.isInterface() && !CharOperation.equals(type.compoundName, TypeConstants.JAVA_LANG_OBJECT)) { |
| ReferenceBinding superClass = type.superclass(); |
| if (superClass.isParameterizedType()) { |
| MethodBinding[] methods = superClass.getMethods(this.pattern.selector); |
| int length = methods.length; |
| for (int i = 0; i<length; i++) { |
| if (methods[i].areParametersEqual(method)) { |
| if (matchMethod == null) { |
| if (methodParametersEqualsPattern(methods[i].original())) return true; |
| } else { |
| if (methodsHaveSameParameters(methods[i].original(), matchMethod)) return true; |
| } |
| } |
| } |
| } |
| if (matchOverriddenMethod(superClass, method, matchMethod)) { |
| return true; |
| } |
| } |
| |
| // matches interfaces |
| ReferenceBinding[] interfaces = type.superInterfaces(); |
| if (interfaces == null) return false; |
| int iLength = interfaces.length; |
| for (int i = 0; i<iLength; i++) { |
| if (interfaces[i].isParameterizedType()) { |
| MethodBinding[] methods = interfaces[i].getMethods(this.pattern.selector); |
| int length = methods.length; |
| for (int j = 0; j<length; j++) { |
| if (methods[j].areParametersEqual(method)) { |
| if (matchMethod == null) { |
| if (methodParametersEqualsPattern(methods[j].original())) return true; |
| } else { |
| if (methodsHaveSameParameters(methods[j].original(), matchMethod)) return true; |
| } |
| } |
| } |
| } |
| if (matchOverriddenMethod(interfaces[i], method, matchMethod)) { |
| return true; |
| } |
| } |
| return false; |
| } |
| /** |
| * @see org.eclipse.jdt.internal.core.search.matching.PatternLocator#matchReportReference(org.eclipse.jdt.internal.compiler.ast.ASTNode, org.eclipse.jdt.core.IJavaElement, Binding, int, org.eclipse.jdt.internal.core.search.matching.MatchLocator) |
| */ |
| protected void matchReportReference(ASTNode reference, IJavaElement 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*/, false/*not synthetic*/, reference); |
| if (this.pattern.findReferences && reference instanceof MessageSend) { |
| IJavaElement focus = ((InternalSearchPattern) this.pattern).focus; |
| // verify closest match if pattern was bound |
| // (see bug 70827) |
| if (focus != null && focus.getElementType() == IJavaElement.METHOD) { |
| if (methodBinding != null) { |
| boolean isPrivate = Flags.isPrivate(((IMethod) 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 { |
| if (reference instanceof SingleMemberAnnotation) { |
| reference = ((SingleMemberAnnotation)reference).memberValuePairs()[0]; |
| match.setImplicit(true); |
| } |
| 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 { |
| |
| // Look if there's a need to special report for parameterized type |
| boolean isParameterized = false; |
| if (methodBinding instanceof ParameterizedGenericMethodBinding) { // parameterized generic method |
| isParameterized = true; |
| |
| // Update match regarding method type arguments |
| ParameterizedGenericMethodBinding parameterizedMethodBinding = (ParameterizedGenericMethodBinding) methodBinding; |
| match.setRaw(parameterizedMethodBinding.isRaw); |
| TypeBinding[] typeArguments = /*parameterizedMethodBinding.isRaw ? null :*/ parameterizedMethodBinding.typeArguments; |
| updateMatch(typeArguments, locator, this.pattern.methodArguments, this.pattern.hasMethodParameters()); |
| |
| // Update match regarding declaring class type arguments |
| if (methodBinding.declaringClass.isParameterizedType() || methodBinding.declaringClass.isRawType()) { |
| ParameterizedTypeBinding parameterizedBinding = (ParameterizedTypeBinding)methodBinding.declaringClass; |
| if (!this.pattern.hasTypeArguments() && this.pattern.hasMethodArguments()) { |
| // special case for pattern which defines method arguments but not its declaring type |
| // in this case, we do not refine accuracy using declaring type arguments...! |
| } else { |
| updateMatch(parameterizedBinding, this.pattern.getTypeArguments(), this.pattern.hasTypeParameters(), 0, locator); |
| } |
| } else if (this.pattern.hasTypeArguments()) { |
| match.setRule(SearchPattern.R_ERASURE_MATCH); |
| } |
| |
| // Update match regarding method parameters |
| // TODO ? (frederic) |
| |
| // Update match regarding method return type |
| // TODO ? (frederic) |
| |
| // Special case for errors |
| if (match.getRule() != 0 && messageSend.resolvedType == null) { |
| match.setRule(SearchPattern.R_ERASURE_MATCH); |
| } |
| } else if (methodBinding instanceof ParameterizedMethodBinding) { |
| isParameterized = true; |
| if (methodBinding.declaringClass.isParameterizedType() || methodBinding.declaringClass.isRawType()) { |
| ParameterizedTypeBinding parameterizedBinding = (ParameterizedTypeBinding)methodBinding.declaringClass; |
| updateMatch(parameterizedBinding, this.pattern.getTypeArguments(), this.pattern.hasTypeParameters(), 0, locator); |
| } else if (this.pattern.hasTypeArguments()) { |
| match.setRule(SearchPattern.R_ERASURE_MATCH); |
| } |
| |
| // Update match regarding method parameters |
| // TODO ? (frederic) |
| |
| // Update match regarding method return type |
| // TODO ? (frederic) |
| |
| // Special case for errors |
| if (match.getRule() != 0 && messageSend.resolvedType == null) { |
| match.setRule(SearchPattern.R_ERASURE_MATCH); |
| } |
| } else 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); |
| if (isParameterized && this.pattern.hasMethodArguments()) { |
| locator.reportAccurateParameterizedMethodReference(match, messageSend, messageSend.typeArguments); |
| } else { |
| locator.report(match); |
| } |
| } |
| /* |
| * Return whether method parameters are equals to pattern ones. |
| */ |
| private boolean methodParametersEqualsPattern(MethodBinding method) { |
| TypeBinding[] methodParameters = method.parameters; |
| |
| int length = methodParameters==null ? 0 : methodParameters.length; |
| int patternLength = this.pattern.parameterSimpleNames==null ? 0 : this.pattern.parameterSimpleNames.length; |
| if (length != patternLength) 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; |
| } |
| private boolean methodsHaveSameParameters(MethodBinding method, MethodBinding matchMethod) { |
| TypeBinding[] methodParameters = method.parameters; |
| TypeBinding[] matchMethodParameters = matchMethod.parameters; |
| |
| int length = methodParameters==null ? 0 : methodParameters.length; |
| int matchLength = matchMethodParameters==null ? 0 : matchMethodParameters.length; |
| if (length != matchLength) return false; |
| |
| for (int i = 0; i < length; i++) { |
| if (!CharOperation.equals(methodParameters[i].readableName(), matchMethodParameters[i].readableName(), this.isCaseSensitive)) { |
| return false; |
| } |
| } |
| return true; |
| } |
| public SearchMatch newDeclarationMatch(ASTNode reference, IJavaElement 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 method binding override a method in super hierarchy which original match pattern then report match |
| if (matchOverriddenMethod(methodBinding.declaringClass, methodBinding, null)) { |
| this.methodDeclarationsWithInvalidParam.put(reference, Boolean.TRUE); |
| return super.newDeclarationMatch(reference, element, elementBinding, accuracy, length, locator); |
| } |
| // If pattern binding override a method in super hierarchy which original match method binding then report match |
| MethodBinding patternBinding = locator.getMethodBinding(this.pattern); |
| if (patternBinding != null) { |
| if (matchOverriddenMethod(patternBinding.declaringClass, patternBinding, methodBinding)) { |
| 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 IJavaElement.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; |
| TypeBinding[] parameters = methodBinding.original().parameters; |
| int parameterLength = parameters.length; |
| 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); |
| } |
| IMethod method = type.getMethod(new String(bindingSelector), parameterTypes); |
| if (knownMethods.includes(method)) return; |
| |
| knownMethods.add(method); |
| IResource resource = type.getResource(); |
| boolean isBinary = type.isBinary(); |
| IBinaryType info = null; |
| if (isBinary) { |
| if (resource == null) |
| resource = type.getJavaProject().getProject(); |
| info = locator.getBinaryInfo((org.eclipse.jdt.internal.core.ClassFile)type.getClassFile(), resource); |
| locator.reportBinaryMemberDeclaration(resource, method, methodBinding, info, SearchMatch.A_ACCURATE); |
| } else { |
| if (declaringClass instanceof ParameterizedTypeBinding) |
| declaringClass = ((ParameterizedTypeBinding) declaringClass).type; |
| ClassScope scope = ((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 = (IMethod) ((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 (possibleMatchingNode instanceof SingleMemberAnnotation) { |
| SingleMemberAnnotation annotation = (SingleMemberAnnotation) possibleMatchingNode; |
| return resolveLevel(annotation.memberValuePairs()[0].binding); |
| } |
| if (possibleMatchingNode instanceof MemberValuePair) { |
| MemberValuePair memberValuePair = (MemberValuePair) possibleMatchingNode; |
| return resolveLevel(memberValuePair.binding); |
| } |
| } |
| if (this.pattern.findDeclarations) { |
| if (possibleMatchingNode instanceof MethodDeclaration) { |
| return resolveLevel(((MethodDeclaration) possibleMatchingNode).binding); |
| } |
| } |
| 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) |
| : 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 ArrayBinding)) { |
| declaringLevel = resolveLevelAsSubtype(qualifiedPattern, method.declaringClass); |
| if (declaringLevel == IMPOSSIBLE_MATCH) { |
| if (method.declaringClass == null || this.allSuperDeclaringTypeNames == null) { |
| declaringLevel = INACCURATE_MATCH; |
| } else { |
| char[][] compoundName = method.declaringClass.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 |
| } |
| } |
| } 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) { |
| if (type == null) return INACCURATE_MATCH; |
| |
| int level = resolveLevelForType(qualifiedPattern, type); |
| if (level != IMPOSSIBLE_MATCH) return level; |
| |
| // matches superclass |
| if (!type.isInterface() && !CharOperation.equals(type.compoundName, TypeConstants.JAVA_LANG_OBJECT)) { |
| level = resolveLevelAsSubtype(qualifiedPattern, type.superclass()); |
| if (level != IMPOSSIBLE_MATCH) return level; |
| } |
| |
| // matches interfaces |
| ReferenceBinding[] interfaces = type.superInterfaces(); |
| if (interfaces == null) return INACCURATE_MATCH; |
| for (int i = 0; i < interfaces.length; i++) { |
| level = resolveLevelAsSubtype(qualifiedPattern, interfaces[i]); |
| if (level != IMPOSSIBLE_MATCH) return level; |
| } |
| return IMPOSSIBLE_MATCH; |
| } |
| public String toString() { |
| return "Locator for " + this.pattern.toString(); //$NON-NLS-1$ |
| } |
| } |