/*******************************************************************************
* Copyright (c) 2000, 2009 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.CoreException;
import org.eclipse.jdt.core.Flags;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IMethod;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.Signature;
import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.core.search.MethodDeclarationMatch;
import org.eclipse.jdt.core.search.MethodReferenceMatch;
import org.eclipse.jdt.core.search.SearchMatch;
import org.eclipse.jdt.core.search.SearchPattern;
import org.eclipse.jdt.internal.compiler.ast.ASTNode;
import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
import org.eclipse.jdt.internal.compiler.ast.Annotation;
import org.eclipse.jdt.internal.compiler.ast.Argument;
import org.eclipse.jdt.internal.compiler.ast.ImportReference;
import org.eclipse.jdt.internal.compiler.ast.MemberValuePair;
import org.eclipse.jdt.internal.compiler.ast.MessageSend;
import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration;
import org.eclipse.jdt.internal.compiler.ast.SingleMemberAnnotation;
import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
import org.eclipse.jdt.internal.compiler.env.IBinaryType;
import org.eclipse.jdt.internal.compiler.lookup.Binding;
import org.eclipse.jdt.internal.compiler.lookup.ClassScope;
import org.eclipse.jdt.internal.compiler.lookup.MethodBinding;
import org.eclipse.jdt.internal.compiler.lookup.ParameterizedGenericMethodBinding;
import org.eclipse.jdt.internal.compiler.lookup.ParameterizedMethodBinding;
import org.eclipse.jdt.internal.compiler.lookup.ParameterizedTypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.ProblemMethodBinding;
import org.eclipse.jdt.internal.compiler.lookup.ProblemReasons;
import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
import org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.TypeConstants;
import org.eclipse.jdt.internal.compiler.util.SimpleSet;
import org.eclipse.jdt.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();
}
protected int fineGrain() {
return this.pattern.fineGrain;
}
private MethodBinding getMethodBinding(ReferenceBinding type, TypeBinding[] argumentTypes) {
MethodBinding[] methods= type.getMethods(this.pattern.selector);
MethodBinding method= null;
methodsLoop: for (int i= 0, length= methods.length; i < length; i++) {
method= methods[i];
TypeBinding[] parameters= method.parameters;
if (argumentTypes.length == parameters.length) {
for (int j= 0, l= parameters.length; j < l; j++) {
if (parameters[j].erasure() != argumentTypes[j].erasure()) {
continue methodsLoop;
}
}
return method;
}
}
return null;
}
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 (JavaModelException 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 (this.allSuperDeclaringTypeNames == null)
return false;
int length= this.allSuperDeclaringTypeNames.length;
for (int i= 0; i < length; i++) {
if (CharOperation.equals(this.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.bits & ASTNode.OnDemand) == 0) && matchesName(this.pattern.selector, importRef.tokens[length])) {
char[][] compoundName= new char[length][];
System.arraycopy(importRef.tokens, 0, compoundName, 0, length);
char[] declaringType= CharOperation.concat(this.pattern.declaringQualification, this.pattern.declaringSimpleName, '.');
if (matchesName(declaringType, CharOperation.concatWith(compoundName, '.'))) {
declarationsLevel= 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= 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 (!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, 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.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, 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, 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 || this.pattern.selector == 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 (methods[i].original().areParametersEqual(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 (methods[j].original().areParametersEqual(matchMethod))
return true;
}
}
}
}
if (matchOverriddenMethod(interfaces[i], method, matchMethod)) {
return true;
}
}
return false;
}
protected void matchReportReference(ASTNode reference, IJavaElement element, Binding elementBinding, int accuracy, MatchLocator locator) throws CoreException {
matchReportReference(reference, element, null, null, elementBinding, accuracy, locator);
}
/**
* @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, IJavaElement localElement, IJavaElement[] otherElements, 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 {
MethodReferenceMatch methodReferenceMatch= locator.newMethodReferenceMatch(element, elementBinding, accuracy, -1, -1, false /*not constructor*/, false/*not synthetic*/, reference);
methodReferenceMatch.setLocalElement(localElement);
this.match= methodReferenceMatch;
if (this.pattern.findReferences && reference instanceof MessageSend) {
IJavaElement focus= this.pattern.focus;
// verify closest match if pattern was bound
// (see bug 70827)
if (focus != null && focus.getElementType() == IJavaElement.METHOD) {
if (methodBinding != null && methodBinding.declaringClass != 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];
this.match.setImplicit(true);
}
int offset= reference.sourceStart;
int length= reference.sourceEnd - offset + 1;
this.match.setOffset(offset);
this.match.setLength(length);
locator.report(this.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;
this.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() || parameterizedBinding.isParameterizedWithOwnVariables()) {
// 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()) {
this.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 (this.match.getRule() != 0 && messageSend.resolvedType == null) {
this.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;
if (!parameterizedBinding.isParameterizedWithOwnVariables()) {
updateMatch(parameterizedBinding, this.pattern.getTypeArguments(), this.pattern.hasTypeParameters(), 0, locator);
}
} else if (this.pattern.hasTypeArguments()) {
this.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 (this.match.getRule() != 0 && messageSend.resolvedType == null) {
this.match.setRule(SearchPattern.R_ERASURE_MATCH);
}
} else if (this.pattern.hasMethodArguments()) { // binding has no type params, compatible erasure if pattern does
this.match.setRule(SearchPattern.R_ERASURE_MATCH);
}
// See whether it is necessary to report or not
if (this.match.getRule() == 0)
return; // impossible match
boolean report= (this.isErasureMatch && this.match.isErasure()) || (this.isEquivalentMatch && this.match.isEquivalent()) || this.match.isExact();
if (!report)
return;
// Report match
int offset= (int)(messageSend.nameSourcePosition >>> 32);
this.match.setOffset(offset);
this.match.setLength(messageSend.sourceEnd - offset + 1);
if (isParameterized && this.pattern.hasMethodArguments()) {
locator.reportAccurateParameterizedMethodReference(this.match, messageSend, messageSend.typeArguments);
} else {
locator.report(this.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, 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 (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 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
// Report match for binary
if (type.isBinary()) {
IMethod method= null;
TypeBinding[] parameters= methodBinding.original().parameters;
int parameterLength= parameters.length;
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);
if (method == null || knownMethods.addIfNotIncluded(method) == null)
return;
IResource resource= type.getResource();
if (resource == null)
resource= type.getJavaProject().getProject();
IBinaryType info= locator.getBinaryInfo((org.eclipse.jdt.internal.core.ClassFile)type.getClassFile(), resource);
locator.reportBinaryMemberDeclaration(resource, method, methodBinding, info, SearchMatch.A_ACCURATE);
return;
}
// When source is available, report match if method is found in the declaring type
IResource resource= type.getResource();
if (declaringClass instanceof ParameterizedTypeBinding)
declaringClass= ((ParameterizedTypeBinding)declaringClass).genericType();
ClassScope scope= ((SourceTypeBinding)declaringClass).scope;
if (scope != null) {
TypeDeclaration typeDecl= scope.referenceContext;
AbstractMethodDeclaration methodDecl= typeDecl.declarationOf(methodBinding.original());
if (methodDecl != null) {
// Create method handle from method declaration
String methodName= new String(methodBinding.selector);
Argument[] arguments= methodDecl.arguments;
int length= arguments == null ? 0 : arguments.length;
String[] parameterTypes= new String[length];
for (int i= 0; i < length; i++) {
char[][] typeName= arguments[i].type.getParameterizedTypeName();
parameterTypes[i]= Signature.createTypeSignature(CharOperation.concatWith(typeName, '.'), false);
}
IMethod method= type.getMethod(methodName, parameterTypes);
if (method == null || knownMethods.addIfNotIncluded(method) == null)
return;
// Create and report corresponding match
int offset= methodDecl.sourceStart;
this.match= new MethodDeclarationMatch(method, SearchMatch.A_ACCURATE, offset, methodDecl.sourceEnd - offset + 1, locator.getParticipant(), resource);
locator.report(this.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, 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 (this.pattern.parameterSimpleNames == null || argLength == this.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 {
if (resolveLevelAsSuperInvocation(methodReceiverType, method.parameters, true)) {
declaringLevel= 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
}
}
}
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) {
MethodBinding method= argumentTypes == null ? null : getMethodBinding(type, argumentTypes);
if (((method != null && !method.isAbstract()) || !type.isAbstract()) && !type.isInterface()) { // if concrete, then method is overridden
level|= OVERRIDDEN_METHOD_FLAVOR;
}
return level;
}
// matches superclass
if (!type.isInterface() && !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 method= getMethodBinding(type, argumentTypes);
if (method != null) { // 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() && !type.isInterface()) {
// store the fact that the method is overridden
level|= OVERRIDDEN_METHOD_FLAVOR;
}
}
}
return level | SUB_INVOCATION_FLAVOR; // add flavor to returned 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], null);
if (level != IMPOSSIBLE_MATCH) {
if (!type.isAbstract() && !type.isInterface()) { // if concrete class, then method is overridden
level|= OVERRIDDEN_METHOD_FLAVOR;
}
return level | SUB_INVOCATION_FLAVOR; // add flavor to returned level
}
}
return IMPOSSIBLE_MATCH;
}
/*
* Return whether the given type binding or one of its possible super interfaces
* matches a type in the declaring type names hierarchy.
*/
private boolean resolveLevelAsSuperInvocation(ReferenceBinding type, TypeBinding[] argumentTypes, boolean methodAlreadyVerified) {
char[][] compoundName= type.compoundName;
for (int i= 0, max= this.allSuperDeclaringTypeNames.length; i < max; i++) {
if (CharOperation.equals(this.allSuperDeclaringTypeNames[i], compoundName)) {
// need to verify if the type implements the pattern method
if (methodAlreadyVerified)
return true; // already verified before enter into this method (see resolveLevel(MessageSend))
MethodBinding[] methods= type.getMethods(this.pattern.selector);
for (int j= 0, length= methods.length; j < length; j++) {
MethodBinding method= methods[j];
TypeBinding[] parameters= method.parameters;
if (argumentTypes.length == parameters.length) {
boolean found= true;
for (int k= 0, l= parameters.length; k < l; k++) {
if (parameters[k].erasure() != argumentTypes[k].erasure()) {
found= false;
break;
}
}
if (found) {
return true;
}
}
}
break;
}
}
// If the given type is an interface then a common super interface may be found
// in a parallel branch of the super hierarchy, so we need to verify all super interfaces.
// If it's a class then there's only one possible branch for the hierarchy and
// this branch has been already verified by the test above
if (type.isInterface()) {
ReferenceBinding[] interfaces= type.superInterfaces();
if (interfaces == null)
return false;
for (int i= 0; i < interfaces.length; i++) {
if (resolveLevelAsSuperInvocation(interfaces[i], argumentTypes, false)) {
return true;
}
}
}
return false;
}
public String toString() {
return "Locator for " + this.pattern.toString(); //$NON-NLS-1$
}
}