/******************************************************************************* * Copyright (c) 2000, 2008 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.jdt.internal.core.search.matching; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.SubProgressMonitor; import org.eclipse.jdt.core.IClassFile; import org.eclipse.jdt.core.ICompilationUnit; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.IType; import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.core.compiler.CharOperation; import org.eclipse.jdt.core.search.IJavaSearchConstants; import org.eclipse.jdt.core.search.IJavaSearchScope; import org.eclipse.jdt.core.search.SearchEngine; import org.eclipse.jdt.core.search.SearchParticipant; import org.eclipse.jdt.core.search.SearchPattern; import org.eclipse.jdt.internal.compiler.ASTVisitor; import org.eclipse.jdt.internal.compiler.CompilationResult; import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration; import org.eclipse.jdt.internal.compiler.ast.ConstructorDeclaration; import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration; import org.eclipse.jdt.internal.compiler.ast.Initializer; import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration; import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; import org.eclipse.jdt.internal.compiler.env.AccessRuleSet; import org.eclipse.jdt.internal.compiler.lookup.BinaryTypeBinding; import org.eclipse.jdt.internal.compiler.lookup.BlockScope; import org.eclipse.jdt.internal.compiler.lookup.ClassScope; import org.eclipse.jdt.internal.compiler.lookup.CompilationUnitScope; import org.eclipse.jdt.internal.compiler.lookup.MethodScope; import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding; import org.eclipse.jdt.internal.compiler.problem.AbortCompilation; import org.eclipse.jdt.internal.core.JavaModelManager; import org.eclipse.jdt.internal.core.JavaProject; import org.eclipse.jdt.internal.core.Openable; import org.eclipse.jdt.internal.core.SourceType; import org.eclipse.jdt.internal.core.search.IndexQueryRequestor; import org.eclipse.jdt.internal.core.search.JavaSearchParticipant; import org.eclipse.jdt.internal.core.search.PathCollector; import org.eclipse.jdt.internal.core.search.PatternSearchJob; import org.eclipse.jdt.internal.core.search.indexing.IIndexConstants; import org.eclipse.jdt.internal.core.search.indexing.IndexManager; import org.eclipse.jdt.internal.core.util.ASTNodeFinder; import org.eclipse.jdt.internal.core.util.Util; /** * Collects the super type names of a given declaring type. Returns NOT_FOUND_DECLARING_TYPE if the * declaring type was not found. Returns null if the declaring type pattern doesn't require an exact * match. */ public class SuperTypeNamesCollector { /** * An ast visitor that visits type declarations and member type declarations collecting their * super type names. */ public class TypeDeclarationVisitor extends ASTVisitor { public boolean visit(TypeDeclaration typeDeclaration, BlockScope scope) { ReferenceBinding binding= typeDeclaration.binding; if (SuperTypeNamesCollector.this.matches(binding)) collectSuperTypeNames(binding); return true; } public boolean visit(TypeDeclaration typeDeclaration, CompilationUnitScope scope) { ReferenceBinding binding= typeDeclaration.binding; if (SuperTypeNamesCollector.this.matches(binding)) collectSuperTypeNames(binding); return true; } public boolean visit(TypeDeclaration memberTypeDeclaration, ClassScope scope) { ReferenceBinding binding= memberTypeDeclaration.binding; if (SuperTypeNamesCollector.this.matches(binding)) collectSuperTypeNames(binding); return true; } public boolean visit(FieldDeclaration fieldDeclaration, MethodScope scope) { return false; // don't visit field declarations } public boolean visit(Initializer initializer, MethodScope scope) { return false; // don't visit initializers } public boolean visit(ConstructorDeclaration constructorDeclaration, ClassScope scope) { return false; // don't visit constructor declarations } public boolean visit(MethodDeclaration methodDeclaration, ClassScope scope) { return false; // don't visit method declarations } } SearchPattern pattern; char[] typeSimpleName; char[] typeQualification; MatchLocator locator; IType type; IProgressMonitor progressMonitor; char[][][] result; int resultIndex; public SuperTypeNamesCollector( SearchPattern pattern, char[] typeSimpleName, char[] typeQualification, MatchLocator locator, IType type, IProgressMonitor progressMonitor) { this.pattern= pattern; this.typeSimpleName= typeSimpleName; this.typeQualification= typeQualification; this.locator= locator; this.type= type; this.progressMonitor= progressMonitor; } protected void addToResult(char[][] compoundName) { int resultLength= this.result.length; for (int i= 0; i < resultLength; i++) if (CharOperation.equals(this.result[i], compoundName)) return; // already known if (resultLength == this.resultIndex) System.arraycopy(this.result, 0, this.result= new char[resultLength * 2][][], 0, resultLength); this.result[this.resultIndex++]= compoundName; } /* * Parse the given compiation unit and build its type bindings. */ protected CompilationUnitDeclaration buildBindings(ICompilationUnit compilationUnit, boolean isTopLevelOrMember) throws JavaModelException { // source unit org.eclipse.jdt.internal.compiler.env.ICompilationUnit sourceUnit= (org.eclipse.jdt.internal.compiler.env.ICompilationUnit)compilationUnit; CompilationResult compilationResult= new CompilationResult(sourceUnit, 1, 1, 0); CompilationUnitDeclaration unit= isTopLevelOrMember ? this.locator.basicParser().dietParse(sourceUnit, compilationResult) : this.locator.basicParser().parse(sourceUnit, compilationResult); if (unit != null) { this.locator.lookupEnvironment.buildTypeBindings(unit, null /*no access restriction*/); this.locator.lookupEnvironment.completeTypeBindings(unit, !isTopLevelOrMember); if (!isTopLevelOrMember) { if (unit.scope != null) unit.scope.faultInTypes(); // fault in fields & methods unit.resolve(); } } return unit; } public char[][][] collect() throws JavaModelException { if (this.type != null) { // Collect the paths of the cus that are in the hierarchy of the given type this.result= new char[1][][]; this.resultIndex= 0; JavaProject javaProject= (JavaProject)this.type.getJavaProject(); this.locator.initialize(javaProject, 0); try { if (this.type.isBinary()) { BinaryTypeBinding binding= this.locator.cacheBinaryType(this.type, null); if (binding != null) collectSuperTypeNames(binding); } else { ICompilationUnit unit= this.type.getCompilationUnit(); SourceType sourceType= (SourceType)this.type; boolean isTopLevelOrMember= sourceType.getOuterMostLocalContext() == null; CompilationUnitDeclaration parsedUnit= buildBindings(unit, isTopLevelOrMember); if (parsedUnit != null) { TypeDeclaration typeDecl= new ASTNodeFinder(parsedUnit).findType(this.type); if (typeDecl != null && typeDecl.binding != null) collectSuperTypeNames(typeDecl.binding); } } } catch (AbortCompilation e) { // problem with classpath: report inacurrate matches return null; } if (this.result.length > this.resultIndex) System.arraycopy(this.result, 0, this.result= new char[this.resultIndex][][], 0, this.resultIndex); return this.result; } // Collect the paths of the cus that declare a type which matches declaringQualification + declaringSimpleName String[] paths= getPathsOfDeclaringType(); if (paths == null) return null; // Create bindings from source types and binary types and collect super type names of the type declaration // that match the given declaring type Util.sort(paths); // sort by projects JavaProject previousProject= null; this.result= new char[1][][]; this.resultIndex= 0; for (int i= 0, length= paths.length; i < length; i++) { try { Openable openable= this.locator.handleFactory.createOpenable(paths[i], this.locator.scope); if (openable == null) continue; // outside classpath IJavaProject project= openable.getJavaProject(); if (!project.equals(previousProject)) { previousProject= (JavaProject)project; this.locator.initialize(previousProject, 0); } if (openable instanceof ICompilationUnit) { ICompilationUnit unit= (ICompilationUnit)openable; CompilationUnitDeclaration parsedUnit= buildBindings(unit, true /*only toplevel and member types are visible to the focus type*/); if (parsedUnit != null) parsedUnit.traverse(new TypeDeclarationVisitor(), parsedUnit.scope); } else if (openable instanceof IClassFile) { IClassFile classFile= (IClassFile)openable; BinaryTypeBinding binding= this.locator.cacheBinaryType(classFile.getType(), null); if (matches(binding)) collectSuperTypeNames(binding); } } catch (AbortCompilation e) { // ignore: continue with next element } catch (JavaModelException e) { // ignore: continue with next element } } if (this.result.length > this.resultIndex) System.arraycopy(this.result, 0, this.result= new char[this.resultIndex][][], 0, this.resultIndex); return this.result; } /** * Collects the names of all the supertypes of the given type. */ protected void collectSuperTypeNames(ReferenceBinding binding) { ReferenceBinding superclass= binding.superclass(); if (superclass != null) { addToResult(superclass.compoundName); collectSuperTypeNames(superclass); } ReferenceBinding[] interfaces= binding.superInterfaces(); if (interfaces != null) { for (int i= 0; i < interfaces.length; i++) { ReferenceBinding interfaceBinding= interfaces[i]; addToResult(interfaceBinding.compoundName); collectSuperTypeNames(interfaceBinding); } } } protected String[] getPathsOfDeclaringType() { if (this.typeQualification == null && this.typeSimpleName == null) return null; final PathCollector pathCollector= new PathCollector(); IJavaSearchScope scope= SearchEngine.createWorkspaceScope(); IndexManager indexManager= JavaModelManager.getIndexManager(); SearchPattern searchPattern= new TypeDeclarationPattern( this.typeSimpleName != null ? null : this.typeQualification, // use the qualification only if no simple name null, // do find member types this.typeSimpleName, IIndexConstants.TYPE_SUFFIX, this.pattern.getMatchRule()); IndexQueryRequestor searchRequestor= new IndexQueryRequestor() { public boolean acceptIndexMatch(String documentPath, SearchPattern indexRecord, SearchParticipant participant, AccessRuleSet access) { TypeDeclarationPattern record= (TypeDeclarationPattern)indexRecord; if (record.enclosingTypeNames != IIndexConstants.ONE_ZERO_CHAR) { // filter out local and anonymous classes pathCollector.acceptIndexMatch(documentPath, indexRecord, participant, access); } return true; } }; indexManager.performConcurrentJob( new PatternSearchJob( searchPattern, new JavaSearchParticipant(), scope, searchRequestor), IJavaSearchConstants.WAIT_UNTIL_READY_TO_SEARCH, this.progressMonitor == null ? null : new SubProgressMonitor(this.progressMonitor, 100)); return pathCollector.getPaths(); } protected boolean matches(char[][] compoundName) { int length= compoundName.length; if (length == 0) return false; char[] simpleName= compoundName[length - 1]; int last= length - 1; if (this.typeSimpleName == null || this.pattern.matchesName(simpleName, this.typeSimpleName)) { // most frequent case: simple name equals last segment of compoundName char[][] qualification= new char[last][]; System.arraycopy(compoundName, 0, qualification, 0, last); return this.pattern.matchesName(this.typeQualification, CharOperation.concatWith(qualification, '.')); } if (!CharOperation.endsWith(simpleName, this.typeSimpleName)) return false; // member type -> transform A.B.C$D into A.B.C.D System.arraycopy(compoundName, 0, compoundName= new char[length + 1][], 0, last); int dollar= CharOperation.indexOf('$', simpleName); if (dollar == -1) return false; compoundName[last]= CharOperation.subarray(simpleName, 0, dollar); compoundName[length]= CharOperation.subarray(simpleName, dollar + 1, simpleName.length); return this.matches(compoundName); } protected boolean matches(ReferenceBinding binding) { return binding != null && binding.compoundName != null && this.matches(binding.compoundName); } }