/******************************************************************************* * 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 java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.Map; import java.util.zip.ZipFile; import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.OperationCanceledException; import org.eclipse.jdt.core.Flags; import org.eclipse.jdt.core.IAnnotatable; import org.eclipse.jdt.core.IAnnotation; import org.eclipse.jdt.core.IClassFile; import org.eclipse.jdt.core.IJavaElement; import org.eclipse.jdt.core.IJavaModelStatusConstants; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.IMember; import org.eclipse.jdt.core.IMethod; import org.eclipse.jdt.core.IPackageFragment; import org.eclipse.jdt.core.IPackageFragmentRoot; import org.eclipse.jdt.core.ISourceRange; 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.compiler.IProblem; import org.eclipse.jdt.core.compiler.InvalidInputException; import org.eclipse.jdt.core.search.FieldDeclarationMatch; import org.eclipse.jdt.core.search.FieldReferenceMatch; import org.eclipse.jdt.core.search.IJavaSearchScope; import org.eclipse.jdt.core.search.LocalVariableDeclarationMatch; import org.eclipse.jdt.core.search.LocalVariableReferenceMatch; import org.eclipse.jdt.core.search.MethodDeclarationMatch; import org.eclipse.jdt.core.search.MethodReferenceMatch; import org.eclipse.jdt.core.search.PackageDeclarationMatch; import org.eclipse.jdt.core.search.PackageReferenceMatch; import org.eclipse.jdt.core.search.ReferenceMatch; import org.eclipse.jdt.core.search.SearchDocument; import org.eclipse.jdt.core.search.SearchMatch; import org.eclipse.jdt.core.search.SearchParticipant; import org.eclipse.jdt.core.search.SearchPattern; import org.eclipse.jdt.core.search.SearchRequestor; import org.eclipse.jdt.core.search.TypeDeclarationMatch; import org.eclipse.jdt.core.search.TypeParameterDeclarationMatch; import org.eclipse.jdt.core.search.TypeParameterReferenceMatch; import org.eclipse.jdt.core.search.TypeReferenceMatch; import org.eclipse.jdt.internal.compiler.CompilationResult; import org.eclipse.jdt.internal.compiler.DefaultErrorHandlingPolicies; import org.eclipse.jdt.internal.compiler.ast.ASTNode; import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration; import org.eclipse.jdt.internal.compiler.ast.AbstractVariableDeclaration; import org.eclipse.jdt.internal.compiler.ast.AllocationExpression; import org.eclipse.jdt.internal.compiler.ast.Annotation; import org.eclipse.jdt.internal.compiler.ast.Argument; import org.eclipse.jdt.internal.compiler.ast.ArrayTypeReference; import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration; import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration; import org.eclipse.jdt.internal.compiler.ast.ImportReference; import org.eclipse.jdt.internal.compiler.ast.MemberValuePair; import org.eclipse.jdt.internal.compiler.ast.ParameterizedQualifiedTypeReference; import org.eclipse.jdt.internal.compiler.ast.ParameterizedSingleTypeReference; import org.eclipse.jdt.internal.compiler.ast.QualifiedAllocationExpression; import org.eclipse.jdt.internal.compiler.ast.QualifiedNameReference; import org.eclipse.jdt.internal.compiler.ast.QualifiedTypeReference; import org.eclipse.jdt.internal.compiler.ast.SingleMemberAnnotation; import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; import org.eclipse.jdt.internal.compiler.ast.TypeParameter; import org.eclipse.jdt.internal.compiler.ast.TypeReference; import org.eclipse.jdt.internal.compiler.ast.Wildcard; import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; import org.eclipse.jdt.internal.compiler.classfmt.ClassFileReader; import org.eclipse.jdt.internal.compiler.classfmt.ClassFormatException; import org.eclipse.jdt.internal.compiler.env.AccessRestriction; import org.eclipse.jdt.internal.compiler.env.IBinaryMethod; import org.eclipse.jdt.internal.compiler.env.IBinaryType; import org.eclipse.jdt.internal.compiler.env.ICompilationUnit; import org.eclipse.jdt.internal.compiler.env.INameEnvironment; import org.eclipse.jdt.internal.compiler.env.ISourceType; import org.eclipse.jdt.internal.compiler.impl.CompilerOptions; import org.eclipse.jdt.internal.compiler.impl.ITypeRequestor; import org.eclipse.jdt.internal.compiler.lookup.BinaryTypeBinding; import org.eclipse.jdt.internal.compiler.lookup.Binding; import org.eclipse.jdt.internal.compiler.lookup.ClassScope; import org.eclipse.jdt.internal.compiler.lookup.CompilationUnitScope; import org.eclipse.jdt.internal.compiler.lookup.LookupEnvironment; import org.eclipse.jdt.internal.compiler.lookup.MethodBinding; import org.eclipse.jdt.internal.compiler.lookup.MethodScope; import org.eclipse.jdt.internal.compiler.lookup.PackageBinding; 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.TagBits; import org.eclipse.jdt.internal.compiler.lookup.TypeBinding; import org.eclipse.jdt.internal.compiler.lookup.TypeVariableBinding; import org.eclipse.jdt.internal.compiler.parser.Parser; import org.eclipse.jdt.internal.compiler.parser.Scanner; import org.eclipse.jdt.internal.compiler.parser.SourceTypeConverter; import org.eclipse.jdt.internal.compiler.parser.TerminalTokens; import org.eclipse.jdt.internal.compiler.problem.AbortCompilation; import org.eclipse.jdt.internal.compiler.problem.AbortCompilationUnit; import org.eclipse.jdt.internal.compiler.problem.DefaultProblemFactory; import org.eclipse.jdt.internal.compiler.problem.ProblemReporter; import org.eclipse.jdt.internal.compiler.util.Messages; import org.eclipse.jdt.internal.compiler.util.SimpleLookupTable; import org.eclipse.jdt.internal.compiler.util.SimpleSet; import org.eclipse.jdt.internal.compiler.util.SuffixConstants; import org.eclipse.jdt.internal.core.BinaryMember; import org.eclipse.jdt.internal.core.BinaryType; import org.eclipse.jdt.internal.core.ClassFile; import org.eclipse.jdt.internal.core.CompilationUnit; import org.eclipse.jdt.internal.core.JarPackageFragmentRoot; import org.eclipse.jdt.internal.core.JavaElement; import org.eclipse.jdt.internal.core.JavaModelManager; import org.eclipse.jdt.internal.core.JavaProject; import org.eclipse.jdt.internal.core.LocalVariable; import org.eclipse.jdt.internal.core.NameLookup; import org.eclipse.jdt.internal.core.Openable; import org.eclipse.jdt.internal.core.PackageFragment; import org.eclipse.jdt.internal.core.PackageFragmentRoot; import org.eclipse.jdt.internal.core.SearchableEnvironment; import org.eclipse.jdt.internal.core.SourceMapper; import org.eclipse.jdt.internal.core.SourceMethod; import org.eclipse.jdt.internal.core.SourceTypeElementInfo; import org.eclipse.jdt.internal.core.hierarchy.HierarchyResolver; import org.eclipse.jdt.internal.core.index.Index; import org.eclipse.jdt.internal.core.search.BasicSearchEngine; import org.eclipse.jdt.internal.core.search.HierarchyScope; import org.eclipse.jdt.internal.core.search.IndexQueryRequestor; import org.eclipse.jdt.internal.core.search.IndexSelector; import org.eclipse.jdt.internal.core.search.JavaSearchDocument; import org.eclipse.jdt.internal.core.util.HandleFactory; import org.eclipse.jdt.internal.core.util.Util; public class MatchLocator implements ITypeRequestor { public static final int MAX_AT_ONCE; static { long maxMemory= Runtime.getRuntime().maxMemory(); int ratio= (int)Math.round(((double)maxMemory) / (64 * 0x100000)); switch (ratio) { case 0: case 1: MAX_AT_ONCE= 100; break; case 2: MAX_AT_ONCE= 200; break; case 3: MAX_AT_ONCE= 300; break; default: MAX_AT_ONCE= 400; break; } } // permanent state public SearchPattern pattern; public PatternLocator patternLocator; public int matchContainer; public SearchRequestor requestor; public IJavaSearchScope scope; public IProgressMonitor progressMonitor; public org.eclipse.jdt.core.ICompilationUnit[] workingCopies; public HandleFactory handleFactory; // cache of all super type names if scope is hierarchy scope public char[][][] allSuperTypeNames; // the following is valid for the current project public MatchLocatorParser parser; private Parser basicParser; public INameEnvironment nameEnvironment; public NameLookup nameLookup; public LookupEnvironment lookupEnvironment; public HierarchyResolver hierarchyResolver; public CompilerOptions options; // management of PossibleMatch to be processed public int numberOfMatches; // (numberOfMatches - 1) is the last unit in matchesToProcess public PossibleMatch[] matchesToProcess; public PossibleMatch currentPossibleMatch; /* * Time spent in the IJavaSearchResultCollector */ public long resultCollectorTime= 0; // Progress information int progressStep; int progressWorked; // Binding resolution and cache CompilationUnitScope unitScope; SimpleLookupTable bindings; // Cache for method handles HashSet methodHandles; private final boolean searchPackageDeclaration; public static class WorkingCopyDocument extends JavaSearchDocument { public org.eclipse.jdt.core.ICompilationUnit workingCopy; WorkingCopyDocument(org.eclipse.jdt.core.ICompilationUnit workingCopy, SearchParticipant participant) { super(workingCopy.getPath().toString(), participant); this.charContents= ((CompilationUnit)workingCopy).getContents(); this.workingCopy= workingCopy; } public String toString() { return "WorkingCopyDocument for " + getPath(); //$NON-NLS-1$ } } public static class WrappedCoreException extends RuntimeException { private static final long serialVersionUID= 8354329870126121212L; // backward compatible public CoreException coreException; public WrappedCoreException(CoreException coreException) { this.coreException= coreException; } } public static SearchDocument[] addWorkingCopies(SearchPattern pattern, SearchDocument[] indexMatches, org.eclipse.jdt.core.ICompilationUnit[] copies, SearchParticipant participant) { if (copies == null) return indexMatches; // working copies take precedence over corresponding compilation units HashMap workingCopyDocuments= workingCopiesThatCanSeeFocus(copies, pattern, participant); if (workingCopyDocuments.size() == 0) return indexMatches; SearchDocument[] matches= null; int length= indexMatches.length; for (int i= 0; i < length; i++) { SearchDocument searchDocument= indexMatches[i]; if (searchDocument.getParticipant() == participant) { SearchDocument workingCopyDocument= (SearchDocument)workingCopyDocuments.remove(searchDocument.getPath()); if (workingCopyDocument != null) { if (matches == null) { System.arraycopy(indexMatches, 0, matches= new SearchDocument[length], 0, length); } matches[i]= workingCopyDocument; } } } if (matches == null) { // no working copy matches= indexMatches; } int remainingWorkingCopiesSize= workingCopyDocuments.size(); if (remainingWorkingCopiesSize != 0) { System.arraycopy(matches, 0, matches= new SearchDocument[length + remainingWorkingCopiesSize], 0, length); Iterator iterator= workingCopyDocuments.values().iterator(); int index= length; while (iterator.hasNext()) { matches[index++]= (SearchDocument)iterator.next(); } } return matches; } public static void setFocus(SearchPattern pattern, IJavaElement focus) { pattern.focus= focus; } /* * Returns the working copies that can see the given focus. */ private static HashMap workingCopiesThatCanSeeFocus(org.eclipse.jdt.core.ICompilationUnit[] copies, SearchPattern pattern, SearchParticipant participant) { if (copies == null) return new HashMap(); HashMap result= new HashMap(); for (int i= 0, length= copies.length; i < length; i++) { org.eclipse.jdt.core.ICompilationUnit workingCopy= copies[i]; IPath projectOrJar= MatchLocator.getProjectOrJar(workingCopy).getPath(); if (pattern.focus == null || IndexSelector.canSeeFocus(pattern, projectOrJar)) { result.put( workingCopy.getPath().toString(), new WorkingCopyDocument(workingCopy, participant) ); } } return result; } public static ClassFileReader classFileReader(IType type) { IClassFile classFile= type.getClassFile(); JavaModelManager manager= JavaModelManager.getJavaModelManager(); if (classFile.isOpen()) return (ClassFileReader)manager.getInfo(type); PackageFragment pkg= (PackageFragment)type.getPackageFragment(); IPackageFragmentRoot root= (IPackageFragmentRoot)pkg.getParent(); try { if (!root.isArchive()) return Util.newClassFileReader(((JavaElement)type).resource()); ZipFile zipFile= null; try { IPath zipPath= root.getPath(); if (JavaModelManager.ZIP_ACCESS_VERBOSE) System.out.println("(" + Thread.currentThread() + ") [MatchLocator.classFileReader()] Creating ZipFile on " + zipPath); //$NON-NLS-1$ //$NON-NLS-2$ zipFile= manager.getZipFile(zipPath); String classFileName= classFile.getElementName(); String path= Util.concatWith(pkg.names, classFileName, '/'); return ClassFileReader.read(zipFile, path); } finally { manager.closeZipFile(zipFile); } } catch (ClassFormatException e) { // invalid class file: return null } catch (CoreException e) { // cannot read class file: return null } catch (IOException e) { // cannot read class file: return null } return null; } /** * Query a given index for matching entries. Assumes the sender has opened the index and will * close when finished. */ public static void findIndexMatches(SearchPattern pattern, Index index, IndexQueryRequestor requestor, SearchParticipant participant, IJavaSearchScope scope, IProgressMonitor monitor) throws IOException { pattern.findIndexMatches(index, requestor, participant, scope, monitor); } public static IJavaElement getProjectOrJar(IJavaElement element) { while (!(element instanceof IJavaProject) && !(element instanceof JarPackageFragmentRoot)) { element= element.getParent(); } return element; } public static IJavaElement projectOrJarFocus(SearchPattern pattern) { return pattern == null || pattern.focus == null ? null : getProjectOrJar(pattern.focus); } public MatchLocator( SearchPattern pattern, SearchRequestor requestor, IJavaSearchScope scope, IProgressMonitor progressMonitor) { this.pattern= pattern; this.patternLocator= PatternLocator.patternLocator(this.pattern); this.matchContainer= this.patternLocator == null ? 0 : this.patternLocator.matchContainer(); this.requestor= requestor; this.scope= scope; this.progressMonitor= progressMonitor; if (pattern instanceof PackageDeclarationPattern) { this.searchPackageDeclaration= true; } else if (pattern instanceof OrPattern) { this.searchPackageDeclaration= ((OrPattern)pattern).hasPackageDeclaration(); } else { this.searchPackageDeclaration= false; } } /** * Add an additional binary type */ public void accept(IBinaryType binaryType, PackageBinding packageBinding, AccessRestriction accessRestriction) { this.lookupEnvironment.createBinaryTypeFrom(binaryType, packageBinding, accessRestriction); } /** * Add an additional compilation unit into the loop -> build compilation unit declarations, * their bindings and record their results. */ public void accept(ICompilationUnit sourceUnit, AccessRestriction accessRestriction) { // Switch the current policy and compilation result for this unit to the requested one. CompilationResult unitResult= new CompilationResult(sourceUnit, 1, 1, this.options.maxProblemsPerUnit); try { CompilationUnitDeclaration parsedUnit= basicParser().dietParse(sourceUnit, unitResult); this.lookupEnvironment.buildTypeBindings(parsedUnit, accessRestriction); this.lookupEnvironment.completeTypeBindings(parsedUnit, true); } catch (AbortCompilationUnit e) { // at this point, currentCompilationUnitResult may not be sourceUnit, but some other // one requested further along to resolve sourceUnit. if (unitResult.compilationUnit == sourceUnit) { // only report once //requestor.acceptResult(unitResult.tagAsAccepted()); } else { throw e; // want to abort enclosing request to compile } } // Display unit error in debug mode if (BasicSearchEngine.VERBOSE) { if (unitResult.problemCount > 0) { System.out.println(unitResult); } } } /** * Add additional source types */ public void accept(ISourceType[] sourceTypes, PackageBinding packageBinding, AccessRestriction accessRestriction) { // case of SearchableEnvironment of an IJavaProject is used ISourceType sourceType= sourceTypes[0]; while (sourceType.getEnclosingType() != null) sourceType= sourceType.getEnclosingType(); if (sourceType instanceof SourceTypeElementInfo) { // get source SourceTypeElementInfo elementInfo= (SourceTypeElementInfo)sourceType; IType type= elementInfo.getHandle(); ICompilationUnit sourceUnit= (ICompilationUnit)type.getCompilationUnit(); accept(sourceUnit, accessRestriction); } else { CompilationResult result= new CompilationResult(sourceType.getFileName(), 1, 1, 0); CompilationUnitDeclaration unit= SourceTypeConverter.buildCompilationUnit( sourceTypes, SourceTypeConverter.FIELD_AND_METHOD // need field and methods | SourceTypeConverter.MEMBER_TYPE, // need member types // no need for field initialization this.lookupEnvironment.problemReporter, result); this.lookupEnvironment.buildTypeBindings(unit, accessRestriction); this.lookupEnvironment.completeTypeBindings(unit, true); } } protected Parser basicParser() { if (this.basicParser == null) { ProblemReporter problemReporter= new ProblemReporter( DefaultErrorHandlingPolicies.proceedWithAllProblems(), this.options, new DefaultProblemFactory()); this.basicParser= new Parser(problemReporter, false); this.basicParser.reportOnlyOneSyntaxError= true; } return this.basicParser; } /* * Caches the given binary type in the lookup environment and returns it. * Returns the existing one if already cached. * Returns null if source type binding was cached. */ protected BinaryTypeBinding cacheBinaryType(IType type, IBinaryType binaryType) throws JavaModelException { IType enclosingType= type.getDeclaringType(); if (enclosingType != null) cacheBinaryType(enclosingType, null); // cache enclosing types first, so that binary type can be found in lookup enviroment if (binaryType == null) { ClassFile classFile= (ClassFile)type.getClassFile(); try { binaryType= getBinaryInfo(classFile, classFile.resource()); } catch (CoreException e) { if (e instanceof JavaModelException) { throw (JavaModelException)e; } else { throw new JavaModelException(e); } } } BinaryTypeBinding binding= this.lookupEnvironment.cacheBinaryType(binaryType, null /*no access restriction*/); if (binding == null) { // it was already cached as a result of a previous query char[][] compoundName= CharOperation.splitOn('.', type.getFullyQualifiedName().toCharArray()); ReferenceBinding referenceBinding= this.lookupEnvironment.getCachedType(compoundName); if (referenceBinding != null && (referenceBinding instanceof BinaryTypeBinding)) binding= (BinaryTypeBinding)referenceBinding; // if the binding could be found and if it comes from a binary type } return binding; } /* * Computes the super type names of the focus type if any. */ protected char[][][] computeSuperTypeNames(IType focusType) { String fullyQualifiedName= focusType.getFullyQualifiedName(); int lastDot= fullyQualifiedName.lastIndexOf('.'); char[] qualification= lastDot == -1 ? CharOperation.NO_CHAR : fullyQualifiedName.substring(0, lastDot).toCharArray(); char[] simpleName= focusType.getElementName().toCharArray(); SuperTypeNamesCollector superTypeNamesCollector= new SuperTypeNamesCollector( this.pattern, simpleName, qualification, new MatchLocator(this.pattern, this.requestor, this.scope, this.progressMonitor), // clone MatchLocator so that it has no side effect focusType, this.progressMonitor); try { this.allSuperTypeNames= superTypeNamesCollector.collect(); } catch (JavaModelException e) { // problem collecting super type names: leave it null } return this.allSuperTypeNames; } /** * Creates an IMethod from the given method declaration and type. */ protected IJavaElement createHandle(AbstractMethodDeclaration method, IJavaElement parent) { if (!(parent instanceof IType)) return parent; IType type= (IType)parent; Argument[] arguments= method.arguments; int argCount= arguments == null ? 0 : arguments.length; if (type.isBinary()) { // don't cache the methods of the binary type // fall thru if its a constructor with a synthetic argument... find it the slower way ClassFileReader reader= classFileReader(type); if (reader != null) { // build arguments names boolean firstIsSynthetic= false; if (reader.isMember() && method.isConstructor() && !Flags.isStatic(reader.getModifiers())) { // see https://bugs.eclipse.org/bugs/show_bug.cgi?id=48261 firstIsSynthetic= true; argCount++; } char[][] argumentTypeNames= new char[argCount][]; for (int i= 0; i < argCount; i++) { char[] typeName= null; if (i == 0 && firstIsSynthetic) { typeName= type.getDeclaringType().getFullyQualifiedName().toCharArray(); } else if (arguments != null) { TypeReference typeRef= arguments[firstIsSynthetic ? i - 1 : i].type; typeName= CharOperation.concatWith(typeRef.getTypeName(), '.'); for (int k= 0, dim= typeRef.dimensions(); k < dim; k++) typeName= CharOperation.concat(typeName, new char[] { '[', ']' }); } if (typeName == null) { // invalid type name return null; } argumentTypeNames[i]= typeName; } // return binary method IMethod binaryMethod= createBinaryMethodHandle(type, method.selector, argumentTypeNames); if (binaryMethod == null) { // when first attempt fails, try with similar matches if any... PossibleMatch similarMatch= this.currentPossibleMatch.getSimilarMatch(); while (similarMatch != null) { type= ((ClassFile)similarMatch.openable).getType(); binaryMethod= createBinaryMethodHandle(type, method.selector, argumentTypeNames); if (binaryMethod != null) { return binaryMethod; } similarMatch= similarMatch.getSimilarMatch(); } } return binaryMethod; } if (BasicSearchEngine.VERBOSE) { System.out.println("Not able to createHandle for the method " + //$NON-NLS-1$ CharOperation.charToString(method.selector) + " May miss some results"); //$NON-NLS-1$ } return null; } String[] parameterTypeSignatures= new String[argCount]; if (arguments != null) { for (int i= 0; i < argCount; i++) { TypeReference typeRef= arguments[i].type; char[] typeName= CharOperation.concatWith(typeRef.getParameterizedTypeName(), '.'); parameterTypeSignatures[i]= Signature.createTypeSignature(typeName, false); } } return createMethodHandle(type, new String(method.selector), parameterTypeSignatures); } /* * Create binary method handle */ IMethod createBinaryMethodHandle(IType type, char[] methodSelector, char[][] argumentTypeNames) { ClassFileReader reader= MatchLocator.classFileReader(type); if (reader != null) { IBinaryMethod[] methods= reader.getMethods(); if (methods != null) { int argCount= argumentTypeNames == null ? 0 : argumentTypeNames.length; nextMethod: for (int i= 0, methodsLength= methods.length; i < methodsLength; i++) { IBinaryMethod binaryMethod= methods[i]; char[] selector= binaryMethod.isConstructor() ? type.getElementName().toCharArray() : binaryMethod.getSelector(); if (CharOperation.equals(selector, methodSelector)) { char[] signature= binaryMethod.getGenericSignature(); if (signature == null) signature= binaryMethod.getMethodDescriptor(); char[][] parameterTypes= Signature.getParameterTypes(signature); if (argCount != parameterTypes.length) continue nextMethod; if (argumentTypeNames != null) { for (int j= 0; j < argCount; j++) { char[] parameterTypeName= ClassFileMatchLocator.convertClassFileFormat(parameterTypes[j]); if (!CharOperation.endsWith(Signature.toCharArray(Signature.getTypeErasure(parameterTypeName)), CharOperation.replaceOnCopy(argumentTypeNames[j], '$', '.'))) continue nextMethod; parameterTypes[j]= parameterTypeName; } } return (IMethod)createMethodHandle(type, new String(selector), CharOperation.toStrings(parameterTypes)); } } } } return null; } /* * Create method handle. * Store occurences for create handle to retrieve possible duplicate ones. */ private IJavaElement createMethodHandle(IType type, String methodName, String[] parameterTypeSignatures) { IMethod methodHandle= type.getMethod(methodName, parameterTypeSignatures); if (methodHandle instanceof SourceMethod) { while (this.methodHandles.contains(methodHandle)) { ((SourceMethod)methodHandle).occurrenceCount++; } } this.methodHandles.add(methodHandle); return methodHandle; } /** * Creates an IField from the given field declaration and type. */ protected IJavaElement createHandle(FieldDeclaration fieldDeclaration, TypeDeclaration typeDeclaration, IJavaElement parent) { if (!(parent instanceof IType)) return parent; IType type= (IType)parent; switch (fieldDeclaration.getKind()) { case AbstractVariableDeclaration.FIELD: case AbstractVariableDeclaration.ENUM_CONSTANT: return ((IType)parent).getField(new String(fieldDeclaration.name)); } if (type.isBinary()) { // do not return initializer for binary types // see bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=98378 return type; } // find occurence count of the given initializer in its type declaration int occurrenceCount= 0; FieldDeclaration[] fields= typeDeclaration.fields; for (int i= 0, length= fields.length; i < length; i++) { if (fields[i].getKind() == AbstractVariableDeclaration.INITIALIZER) { occurrenceCount++; if (fields[i].equals(fieldDeclaration)) break; } } return ((IType)parent).getInitializer(occurrenceCount); } /** * Create an handle for a local variable declaration (may be a local variable or type * parameter). */ protected IJavaElement createHandle(AbstractVariableDeclaration variableDeclaration, IJavaElement parent) { switch (variableDeclaration.getKind()) { case AbstractVariableDeclaration.LOCAL_VARIABLE: if (variableDeclaration.type.resolvedType != null) { return new LocalVariable((JavaElement)parent, new String(variableDeclaration.name), variableDeclaration.declarationSourceStart, variableDeclaration.declarationSourceEnd, variableDeclaration.sourceStart, variableDeclaration.sourceEnd, new String(variableDeclaration.type.resolvedType.signature()), variableDeclaration.annotations); } break; case AbstractVariableDeclaration.PARAMETER: if (variableDeclaration.type.resolvedType != null) { return new LocalVariable((JavaElement)parent, new String(variableDeclaration.name), variableDeclaration.declarationSourceStart, variableDeclaration.declarationSourceEnd, variableDeclaration.sourceStart, variableDeclaration.sourceEnd, new String(variableDeclaration.type.resolvedType.signature()), variableDeclaration.annotations); } break; case AbstractVariableDeclaration.TYPE_PARAMETER: return new org.eclipse.jdt.internal.core.TypeParameter((JavaElement)parent, new String(variableDeclaration.name)); } return null; } /** * Create an handle for a local variable declaration (may be a local variable or type * parameter). */ protected IJavaElement createHandle(Annotation annotation, IAnnotatable parent) { if (parent == null) return null; TypeReference typeRef= annotation.type; char[][] typeName= typeRef.getTypeName(); String name= new String(typeName[typeName.length - 1]); try { IAnnotation[] annotations= parent.getAnnotations(); int length= annotations == null ? 0 : annotations.length; for (int i= 0; i < length; i++) { if (annotations[i].getElementName().equals(name)) { return annotations[i]; } } } catch (JavaModelException jme) { // skip } return null; } /* * Create handles for a list of fields */ private IJavaElement[] createHandles(FieldDeclaration[] fields, TypeDeclaration type, IJavaElement parent) { IJavaElement[] otherElements= null; if (fields != null) { int length= fields.length; int size= 0; while (size < length && fields[size] != null) { size++; } otherElements= new IJavaElement[size]; for (int j= 0; j < size; j++) { otherElements[j]= createHandle(fields[j], type, parent); } } return otherElements; } /* * Creates hierarchy resolver if needed. * Returns whether focus is visible. */ protected boolean createHierarchyResolver(IType focusType, PossibleMatch[] possibleMatches) { // cache focus type if not a possible match char[][] compoundName= CharOperation.splitOn('.', focusType.getFullyQualifiedName().toCharArray()); boolean isPossibleMatch= false; for (int i= 0, length= possibleMatches.length; i < length; i++) { if (CharOperation.equals(possibleMatches[i].compoundName, compoundName)) { isPossibleMatch= true; break; } } if (!isPossibleMatch) { if (focusType.isBinary()) { try { cacheBinaryType(focusType, null); } catch (JavaModelException e) { return false; } } else { // cache all types in the focus' compilation unit (even secondary types) accept((ICompilationUnit)focusType.getCompilationUnit(), null /*TODO no access restriction*/); } } // resolve focus type this.hierarchyResolver= new HierarchyResolver(this.lookupEnvironment, null/*hierarchy is not going to be computed*/); ReferenceBinding binding= this.hierarchyResolver.setFocusType(compoundName); return binding != null && binding.isValidBinding() && (binding.tagBits & TagBits.HierarchyHasProblems) == 0; } /** * Creates an IImportDeclaration from the given import statement */ protected IJavaElement createImportHandle(ImportReference importRef) { char[] importName= CharOperation.concatWith(importRef.getImportName(), '.'); if ((importRef.bits & ASTNode.OnDemand) != 0) importName= CharOperation.concat(importName, ".*".toCharArray()); //$NON-NLS-1$ Openable openable= this.currentPossibleMatch.openable; if (openable instanceof CompilationUnit) return ((CompilationUnit)openable).getImport(new String(importName)); // binary types do not contain import statements so just answer the top-level type as the element IType binaryType= ((ClassFile)openable).getType(); String typeName= binaryType.getElementName(); int lastDollar= typeName.lastIndexOf('$'); if (lastDollar == -1) return binaryType; return createTypeHandle(typeName.substring(0, lastDollar)); } /** * Creates an IImportDeclaration from the given import statement */ protected IJavaElement createPackageDeclarationHandle(CompilationUnitDeclaration unit) { if (unit.isPackageInfo()) { char[] packName= CharOperation.concatWith(unit.currentPackage.getImportName(), '.'); Openable openable= this.currentPossibleMatch.openable; if (openable instanceof CompilationUnit) { return ((CompilationUnit)openable).getPackageDeclaration(new String(packName)); } } return createTypeHandle(new String(unit.getMainTypeName())); } /** * Creates an IType from the given simple top level type name. */ protected IType createTypeHandle(String simpleTypeName) { Openable openable= this.currentPossibleMatch.openable; if (openable instanceof CompilationUnit) return ((CompilationUnit)openable).getType(simpleTypeName); IType binaryType= ((ClassFile)openable).getType(); String binaryTypeQualifiedName= binaryType.getTypeQualifiedName(); if (simpleTypeName.equals(binaryTypeQualifiedName)) return binaryType; // answer only top-level types, sometimes the classFile is for a member/local type // type name may be null for anonymous (see bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=164791) String classFileName= simpleTypeName.length() == 0 ? binaryTypeQualifiedName : simpleTypeName; IClassFile classFile= binaryType.getPackageFragment().getClassFile(classFileName + SuffixConstants.SUFFIX_STRING_class); return classFile.getType(); } protected boolean encloses(IJavaElement element) { if (element != null) { if (this.scope instanceof HierarchyScope) return ((HierarchyScope)this.scope).encloses(element, this.progressMonitor); else return this.scope.encloses(element); } return false; } /* (non-Javadoc) * Return info about last type argument of a parameterized type reference. * These info are made of concatenation of 2 int values which are respectively * depth and end position of the last type argument. * For example, this method will return 0x300000020 for type ref List<List<List<String>>> * if end position of type reference "String" equals 32. */ private long findLastTypeArgumentInfo(TypeReference typeRef) { // Get last list of type arguments for parameterized qualified type reference TypeReference lastTypeArgument= typeRef; int depth= 0; while (true) { TypeReference[] lastTypeArguments= null; if (lastTypeArgument instanceof ParameterizedQualifiedTypeReference) { ParameterizedQualifiedTypeReference pqtRef= (ParameterizedQualifiedTypeReference)lastTypeArgument; for (int i= pqtRef.typeArguments.length - 1; i >= 0 && lastTypeArguments == null; i--) { lastTypeArguments= pqtRef.typeArguments[i]; } } // Get last type argument for single type reference of last list of argument of parameterized qualified type reference TypeReference last= null; if (lastTypeArgument instanceof ParameterizedSingleTypeReference || lastTypeArguments != null) { if (lastTypeArguments == null) { lastTypeArguments= ((ParameterizedSingleTypeReference)lastTypeArgument).typeArguments; } if (lastTypeArguments != null) { for (int i= lastTypeArguments.length - 1; i >= 0 && last == null; i++) { last= lastTypeArguments[i]; } } } if (last == null) break; depth++; lastTypeArgument= last; } // Current type reference is not parameterized. So, it is the last type argument return (((long)depth) << 32) + lastTypeArgument.sourceEnd; } protected IBinaryType getBinaryInfo(ClassFile classFile, IResource resource) throws CoreException { BinaryType binaryType= (BinaryType)classFile.getType(); if (classFile.isOpen()) return (IBinaryType)binaryType.getElementInfo(); // reuse the info from the java model cache // create a temporary info IBinaryType info; try { PackageFragment pkg= (PackageFragment)classFile.getParent(); PackageFragmentRoot root= (PackageFragmentRoot)pkg.getParent(); if (root.isArchive()) { // class file in a jar String classFileName= classFile.getElementName(); String classFilePath= Util.concatWith(pkg.names, classFileName, '/'); ZipFile zipFile= null; try { zipFile= ((JarPackageFragmentRoot)root).getJar(); info= ClassFileReader.read(zipFile, classFilePath); } finally { JavaModelManager.getJavaModelManager().closeZipFile(zipFile); } } else { // class file in a directory info= Util.newClassFileReader(resource); } if (info == null) throw binaryType.newNotPresentException(); return info; } catch (ClassFormatException e) { //e.printStackTrace(); return null; } catch (java.io.IOException e) { throw new JavaModelException(e, IJavaModelStatusConstants.IO_EXCEPTION); } } protected IType getFocusType() { return this.scope instanceof HierarchyScope ? ((HierarchyScope)this.scope).focusType : null; } protected void getMethodBodies(CompilationUnitDeclaration unit, MatchingNodeSet nodeSet) { if (unit.ignoreMethodBodies) { unit.ignoreFurtherInvestigation= true; return; // if initial diet parse did not work, no need to dig into method bodies. } // save existing values to restore them at the end of the parsing process // see bug 47079 for more details int[] oldLineEnds= this.parser.scanner.lineEnds; int oldLinePtr= this.parser.scanner.linePtr; try { CompilationResult compilationResult= unit.compilationResult; this.parser.scanner.setSource(compilationResult); if (this.parser.javadocParser.checkDocComment) { char[] contents= compilationResult.compilationUnit.getContents(); this.parser.javadocParser.scanner.setSource(contents); } this.parser.nodeSet= nodeSet; this.parser.parseBodies(unit); } finally { this.parser.nodeSet= null; // this is done to prevent any side effects on the compilation unit result // line separator positions array. this.parser.scanner.lineEnds= oldLineEnds; this.parser.scanner.linePtr= oldLinePtr; } } protected TypeBinding getType(Object typeKey, char[] typeName) { if (this.unitScope == null || typeName == null || typeName.length == 0) return null; // Try to get binding from cache Binding binding= (Binding)this.bindings.get(typeKey); if (binding != null) { if (binding instanceof TypeBinding && binding.isValidBinding()) return (TypeBinding)binding; return null; } // Get binding from unit scope char[][] compoundName= CharOperation.splitOn('.', typeName); TypeBinding typeBinding= this.unitScope.getType(compoundName, compoundName.length); this.bindings.put(typeKey, typeBinding); return typeBinding.isValidBinding() ? typeBinding : null; } public MethodBinding getMethodBinding(MethodPattern methodPattern) { if (this.unitScope == null) return null; // Try to get binding from cache Binding binding= (Binding)this.bindings.get(methodPattern); if (binding != null) { if (binding instanceof MethodBinding && binding.isValidBinding()) return (MethodBinding)binding; return null; } // Get binding from unit scope char[] typeName= PatternLocator.qualifiedPattern(methodPattern.declaringSimpleName, methodPattern.declaringQualification); if (typeName == null) { if (methodPattern.declaringType == null) return null; typeName= methodPattern.declaringType.getFullyQualifiedName().toCharArray(); } TypeBinding declaringTypeBinding= getType(typeName, typeName); if (declaringTypeBinding != null) { if (declaringTypeBinding.isArrayType()) { declaringTypeBinding= declaringTypeBinding.leafComponentType(); } if (!declaringTypeBinding.isBaseType()) { char[][] parameterTypes= methodPattern.parameterSimpleNames; if (parameterTypes == null) return null; int paramTypeslength= parameterTypes.length; ReferenceBinding referenceBinding= (ReferenceBinding)declaringTypeBinding; MethodBinding[] methods= referenceBinding.getMethods(methodPattern.selector); int methodsLength= methods.length; TypeVariableBinding[] refTypeVariables= referenceBinding.typeVariables(); int typeVarLength= refTypeVariables == null ? 0 : refTypeVariables.length; for (int i= 0; i < methodsLength; i++) { TypeBinding[] methodParameters= methods[i].parameters; int paramLength= methodParameters == null ? 0 : methodParameters.length; TypeVariableBinding[] methodTypeVariables= methods[i].typeVariables; int methTypeVarLength= methodTypeVariables == null ? 0 : methodTypeVariables.length; boolean found= false; if (methodParameters != null && paramLength == paramTypeslength) { for (int p= 0; p < paramLength; p++) { if (CharOperation.equals(methodParameters[p].sourceName(), parameterTypes[p])) { // param erasure match found= true; } else { // type variable found= false; if (refTypeVariables != null) { for (int v= 0; v < typeVarLength; v++) { if (!CharOperation.equals(refTypeVariables[v].sourceName, parameterTypes[p])) { found= false; break; } found= true; } } if (!found && methodTypeVariables != null) { for (int v= 0; v < methTypeVarLength; v++) { if (!CharOperation.equals(methodTypeVariables[v].sourceName, parameterTypes[p])) { found= false; break; } found= true; } } if (!found) break; } } } if (found) { this.bindings.put(methodPattern, methods[i]); return methods[i]; } } } } this.bindings.put(methodPattern, new ProblemMethodBinding(methodPattern.selector, null, ProblemReasons.NotFound)); return null; } protected boolean hasAlreadyDefinedType(CompilationUnitDeclaration parsedUnit) { CompilationResult result= parsedUnit.compilationResult; if (result == null) return false; for (int i= 0; i < result.problemCount; i++) if (result.problems[i].getID() == IProblem.DuplicateTypes) return true; return false; } /** * Create a new parser for the given project, as well as a lookup environment. */ public void initialize(JavaProject project, int possibleMatchSize) throws JavaModelException { // clean up name environment only if there are several possible match as it is reused // when only one possible match (bug 58581) if (this.nameEnvironment != null && possibleMatchSize != 1) this.nameEnvironment.cleanup(); SearchableEnvironment searchableEnvironment= project.newSearchableNameEnvironment(this.workingCopies); // if only one possible match, a file name environment costs too much, // so use the existing searchable environment which will populate the java model // only for this possible match and its required types. this.nameEnvironment= possibleMatchSize == 1 ? (INameEnvironment)searchableEnvironment : (INameEnvironment)new JavaSearchNameEnvironment(project, this.workingCopies); // create lookup environment Map map= project.getOptions(true); map.put(CompilerOptions.OPTION_TaskTags, org.eclipse.jdt.internal.compiler.util.Util.EMPTY_STRING); this.options= new CompilerOptions(map); ProblemReporter problemReporter= new ProblemReporter( DefaultErrorHandlingPolicies.proceedWithAllProblems(), this.options, new DefaultProblemFactory()); this.lookupEnvironment= new LookupEnvironment(this, this.options, problemReporter, this.nameEnvironment); this.parser= MatchLocatorParser.createParser(problemReporter, this); // basic parser needs also to be reset as project options may have changed // see bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=163072 this.basicParser= null; // remember project's name lookup this.nameLookup= searchableEnvironment.nameLookup; // initialize queue of units this.numberOfMatches= 0; this.matchesToProcess= new PossibleMatch[possibleMatchSize]; } protected void locateMatches(JavaProject javaProject, PossibleMatch[] possibleMatches, int start, int length) throws CoreException { initialize(javaProject, length); // create and resolve binding (equivalent to beginCompilation() in Compiler) boolean mustResolvePattern= this.pattern.mustResolve; boolean mustResolve= mustResolvePattern; this.patternLocator.mayBeGeneric= this.options.sourceLevel >= ClassFileConstants.JDK1_5; boolean bindingsWereCreated= mustResolve; try { for (int i= start, maxUnits= start + length; i < maxUnits; i++) { PossibleMatch possibleMatch= possibleMatches[i]; try { if (!parseAndBuildBindings(possibleMatch, mustResolvePattern)) continue; // Currently we only need to resolve over pattern flag if there's potential parameterized types if (this.patternLocator.mayBeGeneric) { // If pattern does not resolve then rely on possible match node set resolution // which may have been modified while locator was adding possible matches to it if (!mustResolvePattern && !mustResolve) { mustResolve= possibleMatch.nodeSet.mustResolve; bindingsWereCreated= mustResolve; } } else { // Reset matching node resolution with pattern one if there's no potential parameterized type // to minimize side effect on previous search behavior possibleMatch.nodeSet.mustResolve= mustResolvePattern; } // possible match node resolution has been merged with pattern one, so rely on it to know // whether we need to process compilation unit now or later if (!possibleMatch.nodeSet.mustResolve) { if (this.progressMonitor != null) { this.progressWorked++; if ((this.progressWorked % this.progressStep) == 0) this.progressMonitor.worked(this.progressStep); } process(possibleMatch, bindingsWereCreated); if (this.numberOfMatches > 0 && this.matchesToProcess[this.numberOfMatches - 1] == possibleMatch) { // forget last possible match as it was processed this.numberOfMatches--; } } } finally { if (possibleMatch.hasSimilarMatch()) { // If there is similar match, then also process it // see bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=211872 possibleMatches[i]= possibleMatch.getSimilarMatch(); i--; } if (!possibleMatch.nodeSet.mustResolve) possibleMatch.cleanUp(); } } if (mustResolve) this.lookupEnvironment.completeTypeBindings(); // create hierarchy resolver if needed IType focusType= getFocusType(); if (focusType == null) { this.hierarchyResolver= null; } else if (!createHierarchyResolver(focusType, possibleMatches)) { // focus type is not visible, use the super type names instead of the bindings if (computeSuperTypeNames(focusType) == null) return; } } catch (AbortCompilation e) { bindingsWereCreated= false; } if (!mustResolve) { return; } // possible match resolution for (int i= 0; i < this.numberOfMatches; i++) { if (this.progressMonitor != null && this.progressMonitor.isCanceled()) throw new OperationCanceledException(); PossibleMatch possibleMatch= this.matchesToProcess[i]; this.matchesToProcess[i]= null; // release reference to processed possible match try { process(possibleMatch, bindingsWereCreated); } catch (AbortCompilation e) { // problem with class path: it could not find base classes // continue and try next matching openable reporting innacurate matches (since bindings will be null) bindingsWereCreated= false; } catch (JavaModelException e) { // problem with class path: it could not find base classes // continue and try next matching openable reporting innacurate matches (since bindings will be null) bindingsWereCreated= false; } finally { if (this.progressMonitor != null) { this.progressWorked++; if ((this.progressWorked % this.progressStep) == 0) this.progressMonitor.worked(this.progressStep); } if (this.options.verbose) System.out.println( Messages.bind(Messages.compilation_done, new String[] { String.valueOf(i + 1), String.valueOf(this.numberOfMatches), new String(possibleMatch.parsedUnit.getFileName()) })); // cleanup compilation unit result possibleMatch.cleanUp(); } } } /** * Locate the matches amongst the possible matches. */ protected void locateMatches(JavaProject javaProject, PossibleMatchSet matchSet, int expected) throws CoreException { PossibleMatch[] possibleMatches= matchSet.getPossibleMatches(javaProject.getPackageFragmentRoots()); int length= possibleMatches.length; // increase progress from duplicate matches not stored in matchSet while adding... if (this.progressMonitor != null && expected > length) { this.progressWorked+= expected - length; this.progressMonitor.worked(expected - length); } // locate matches (processed matches are limited to avoid problem while using VM default memory heap size) for (int index= 0; index < length;) { int max= Math.min(MAX_AT_ONCE, length - index); locateMatches(javaProject, possibleMatches, index, max); index+= max; } this.patternLocator.clear(); } /** * Locate the matches in the given files and report them using the search requestor. */ public void locateMatches(SearchDocument[] searchDocuments) throws CoreException { if (this.patternLocator == null) return; int docsLength= searchDocuments.length; int progressLength= docsLength; if (BasicSearchEngine.VERBOSE) { System.out.println("Locating matches in documents ["); //$NON-NLS-1$ for (int i= 0; i < docsLength; i++) System.out.println("\t" + searchDocuments[i]); //$NON-NLS-1$ System.out.println("]"); //$NON-NLS-1$ } IJavaProject[] javaModelProjects= null; if (this.searchPackageDeclaration) { javaModelProjects= JavaModelManager.getJavaModelManager().getJavaModel().getJavaProjects(); progressLength+= javaModelProjects.length; } // init infos for progress increasing int n= progressLength < 1000 ? Math.min(Math.max(progressLength / 200 + 1, 2), 4) : 5 * (progressLength / 1000); this.progressStep= progressLength < n ? 1 : progressLength / n; // step should not be 0 this.progressWorked= 0; // extract working copies ArrayList copies= new ArrayList(); for (int i= 0; i < docsLength; i++) { SearchDocument document= searchDocuments[i]; if (document instanceof WorkingCopyDocument) { copies.add(((WorkingCopyDocument)document).workingCopy); } } int copiesLength= copies.size(); this.workingCopies= new org.eclipse.jdt.core.ICompilationUnit[copiesLength]; copies.toArray(this.workingCopies); JavaModelManager manager= JavaModelManager.getJavaModelManager(); this.bindings= new SimpleLookupTable(); try { // optimize access to zip files during search operation manager.cacheZipFiles(this); // initialize handle factory (used as a cache of handles so as to optimize space) if (this.handleFactory == null) this.handleFactory= new HandleFactory(); if (this.progressMonitor != null) { this.progressMonitor.beginTask("", searchDocuments.length); //$NON-NLS-1$ } // initialize pattern for polymorphic search (ie. method reference pattern) this.patternLocator.initializePolymorphicSearch(this); JavaProject previousJavaProject= null; PossibleMatchSet matchSet= new PossibleMatchSet(); Util.sort(searchDocuments, new Util.Comparer() { public int compare(Object a, Object b) { return ((SearchDocument)a).getPath().compareTo(((SearchDocument)b).getPath()); } }); int displayed= 0; // progress worked displayed String previousPath= null; SearchParticipant searchParticipant= null; for (int i= 0; i < docsLength; i++) { if (this.progressMonitor != null && this.progressMonitor.isCanceled()) { throw new OperationCanceledException(); } // skip duplicate paths SearchDocument searchDocument= searchDocuments[i]; if (searchParticipant == null) { searchParticipant= searchDocument.getParticipant(); } searchDocuments[i]= null; // free current document String pathString= searchDocument.getPath(); if (i > 0 && pathString.equals(previousPath)) { if (this.progressMonitor != null) { this.progressWorked++; if ((this.progressWorked % this.progressStep) == 0) this.progressMonitor.worked(this.progressStep); } displayed++; continue; } previousPath= pathString; Openable openable; org.eclipse.jdt.core.ICompilationUnit workingCopy= null; if (searchDocument instanceof WorkingCopyDocument) { workingCopy= ((WorkingCopyDocument)searchDocument).workingCopy; openable= (Openable)workingCopy; } else { openable= this.handleFactory.createOpenable(pathString, this.scope); } if (openable == null) { if (this.progressMonitor != null) { this.progressWorked++; if ((this.progressWorked % this.progressStep) == 0) this.progressMonitor.worked(this.progressStep); } displayed++; continue; // match is outside classpath } // create new parser and lookup environment if this is a new project IResource resource= null; JavaProject javaProject= (JavaProject)openable.getJavaProject(); resource= workingCopy != null ? workingCopy.getResource() : openable.getResource(); if (resource == null) resource= javaProject.getProject(); // case of a file in an external jar or external folder if (!javaProject.equals(previousJavaProject)) { // locate matches in previous project if (previousJavaProject != null) { try { locateMatches(previousJavaProject, matchSet, i - displayed); displayed= i; } catch (JavaModelException e) { // problem with classpath in this project -> skip it } matchSet.reset(); } previousJavaProject= javaProject; } matchSet.add(new PossibleMatch(this, resource, openable, searchDocument, this.pattern.mustResolve)); } // last project if (previousJavaProject != null) { try { locateMatches(previousJavaProject, matchSet, docsLength - displayed); } catch (JavaModelException e) { // problem with classpath in last project -> ignore } } if (this.searchPackageDeclaration) { locatePackageDeclarations(searchParticipant, javaModelProjects); } } finally { if (this.progressMonitor != null) this.progressMonitor.done(); if (this.nameEnvironment != null) this.nameEnvironment.cleanup(); manager.flushZipFiles(this); this.bindings= null; } } /** * Locates the package declarations corresponding to this locator's pattern. */ protected void locatePackageDeclarations(SearchParticipant participant, IJavaProject[] projects) throws CoreException { locatePackageDeclarations(this.pattern, participant, projects); } /** * Locates the package declarations corresponding to the search pattern. */ protected void locatePackageDeclarations(SearchPattern searchPattern, SearchParticipant participant, IJavaProject[] projects) throws CoreException { if (this.progressMonitor != null && this.progressMonitor.isCanceled()) { throw new OperationCanceledException(); } if (searchPattern instanceof OrPattern) { SearchPattern[] patterns= ((OrPattern)searchPattern).patterns; for (int i= 0, length= patterns.length; i < length; i++) { locatePackageDeclarations(patterns[i], participant, projects); } } else if (searchPattern instanceof PackageDeclarationPattern) { IJavaElement focus= searchPattern.focus; if (focus != null) { if (encloses(focus)) { SearchMatch match= new PackageDeclarationMatch(focus.getAncestor(IJavaElement.PACKAGE_FRAGMENT), SearchMatch.A_ACCURATE, -1, -1, participant, focus.getResource()); report(match); } return; } PackageDeclarationPattern pkgPattern= (PackageDeclarationPattern)searchPattern; boolean isWorkspaceScope= this.scope == JavaModelManager.getJavaModelManager().getWorkspaceScope(); IPath[] scopeProjectsAndJars= isWorkspaceScope ? null : this.scope.enclosingProjectsAndJars(); int scopeLength= isWorkspaceScope ? 0 : scopeProjectsAndJars.length; SimpleSet packages= new SimpleSet(); for (int i= 0, length= projects.length; i < length; i++) { IJavaProject javaProject= projects[i]; if (this.progressMonitor != null) { if (this.progressMonitor.isCanceled()) throw new OperationCanceledException(); this.progressWorked++; if ((this.progressWorked % this.progressStep) == 0) this.progressMonitor.worked(this.progressStep); } // Verify that project belongs to the scope if (!isWorkspaceScope) { boolean found= false; for (int j= 0; j < scopeLength; j++) { if (javaProject.getPath().equals(scopeProjectsAndJars[j])) { found= true; break; } } if (!found) continue; } // Get all project package fragment names this.nameLookup= ((JavaProject)projects[i]).newNameLookup(this.workingCopies); IPackageFragment[] packageFragments= this.nameLookup.findPackageFragments(new String(pkgPattern.pkgName), false, true); int pLength= packageFragments == null ? 0 : packageFragments.length; // Report matches avoiding duplicate names for (int p= 0; p < pLength; p++) { IPackageFragment fragment= packageFragments[p]; if (packages.addIfNotIncluded(fragment) == null) continue; if (encloses(fragment)) { IResource resource= fragment.getResource(); if (resource == null) // case of a file in an external jar resource= javaProject.getProject(); try { if (encloses(fragment)) { SearchMatch match= new PackageDeclarationMatch(fragment, SearchMatch.A_ACCURATE, -1, -1, participant, resource); report(match); } } catch (JavaModelException e) { throw e; } catch (CoreException e) { throw new JavaModelException(e); } } } } } } //*/ protected IType lookupType(ReferenceBinding typeBinding) { if (typeBinding == null) return null; char[] packageName= typeBinding.qualifiedPackageName(); IPackageFragment[] pkgs= this.nameLookup.findPackageFragments( (packageName == null || packageName.length == 0) ? IPackageFragment.DEFAULT_PACKAGE_NAME : new String(packageName), false); // iterate type lookup in each package fragment char[] sourceName= typeBinding.qualifiedSourceName(); String typeName= new String(sourceName); int acceptFlag= 0; if (typeBinding.isAnnotationType()) { acceptFlag= NameLookup.ACCEPT_ANNOTATIONS; } else if (typeBinding.isEnum()) { acceptFlag= NameLookup.ACCEPT_ENUMS; } else if (typeBinding.isInterface()) { acceptFlag= NameLookup.ACCEPT_INTERFACES; } else if (typeBinding.isClass()) { acceptFlag= NameLookup.ACCEPT_CLASSES; } if (pkgs != null) { for (int i= 0, length= pkgs.length; i < length; i++) { IType type= this.nameLookup.findType(typeName, pkgs[i], false, acceptFlag, true/*consider secondary types*/); if (type != null) return type; } } // search inside enclosing element char[][] qualifiedName= CharOperation.splitOn('.', sourceName); int length= qualifiedName.length; if (length == 0) return null; IType type= createTypeHandle(new String(qualifiedName[0])); // find the top-level type if (type == null) return null; for (int i= 1; i < length; i++) { type= type.getType(new String(qualifiedName[i])); if (type == null) return null; } if (type.exists()) return type; return null; } public SearchMatch newDeclarationMatch( IJavaElement element, Binding binding, int accuracy, int offset, int length) { SearchParticipant participant= getParticipant(); IResource resource= this.currentPossibleMatch.resource; return newDeclarationMatch(element, binding, accuracy, offset, length, participant, resource); } public SearchMatch newDeclarationMatch( IJavaElement element, Binding binding, int accuracy, int offset, int length, SearchParticipant participant, IResource resource) { switch (element.getElementType()) { case IJavaElement.PACKAGE_FRAGMENT: return new PackageDeclarationMatch(element, accuracy, offset, length, participant, resource); case IJavaElement.TYPE: return new TypeDeclarationMatch(binding == null ? element : ((JavaElement)element).resolved(binding), accuracy, offset, length, participant, resource); case IJavaElement.FIELD: return new FieldDeclarationMatch(binding == null ? element : ((JavaElement)element).resolved(binding), accuracy, offset, length, participant, resource); case IJavaElement.METHOD: return new MethodDeclarationMatch(binding == null ? element : ((JavaElement)element).resolved(binding), accuracy, offset, length, participant, resource); case IJavaElement.LOCAL_VARIABLE: return new LocalVariableDeclarationMatch(element, accuracy, offset, length, participant, resource); case IJavaElement.PACKAGE_DECLARATION: return new PackageDeclarationMatch(element, accuracy, offset, length, participant, resource); case IJavaElement.TYPE_PARAMETER: return new TypeParameterDeclarationMatch(element, accuracy, offset, length, participant, resource); default: return null; } } public FieldReferenceMatch newFieldReferenceMatch( IJavaElement enclosingElement, IJavaElement localElement, Binding enclosingBinding, int accuracy, int offset, int length, ASTNode reference) { int bits= reference.bits; boolean isCompoundAssigned= (bits & ASTNode.IsCompoundAssigned) != 0; boolean isReadAccess= isCompoundAssigned || (bits & ASTNode.IsStrictlyAssigned) == 0; boolean isWriteAccess= isCompoundAssigned || (bits & ASTNode.IsStrictlyAssigned) != 0; if (isWriteAccess) { if (reference instanceof QualifiedNameReference) { char[][] tokens= ((QualifiedNameReference)reference).tokens; char[] lastToken= tokens[tokens.length - 1]; if (this.pattern instanceof OrPattern) { SearchPattern[] patterns= ((OrPattern)this.pattern).patterns; for (int i= 0, pLength= patterns.length; i < pLength; i++) { if (!this.patternLocator.matchesName(((VariablePattern)patterns[i]).name, lastToken)) { isWriteAccess= false; isReadAccess= true; } } } else if (!this.patternLocator.matchesName(((VariablePattern)this.pattern).name, lastToken)) { isWriteAccess= false; isReadAccess= true; } } } boolean insideDocComment= (bits & ASTNode.InsideJavadoc) != 0; SearchParticipant participant= getParticipant(); IResource resource= this.currentPossibleMatch.resource; if (enclosingBinding != null) { enclosingElement= ((JavaElement)enclosingElement).resolved(enclosingBinding); } FieldReferenceMatch match= new FieldReferenceMatch(enclosingElement, accuracy, offset, length, isReadAccess, isWriteAccess, insideDocComment, participant, resource); match.setLocalElement(localElement); return match; } public SearchMatch newLocalVariableReferenceMatch( IJavaElement enclosingElement, int accuracy, int offset, int length, ASTNode reference) { int bits= reference.bits; boolean isCompoundAssigned= (bits & ASTNode.IsCompoundAssigned) != 0; boolean isReadAccess= isCompoundAssigned || (bits & ASTNode.IsStrictlyAssigned) == 0; boolean isWriteAccess= isCompoundAssigned || (bits & ASTNode.IsStrictlyAssigned) != 0; if (isWriteAccess) { if (reference instanceof QualifiedNameReference) { char[][] tokens= ((QualifiedNameReference)reference).tokens; char[] lastToken= tokens[tokens.length - 1]; if (this.pattern instanceof OrPattern) { SearchPattern[] patterns= ((OrPattern)this.pattern).patterns; for (int i= 0, pLength= patterns.length; i < pLength; i++) { if (!this.patternLocator.matchesName(((VariablePattern)patterns[i]).name, lastToken)) { isWriteAccess= false; isReadAccess= true; } } } else if (!this.patternLocator.matchesName(((VariablePattern)this.pattern).name, lastToken)) { isWriteAccess= false; isReadAccess= true; } } } boolean insideDocComment= (bits & ASTNode.InsideJavadoc) != 0; SearchParticipant participant= getParticipant(); IResource resource= this.currentPossibleMatch.resource; return new LocalVariableReferenceMatch(enclosingElement, accuracy, offset, length, isReadAccess, isWriteAccess, insideDocComment, participant, resource); } public MethodReferenceMatch newMethodReferenceMatch( IJavaElement enclosingElement, Binding enclosingBinding, int accuracy, int offset, int length, boolean isConstructor, boolean isSynthetic, ASTNode reference) { SearchParticipant participant= getParticipant(); IResource resource= this.currentPossibleMatch.resource; boolean insideDocComment= (reference.bits & ASTNode.InsideJavadoc) != 0; if (enclosingBinding != null) enclosingElement= ((JavaElement)enclosingElement).resolved(enclosingBinding); boolean isOverridden= (accuracy & PatternLocator.SUPER_INVOCATION_FLAVOR) != 0; return new MethodReferenceMatch(enclosingElement, accuracy, offset, length, isConstructor, isSynthetic, isOverridden, insideDocComment, participant, resource); } public PackageReferenceMatch newPackageReferenceMatch( IJavaElement enclosingElement, int accuracy, int offset, int length, ASTNode reference) { SearchParticipant participant= getParticipant(); IResource resource= this.currentPossibleMatch.resource; boolean insideDocComment= (reference.bits & ASTNode.InsideJavadoc) != 0; return new PackageReferenceMatch(enclosingElement, accuracy, offset, length, insideDocComment, participant, resource); } public SearchMatch newTypeParameterReferenceMatch( IJavaElement enclosingElement, int accuracy, int offset, int length, ASTNode reference) { int bits= reference.bits; boolean insideDocComment= (bits & ASTNode.InsideJavadoc) != 0; SearchParticipant participant= getParticipant(); IResource resource= this.currentPossibleMatch.resource; return new TypeParameterReferenceMatch(enclosingElement, accuracy, offset, length, insideDocComment, participant, resource); } public TypeReferenceMatch newTypeReferenceMatch( IJavaElement enclosingElement, Binding enclosingBinding, int accuracy, int offset, int length, ASTNode reference) { SearchParticipant participant= getParticipant(); IResource resource= this.currentPossibleMatch.resource; boolean insideDocComment= (reference.bits & ASTNode.InsideJavadoc) != 0; if (enclosingBinding != null) enclosingElement= ((JavaElement)enclosingElement).resolved(enclosingBinding); return new TypeReferenceMatch(enclosingElement, accuracy, offset, length, insideDocComment, participant, resource); } public TypeReferenceMatch newTypeReferenceMatch( IJavaElement enclosingElement, Binding enclosingBinding, int accuracy, ASTNode reference) { return newTypeReferenceMatch(enclosingElement, enclosingBinding, accuracy, reference.sourceStart, reference.sourceEnd - reference.sourceStart + 1, reference); } /** * Add the possibleMatch to the loop -> build compilation unit declarations, their bindings and * record their results. */ protected boolean parseAndBuildBindings(PossibleMatch possibleMatch, boolean mustResolve) throws CoreException { if (this.progressMonitor != null && this.progressMonitor.isCanceled()) throw new OperationCanceledException(); try { if (BasicSearchEngine.VERBOSE) System.out.println("Parsing " + possibleMatch.openable.toStringWithAncestors()); //$NON-NLS-1$ this.parser.nodeSet= possibleMatch.nodeSet; CompilationResult unitResult= new CompilationResult(possibleMatch, 1, 1, this.options.maxProblemsPerUnit); CompilationUnitDeclaration parsedUnit= this.parser.dietParse(possibleMatch, unitResult); if (parsedUnit != null) { if (!parsedUnit.isEmpty()) { if (mustResolve) { this.lookupEnvironment.buildTypeBindings(parsedUnit, null /*no access restriction*/); } if (hasAlreadyDefinedType(parsedUnit)) return false; // skip type has it is hidden so not visible getMethodBodies(parsedUnit, possibleMatch.nodeSet); if (this.patternLocator.mayBeGeneric && !mustResolve && possibleMatch.nodeSet.mustResolve) { // special case: possible match node set force resolution although pattern does not // => we need to build types for this compilation unit this.lookupEnvironment.buildTypeBindings(parsedUnit, null /*no access restriction*/); } } // add the possibleMatch with its parsedUnit to matchesToProcess possibleMatch.parsedUnit= parsedUnit; int size= this.matchesToProcess.length; if (this.numberOfMatches == size) System.arraycopy(this.matchesToProcess, 0, this.matchesToProcess= new PossibleMatch[size == 0 ? 1 : size * 2], 0, this.numberOfMatches); this.matchesToProcess[this.numberOfMatches++]= possibleMatch; } } finally { this.parser.nodeSet= null; } return true; } /* * Process a compilation unit already parsed and build. */ protected void process(PossibleMatch possibleMatch, boolean bindingsWereCreated) throws CoreException { this.currentPossibleMatch= possibleMatch; CompilationUnitDeclaration unit= possibleMatch.parsedUnit; try { if (unit.isEmpty()) { if (this.currentPossibleMatch.openable instanceof ClassFile) { ClassFile classFile= (ClassFile)this.currentPossibleMatch.openable; IBinaryType info= null; try { info= getBinaryInfo(classFile, classFile.resource()); } catch (CoreException ce) { // Do nothing } if (info != null) { boolean mayBeGeneric= this.patternLocator.mayBeGeneric; this.patternLocator.mayBeGeneric= false; // there's no longer generic in class files try { new ClassFileMatchLocator().locateMatches(this, classFile, info); } finally { this.patternLocator.mayBeGeneric= mayBeGeneric; } } } return; } if (hasAlreadyDefinedType(unit)) return; // skip type has it is hidden so not visible // Move getMethodBodies to #parseAndBuildings(...) method to allow possible match resolution management //getMethodBodies(unit); boolean mustResolve= (this.pattern.mustResolve || possibleMatch.nodeSet.mustResolve); if (bindingsWereCreated && mustResolve) { if (unit.types != null) { if (BasicSearchEngine.VERBOSE) System.out.println("Resolving " + this.currentPossibleMatch.openable.toStringWithAncestors()); //$NON-NLS-1$ this.lookupEnvironment.unitBeingCompleted= unit; reduceParseTree(unit); if (unit.scope != null) { // fault in fields & methods unit.scope.faultInTypes(); } unit.resolve(); } else if (unit.isPackageInfo()) { if (BasicSearchEngine.VERBOSE) System.out.println("Resolving " + this.currentPossibleMatch.openable.toStringWithAncestors()); //$NON-NLS-1$ unit.resolve(); } } reportMatching(unit, mustResolve); } catch (AbortCompilation e) { // could not resolve: report inaccurate matches reportMatching(unit, false); // do not resolve when cu has errors if (!(e instanceof AbortCompilationUnit)) { // problem with class path throw e; } } finally { this.lookupEnvironment.unitBeingCompleted= null; this.currentPossibleMatch= null; } } protected void purgeMethodStatements(TypeDeclaration type, boolean checkEachMethod) { checkEachMethod= checkEachMethod && this.currentPossibleMatch.nodeSet.hasPossibleNodes(type.declarationSourceStart, type.declarationSourceEnd); AbstractMethodDeclaration[] methods= type.methods; if (methods != null) { if (checkEachMethod) { for (int j= 0, length= methods.length; j < length; j++) { AbstractMethodDeclaration method= methods[j]; if (!this.currentPossibleMatch.nodeSet.hasPossibleNodes(method.declarationSourceStart, method.declarationSourceEnd)) { method.statements= null; method.javadoc= null; } } } else { for (int j= 0, length= methods.length; j < length; j++) { methods[j].statements= null; methods[j].javadoc= null; } } } TypeDeclaration[] memberTypes= type.memberTypes; if (memberTypes != null) for (int i= 0, l= memberTypes.length; i < l; i++) purgeMethodStatements(memberTypes[i], checkEachMethod); } /** * Called prior to the unit being resolved. Reduce the parse tree where possible. */ protected void reduceParseTree(CompilationUnitDeclaration unit) { // remove statements from methods that have no possible matching nodes TypeDeclaration[] types= unit.types; for (int i= 0, l= types.length; i < l; i++) purgeMethodStatements(types[i], true); } public SearchParticipant getParticipant() { return this.currentPossibleMatch.document.getParticipant(); } protected void report(SearchMatch match) throws CoreException { if (match == null) { if (BasicSearchEngine.VERBOSE) { System.out.println("Cannot report a null match!!!"); //$NON-NLS-1$ } return; } long start= -1; if (BasicSearchEngine.VERBOSE) { start= System.currentTimeMillis(); System.out.println("Reporting match"); //$NON-NLS-1$ System.out.println("\tResource: " + match.getResource());//$NON-NLS-1$ System.out.println("\tPositions: [offset=" + match.getOffset() + ", length=" + match.getLength() + "]"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ try { if (this.parser != null && match.getOffset() > 0 && match.getLength() > 0 && !(match.getElement() instanceof BinaryMember)) { String selection= new String(this.parser.scanner.source, match.getOffset(), match.getLength()); System.out.println("\tSelection: -->" + selection + "<--"); //$NON-NLS-1$ //$NON-NLS-2$ } } catch (Exception e) { // it's just for debug purposes... ignore all exceptions in this area } try { JavaElement javaElement= (JavaElement)match.getElement(); System.out.println("\tJava element: " + javaElement.toStringWithAncestors()); //$NON-NLS-1$ if (!javaElement.exists()) { System.out.println("\t\tWARNING: this element does NOT exist!"); //$NON-NLS-1$ } } catch (Exception e) { // it's just for debug purposes... ignore all exceptions in this area } if (match instanceof ReferenceMatch) { try { ReferenceMatch refMatch= (ReferenceMatch)match; JavaElement local= (JavaElement)refMatch.getLocalElement(); if (local != null) { System.out.println("\tLocal element: " + local.toStringWithAncestors()); //$NON-NLS-1$ } if (match instanceof TypeReferenceMatch) { IJavaElement[] others= ((TypeReferenceMatch)refMatch).getOtherElements(); if (others != null) { int length= others.length; if (length > 0) { System.out.println("\tOther elements:"); //$NON-NLS-1$ for (int i= 0; i < length; i++) { JavaElement other= (JavaElement)others[i]; System.out.println("\t\t- " + other.toStringWithAncestors()); //$NON-NLS-1$ } } } } } catch (Exception e) { // it's just for debug purposes... ignore all exceptions in this area } } System.out.println(match.getAccuracy() == SearchMatch.A_ACCURATE ? "\tAccuracy: EXACT_MATCH" //$NON-NLS-1$ : "\tAccuracy: POTENTIAL_MATCH"); //$NON-NLS-1$ System.out.print("\tRule: "); //$NON-NLS-1$ if (match.isExact()) { System.out.print("EXACT"); //$NON-NLS-1$ } else if (match.isEquivalent()) { System.out.print("EQUIVALENT"); //$NON-NLS-1$ } else if (match.isErasure()) { System.out.print("ERASURE"); //$NON-NLS-1$ } else { System.out.print("INVALID RULE"); //$NON-NLS-1$ } if (match instanceof MethodReferenceMatch) { MethodReferenceMatch methodReferenceMatch= (MethodReferenceMatch)match; if (methodReferenceMatch.isSuperInvocation()) { System.out.print("+SUPER INVOCATION"); //$NON-NLS-1$ } if (methodReferenceMatch.isImplicit()) { System.out.print("+IMPLICIT"); //$NON-NLS-1$ } if (methodReferenceMatch.isSynthetic()) { System.out.print("+SYNTHETIC"); //$NON-NLS-1$ } } System.out.println("\n\tRaw: " + match.isRaw()); //$NON-NLS-1$ } this.requestor.acceptSearchMatch(match); if (BasicSearchEngine.VERBOSE) this.resultCollectorTime+= System.currentTimeMillis() - start; } /** * Finds the accurate positions of the sequence of tokens given by qualifiedName in the source * and reports a reference to this this qualified name to the search requestor. */ protected void reportAccurateTypeReference(SearchMatch match, ASTNode typeRef, char[] name) throws CoreException { if (match.getRule() == 0) return; if (!encloses((IJavaElement)match.getElement())) return; int sourceStart= typeRef.sourceStart; int sourceEnd= typeRef.sourceEnd; // Compute source positions of the qualified reference if (name != null) { Scanner scanner= this.parser.scanner; scanner.setSource(this.currentPossibleMatch.getContents()); scanner.resetTo(sourceStart, sourceEnd); int token= -1; int currentPosition; do { currentPosition= scanner.currentPosition; try { token= scanner.getNextToken(); } catch (InvalidInputException e) { // ignore } if (token == TerminalTokens.TokenNameIdentifier && this.pattern.matchesName(name, scanner.getCurrentTokenSource())) { int length= scanner.currentPosition - currentPosition; match.setOffset(currentPosition); match.setLength(length); report(match); return; } } while (token != TerminalTokens.TokenNameEOF); } // Report match match.setOffset(sourceStart); match.setLength(sourceEnd - sourceStart + 1); report(match); } /** * Finds the accurate positions of the sequence of tokens given by qualifiedName in the source * and reports a reference to this parameterized type name to the search requestor. * * @since 3.1 */ protected void reportAccurateParameterizedMethodReference(SearchMatch match, ASTNode statement, TypeReference[] typeArguments) throws CoreException { if (match.getRule() == 0) return; if (!encloses((IJavaElement)match.getElement())) return; // If there's type arguments, look for end (ie. char '>') of last one. int start= match.getOffset(); if (typeArguments != null && typeArguments.length > 0) { boolean isErasureMatch= (this.pattern instanceof OrPattern) ? ((OrPattern)this.pattern).isErasureMatch() : ((JavaSearchPattern)this.pattern).isErasureMatch(); if (!isErasureMatch) { // Initialize scanner Scanner scanner= this.parser.scanner; char[] source= this.currentPossibleMatch.getContents(); scanner.setSource(source); // Search previous opening '<' start= typeArguments[0].sourceStart; int end= statement.sourceEnd; scanner.resetTo(start, end); int lineStart= start; try { linesUp: while (true) { while (scanner.source[scanner.currentPosition] != '\n') { scanner.currentPosition--; if (scanner.currentPosition == 0) break linesUp; } lineStart= scanner.currentPosition + 1; scanner.resetTo(lineStart, end); while (!scanner.atEnd()) { if (scanner.getNextToken() == TerminalTokens.TokenNameLESS) { start= scanner.getCurrentTokenStartPosition(); break linesUp; } } end= lineStart - 2; scanner.currentPosition= end; } } catch (InvalidInputException ex) { // give up } } } // Report match match.setOffset(start); match.setLength(statement.sourceEnd - start + 1); report(match); } /** * Finds the accurate positions of the sequence of tokens given by qualifiedName in the source * and reports a reference to this parameterized type name to the search requestor. * * @since 3.1 */ protected void reportAccurateParameterizedTypeReference(SearchMatch match, TypeReference typeRef, int index, TypeReference[] typeArguments) throws CoreException { if (match.getRule() == 0) return; if (!encloses((IJavaElement)match.getElement())) return; // If there's type arguments, look for end (ie. char '>') of last one. int end= typeRef.sourceEnd; if (typeArguments != null) { boolean shouldMatchErasure= (this.pattern instanceof OrPattern) ? ((OrPattern)this.pattern).isErasureMatch() : ((JavaSearchPattern)this.pattern).isErasureMatch(); boolean hasSignatures= (this.pattern instanceof OrPattern) ? ((OrPattern)this.pattern).hasSignatures() : ((JavaSearchPattern)this.pattern).hasSignatures(); if (shouldMatchErasure || !hasSignatures) { // if pattern is erasure only, then select the end of the reference if (typeRef instanceof QualifiedTypeReference && index >= 0) { long[] positions= ((QualifiedTypeReference)typeRef).sourcePositions; end= (int)positions[index]; } else if (typeRef instanceof ArrayTypeReference) { end= ((ArrayTypeReference)typeRef).originalSourceEnd; } } else { // Initialize scanner Scanner scanner= this.parser.scanner; char[] source= this.currentPossibleMatch.getContents(); scanner.setSource(source); // Set scanner position at end of last type argument scanner.resetTo(end, source.length - 1); int depth= 0; for (int i= typeArguments.length - 1; i >= 0; i--) { if (typeArguments[i] != null) { long lastTypeArgInfo= findLastTypeArgumentInfo(typeArguments[i]); depth= (int)(lastTypeArgInfo >>> 32) + 1; scanner.resetTo(((int)lastTypeArgInfo) + 1, scanner.eofPosition - 1); break; } } // Now, scan to search next closing '>' while (depth-- > 0) { while (!scanner.atEnd()) { if (scanner.getNextChar() == '>') { end= scanner.currentPosition - 1; break; } } } } } // Report match match.setLength(end - match.getOffset() + 1); report(match); } /** * Finds the accurate positions of each valid token in the source and reports a reference to * this token to the search requestor. A token is valid if it has an accuracy which is not -1. */ protected void reportAccurateEnumConstructorReference(SearchMatch match, FieldDeclaration field, AllocationExpression allocation) throws CoreException { // Verify that field declaration is really an enum constant if (allocation == null || allocation.enumConstant == null) { report(match); return; } // Get scan area int sourceStart= match.getOffset() + match.getLength(); if (allocation.arguments != null && allocation.arguments.length > 0) { sourceStart= allocation.arguments[allocation.arguments.length - 1].sourceEnd + 1; } int sourceEnd= field.declarationSourceEnd; if (allocation instanceof QualifiedAllocationExpression) { QualifiedAllocationExpression qualifiedAllocation= (QualifiedAllocationExpression)allocation; if (qualifiedAllocation.anonymousType != null) { sourceEnd= qualifiedAllocation.anonymousType.sourceStart - 1; } } // Scan to find last closing parenthesis Scanner scanner= this.parser.scanner; scanner.setSource(this.currentPossibleMatch.getContents()); scanner.resetTo(sourceStart, sourceEnd); try { int token= scanner.getNextToken(); while (token != TerminalTokens.TokenNameEOF) { if (token == TerminalTokens.TokenNameRPAREN) { sourceEnd= scanner.getCurrentTokenEndPosition(); } token= scanner.getNextToken(); } } catch (InvalidInputException iie) { // give up } // Report match match.setLength(sourceEnd - match.getOffset() + 1); report(match); } /** * Finds the accurate positions of each valid token in the source and reports a reference to * this token to the search requestor. A token is valid if it has an accuracy which is not -1. */ protected void reportAccurateFieldReference(SearchMatch[] matches, QualifiedNameReference qNameRef) throws CoreException { if (matches == null) return; // there's nothing to accurate in this case int matchesLength= matches.length; int sourceStart= qNameRef.sourceStart; int sourceEnd= qNameRef.sourceEnd; char[][] tokens= qNameRef.tokens; // compute source positions of the qualified reference Scanner scanner= this.parser.scanner; scanner.setSource(this.currentPossibleMatch.getContents()); scanner.resetTo(sourceStart, sourceEnd); int sourceLength= sourceEnd - sourceStart + 1; int refSourceStart= -1, refSourceEnd= -1; int length= tokens.length; int token= -1; int previousValid= -1; int i= 0; int index= 0; do { int currentPosition= scanner.currentPosition; // read token try { token= scanner.getNextToken(); } catch (InvalidInputException e) { //ignore } if (token != TerminalTokens.TokenNameEOF) { char[] currentTokenSource= scanner.getCurrentTokenSource(); boolean equals= false; while (i < length && !(equals= this.pattern.matchesName(tokens[i++], currentTokenSource))) {/*empty*/ } if (equals && (previousValid == -1 || previousValid == i - 2)) { previousValid= i - 1; if (refSourceStart == -1) refSourceStart= currentPosition; refSourceEnd= scanner.currentPosition - 1; } else { i= 0; refSourceStart= -1; previousValid= -1; } // read '.' try { token= scanner.getNextToken(); } catch (InvalidInputException e) { // ignore } } SearchMatch match= matches[index]; if (match != null && match.getRule() != 0) { if (!encloses((IJavaElement)match.getElement())) return; // accept reference if (refSourceStart != -1) { match.setOffset(refSourceStart); match.setLength(refSourceEnd - refSourceStart + 1); report(match); } else { match.setOffset(sourceStart); match.setLength(sourceLength); report(match); } i= 0; } refSourceStart= -1; previousValid= -1; if (index < matchesLength - 1) { index++; } } while (token != TerminalTokens.TokenNameEOF); } protected void reportBinaryMemberDeclaration(IResource resource, IMember binaryMember, Binding binaryMemberBinding, IBinaryType info, int accuracy) throws CoreException { ClassFile classFile= (ClassFile)binaryMember.getClassFile(); ISourceRange range= classFile.isOpen() ? binaryMember.getNameRange() : SourceMapper.UNKNOWN_RANGE; if (range.getOffset() == -1) { BinaryType type= (BinaryType)classFile.getType(); String sourceFileName= type.sourceFileName(info); if (sourceFileName != null) { SourceMapper mapper= classFile.getSourceMapper(); if (mapper != null) { char[] contents= mapper.findSource(type, sourceFileName); if (contents != null) range= mapper.mapSource(type, contents, info, binaryMember); } } } if (resource == null) resource= this.currentPossibleMatch.resource; SearchMatch match= newDeclarationMatch(binaryMember, binaryMemberBinding, accuracy, range.getOffset(), range.getLength(), getParticipant(), resource); report(match); } /** * Visit the given method declaration and report the nodes that match exactly the search pattern * (ie. the ones in the matching nodes set) Note that the method declaration has already been * checked. */ protected void reportMatching(AbstractMethodDeclaration method, TypeDeclaration type, IJavaElement parent, int accuracy, boolean typeInHierarchy, MatchingNodeSet nodeSet) throws CoreException { IJavaElement enclosingElement= null; // report method declaration itself if (accuracy > -1) { enclosingElement= createHandle(method, parent); if (enclosingElement != null) { // skip if unable to find method // compute source positions of the selector Scanner scanner= this.parser.scanner; int nameSourceStart= method.sourceStart; scanner.setSource(this.currentPossibleMatch.getContents()); scanner.resetTo(nameSourceStart, method.sourceEnd); try { scanner.getNextToken(); } catch (InvalidInputException e) { // ignore } if (encloses(enclosingElement)) { SearchMatch match= null; if (method.isDefaultConstructor()) { // Use type for match associated element as default constructor does not exist in source int offset= type.sourceStart; match= this.patternLocator.newDeclarationMatch(type, parent, type.binding, accuracy, type.sourceEnd - offset + 1, this); } else { int length= scanner.currentPosition - nameSourceStart; match= this.patternLocator.newDeclarationMatch(method, enclosingElement, method.binding, accuracy, length, this); } if (match != null) { report(match); } } } } // handle nodes for the local type first if ((method.bits & ASTNode.HasLocalType) != 0) { if (enclosingElement == null) { enclosingElement= createHandle(method, parent); } // Traverse method declaration to report matches both in local types declaration // and in local variables declaration ASTNode[] nodes= typeInHierarchy ? nodeSet.matchingNodes(method.declarationSourceStart, method.declarationSourceEnd) : null; boolean report= (this.matchContainer & PatternLocator.METHOD_CONTAINER) != 0 && encloses(enclosingElement); MemberDeclarationVisitor declarationVisitor= new MemberDeclarationVisitor(enclosingElement, report ? nodes : null, nodeSet, this); try { method.traverse(declarationVisitor, (ClassScope)null); } catch (WrappedCoreException e) { throw e.coreException; } // Report all nodes and remove them if (nodes != null) { int length= nodes.length; for (int i= 0; i < length; i++) { Integer level= (Integer)nodeSet.matchingNodes.removeKey(nodes[i]); if (report && level != null) { this.patternLocator.matchReportReference(nodes[i], enclosingElement, declarationVisitor.getLocalElement(i), declarationVisitor.getOtherElements(i), method.binding, level.intValue(), this); } } } } // report the type parameters TypeParameter[] typeParameters= method.typeParameters(); if (typeParameters != null) { if (enclosingElement == null) { enclosingElement= createHandle(method, parent); } if (enclosingElement != null) { reportMatching(typeParameters, enclosingElement, parent, method.binding, nodeSet); } } // report annotations if (method.annotations != null) { if (enclosingElement == null) { enclosingElement= createHandle(method, parent); } if (enclosingElement != null) { reportMatching(method.annotations, enclosingElement, null, method.binding, nodeSet, true, true); } } // references in this method if (typeInHierarchy) { ASTNode[] nodes= nodeSet.matchingNodes(method.declarationSourceStart, method.declarationSourceEnd); if (nodes != null) { if ((this.matchContainer & PatternLocator.METHOD_CONTAINER) != 0) { if (enclosingElement == null) { enclosingElement= createHandle(method, parent); } if (encloses(enclosingElement)) { if (this.pattern.mustResolve) { // Visit only if the pattern must resolve MemberDeclarationVisitor declarationVisitor= new MemberDeclarationVisitor(enclosingElement, nodes, nodeSet, this); method.traverse(declarationVisitor, (ClassScope)null); int length= nodes.length; for (int i= 0; i < length; i++) { Integer level= (Integer)nodeSet.matchingNodes.removeKey(nodes[i]); if (level != null) { // ensure that the reference has not been already reported while visiting this.patternLocator.matchReportReference(nodes[i], enclosingElement, declarationVisitor.getLocalElement(i), declarationVisitor.getOtherElements(i), method.binding, level.intValue(), this); } } } else { for (int i= 0, l= nodes.length; i < l; i++) { ASTNode node= nodes[i]; Integer level= (Integer)nodeSet.matchingNodes.removeKey(node); if (level != null) { // ensure that the reference has not been already reported while visiting this.patternLocator.matchReportReference(node, enclosingElement, null, null, method.binding, level.intValue(), this); } } } return; } } // Remove all remaining nodes for (int i= 0, l= nodes.length; i < l; i++) { nodeSet.matchingNodes.removeKey(nodes[i]); } } } } /** * Report matching in annotations. * * @param otherElements TODO */ protected void reportMatching(Annotation[] annotations, IJavaElement enclosingElement, IJavaElement[] otherElements, Binding elementBinding, MatchingNodeSet nodeSet, boolean matchedContainer, boolean enclosesElement) throws CoreException { for (int i= 0, al= annotations.length; i < al; i++) { Annotation annotationType= annotations[i]; IJavaElement localAnnotation= null; IJavaElement[] otherAnnotations= null; int length= otherElements == null ? 0 : otherElements.length; boolean handlesCreated= false; // Look for annotation type ref TypeReference typeRef= annotationType.type; Integer level= (Integer)nodeSet.matchingNodes.removeKey(typeRef); if (level != null && enclosesElement && matchedContainer) { localAnnotation= createHandle(annotationType, (IAnnotatable)enclosingElement); if (length > 0) { otherAnnotations= new IJavaElement[length]; for (int o= 0; o < length; o++) { otherAnnotations[o]= createHandle(annotationType, (IAnnotatable)otherElements[o]); } } handlesCreated= true; this.patternLocator.matchReportReference(typeRef, enclosingElement, localAnnotation, otherAnnotations, elementBinding, level.intValue(), this); } // Look for attribute ref MemberValuePair[] pairs= annotationType.memberValuePairs(); for (int j= 0, pl= pairs.length; j < pl; j++) { MemberValuePair pair= pairs[j]; level= (Integer)nodeSet.matchingNodes.removeKey(pair); if (level != null && enclosesElement) { ASTNode reference= (annotationType instanceof SingleMemberAnnotation) ? (ASTNode)annotationType : pair; if (!handlesCreated) { localAnnotation= createHandle(annotationType, (IAnnotatable)enclosingElement); if (length > 0) { otherAnnotations= new IJavaElement[length]; for (int o= 0; o < length; o++) { otherAnnotations[o]= createHandle(annotationType, (IAnnotatable)otherElements[o]); } } handlesCreated= true; } this.patternLocator.matchReportReference(reference, enclosingElement, localAnnotation, otherAnnotations, pair.binding, level.intValue(), this); } } // Look for reference inside annotation ASTNode[] nodes= nodeSet.matchingNodes(annotationType.sourceStart, annotationType.declarationSourceEnd); if (nodes != null) { if (!matchedContainer) { for (int j= 0, nl= nodes.length; j < nl; j++) { nodeSet.matchingNodes.removeKey(nodes[j]); } } else { for (int j= 0, nl= nodes.length; j < nl; j++) { ASTNode node= nodes[j]; level= (Integer)nodeSet.matchingNodes.removeKey(node); if (enclosesElement) { if (!handlesCreated) { localAnnotation= createHandle(annotationType, (IAnnotatable)enclosingElement); if (length > 0) { otherAnnotations= new IJavaElement[length]; for (int o= 0; o < length; o++) { otherAnnotations[o]= createHandle(annotationType, (IAnnotatable)otherElements[o]); } } handlesCreated= true; } this.patternLocator.matchReportReference(node, enclosingElement, localAnnotation, otherAnnotations, elementBinding, level.intValue(), this); } } } } } } /** * Visit the given resolved parse tree and report the nodes that match the search pattern. */ protected void reportMatching(CompilationUnitDeclaration unit, boolean mustResolve) throws CoreException { MatchingNodeSet nodeSet= this.currentPossibleMatch.nodeSet; boolean locatorMustResolve= this.patternLocator.mustResolve; if (nodeSet.mustResolve) this.patternLocator.mustResolve= true; if (BasicSearchEngine.VERBOSE) { System.out.println("Report matching: "); //$NON-NLS-1$ int size= nodeSet.matchingNodes == null ? 0 : nodeSet.matchingNodes.elementSize; System.out.print(" - node set: accurate=" + size); //$NON-NLS-1$ size= nodeSet.possibleMatchingNodesSet == null ? 0 : nodeSet.possibleMatchingNodesSet.elementSize; System.out.println(", possible=" + size); //$NON-NLS-1$ System.out.print(" - must resolve: " + mustResolve); //$NON-NLS-1$ System.out.print(" (locator: " + this.patternLocator.mustResolve); //$NON-NLS-1$ System.out.println(", nodeSet: " + nodeSet.mustResolve + ')'); //$NON-NLS-1$ System.out.println(" - fine grain flags=" + JavaSearchPattern.getFineGrainFlagString(this.patternLocator.fineGrain())); //$NON-NLS-1$ } if (mustResolve) { this.unitScope= unit.scope.compilationUnitScope(); // move the possible matching nodes that exactly match the search pattern to the matching nodes set Object[] nodes= nodeSet.possibleMatchingNodesSet.values; for (int i= 0, l= nodes.length; i < l; i++) { ASTNode node= (ASTNode)nodes[i]; if (node == null) continue; if (node instanceof ImportReference) { // special case for import refs: they don't know their binding // import ref cannot be in the hierarchy of a type if (this.hierarchyResolver != null) continue; ImportReference importRef= (ImportReference)node; Binding binding= (importRef.bits & ASTNode.OnDemand) != 0 ? this.unitScope.getImport(CharOperation.subarray(importRef.tokens, 0, importRef.tokens.length), true, importRef.isStatic()) : this.unitScope.getImport(importRef.tokens, false, importRef.isStatic()); this.patternLocator.matchLevelAndReportImportRef(importRef, binding, this); } else { nodeSet.addMatch(node, this.patternLocator.resolveLevel(node)); } } nodeSet.possibleMatchingNodesSet= new SimpleSet(3); if (BasicSearchEngine.VERBOSE) { int size= nodeSet.matchingNodes == null ? 0 : nodeSet.matchingNodes.elementSize; System.out.print(" - node set: accurate=" + size); //$NON-NLS-1$ size= nodeSet.possibleMatchingNodesSet == null ? 0 : nodeSet.possibleMatchingNodesSet.elementSize; System.out.println(", possible=" + size); //$NON-NLS-1$ } } else { this.unitScope= null; } if (nodeSet.matchingNodes.elementSize == 0) return; // no matching nodes were found this.methodHandles= new HashSet(); boolean matchedUnitContainer= (this.matchContainer & PatternLocator.COMPILATION_UNIT_CONTAINER) != 0; // report references in javadoc if (unit.javadoc != null) { ASTNode[] nodes= nodeSet.matchingNodes(unit.javadoc.sourceStart, unit.javadoc.sourceEnd); if (nodes != null) { if (!matchedUnitContainer) { for (int i= 0, l= nodes.length; i < l; i++) nodeSet.matchingNodes.removeKey(nodes[i]); } else { IJavaElement element= createPackageDeclarationHandle(unit); for (int i= 0, l= nodes.length; i < l; i++) { ASTNode node= nodes[i]; Integer level= (Integer)nodeSet.matchingNodes.removeKey(node); if (encloses(element)) { this.patternLocator.matchReportReference(node, element, null, null, null/*no binding*/, level.intValue(), this); } } } } } if (matchedUnitContainer) { ImportReference pkg= unit.currentPackage; if (pkg != null && pkg.annotations != null) { IJavaElement element= createPackageDeclarationHandle(unit); if (element != null) { reportMatching(pkg.annotations, element, null, null, nodeSet, true, encloses(element)); } } ImportReference[] imports= unit.imports; if (imports != null) { for (int i= 0, l= imports.length; i < l; i++) { ImportReference importRef= imports[i]; Integer level= (Integer)nodeSet.matchingNodes.removeKey(importRef); if (level != null) { this.patternLocator.matchReportImportRef(importRef, null /*no binding*/, createImportHandle(importRef), level.intValue(), this); } } } } TypeDeclaration[] types= unit.types; if (types != null) { for (int i= 0, l= types.length; i < l; i++) { if (nodeSet.matchingNodes.elementSize == 0) return; // reported all the matching nodes TypeDeclaration type= types[i]; Integer level= (Integer)nodeSet.matchingNodes.removeKey(type); int accuracy= (level != null && matchedUnitContainer) ? level.intValue() : -1; reportMatching(type, null, accuracy, nodeSet, 1); } } // Clear handle cache this.methodHandles= null; this.bindings.removeKey(this.pattern); this.patternLocator.mustResolve= locatorMustResolve; } /** * Visit the given field declaration and report the nodes that match exactly the search pattern * (ie. the ones in the matching nodes set) */ protected void reportMatching(FieldDeclaration field, FieldDeclaration[] otherFields, TypeDeclaration type, IJavaElement parent, int accuracy, boolean typeInHierarchy, MatchingNodeSet nodeSet) throws CoreException { IJavaElement enclosingElement= null; if (accuracy > -1) { enclosingElement= createHandle(field, type, parent); if (encloses(enclosingElement)) { int offset= field.sourceStart; SearchMatch match= newDeclarationMatch(enclosingElement, field.binding, accuracy, offset, field.sourceEnd - offset + 1); if (field.initialization instanceof AllocationExpression) { reportAccurateEnumConstructorReference(match, field, (AllocationExpression)field.initialization); } else { report(match); } } } // handle the nodes for the local type first if ((field.bits & ASTNode.HasLocalType) != 0) { if (enclosingElement == null) { enclosingElement= createHandle(field, type, parent); } // Traverse field declaration(s) to report matches both in local types declaration // and in local variables declaration int fieldEnd= field.endPart2Position == 0 ? field.declarationSourceEnd : field.endPart2Position; ASTNode[] nodes= typeInHierarchy ? nodeSet.matchingNodes(field.sourceStart, fieldEnd) : null; boolean report= (this.matchContainer & PatternLocator.FIELD_CONTAINER) != 0 && encloses(enclosingElement); MemberDeclarationVisitor declarationVisitor= new MemberDeclarationVisitor(enclosingElement, report ? nodes : null, nodeSet, this); try { field.traverse(declarationVisitor, (MethodScope)null); } catch (WrappedCoreException e) { throw e.coreException; } // Report all nodes and remove them if (nodes != null) { int length= nodes.length; for (int i= 0; i < length; i++) { ASTNode node= nodes[i]; Integer level= (Integer)nodeSet.matchingNodes.removeKey(node); if (report && level != null) { if (node instanceof TypeDeclaration) { // use field declaration to report match (see bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=88174) AllocationExpression allocation= ((TypeDeclaration)node).allocation; if (allocation != null && allocation.enumConstant != null) { node= field; } } this.patternLocator.matchReportReference(node, enclosingElement, declarationVisitor.getLocalElement(i), declarationVisitor.getOtherElements(i), field.binding, level.intValue(), this); } } } } // report annotations IJavaElement[] otherElements= null; if (field.annotations != null) { if (enclosingElement == null) { enclosingElement= createHandle(field, type, parent); } if (otherFields != null) { otherElements= createHandles(otherFields, type, parent); } reportMatching(field.annotations, enclosingElement, otherElements, field.binding, nodeSet, true, true); } if (typeInHierarchy) { // Look at field declaration if (field.endPart1Position != 0) { // not necessary if field is an initializer ASTNode[] nodes= nodeSet.matchingNodes(field.declarationSourceStart, field.endPart1Position); if (nodes != null) { if ((this.matchContainer & PatternLocator.FIELD_CONTAINER) == 0) { for (int i= 0, l= nodes.length; i < l; i++) nodeSet.matchingNodes.removeKey(nodes[i]); } else { if (enclosingElement == null) enclosingElement= createHandle(field, type, parent); if (encloses(enclosingElement)) { for (int i= 0, l= nodes.length; i < l; i++) { ASTNode node= nodes[i]; Integer level= (Integer)nodeSet.matchingNodes.removeKey(node); if (otherFields != null && otherElements == null) { otherElements= createHandles(otherFields, type, parent); } this.patternLocator.matchReportReference(node, enclosingElement, null, otherElements, field.binding, level.intValue(), this); } } } } } // Look in initializer int fieldEnd= field.endPart2Position == 0 ? field.declarationSourceEnd : field.endPart2Position; ASTNode[] nodes= nodeSet.matchingNodes(field.sourceStart, fieldEnd); if (nodes != null) { if ((this.matchContainer & PatternLocator.FIELD_CONTAINER) == 0) { for (int i= 0, l= nodes.length; i < l; i++) { nodeSet.matchingNodes.removeKey(nodes[i]); } } else { if (enclosingElement == null) { enclosingElement= createHandle(field, type, parent); } if (encloses(enclosingElement)) { MemberDeclarationVisitor declarationVisitor= new MemberDeclarationVisitor(enclosingElement, nodes, nodeSet, this); field.traverse(declarationVisitor, (MethodScope)null); int length= nodes.length; for (int i= 0; i < length; i++) { ASTNode node= nodes[i]; Integer level= (Integer)nodeSet.matchingNodes.removeKey(node); if (level != null) { // ensure that the reference has not been already reported while visiting if (node instanceof TypeDeclaration) { // use field declaration to report match (see bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=88174) AllocationExpression allocation= ((TypeDeclaration)node).allocation; if (allocation != null && allocation.enumConstant != null) { node= field; } } this.patternLocator.matchReportReference(node, enclosingElement, declarationVisitor.getLocalElement(i), declarationVisitor.getOtherElements(i), field.binding, level.intValue(), this); } } return; } } } } } /** * Visit the given type declaration and report the nodes that match exactly the search pattern * (ie. the ones in the matching nodes set) */ protected void reportMatching(TypeDeclaration type, IJavaElement parent, int accuracy, MatchingNodeSet nodeSet, int occurrenceCount) throws CoreException { // create type handle IJavaElement enclosingElement= parent; if (enclosingElement == null) { enclosingElement= createTypeHandle(new String(type.name)); } else if (enclosingElement instanceof IType) { enclosingElement= ((IType)parent).getType(new String(type.name)); } else if (enclosingElement instanceof IMember) { IMember member= (IMember)parent; if (member.isBinary()) { enclosingElement= ((IClassFile)this.currentPossibleMatch.openable).getType(); } else { enclosingElement= member.getType(new String(type.name), occurrenceCount); } } if (enclosingElement == null) return; boolean enclosesElement= encloses(enclosingElement); // report the type declaration if (accuracy > -1 && enclosesElement) { int offset= type.sourceStart; SearchMatch match= this.patternLocator.newDeclarationMatch(type, enclosingElement, type.binding, accuracy, type.sourceEnd - offset + 1, this); report(match); } boolean matchedClassContainer= (this.matchContainer & PatternLocator.CLASS_CONTAINER) != 0; // report the type parameters if (type.typeParameters != null) { reportMatching(type.typeParameters, enclosingElement, parent, type.binding, nodeSet); } // report annotations if (type.annotations != null) { reportMatching(type.annotations, enclosingElement, null, type.binding, nodeSet, matchedClassContainer, enclosesElement); } // report references in javadoc if (type.javadoc != null) { ASTNode[] nodes= nodeSet.matchingNodes(type.declarationSourceStart, type.sourceStart); if (nodes != null) { if (!matchedClassContainer) { for (int i= 0, l= nodes.length; i < l; i++) nodeSet.matchingNodes.removeKey(nodes[i]); } else { for (int i= 0, l= nodes.length; i < l; i++) { ASTNode node= nodes[i]; Integer level= (Integer)nodeSet.matchingNodes.removeKey(node); if (enclosesElement) { this.patternLocator.matchReportReference(node, enclosingElement, null, null, type.binding, level.intValue(), this); } } } } } // super types if ((type.bits & ASTNode.IsAnonymousType) != 0) { TypeReference superType= type.allocation.type; if (superType != null) { Integer level= (Integer)nodeSet.matchingNodes.removeKey(superType); if (level != null && matchedClassContainer) this.patternLocator.matchReportReference(superType, enclosingElement, null, null, type.binding, level.intValue(), this); } } else { TypeReference superClass= type.superclass; if (superClass != null) { reportMatchingSuper(superClass, enclosingElement, type.binding, nodeSet, matchedClassContainer); } TypeReference[] superInterfaces= type.superInterfaces; if (superInterfaces != null) { for (int i= 0, l= superInterfaces.length; i < l; i++) { reportMatchingSuper(superInterfaces[i], enclosingElement, type.binding, nodeSet, matchedClassContainer); } } } // filter out element not in hierarchy scope boolean typeInHierarchy= type.binding == null || typeInHierarchy(type.binding); matchedClassContainer= matchedClassContainer && typeInHierarchy; // Visit fields FieldDeclaration[] fields= type.fields; if (fields != null) { if (nodeSet.matchingNodes.elementSize == 0) return; // end as all matching nodes were reported FieldDeclaration[] otherFields= null; int first= -1; int length= fields.length; for (int i= 0; i < length; i++) { FieldDeclaration field= fields[i]; boolean last= field.endPart2Position == 0 || field.declarationEnd == field.endPart2Position; // Store first index of multiple field declaration if (!last) { if (first == -1) { first= i; } } if (first >= 0) { // Store all multiple fields but first one for other elements if (i > first) { if (otherFields == null) { otherFields= new FieldDeclaration[length - i]; } otherFields[i - 1 - first]= field; } // On last field, report match with all other elements if (last) { for (int j= first; j <= i; j++) { Integer level= (Integer)nodeSet.matchingNodes.removeKey(fields[j]); int value= (level != null && matchedClassContainer) ? level.intValue() : -1; reportMatching(fields[j], otherFields, type, enclosingElement, value, typeInHierarchy, nodeSet); } first= -1; otherFields= null; } } else { // Single field, report normally Integer level= (Integer)nodeSet.matchingNodes.removeKey(field); int value= (level != null && matchedClassContainer) ? level.intValue() : -1; reportMatching(field, null, type, enclosingElement, value, typeInHierarchy, nodeSet); } } } // Visit methods AbstractMethodDeclaration[] methods= type.methods; if (methods != null) { if (nodeSet.matchingNodes.elementSize == 0) return; // end as all matching nodes were reported for (int i= 0, l= methods.length; i < l; i++) { AbstractMethodDeclaration method= methods[i]; Integer level= (Integer)nodeSet.matchingNodes.removeKey(method); int value= (level != null && matchedClassContainer) ? level.intValue() : -1; reportMatching(method, type, enclosingElement, value, typeInHierarchy, nodeSet); } } // Visit types TypeDeclaration[] memberTypes= type.memberTypes; if (memberTypes != null) { for (int i= 0, l= memberTypes.length; i < l; i++) { if (nodeSet.matchingNodes.elementSize == 0) return; // end as all matching nodes were reported TypeDeclaration memberType= memberTypes[i]; Integer level= (Integer)nodeSet.matchingNodes.removeKey(memberType); int value= (level != null && matchedClassContainer) ? level.intValue() : -1; reportMatching(memberType, enclosingElement, value, nodeSet, 1); } } } /** * Report matches in type parameters. */ protected void reportMatching(TypeParameter[] typeParameters, IJavaElement enclosingElement, IJavaElement parent, Binding binding, MatchingNodeSet nodeSet) throws CoreException { if (typeParameters == null) return; for (int i= 0, l= typeParameters.length; i < l; i++) { TypeParameter typeParameter= typeParameters[i]; if (typeParameter != null) { Integer level= (Integer)nodeSet.matchingNodes.removeKey(typeParameter); if (level != null) { if (level.intValue() > -1 && encloses(enclosingElement)) { int offset= typeParameter.sourceStart; SearchMatch match= this.patternLocator.newDeclarationMatch(typeParameter, enclosingElement, binding, level.intValue(), typeParameter.sourceEnd - offset + 1, this); report(match); } } if (typeParameter.type != null) { level= (Integer)nodeSet.matchingNodes.removeKey(typeParameter.type); if (level != null) { IJavaElement localElement= createHandle(typeParameter, enclosingElement); this.patternLocator.matchReportReference(typeParameter.type, enclosingElement, localElement, null, binding, level.intValue(), this); } if (typeParameter.type instanceof ParameterizedSingleTypeReference) { ParameterizedSingleTypeReference paramSTR= (ParameterizedSingleTypeReference)typeParameter.type; if (paramSTR.typeArguments != null) { int length= paramSTR.typeArguments.length; for (int k= 0; k < length; k++) { TypeReference typeArgument= paramSTR.typeArguments[k]; level= (Integer)nodeSet.matchingNodes.removeKey(typeArgument); if (level != null) { IJavaElement localElement= createHandle(typeParameter, enclosingElement); this.patternLocator.matchReportReference(typeArgument, enclosingElement, localElement, null, binding, level.intValue(), this); } if (typeArgument instanceof Wildcard) { TypeReference wildcardBound= ((Wildcard)typeArgument).bound; if (wildcardBound != null) { level= (Integer)nodeSet.matchingNodes.removeKey(wildcardBound); if (level != null) { IJavaElement localElement= createHandle(typeParameter, enclosingElement); this.patternLocator.matchReportReference(wildcardBound, enclosingElement, localElement, null, binding, level.intValue(), this); } } } } } } } if (typeParameter.bounds != null) { for (int j= 0, b= typeParameter.bounds.length; j < b; j++) { TypeReference typeParameterBound= typeParameter.bounds[j]; level= (Integer)nodeSet.matchingNodes.removeKey(typeParameterBound); if (level != null) { IJavaElement localElement= createHandle(typeParameter, enclosingElement); this.patternLocator.matchReportReference(typeParameterBound, enclosingElement, localElement, null, binding, level.intValue(), this); } if (typeParameterBound instanceof ParameterizedSingleTypeReference) { ParameterizedSingleTypeReference paramSTR= (ParameterizedSingleTypeReference)typeParameterBound; if (paramSTR.typeArguments != null) { int length= paramSTR.typeArguments.length; for (int k= 0; k < length; k++) { TypeReference typeArgument= paramSTR.typeArguments[k]; level= (Integer)nodeSet.matchingNodes.removeKey(typeArgument); if (level != null) { IJavaElement localElement= createHandle(typeParameter, enclosingElement); this.patternLocator.matchReportReference(typeArgument, enclosingElement, localElement, null, binding, level.intValue(), this); } if (typeArgument instanceof Wildcard) { TypeReference wildcardBound= ((Wildcard)typeArgument).bound; if (wildcardBound != null) { level= (Integer)nodeSet.matchingNodes.removeKey(wildcardBound); if (level != null) { IJavaElement localElement= createHandle(typeParameter, enclosingElement); this.patternLocator.matchReportReference(wildcardBound, enclosingElement, localElement, null, binding, level.intValue(), this); } } } } } } } } } } } protected void reportMatchingSuper(TypeReference superReference, IJavaElement enclosingElement, Binding elementBinding, MatchingNodeSet nodeSet, boolean matchedClassContainer) throws CoreException { ASTNode[] nodes= null; if (superReference instanceof ParameterizedSingleTypeReference || superReference instanceof ParameterizedQualifiedTypeReference) { long lastTypeArgumentInfo= findLastTypeArgumentInfo(superReference); nodes= nodeSet.matchingNodes(superReference.sourceStart, (int)lastTypeArgumentInfo); } if (nodes != null) { if ((this.matchContainer & PatternLocator.CLASS_CONTAINER) == 0) { for (int i= 0, l= nodes.length; i < l; i++) nodeSet.matchingNodes.removeKey(nodes[i]); } else { if (encloses(enclosingElement)) for (int i= 0, l= nodes.length; i < l; i++) { ASTNode node= nodes[i]; Integer level= (Integer)nodeSet.matchingNodes.removeKey(node); this.patternLocator.matchReportReference(node, enclosingElement, null, null, elementBinding, level.intValue(), this); } } } else if (encloses(enclosingElement)) { Integer level= (Integer)nodeSet.matchingNodes.removeKey(superReference); if (level != null && matchedClassContainer) this.patternLocator.matchReportReference(superReference, enclosingElement, null, null, elementBinding, level.intValue(), this); } } protected boolean typeInHierarchy(ReferenceBinding binding) { if (this.hierarchyResolver == null) return true; // not a hierarchy scope if (this.hierarchyResolver.subOrSuperOfFocus(binding)) return true; if (this.allSuperTypeNames != null) { char[][] compoundName= binding.compoundName; for (int i= 0, length= this.allSuperTypeNames.length; i < length; i++) if (CharOperation.equals(compoundName, this.allSuperTypeNames[i])) return true; } return false; } }