/******************************************************************************* * 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.wst.jsdt.internal.core.search.matching; import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.CoreException; import org.eclipse.wst.jsdt.core.IFunction; import org.eclipse.wst.jsdt.core.IJavaScriptElement; import org.eclipse.wst.jsdt.core.IPackageFragment; import org.eclipse.wst.jsdt.core.IType; import org.eclipse.wst.jsdt.core.compiler.CharOperation; import org.eclipse.wst.jsdt.core.search.SearchMatch; import org.eclipse.wst.jsdt.core.search.SearchPattern; import org.eclipse.wst.jsdt.core.search.TypeDeclarationMatch; import org.eclipse.wst.jsdt.core.search.TypeReferenceMatch; import org.eclipse.wst.jsdt.internal.compiler.ast.ASTNode; import org.eclipse.wst.jsdt.internal.compiler.ast.ArrayTypeReference; import org.eclipse.wst.jsdt.internal.compiler.ast.Expression; import org.eclipse.wst.jsdt.internal.compiler.ast.FieldReference; import org.eclipse.wst.jsdt.internal.compiler.ast.ImportReference; import org.eclipse.wst.jsdt.internal.compiler.ast.LocalDeclaration; import org.eclipse.wst.jsdt.internal.compiler.ast.NameReference; import org.eclipse.wst.jsdt.internal.compiler.ast.QualifiedNameReference; import org.eclipse.wst.jsdt.internal.compiler.ast.QualifiedTypeReference; import org.eclipse.wst.jsdt.internal.compiler.ast.Reference; import org.eclipse.wst.jsdt.internal.compiler.ast.SingleNameReference; import org.eclipse.wst.jsdt.internal.compiler.ast.SingleTypeReference; import org.eclipse.wst.jsdt.internal.compiler.ast.TypeDeclaration; import org.eclipse.wst.jsdt.internal.compiler.ast.TypeReference; import org.eclipse.wst.jsdt.internal.compiler.env.IBinaryType; import org.eclipse.wst.jsdt.internal.compiler.lookup.ArrayBinding; import org.eclipse.wst.jsdt.internal.compiler.lookup.BaseTypeBinding; import org.eclipse.wst.jsdt.internal.compiler.lookup.Binding; import org.eclipse.wst.jsdt.internal.compiler.lookup.BlockScope; import org.eclipse.wst.jsdt.internal.compiler.lookup.ClassScope; import org.eclipse.wst.jsdt.internal.compiler.lookup.FieldBinding; import org.eclipse.wst.jsdt.internal.compiler.lookup.LocalTypeBinding; import org.eclipse.wst.jsdt.internal.compiler.lookup.MethodBinding; import org.eclipse.wst.jsdt.internal.compiler.lookup.ProblemBinding; import org.eclipse.wst.jsdt.internal.compiler.lookup.ProblemFieldBinding; import org.eclipse.wst.jsdt.internal.compiler.lookup.ProblemReferenceBinding; import org.eclipse.wst.jsdt.internal.compiler.lookup.ReferenceBinding; import org.eclipse.wst.jsdt.internal.compiler.lookup.Scope; import org.eclipse.wst.jsdt.internal.compiler.lookup.SourceTypeBinding; import org.eclipse.wst.jsdt.internal.compiler.lookup.TypeBinding; import org.eclipse.wst.jsdt.internal.compiler.util.SimpleSet; import org.eclipse.wst.jsdt.internal.core.JavaElement; public class TypeReferenceLocator extends PatternLocator { protected TypeReferencePattern pattern; protected boolean isDeclarationOfReferencedTypesPattern; public TypeReferenceLocator(TypeReferencePattern pattern) { super(pattern); this.pattern = pattern; this.isDeclarationOfReferencedTypesPattern = this.pattern instanceof DeclarationOfReferencedTypesPattern; } protected IJavaScriptElement findElement(IJavaScriptElement 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; } 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(FunctionDeclaration 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)) { if (node instanceof FieldReference) { FieldReference fieldReference = (FieldReference) node; if (!(fieldReference.receiver instanceof SingleNameReference || fieldReference.receiver instanceof FieldReference)) return IMPOSSIBLE_MATCH; if (matchesName(this.pattern.simpleName, fieldReference.token)) return nodeSet.addMatch(node, POSSIBLE_MATCH); // resolution is needed to find out if it is a type ref } return IMPOSSIBLE_MATCH; } if (this.pattern.simpleName == null) return nodeSet.addMatch(node, ((InternalSearchPattern)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, ((InternalSearchPattern)this.pattern).mustResolve ? POSSIBLE_MATCH : ACCURATE_MATCH); if (node instanceof SingleTypeReference) { if (matchesName(this.pattern.simpleName, ((SingleTypeReference) node).token)) return nodeSet.addMatch(node, ((InternalSearchPattern)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; if (matchesName(this.pattern.simpleName, tokens[tokens.length-1])) 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]); if (this.isCamelCase && matchFirstChar && CharOperation.camelCaseMatch(qualifiedPattern, qualifiedTypeName)) { return POSSIBLE_MATCH; } 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; } } return IMPOSSIBLE_MATCH; } protected void matchReportImportRef(ImportReference importRef, Binding binding, IJavaScriptElement 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; } // Create search match match = locator.newTypeReferenceMatch(element, binding, accuracy, importRef); // set match raw flag and rule match.setRaw(true); if (this.pattern.hasTypeArguments()) { // binding is raw => only compatible erasure if pattern has type arguments match.setRule(match.getRule() & (~SearchPattern.R_FULL_MATCH)); } // Try to find best selection for match ReferenceBinding 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; } else if (binding instanceof MethodBinding) { // may happen for static import typeBinding = ((MethodBinding)binding).declaringClass; } 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 match.setOffset(start); match.setLength(end-start+1); locator.report(match); } return; } lastIndex--; typeBinding = typeBinding.enclosingType(); } } locator.reportAccurateTypeReference(match, importRef, this.pattern.simpleName); } protected void matchReportReference(ArrayTypeReference arrayRef, IJavaScriptElement 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(match); return; } } match = locator.newTypeReferenceMatch(element, elementBinding, accuracy, arrayRef); if (arrayRef.resolvedType != null) { matchReportReference(arrayRef, -1, arrayRef.resolvedType.leafComponentType(), locator); return; } locator.reportAccurateTypeReference(match, arrayRef, this.pattern.simpleName); } /** * Reports the match of the given reference. */ protected void matchReportReference(ASTNode reference, IJavaScriptElement 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, IJavaScriptElement element, IJavaScriptElement localElement, IJavaScriptElement[] 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(match); } } /** * Reports the match of the given reference. Also provide a scope to look for possible local and other elements. */ protected void matchReportReference(ASTNode reference, IJavaScriptElement element, Binding elementBinding, Scope scope, int accuracy, MatchLocator locator) throws CoreException { if (scope == null || (scope.kind != Scope.BLOCK_SCOPE && scope.kind != Scope.METHOD_SCOPE)) { matchReportReference(reference, element, elementBinding, accuracy, locator); return; } // Look if some block scope local variable declarations include reference start position BlockScope blockScope = (BlockScope) scope; LocalDeclaration[] localDeclarations = blockScope.findLocalVariableDeclarations(reference.sourceStart); IJavaScriptElement localElement = null; IJavaScriptElement[] otherElements = null; // Some local variable declaration are matching if (localDeclarations != null) { int length = localDeclarations.length; // Set local element to first matching local declaration int idx = 0; for (; idx<length; idx++) { if (localDeclarations[idx] == null) break; if (reference.sourceStart == localDeclarations[idx].declarationSourceStart) { localElement = locator.createHandle(localDeclarations[idx], element); break; } if (idx>0 && localDeclarations[idx].sourceStart > reference.sourceStart) { localElement = locator.createHandle(localDeclarations[idx-1], element); break; } } if (localElement == null && idx > 0) { if (reference.sourceEnd < localDeclarations[idx-1].declarationEnd) { localElement = locator.createHandle(localDeclarations[idx-1], element); } } // Store other local variable declarations in other elements int size = 0; for (int j=1; j<length; j++) { if (localDeclarations[j] == null) break; if (reference.sourceStart == localDeclarations[j].declarationSourceStart) { if (otherElements == null) { otherElements = new IJavaScriptElement[length-j]; } otherElements[size++] = locator.createHandle(localDeclarations[j], element); } } if (size > 0 && size != (length-1)) { System.arraycopy(otherElements, 0, otherElements = new IJavaScriptElement[size], 0, size); } } // Report match with local and other elements if any matchReportReference(reference, element, localElement, otherElements, elementBinding, accuracy, locator); } protected void matchReportReference(QualifiedNameReference qNameRef, IJavaScriptElement 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]; match.setOffset(start); 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(match, qNameRef, this.pattern.simpleName); } protected void matchReportReference(QualifiedTypeReference qTypeRef, IJavaScriptElement 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]; match.setOffset(start); 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(match, qTypeRef, this.pattern.simpleName); } void matchReportReference(Expression expr, int lastIndex, TypeBinding refBinding, MatchLocator locator) throws CoreException { if (this.pattern.hasTypeArguments()) { // binding has no type params, compatible erasure if pattern does match.setRule(SearchPattern.R_ERASURE_MATCH); } // Report match if (expr instanceof ArrayTypeReference) { locator.reportAccurateTypeReference(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; IJavaScriptElement focus = ((InternalSearchPattern)pattern).focus; if (focus != null && local.enclosingMethod != null && focus.getParent().getElementType() == IJavaScriptElement.METHOD) { IFunction method = (IFunction) focus.getParent(); if (!CharOperation.equals(local.enclosingMethod.selector, method.getElementName().toCharArray())) { return; } } } locator.report(match); } protected int referenceType() { return IJavaScriptElement.TYPE; } protected void reportDeclaration(ASTNode reference, IJavaScriptElement 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) { ReferenceBinding original = ((ProblemReferenceBinding) typeBinding).closestMatch(); if (original == null) return; // original may not be set (bug 71279) typeBinding = original; } 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.getJavaScriptProject().getProject(); info = locator.getBinaryInfo((org.eclipse.wst.jsdt.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 { ClassScope scope = (ClassScope)((SourceTypeBinding) typeBinding).scope; if (scope != null) { TypeDeclaration typeDecl = scope.referenceContext; int offset = typeDecl.sourceStart; match = new TypeDeclarationMatch(((JavaElement) type).resolved(typeBinding), SearchMatch.A_ACCURATE, offset, typeDecl.sourceEnd-offset+1, locator.getParticipant(), resource); locator.report(match); } } knownTypes.add(type); } typeBinding = typeBinding.enclosingType(); IJavaScriptElement parent = type.getParent(); if (parent instanceof IType) { type = (IType)parent; } else { type = null; } maxType--; } } public int resolveLevel(ASTNode node) { if (node instanceof NameReference) return resolveLevel((NameReference) node); if (node instanceof FieldReference) return resolveLevel((FieldReference)node); if (node instanceof TypeReference) return resolveLevel((TypeReference) 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(); if (((InternalSearchPattern) this.pattern).focus instanceof IType && typeBinding instanceof ReferenceBinding) { IPackageFragment pkg = ((IType) ((InternalSearchPattern) this.pattern).focus).getPackageFragment(); // check that type is located inside this instance of a package fragment if (!PackageReferenceLocator.isDeclaringPackageFragment(pkg, (ReferenceBinding) typeBinding)) return IMPOSSIBLE_MATCH; } 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(FieldReference fieldReference) { Binding binding = fieldReference.typeBinding; 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; } 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) { 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$ } }