blob: 389e99b04f7354ec4847b6949edac5a1b55c3ab5 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2017 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* IBM Corporation - initial API and implementation
* Fraunhofer FIRST - extended API and implementation
* Technical University Berlin - extended API and implementation
*******************************************************************************/
package org.eclipse.jdt.internal.core.search.matching;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.SubMonitor;
import org.eclipse.jdt.core.*;
import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.core.search.*;
import org.eclipse.jdt.internal.compiler.ASTVisitor;
import org.eclipse.jdt.internal.compiler.CompilationResult;
import org.eclipse.jdt.internal.compiler.ast.*;
import org.eclipse.jdt.internal.compiler.ast.Initializer;
import org.eclipse.jdt.internal.compiler.env.AccessRuleSet;
import org.eclipse.jdt.internal.compiler.lookup.*;
import org.eclipse.jdt.internal.compiler.problem.AbortCompilation;
import org.eclipse.jdt.internal.core.*;
import org.eclipse.jdt.internal.core.search.*;
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;
import org.eclipse.objectteams.otdt.internal.core.compiler.control.Config;
import org.eclipse.objectteams.otdt.internal.core.compiler.control.Dependencies;
import org.eclipse.objectteams.otdt.internal.core.compiler.control.ITranslationStates;
/**
* 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 {
@Override
public boolean visit(TypeDeclaration typeDeclaration, BlockScope scope) {
ReferenceBinding binding = typeDeclaration.binding;
if (SuperTypeNamesCollector.this.matches(binding))
collectSuperTypeNames(binding, binding.compoundName);
return true;
}
@Override
public boolean visit(TypeDeclaration typeDeclaration, CompilationUnitScope scope) {
ReferenceBinding binding = typeDeclaration.binding;
if (SuperTypeNamesCollector.this.matches(binding))
collectSuperTypeNames(binding, binding.compoundName);
return true;
}
@Override
public boolean visit(TypeDeclaration memberTypeDeclaration, ClassScope scope) {
ReferenceBinding binding = memberTypeDeclaration.binding;
if (SuperTypeNamesCollector.this.matches(binding))
collectSuperTypeNames(binding, binding.compoundName);
return true;
}
@Override
public boolean visit(FieldDeclaration fieldDeclaration, MethodScope scope) {
return false; // don't visit field declarations
}
@Override
public boolean visit(Initializer initializer, MethodScope scope) {
return false; // don't visit initializers
}
@Override
public boolean visit(ConstructorDeclaration constructorDeclaration, ClassScope scope) {
return false; // don't visit constructor declarations
}
@Override
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;
char[][][] samePackageSuperTypeName; // set only if focus is null
int samePackageIndex;
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;
}
private boolean addIfSamePackage(char[][] compoundName, char[][] path) {
if (compoundName.length != path.length) return false;
int resultLength = this.samePackageSuperTypeName.length;
for (int i = 0; i < resultLength; i++)
if (CharOperation.equals(this.samePackageSuperTypeName[i], compoundName)) return false; // already known
for (int i = 0, length = compoundName.length - 1; i < length; i ++) {
if (!CharOperation.equals(compoundName[i], path[i])) return false;
}
if (resultLength == this.samePackageIndex)
System.arraycopy(this.samePackageSuperTypeName, 0, this.samePackageSuperTypeName = new char[resultLength*2][][], 0, resultLength);
this.samePackageSuperTypeName[this.samePackageIndex++] = compoundName;
return true;
}
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 compilation 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) {
//{ObjectTeams: let Dependencies control the processing
try (Config config = Dependencies.setup(this, this.locator.basicParser(), this.locator.lookupEnvironment,
/*buildFieldsAndMethods*/!isTopLevelOrMember, isTopLevelOrMember))
{
// orig:
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();
// :giro
Dependencies.ensureState(unit, ITranslationStates.STATE_RESOLVED);
}
}
// SH}
}
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, null);
} 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, null);
}
}
} catch (AbortCompilation e) {
// problem with classpath: report inaccurate 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.samePackageSuperTypeName = 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 top level and member types are visible to the focus type*/);
if (parsedUnit != null)
parsedUnit.traverse(new TypeDeclarationVisitor(), parsedUnit.scope);
} else if (openable instanceof IOrdinaryClassFile) {
IOrdinaryClassFile classFile = (IOrdinaryClassFile) openable;
BinaryTypeBinding binding = this.locator.cacheBinaryType(classFile.getType(), null);
if (matches(binding))
collectSuperTypeNames(binding, binding.compoundName);
}
} catch (AbortCompilation | 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, char[][] path) {
ReferenceBinding superclass = binding.superclass();
if (path != null && superclass != null) {
boolean samePackage = addIfSamePackage(superclass.compoundName, path);
if (!samePackage) path = null;
}
if (superclass != null) {
addToResult(superclass.compoundName);
collectSuperTypeNames(superclass, path);
}
ReferenceBinding[] interfaces = binding.superInterfaces();
if (interfaces != null) {
for (int i = 0; i < interfaces.length; i++) {
ReferenceBinding interfaceBinding = interfaces[i];
addToResult(interfaceBinding.compoundName);
collectSuperTypeNames(interfaceBinding, path);
}
}
}
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(){
@Override
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;
}
};
SubMonitor subMonitor = SubMonitor.convert(this.progressMonitor, 100);
indexManager.performConcurrentJob(
new PatternSearchJob(
searchPattern,
new JavaSearchParticipant(),
scope,
searchRequestor),
IJavaSearchConstants.WAIT_UNTIL_READY_TO_SEARCH, subMonitor.split(100));
return pathCollector.getPaths();
}
public char[][][] getSamePackageSuperTypeNames() {
return this.samePackageSuperTypeName;
}
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);
}
}