/******************************************************************************* * 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.jdt.internal.core.search.matching; import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.CoreException; import org.eclipse.jdt.core.IJavaElement; import org.eclipse.jdt.core.IMethod; import org.eclipse.jdt.core.IType; import org.eclipse.jdt.core.compiler.CharOperation; import org.eclipse.jdt.core.search.IJavaSearchConstants; import org.eclipse.jdt.core.search.SearchMatch; import org.eclipse.jdt.core.search.SearchPattern; import org.eclipse.jdt.core.search.TypeDeclarationMatch; import org.eclipse.jdt.core.search.TypeReferenceMatch; import org.eclipse.jdt.internal.compiler.ast.ASTNode; import org.eclipse.jdt.internal.compiler.ast.Annotation; import org.eclipse.jdt.internal.compiler.ast.ArrayTypeReference; import org.eclipse.jdt.internal.compiler.ast.Expression; import org.eclipse.jdt.internal.compiler.ast.ImportReference; import org.eclipse.jdt.internal.compiler.ast.NameReference; import org.eclipse.jdt.internal.compiler.ast.ParameterizedQualifiedTypeReference; import org.eclipse.jdt.internal.compiler.ast.ParameterizedSingleTypeReference; import org.eclipse.jdt.internal.compiler.ast.QualifiedNameReference; import org.eclipse.jdt.internal.compiler.ast.QualifiedTypeReference; import org.eclipse.jdt.internal.compiler.ast.Reference; import org.eclipse.jdt.internal.compiler.ast.SingleNameReference; import org.eclipse.jdt.internal.compiler.ast.SingleTypeReference; import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; import org.eclipse.jdt.internal.compiler.ast.TypeReference; import org.eclipse.jdt.internal.compiler.env.IBinaryType; import org.eclipse.jdt.internal.compiler.lookup.ArrayBinding; import org.eclipse.jdt.internal.compiler.lookup.BaseTypeBinding; import org.eclipse.jdt.internal.compiler.lookup.Binding; import org.eclipse.jdt.internal.compiler.lookup.ClassScope; import org.eclipse.jdt.internal.compiler.lookup.FieldBinding; import org.eclipse.jdt.internal.compiler.lookup.LocalTypeBinding; import org.eclipse.jdt.internal.compiler.lookup.MemberTypeBinding; import org.eclipse.jdt.internal.compiler.lookup.MethodBinding; import org.eclipse.jdt.internal.compiler.lookup.ParameterizedTypeBinding; import org.eclipse.jdt.internal.compiler.lookup.ProblemBinding; import org.eclipse.jdt.internal.compiler.lookup.ProblemFieldBinding; import org.eclipse.jdt.internal.compiler.lookup.ProblemReferenceBinding; 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.util.SimpleSet; import org.eclipse.jdt.internal.core.JavaElement; public class TypeReferenceLocator extends PatternLocator { protected TypeReferencePattern pattern; protected boolean isDeclarationOfReferencedTypesPattern; private final int fineGrain; public TypeReferenceLocator(TypeReferencePattern pattern) { super(pattern); this.pattern= pattern; this.fineGrain= pattern == null ? 0 : pattern.fineGrain; this.isDeclarationOfReferencedTypesPattern= this.pattern instanceof DeclarationOfReferencedTypesPattern; } protected IJavaElement findElement(IJavaElement element, int accuracy) { // need exact match to be able to open on type ref if (accuracy != SearchMatch.A_ACCURATE) return null; // element that references the type must be included in the enclosing element DeclarationOfReferencedTypesPattern declPattern= (DeclarationOfReferencedTypesPattern)this.pattern; while (element != null && !declPattern.enclosingElement.equals(element)) element= element.getParent(); return element; } protected int fineGrain() { return this.fineGrain; } public int match(Annotation node, MatchingNodeSet nodeSet) { return match(node.type, nodeSet); } public int match(ASTNode node, MatchingNodeSet nodeSet) { // interested in ImportReference if (!(node instanceof ImportReference)) return IMPOSSIBLE_MATCH; return nodeSet.addMatch(node, matchLevel((ImportReference)node)); } //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) - SKIP IT //public int match(MessageSend node, MatchingNodeSet nodeSet) - SKIP IT public int match(Reference node, MatchingNodeSet nodeSet) { // interested in NameReference & its subtypes if (!(node instanceof NameReference)) return IMPOSSIBLE_MATCH; if (this.pattern.simpleName == null) return nodeSet.addMatch(node, this.pattern.mustResolve ? POSSIBLE_MATCH : ACCURATE_MATCH); if (node instanceof SingleNameReference) { if (matchesName(this.pattern.simpleName, ((SingleNameReference)node).token)) return nodeSet.addMatch(node, POSSIBLE_MATCH); // resolution is needed to find out if it is a type ref } else { char[][] tokens= ((QualifiedNameReference)node).tokens; for (int i= 0, max= tokens.length; i < max; i++) if (matchesName(this.pattern.simpleName, tokens[i])) return nodeSet.addMatch(node, POSSIBLE_MATCH); // resolution is needed to find out if it is a type ref } return IMPOSSIBLE_MATCH; } //public int match(TypeDeclaration node, MatchingNodeSet nodeSet) - SKIP IT public int match(TypeReference node, MatchingNodeSet nodeSet) { if (this.pattern.simpleName == null) return nodeSet.addMatch(node, this.pattern.mustResolve ? POSSIBLE_MATCH : ACCURATE_MATCH); if (node instanceof SingleTypeReference) { if (matchesName(this.pattern.simpleName, ((SingleTypeReference)node).token)) return nodeSet.addMatch(node, this.pattern.mustResolve ? POSSIBLE_MATCH : ACCURATE_MATCH); } else { char[][] tokens= ((QualifiedTypeReference)node).tokens; for (int i= 0, max= tokens.length; i < max; i++) if (matchesName(this.pattern.simpleName, tokens[i])) return nodeSet.addMatch(node, POSSIBLE_MATCH); // resolution is needed to find out if it is a type ref } return IMPOSSIBLE_MATCH; } protected int matchLevel(ImportReference importRef) { if (this.pattern.qualification == null) { if (this.pattern.simpleName == null) return ACCURATE_MATCH; char[][] tokens= importRef.tokens; boolean onDemand= (importRef.bits & ASTNode.OnDemand) != 0; final boolean isStatic= importRef.isStatic(); if (!isStatic && onDemand) { return IMPOSSIBLE_MATCH; } int length= tokens.length; if (matchesName(this.pattern.simpleName, tokens[length - 1])) { return ACCURATE_MATCH; } if (isStatic && !onDemand && length > 1) { if (matchesName(this.pattern.simpleName, tokens[length - 2])) { return ACCURATE_MATCH; } } } else { char[][] tokens= importRef.tokens; char[] qualifiedPattern= this.pattern.simpleName == null ? this.pattern.qualification : CharOperation.concat(this.pattern.qualification, this.pattern.simpleName, '.'); char[] qualifiedTypeName= CharOperation.concatWith(tokens, '.'); if (qualifiedPattern == null) return ACCURATE_MATCH; // null is as if it was "*" if (qualifiedTypeName == null) return IMPOSSIBLE_MATCH; // cannot match null name if (qualifiedTypeName.length == 0) { // empty name if (qualifiedPattern.length == 0) { // can only matches empty pattern return ACCURATE_MATCH; } return IMPOSSIBLE_MATCH; } boolean matchFirstChar= !this.isCaseSensitive || (qualifiedPattern[0] == qualifiedTypeName[0]); switch (this.matchMode) { case SearchPattern.R_EXACT_MATCH: case SearchPattern.R_PREFIX_MATCH: if (CharOperation.prefixEquals(qualifiedPattern, qualifiedTypeName, this.isCaseSensitive)) { return POSSIBLE_MATCH; } break; case SearchPattern.R_PATTERN_MATCH: if (CharOperation.match(qualifiedPattern, qualifiedTypeName, this.isCaseSensitive)) { return POSSIBLE_MATCH; } break; case SearchPattern.R_REGEXP_MATCH: // TODO (frederic) implement regular expression match break; case SearchPattern.R_CAMELCASE_MATCH: if (matchFirstChar && CharOperation.camelCaseMatch(qualifiedPattern, qualifiedTypeName, false)) { return POSSIBLE_MATCH; } // only test case insensitive as CamelCase already verified prefix case sensitive if (!this.isCaseSensitive && CharOperation.prefixEquals(qualifiedPattern, qualifiedTypeName, false)) { return POSSIBLE_MATCH; } break; case SearchPattern.R_CAMELCASE_SAME_PART_COUNT_MATCH: if (matchFirstChar && CharOperation.camelCaseMatch(qualifiedPattern, qualifiedTypeName, true)) { return POSSIBLE_MATCH; } break; } } return IMPOSSIBLE_MATCH; } /* (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) */ protected void matchLevelAndReportImportRef(ImportReference importRef, Binding binding, MatchLocator locator) throws CoreException { Binding refBinding= binding; if (importRef.isStatic()) { // for static import, binding can be a field binding or a member type binding // verify that in this case binding is static and use declaring class for fields if (binding instanceof FieldBinding) { FieldBinding fieldBinding= (FieldBinding)binding; if (!fieldBinding.isStatic()) return; refBinding= fieldBinding.declaringClass; } else if (binding instanceof MethodBinding) { MethodBinding methodBinding= (MethodBinding)binding; if (!methodBinding.isStatic()) return; refBinding= methodBinding.declaringClass; } else if (binding instanceof MemberTypeBinding) { MemberTypeBinding memberBinding= (MemberTypeBinding)binding; if (!memberBinding.isStatic()) return; } // resolve and report int level= resolveLevel(refBinding); if (level >= INACCURATE_MATCH) { matchReportImportRef( importRef, binding, locator.createImportHandle(importRef), level == ACCURATE_MATCH ? SearchMatch.A_ACCURATE : SearchMatch.A_INACCURATE, locator); } return; } super.matchLevelAndReportImportRef(importRef, refBinding, locator); } protected void matchReportImportRef(ImportReference importRef, Binding binding, IJavaElement element, int accuracy, MatchLocator locator) throws CoreException { if (this.isDeclarationOfReferencedTypesPattern) { if ((element= findElement(element, accuracy)) != null) { SimpleSet knownTypes= ((DeclarationOfReferencedTypesPattern)this.pattern).knownTypes; while (binding instanceof ReferenceBinding) { ReferenceBinding typeBinding= (ReferenceBinding)binding; reportDeclaration(typeBinding, 1, locator, knownTypes); binding= typeBinding.enclosingType(); } } return; } // return if this is not necessary to report if (this.pattern.hasTypeArguments() && !this.isEquivalentMatch && !this.isErasureMatch) { return; } // Return if fine grain is on and does not concern import reference if ((this.pattern.fineGrain != 0 && (this.pattern.fineGrain & IJavaSearchConstants.IMPORT_DECLARATION_TYPE_REFERENCE) == 0)) { return; } // Create search match this.match= locator.newTypeReferenceMatch(element, binding, accuracy, importRef); // set match raw flag and rule this.match.setRaw(true); if (this.pattern.hasTypeArguments()) { // binding is raw => only compatible erasure if pattern has type arguments this.match.setRule(this.match.getRule() & (~SearchPattern.R_FULL_MATCH)); } // Try to find best selection for match TypeBinding typeBinding= null; boolean lastButOne= false; if (binding instanceof ReferenceBinding) { typeBinding= (ReferenceBinding)binding; } else if (binding instanceof FieldBinding) { // may happen for static import typeBinding= ((FieldBinding)binding).declaringClass; lastButOne= importRef.isStatic() && ((importRef.bits & ASTNode.OnDemand) == 0); } else if (binding instanceof MethodBinding) { // may happen for static import typeBinding= ((MethodBinding)binding).declaringClass; lastButOne= importRef.isStatic() && ((importRef.bits & ASTNode.OnDemand) == 0); } if (typeBinding != null) { int lastIndex= importRef.tokens.length - 1; if (lastButOne) { // for field or method static import, use last but one token lastIndex--; } if (typeBinding instanceof ProblemReferenceBinding) { ProblemReferenceBinding pbBinding= (ProblemReferenceBinding)typeBinding; typeBinding= pbBinding.closestMatch(); lastIndex= pbBinding.compoundName.length - 1; } // try to match all enclosing types for which the token matches as well. while (typeBinding != null && lastIndex >= 0) { if (resolveLevelForType(typeBinding) != IMPOSSIBLE_MATCH) { if (locator.encloses(element)) { long[] positions= importRef.sourcePositions; // index now depends on pattern type signature int index= lastIndex; if (this.pattern.qualification != null) { index= lastIndex - this.pattern.segmentsSize; } if (index < 0) index= 0; int start= (int)((positions[index]) >>> 32); int end= (int)positions[lastIndex]; // report match this.match.setOffset(start); this.match.setLength(end - start + 1); locator.report(this.match); } return; } lastIndex--; typeBinding= typeBinding.enclosingType(); } } locator.reportAccurateTypeReference(this.match, importRef, this.pattern.simpleName); } protected void matchReportReference(ArrayTypeReference arrayRef, IJavaElement element, Binding elementBinding, int accuracy, MatchLocator locator) throws CoreException { if (this.pattern.simpleName == null) { // TODO (frederic) need to add a test for this case while searching generic types... if (locator.encloses(element)) { int offset= arrayRef.sourceStart; int length= arrayRef.sourceEnd - offset + 1; if (this.match == null) { this.match= locator.newTypeReferenceMatch(element, elementBinding, accuracy, offset, length, arrayRef); } else { this.match.setOffset(offset); this.match.setLength(length); } locator.report(this.match); return; } } this.match= locator.newTypeReferenceMatch(element, elementBinding, accuracy, arrayRef); if (arrayRef.resolvedType != null) { matchReportReference(arrayRef, -1, arrayRef.resolvedType.leafComponentType(), locator); return; } locator.reportAccurateTypeReference(this.match, arrayRef, this.pattern.simpleName); } /** * Reports the match of the given reference. */ protected void matchReportReference(ASTNode reference, IJavaElement element, Binding elementBinding, int accuracy, MatchLocator locator) throws CoreException { matchReportReference(reference, element, null, null, elementBinding, accuracy, locator); } /** * Reports the match of the given reference. Also provide a local and other elements to * eventually report in match. */ protected void matchReportReference(ASTNode reference, IJavaElement element, IJavaElement localElement, IJavaElement[] otherElements, Binding elementBinding, int accuracy, MatchLocator locator) throws CoreException { if (this.isDeclarationOfReferencedTypesPattern) { if ((element= findElement(element, accuracy)) != null) reportDeclaration(reference, element, locator, ((DeclarationOfReferencedTypesPattern)this.pattern).knownTypes); return; } // Create search match TypeReferenceMatch refMatch= locator.newTypeReferenceMatch(element, elementBinding, accuracy, reference); refMatch.setLocalElement(localElement); refMatch.setOtherElements(otherElements); this.match= refMatch; // Report match depending on reference type if (reference instanceof QualifiedNameReference) matchReportReference((QualifiedNameReference)reference, element, elementBinding, accuracy, locator); else if (reference instanceof QualifiedTypeReference) matchReportReference((QualifiedTypeReference)reference, element, elementBinding, accuracy, locator); else if (reference instanceof ArrayTypeReference) matchReportReference((ArrayTypeReference)reference, element, elementBinding, accuracy, locator); else { TypeBinding typeBinding= reference instanceof Expression ? ((Expression)reference).resolvedType : null; if (typeBinding != null) { matchReportReference((Expression)reference, -1, typeBinding, locator); return; } locator.report(this.match); } } protected void matchReportReference(QualifiedNameReference qNameRef, IJavaElement element, Binding elementBinding, int accuracy, MatchLocator locator) throws CoreException { Binding binding= qNameRef.binding; TypeBinding typeBinding= null; int lastIndex= qNameRef.tokens.length - 1; switch (qNameRef.bits & ASTNode.RestrictiveFlagMASK) { case Binding.FIELD: // reading a field typeBinding= qNameRef.actualReceiverType; lastIndex-= qNameRef.otherBindings == null ? 1 : qNameRef.otherBindings.length + 1; break; case Binding.TYPE: //=============only type ============== if (binding instanceof TypeBinding) typeBinding= (TypeBinding)binding; break; case Binding.VARIABLE: //============unbound cases=========== case Binding.TYPE | Binding.VARIABLE: if (binding instanceof ProblemReferenceBinding) { typeBinding= (TypeBinding)binding; } else if (binding instanceof ProblemFieldBinding) { typeBinding= qNameRef.actualReceiverType; lastIndex-= qNameRef.otherBindings == null ? 1 : qNameRef.otherBindings.length + 1; } else if (binding instanceof ProblemBinding) { typeBinding= ((ProblemBinding)binding).searchType; } break; } if (typeBinding instanceof ProblemReferenceBinding) { ProblemReferenceBinding pbBinding= (ProblemReferenceBinding)typeBinding; typeBinding= pbBinding.closestMatch(); lastIndex= pbBinding.compoundName.length - 1; } // Create search match to report if (this.match == null) { this.match= locator.newTypeReferenceMatch(element, elementBinding, accuracy, qNameRef); } // try to match all enclosing types for which the token matches as well. if (typeBinding instanceof ReferenceBinding) { ReferenceBinding refBinding= (ReferenceBinding)typeBinding; while (refBinding != null && lastIndex >= 0) { if (resolveLevelForType(refBinding) == ACCURATE_MATCH) { if (locator.encloses(element)) { long[] positions= qNameRef.sourcePositions; // index now depends on pattern type signature int index= lastIndex; if (this.pattern.qualification != null) { index= lastIndex - this.pattern.segmentsSize; } if (index < 0) index= 0; int start= (int)((positions[index]) >>> 32); int end= (int)positions[lastIndex]; this.match.setOffset(start); this.match.setLength(end - start + 1); // Look if there's a need to special report for parameterized type matchReportReference(qNameRef, lastIndex, refBinding, locator); } return; } lastIndex--; refBinding= refBinding.enclosingType(); } } locator.reportAccurateTypeReference(this.match, qNameRef, this.pattern.simpleName); } protected void matchReportReference(QualifiedTypeReference qTypeRef, IJavaElement element, Binding elementBinding, int accuracy, MatchLocator locator) throws CoreException { TypeBinding typeBinding= qTypeRef.resolvedType; int lastIndex= qTypeRef.tokens.length - 1; if (typeBinding instanceof ArrayBinding) typeBinding= ((ArrayBinding)typeBinding).leafComponentType; if (typeBinding instanceof ProblemReferenceBinding) { ProblemReferenceBinding pbBinding= (ProblemReferenceBinding)typeBinding; typeBinding= pbBinding.closestMatch(); lastIndex= pbBinding.compoundName.length - 1; } // Create search match to report if (this.match == null) { this.match= locator.newTypeReferenceMatch(element, elementBinding, accuracy, qTypeRef); } // try to match all enclosing types for which the token matches as well if (typeBinding instanceof ReferenceBinding) { ReferenceBinding refBinding= (ReferenceBinding)typeBinding; while (refBinding != null && lastIndex >= 0) { if (resolveLevelForType(refBinding) != IMPOSSIBLE_MATCH) { if (locator.encloses(element)) { long[] positions= qTypeRef.sourcePositions; // index now depends on pattern type signature int index= lastIndex; if (this.pattern.qualification != null) { index= lastIndex - this.pattern.segmentsSize; } if (index < 0) index= 0; int start= (int)((positions[index]) >>> 32); int end= (int)positions[lastIndex]; this.match.setOffset(start); this.match.setLength(end - start + 1); // Look if there's a need to special report for parameterized type matchReportReference(qTypeRef, lastIndex, refBinding, locator); } return; } lastIndex--; refBinding= refBinding.enclosingType(); } } locator.reportAccurateTypeReference(this.match, qTypeRef, this.pattern.simpleName); } void matchReportReference(Expression expr, int lastIndex, TypeBinding refBinding, MatchLocator locator) throws CoreException { // Look if there's a need to special report for parameterized type if (refBinding.isParameterizedType() || refBinding.isRawType()) { // Try to refine accuracy ParameterizedTypeBinding parameterizedBinding= (ParameterizedTypeBinding)refBinding; updateMatch(parameterizedBinding, this.pattern.getTypeArguments(), this.pattern.hasTypeParameters(), 0, locator); // 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; // Make a special report for parameterized types if necessary if (refBinding.isParameterizedType() && this.pattern.hasTypeArguments()) { TypeReference typeRef= null; TypeReference[] typeArguments= null; if (expr instanceof ParameterizedQualifiedTypeReference) { typeRef= (ParameterizedQualifiedTypeReference)expr; typeArguments= ((ParameterizedQualifiedTypeReference)expr).typeArguments[lastIndex]; } else if (expr instanceof ParameterizedSingleTypeReference) { typeRef= (ParameterizedSingleTypeReference)expr; typeArguments= ((ParameterizedSingleTypeReference)expr).typeArguments; } if (typeRef != null) { locator.reportAccurateParameterizedTypeReference(this.match, typeRef, lastIndex, typeArguments); return; } } } else if (this.pattern.hasTypeArguments()) { // binding has no type params, compatible erasure if pattern does this.match.setRule(SearchPattern.R_ERASURE_MATCH); } // Report match if (expr instanceof ArrayTypeReference) { locator.reportAccurateTypeReference(this.match, expr, this.pattern.simpleName); return; } if (refBinding.isLocalType()) { // see bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=82673 LocalTypeBinding local= (LocalTypeBinding)refBinding.erasure(); IJavaElement focus= this.pattern.focus; if (focus != null && local.enclosingMethod != null && focus.getParent().getElementType() == IJavaElement.METHOD) { IMethod method= (IMethod)focus.getParent(); if (!CharOperation.equals(local.enclosingMethod.selector, method.getElementName().toCharArray())) { return; } } } if (this.pattern.simpleName == null) { this.match.setOffset(expr.sourceStart); this.match.setLength(expr.sourceEnd - expr.sourceStart + 1); } locator.report(this.match); } protected int referenceType() { return IJavaElement.TYPE; } protected void reportDeclaration(ASTNode reference, IJavaElement element, MatchLocator locator, SimpleSet knownTypes) throws CoreException { int maxType= -1; TypeBinding typeBinding= null; if (reference instanceof TypeReference) { typeBinding= ((TypeReference)reference).resolvedType; maxType= Integer.MAX_VALUE; } else if (reference instanceof QualifiedNameReference) { QualifiedNameReference qNameRef= (QualifiedNameReference)reference; Binding binding= qNameRef.binding; maxType= qNameRef.tokens.length - 1; switch (qNameRef.bits & ASTNode.RestrictiveFlagMASK) { case Binding.FIELD: // reading a field typeBinding= qNameRef.actualReceiverType; maxType-= qNameRef.otherBindings == null ? 1 : qNameRef.otherBindings.length + 1; break; case Binding.TYPE: //=============only type ============== if (binding instanceof TypeBinding) typeBinding= (TypeBinding)binding; break; case Binding.VARIABLE: //============unbound cases=========== case Binding.TYPE | Binding.VARIABLE: if (binding instanceof ProblemFieldBinding) { typeBinding= qNameRef.actualReceiverType; maxType-= qNameRef.otherBindings == null ? 1 : qNameRef.otherBindings.length + 1; } else if (binding instanceof ProblemBinding) { ProblemBinding pbBinding= (ProblemBinding)binding; typeBinding= pbBinding.searchType; // second chance with recorded type so far char[] partialQualifiedName= pbBinding.name; maxType= CharOperation.occurencesOf('.', partialQualifiedName) - 1; // index of last bound token is one before the pb token if (typeBinding == null || maxType < 0) return; } break; } } else if (reference instanceof SingleNameReference) { typeBinding= (TypeBinding)((SingleNameReference)reference).binding; maxType= 1; } if (typeBinding instanceof ArrayBinding) typeBinding= ((ArrayBinding)typeBinding).leafComponentType; if (typeBinding == null || typeBinding instanceof BaseTypeBinding) return; if (typeBinding instanceof ProblemReferenceBinding) { TypeBinding original= typeBinding.closestMatch(); if (original == null) return; // original may not be set (bug 71279) typeBinding= original; } typeBinding= typeBinding.erasure(); reportDeclaration((ReferenceBinding)typeBinding, maxType, locator, knownTypes); } protected void reportDeclaration(ReferenceBinding typeBinding, int maxType, MatchLocator locator, SimpleSet knownTypes) throws CoreException { IType type= locator.lookupType(typeBinding); if (type == null) return; // case of a secondary type 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); } while (maxType >= 0 && type != null) { if (!knownTypes.includes(type)) { if (isBinary) { locator.reportBinaryMemberDeclaration(resource, type, typeBinding, info, SearchMatch.A_ACCURATE); } else { if (typeBinding instanceof ParameterizedTypeBinding) typeBinding= ((ParameterizedTypeBinding)typeBinding).genericType(); ClassScope scope= ((SourceTypeBinding)typeBinding).scope; if (scope != null) { TypeDeclaration typeDecl= scope.referenceContext; int offset= typeDecl.sourceStart; this.match= new TypeDeclarationMatch(((JavaElement)type).resolved(typeBinding), SearchMatch.A_ACCURATE, offset, typeDecl.sourceEnd - offset + 1, locator.getParticipant(), resource); locator.report(this.match); } } knownTypes.add(type); } typeBinding= typeBinding.enclosingType(); IJavaElement parent= type.getParent(); if (parent instanceof IType) { type= (IType)parent; } else { type= null; } maxType--; } } public int resolveLevel(ASTNode node) { if (node instanceof TypeReference) return resolveLevel((TypeReference)node); if (node instanceof NameReference) return resolveLevel((NameReference)node); // if (node instanceof ImportReference) - Not called when resolve is true, see MatchingNodeSet.reportMatching(unit) return IMPOSSIBLE_MATCH; } public int resolveLevel(Binding binding) { if (binding == null) return INACCURATE_MATCH; if (!(binding instanceof TypeBinding)) return IMPOSSIBLE_MATCH; TypeBinding typeBinding= (TypeBinding)binding; if (typeBinding instanceof ArrayBinding) typeBinding= ((ArrayBinding)typeBinding).leafComponentType; if (typeBinding instanceof ProblemReferenceBinding) typeBinding= ((ProblemReferenceBinding)typeBinding).closestMatch(); return resolveLevelForTypeOrEnclosingTypes(this.pattern.simpleName, this.pattern.qualification, typeBinding); } protected int resolveLevel(NameReference nameRef) { Binding binding= nameRef.binding; if (nameRef instanceof SingleNameReference) { if (binding instanceof ProblemReferenceBinding) binding= ((ProblemReferenceBinding)binding).closestMatch(); if (binding instanceof ReferenceBinding) return resolveLevelForType((ReferenceBinding)binding); return binding == null || binding instanceof ProblemBinding ? INACCURATE_MATCH : IMPOSSIBLE_MATCH; } TypeBinding typeBinding= null; QualifiedNameReference qNameRef= (QualifiedNameReference)nameRef; switch (qNameRef.bits & ASTNode.RestrictiveFlagMASK) { case Binding.FIELD: // reading a field if (qNameRef.tokens.length < (qNameRef.otherBindings == null ? 2 : qNameRef.otherBindings.length + 2)) return IMPOSSIBLE_MATCH; // must be at least A.x typeBinding= nameRef.actualReceiverType; break; case Binding.LOCAL: // reading a local variable return IMPOSSIBLE_MATCH; // no type match in it case Binding.TYPE: //=============only type ============== if (binding instanceof TypeBinding) typeBinding= (TypeBinding)binding; break; /* * Handling of unbound qualified name references. The match may reside in the resolved fragment, * which is recorded inside the problem binding, along with the portion of the name until it became a problem. */ case Binding.VARIABLE: //============unbound cases=========== case Binding.TYPE | Binding.VARIABLE: if (binding instanceof ProblemReferenceBinding) { typeBinding= (TypeBinding)binding; } else if (binding instanceof ProblemFieldBinding) { if (qNameRef.tokens.length < (qNameRef.otherBindings == null ? 2 : qNameRef.otherBindings.length + 2)) return IMPOSSIBLE_MATCH; // must be at least A.x typeBinding= nameRef.actualReceiverType; } else if (binding instanceof ProblemBinding) { ProblemBinding pbBinding= (ProblemBinding)binding; if (CharOperation.occurencesOf('.', pbBinding.name) <= 0) // index of last bound token is one before the pb token return INACCURATE_MATCH; typeBinding= pbBinding.searchType; } break; } return resolveLevel(typeBinding); } protected int resolveLevel(TypeReference typeRef) { TypeBinding typeBinding= typeRef.resolvedType; if (typeBinding instanceof ArrayBinding) typeBinding= ((ArrayBinding)typeBinding).leafComponentType; if (typeBinding instanceof ProblemReferenceBinding) typeBinding= ((ProblemReferenceBinding)typeBinding).closestMatch(); if (typeRef instanceof SingleTypeReference) { return resolveLevelForType(typeBinding); } else return resolveLevelForTypeOrEnclosingTypes(this.pattern.simpleName, this.pattern.qualification, typeBinding); } /* (non-Javadoc) * Resolve level for type with a given binding. * This is just an helper to avoid call of method with all parameters... */ protected int resolveLevelForType(TypeBinding typeBinding) { if (typeBinding == null || !typeBinding.isValidBinding()) { if (this.pattern.typeSuffix != TYPE_SUFFIX) return INACCURATE_MATCH; } else { switch (this.pattern.typeSuffix) { case CLASS_SUFFIX: if (!typeBinding.isClass()) return IMPOSSIBLE_MATCH; break; case CLASS_AND_INTERFACE_SUFFIX: if (!(typeBinding.isClass() || (typeBinding.isInterface() && !typeBinding.isAnnotationType()))) return IMPOSSIBLE_MATCH; break; case CLASS_AND_ENUM_SUFFIX: if (!(typeBinding.isClass() || typeBinding.isEnum())) return IMPOSSIBLE_MATCH; break; case INTERFACE_SUFFIX: if (!typeBinding.isInterface() || typeBinding.isAnnotationType()) return IMPOSSIBLE_MATCH; break; case INTERFACE_AND_ANNOTATION_SUFFIX: if (!(typeBinding.isInterface() || typeBinding.isAnnotationType())) return IMPOSSIBLE_MATCH; break; case ENUM_SUFFIX: if (!typeBinding.isEnum()) return IMPOSSIBLE_MATCH; break; case ANNOTATION_TYPE_SUFFIX: if (!typeBinding.isAnnotationType()) return IMPOSSIBLE_MATCH; break; case TYPE_SUFFIX: // nothing } } return resolveLevelForType(this.pattern.simpleName, this.pattern.qualification, this.pattern.getTypeArguments(), 0, typeBinding); } /** * Returns whether the given type binding or one of its enclosing types matches the given simple * name pattern and qualification pattern. Returns ACCURATE_MATCH if it does. Returns * INACCURATE_MATCH if resolve failed. Returns IMPOSSIBLE_MATCH if it doesn't. */ protected int resolveLevelForTypeOrEnclosingTypes(char[] simpleNamePattern, char[] qualificationPattern, TypeBinding binding) { if (binding == null) return INACCURATE_MATCH; if (binding instanceof ReferenceBinding) { ReferenceBinding type= (ReferenceBinding)binding; while (type != null) { int level= resolveLevelForType(type); if (level != IMPOSSIBLE_MATCH) return level; type= type.enclosingType(); } } return IMPOSSIBLE_MATCH; } public String toString() { return "Locator for " + this.pattern.toString(); //$NON-NLS-1$ } }