blob: 1b204e594db0c29787ba15e73f2a11991b3b388a [file] [log] [blame]
package org.eclipse.jdt.internal.core.hierarchy;
/*
* (c) Copyright IBM Corp. 2000, 2001.
* All Rights Reserved.
*/
import org.eclipse.core.runtime.*;
import org.eclipse.core.resources.*;
import org.eclipse.jdt.core.*;
import org.eclipse.core.resources.*;
import org.eclipse.jdt.core.search.*;
import java.util.*;
import org.eclipse.jdt.internal.core.search.matching.SuperTypeReferencePattern;
import org.eclipse.jdt.internal.core.search.indexing.IndexManager;
import org.eclipse.jdt.internal.core.search.matching.SearchPattern;
import org.eclipse.jdt.internal.compiler.HierarchyType;
import org.eclipse.jdt.internal.compiler.HierarchyResolver;
import org.eclipse.jdt.internal.core.search.*;
import org.eclipse.jdt.internal.compiler.util.CharOperation;
import org.eclipse.jdt.internal.compiler.env.IGenericType;
import org.eclipse.jdt.internal.core.search.indexing.AbstractIndexer;
import org.eclipse.jdt.internal.core.*;
import org.eclipse.jdt.internal.core.Util;
import org.eclipse.jdt.internal.compiler.util.HashtableOfObject;
import org.eclipse.jdt.internal.compiler.env.ICompilationUnit;
import org.eclipse.jdt.internal.compiler.problem.DefaultProblemFactory;
public class IndexBasedHierarchyBuilder extends HierarchyBuilder {
/**
* A temporary cache of compilation units to handles to speed info
* to handle translation - it only contains the entries
* for the types in the region (i.e. no supertypes outside
* the region).
*/
protected Hashtable cuToHandle;
/**
* The scope this hierarchy builder should restrain results to.
*/
protected IJavaSearchScope scope;
/**
* Cache used to record binaries recreated from index matches
*/
protected Hashtable binariesFromIndexMatches;
/**
* Collection used to queue subtype index queries
*/
private static class Queue {
public char[][] names = new char[10][];
public int start = 0;
public int end = -1;
public void add(char[] name){
if (++this.end == this.names.length){
this.end -= this.start;
System.arraycopy(this.names, this.start, this.names = new char[this.end*2][], 0, this.end);
this.start = 0;
}
this.names[this.end] = name;
}
public char[] retrieve(){
if (this.start > this.end) return null; // none
char[] name = this.names[this.start++];
if (this.start > this.end){
this.start = 0;
this.end = -1;
}
return name;
}
public String toString(){
StringBuffer buffer = new StringBuffer("Queue:\n"/*nonNLS*/);
for (int i = this.start; i <= this.end; i++){
buffer.append(names[i]).append('\n');
}
return buffer.toString();
}
}
public IndexBasedHierarchyBuilder(TypeHierarchy hierarchy, IJavaSearchScope scope) throws JavaModelException {
super(hierarchy);
this.cuToHandle = new Hashtable(5);
this.binariesFromIndexMatches = new Hashtable(10);
this.scope = scope;
}
/**
* Add the type info from the given hierarchy binary type to the given list of infos.
*/
private void addInfoFromBinaryIndexMatch(Openable handle, HierarchyBinaryType binaryType, Vector infos) throws JavaModelException {
infos.addElement(binaryType);
this.infoToHandle.put(binaryType, handle);
}
/**
* Add the type info from the given class file to the given list of infos.
*/
private void addInfoFromOpenClassFile(ClassFile classFile, Vector infos) throws JavaModelException {
IType type = classFile.getType();
IGenericType info = (IGenericType) ((BinaryType) type).getRawInfo();
infos.addElement(info);
this.infoToHandle.put(info, classFile);
}
/**
* Add the type info from the given CU to the given list of infos.
*/
private void addInfoFromOpenCU(CompilationUnit cu, Vector infos) throws JavaModelException {
IType[] types = cu.getTypes();
for (int j = 0; j < types.length; j++) {
SourceType type = (SourceType)types[j];
this.addInfoFromOpenSourceType(type, infos);
}
}
/**
* Add the type info from the given CU to the given list of infos.
*/
private void addInfoFromOpenSourceType(SourceType type, Vector infos) throws JavaModelException {
IGenericType info = (IGenericType)type.getRawInfo();
infos.addElement(info);
this.infoToHandle.put(info, type);
IType[] members = type.getTypes();
for (int i = 0; i < members.length; i++) {
this.addInfoFromOpenSourceType((SourceType)members[i], infos);
}
}
public void build(boolean computeSubtypes) throws JavaModelException, CoreException {
if (computeSubtypes) {
String[] allPossibleSubtypes = this.determinePossibleSubTypes();
if (allPossibleSubtypes != null) {
this.hierarchy.initialize(allPossibleSubtypes.length);
buildFromPotentialSubtypes(allPossibleSubtypes);
}
} else {
this.hierarchy.initialize(1);
this.buildSupertypes();
}
}
private void buildForProject(JavaProject project, Vector infos, Vector units) throws JavaModelException {
IType focusType = this.getType();
if (focusType != null && focusType.getJavaProject().equals(project)) {
// add focus type
try {
infos.addElement(((JavaElement) focusType).getRawInfo());
} catch (JavaModelException e) {
// if the focus type is not present, or if cannot get workbench path
// we cannot create the hierarchy
return;
}
}
// copy vectors into arrays
IGenericType[] genericTypes;
int infosSize = infos.size();
if (infosSize > 0) {
genericTypes = new IGenericType[infosSize];
infos.copyInto(genericTypes);
} else {
genericTypes = new IGenericType[0];
}
ICompilationUnit[] compilationUnits;
int unitsSize = units.size();
if (unitsSize > 0) {
compilationUnits = new ICompilationUnit[unitsSize];
units.copyInto(compilationUnits);
} else {
compilationUnits = new ICompilationUnit[0];
}
// resolve
if (infosSize > 0 || unitsSize > 0) {
this.searchableEnvironment = (SearchableEnvironment)project.getSearchableNameEnvironment();
if (focusType != null && focusType.getJavaProject().equals(project)) {
this.searchableEnvironment.unitToLookInside = (CompilationUnit)focusType.getCompilationUnit();
}
this.nameLookup = project.getNameLookup();
this.hierarchyResolver =
new HierarchyResolver(this.searchableEnvironment, this, new DefaultProblemFactory());
this.hierarchyResolver.resolve(genericTypes, compilationUnits);
if (focusType != null && focusType.getJavaProject().equals(project)) {
this.searchableEnvironment.unitToLookInside = null;
}
}
}
/**
* Configure this type hierarchy based on the given potential subtypes.
*/
private void buildFromPotentialSubtypes(String[] allPotentialSubTypes) {
// sort by projects
/*
* NOTE: To workaround pb with hierarchy resolver that requests top
* level types in the process of caching an enclosing type, this needs to
* be sorted in reverse alphabetical order so that top level types are cached
* before their inner types.
*/
Util.sortReverseOrder(allPotentialSubTypes);
Vector infos = new Vector();
Vector units = new Vector();
IType focusType = this.getType();
// create element infos for subtypes
JavaModelManager manager = JavaModelManager.getJavaModelManager();
IWorkspace workspace = focusType.getJavaProject().getProject().getWorkspace();
HandleFactory factory = new HandleFactory(workspace.getRoot(), manager);
IJavaProject currentProject = null;
for (int i = 0, length = allPotentialSubTypes.length; i < length; i++) {
try {
String resourcePath = allPotentialSubTypes[i];
Openable handle = factory.createOpenable(resourcePath);
if (handle == null) continue; // match is outside classpath
IJavaProject project = handle.getJavaProject();
if (currentProject == null) {
currentProject = project;
infos = new Vector(5);
units = new Vector(5);
} else if (!currentProject.equals(project)) {
this.buildForProject((JavaProject)currentProject, infos, units);
currentProject = project;
infos = new Vector(5);
units = new Vector(5);
}
if (handle.isOpen()) {
// reuse the info from the java model cache
if (handle instanceof CompilationUnit) {
this.addInfoFromOpenCU((CompilationUnit)handle, infos);
} else if (handle instanceof ClassFile) {
this.addInfoFromOpenClassFile((ClassFile)handle, infos);
}
} else {
HierarchyBinaryType binaryType = (HierarchyBinaryType) binariesFromIndexMatches.get(resourcePath);
if (binaryType != null){
this.addInfoFromBinaryIndexMatch(handle, binaryType, infos);
} else {
// create a temporary info
IJavaElement pkg = handle.getParent();
PackageFragmentRoot root = (PackageFragmentRoot)pkg.getParent();
if (root.isArchive()) {
// class file in a jar
this.createInfoFromClassFileInJar(handle, infos);
} else {
// file in a directory
IPath path = new Path(resourcePath);
IFile file = workspace.getRoot().getFile(path);
IPath location = file.getLocation();
if (location != null){
String osPath = location.toOSString();
if (handle instanceof CompilationUnit) {
// compilation unit in a directory
this.createCompilationUnitFromPath(handle, osPath, units);
} else if (handle instanceof ClassFile) {
// class file in a directory
this.createInfoFromClassFile(handle, osPath, infos);
}
}
}
}
}
worked(1);
} catch (JavaModelException e) {
continue;
}
}
try {
if (currentProject == null) currentProject = focusType.getJavaProject(); // case of no potential subtypes
this.buildForProject((JavaProject)currentProject, infos, units);
} catch (JavaModelException e) {
}
// Compute hierarchy of focus type if not already done (case of a type with potential subtypes that are not real subtypes)
if (!this.hierarchy.contains(focusType)) {
try {
currentProject = focusType.getJavaProject();
this.buildForProject((JavaProject)currentProject, new Vector(), new Vector());
} catch (JavaModelException e) {
}
}
// Add focus if not already in (case of a type with no explicit super type)
if (!this.hierarchy.contains(focusType)) {
this.hierarchy.addRootClass(focusType);
}
}
/**
* Create an ICompilationUnit info from the given compilation unit on disk and
* adds it to the given list of units.
*/
private void createCompilationUnitFromPath(Openable handle, String osPath, Vector units) throws JavaModelException {
BasicCompilationUnit unit =
new BasicCompilationUnit(
null,
osPath);
units.addElement(unit);
this.cuToHandle.put(unit, handle);
}
/**
* Creates the type info from the given class file on disk and
* adds it to the given list of infos.
*/
private void createInfoFromClassFile(Openable handle, String osPath, Vector infos) throws JavaModelException {
IGenericType info = null;
try {
info = org.eclipse.jdt.internal.compiler.classfmt.ClassFileReader.read(osPath);
} catch (org.eclipse.jdt.internal.compiler.classfmt.ClassFormatException e) {
e.printStackTrace();
return;
} catch (java.io.IOException e) {
e.printStackTrace();
return;
}
infos.addElement(info);
this.infoToHandle.put(info, handle);
}
/**
* Create a type info from the given class file in a jar and adds it to the given list of infos.
*/
private void createInfoFromClassFileInJar(Openable classFile, Vector infos) throws JavaModelException {
IJavaElement pkg = classFile.getParent();
String classFilePath = pkg.getElementName().replace('.', '/') + "/"/*nonNLS*/ + classFile.getElementName();
IGenericType info = null;
java.util.zip.ZipFile zipFile = null;
try {
zipFile = ((JarPackageFragmentRoot)pkg.getParent()).getJar();
info = org.eclipse.jdt.internal.compiler.classfmt.ClassFileReader.read(
zipFile,
classFilePath);
} catch (org.eclipse.jdt.internal.compiler.classfmt.ClassFormatException e) {
e.printStackTrace();
return;
} catch (java.io.IOException e) {
e.printStackTrace();
return;
} catch (CoreException e) {
e.printStackTrace();
return;
} finally {
if (zipFile != null) {
try {
zipFile.close();
} catch (java.io.IOException e) {
// ignore
}
}
}
infos.addElement(info);
this.infoToHandle.put(info, classFile);
}
/**
* Returns all of the possible subtypes of this type hierarchy.
* Returns null if they could not be determine.
*/
private String[] determinePossibleSubTypes() throws JavaModelException, CoreException {
class PathCollector implements IPathRequestor {
Hashtable paths = new Hashtable(10);
public void acceptPath(String path) {
paths.put(path, path);
}
}
PathCollector collector = new PathCollector();
IProject project = this.hierarchy.javaProject().getProject();
searchAllPossibleSubTypes(
project.getWorkspace(),
this.getType(),
this.scope,
this.binariesFromIndexMatches,
collector,
IJavaSearchConstants.WAIT_UNTIL_READY_TO_SEARCH,
this.hierarchy.fProgressMonitor);
Hashtable paths = collector.paths;
int length = paths.size();
String[] result = new String[length];
int count = 0;
for (Enumeration elements = paths.elements(); elements.hasMoreElements(); ) {
result[count++] = (String) elements.nextElement();
}
return result;
}
/**
* Returns a handle for the given generic type or null if not found.
*/
protected IType getHandle(IGenericType genericType) {
if (genericType instanceof HierarchyType) {
IType type = (IType)this.infoToHandle.get(genericType);
if (type == null) {
HierarchyType hierarchyType = (HierarchyType)genericType;
CompilationUnit unit = (CompilationUnit)this.cuToHandle.get(hierarchyType.originatingUnit);
// collect enclosing type names
Vector enclosingTypeNames = new Vector();
HierarchyType enclosingType = hierarchyType;
do {
enclosingTypeNames.addElement(enclosingType.name);
enclosingType = enclosingType.enclosingType;
} while (enclosingType != null);
int length = enclosingTypeNames.size();
char[][] simpleTypeNames = new char[length][];
enclosingTypeNames.copyInto(simpleTypeNames);
// build handle
type = unit.getType(new String(simpleTypeNames[length-1]));
for (int i = length-2; i >= 0; i--) {
type = type.getType(new String(simpleTypeNames[i]));
}
this.infoToHandle.put(genericType, type);
}
return type;
} else
return super.getHandle(genericType);
}
/**
* Find the set of candidate subtypes of a given type.
*
* The requestor is notified of super type references (with actual path of
* its occurrence) for all types which are potentially involved inside a particular
* hierarchy.
* The match locator is not used here to narrow down the results, the type hierarchy
* resolver is rather used to compute the whole hierarchy at once.
*/
public static void searchAllPossibleSubTypes(
IWorkspace workbench,
IType type,
IJavaSearchScope scope,
final Hashtable binariesFromIndexMatches,
final IPathRequestor pathRequestor,
int waitingPolicy, // WaitUntilReadyToSearch | ForceImmediateSearch | CancelIfNotReadyToSearch
IProgressMonitor progressMonitor) throws JavaModelException, CoreException {
/* embed constructs inside arrays so as to pass them to (inner) collector */
final Queue awaitings = new Queue();
final HashtableOfObject foundSuperNames = new HashtableOfObject(5);
IndexManager indexManager = ((JavaModelManager)JavaModelManager.getJavaModelManager()).getIndexManager();
if (indexManager == null) return;
/* use a special collector to collect paths and queue new subtype names */
IIndexSearchRequestor searchRequestor = new IndexSearchAdapter(){
public void acceptSuperTypeReference(String resourcePath, char[] qualification, char[] typeName, char[] enclosingTypeName, char classOrInterface, char[] superQualification, char[] superTypeName, char superClassOrInterface, int modifiers) {
pathRequestor.acceptPath(resourcePath);
if (resourcePath.endsWith("class"/*nonNLS*/)){
HierarchyBinaryType binaryType = (HierarchyBinaryType)binariesFromIndexMatches.get(resourcePath);
if (binaryType == null){
binaryType = new HierarchyBinaryType(modifiers, qualification, typeName, enclosingTypeName, classOrInterface);
binariesFromIndexMatches.put(resourcePath, binaryType);
}
binaryType.recordSuperType(superTypeName, superQualification, superClassOrInterface);
}
if (!foundSuperNames.containsKey(typeName)){
foundSuperNames.put(typeName, typeName);
awaitings.add(typeName);
}
}
};
SuperTypeReferencePattern pattern = new SuperTypeReferencePattern(null, null, IJavaSearchConstants.EXACT_MATCH, IJavaSearchConstants.CASE_SENSITIVE);
SubTypeSearchJob job = new SubTypeSearchJob(
pattern,
scope,
type,
IInfoConstants.PathInfo,
searchRequestor,
indexManager,
progressMonitor);
/* iterate all queued names */
awaitings.add(type.getElementName().toCharArray());
while (awaitings.start <= awaitings.end){
if (progressMonitor != null && progressMonitor.isCanceled()) return;
char[] currentTypeName = awaitings.retrieve();
/* all subclasses of OBJECT are actually all types */
if (CharOperation.equals(currentTypeName, AbstractIndexer.OBJECT)){
currentTypeName = null;
}
/* search all index references to a given supertype */
pattern.superSimpleName = currentTypeName;
indexManager.performConcurrentJob(job, waitingPolicy, progressMonitor);
/* in case, we search all subtypes, no need to search further */
if (currentTypeName == null) break;
}
/* close all cached index inputs */
job.closeAll();
}
}