blob: afb64f0fd03825d2528e1c954f2be9a3a63784d9 [file] [log] [blame]
package org.eclipse.jdt.internal.core.hierarchy;
/*
* (c) Copyright IBM Corp. 2000, 2001.
* All Rights Reserved.
*/
import org.eclipse.core.resources.*;
import org.eclipse.core.runtime.*;
import org.eclipse.jdt.internal.compiler.*;
import org.eclipse.jdt.internal.compiler.env.*;
import org.eclipse.jdt.core.ElementChangedEvent;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.IElementChangedListener;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaElementDelta;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IPackageFragment;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.ITypeHierarchy;
import org.eclipse.jdt.core.ITypeHierarchyChangedListener;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.search.*;
import org.eclipse.jdt.internal.core.*;
import org.eclipse.jdt.internal.core.Util;
import org.eclipse.jdt.internal.core.search.*;
import java.util.*;
import java.util.zip.ZipFile;
/**
* @see ITypeHierarchy
*/
public class TypeHierarchy implements ITypeHierarchy, IElementChangedListener {
/**
* The type the hierarchy was specifically computed for,
* possibly null.
*/
protected IType fType;
protected Hashtable fClassToSuperclass;
protected Hashtable fTypeToSuperInterfaces;
protected Hashtable fTypeToSubtypes;
protected TypeVector fRootClasses= new TypeVector();
protected Vector fInterfaces= new Vector(10);
protected static final IType[] fgEmpty= new IType[0];
/**
* The progress monitor to report work completed too.
*/
protected IProgressMonitor fProgressMonitor = null;
/**
* Change listeners - null if no one is listening.
*/
protected Vector fChangeListeners= null;
/**
* A set of the compilation units and class
* files that are considered in this hierarchy. Null if
* not activated.
*/
protected Hashtable files= null;
/**
* A region describing the packages considered by this
* hierarchy. Null if not activated.
*/
protected Region fPackageRegion= null;
/**
* A region describing the package fragment roots considered by this
* hierarchy. Null if not activated.
*/
protected Region fRootRegion= null;
/**
* A region describing the projects considered by this
* hierarchy. Null if not activated.
*/
protected Region fProjectRegion= null;
/**
* A boolean indicating if this hierarchy is actively tracking changes
* in the Java Model.
*/
protected boolean fIsActivated= false;
/**
* A boolean indicating if the hierarchy exists
*
* fix for 1FW67PA
*/
protected boolean fExists= true;
/**
* Whether this hierarchy should contains subtypes.
*/
protected boolean computeSubtypes;
/**
* The scope this hierarchy should restrain itsef in.
*/
IJavaSearchScope scope;
/**
* Creates a TypeHierarchy on the given type.
*/
public TypeHierarchy(IType type, IJavaSearchScope scope, boolean computeSubtypes) throws JavaModelException {
fType = type;
this.computeSubtypes = computeSubtypes;
this.scope = scope;
}
/**
* Activates this hierarchy for change listeners
*/
protected void activate() {
// determine my file, package, root, & project regions.
this.files = new Hashtable(5);
fProjectRegion = new Region();
fPackageRegion = new Region();
fRootRegion = new Region();
IType[] types = getAllTypes();
for (int i = 0; i < types.length; i++) {
IType type = types[i];
Openable o = (Openable) ((JavaElement) type).getOpenableParent();
if (o != null) {
this.files.put(o, o);
}
IPackageFragment pkg = type.getPackageFragment();
fPackageRegion.add(pkg);
fRootRegion.add(pkg.getParent());
IJavaProject project = type.getJavaProject();
if (project != null) {
fProjectRegion.add(project);
}
checkCanceled();
}
JavaModelManager.getJavaModelManager().addElementChangedListener(this);
fIsActivated = true;
}
/**
* Adds all of the elements in the collection to the vector if the
* element is not already in the vector.
*/
private void addAllCheckingDuplicates(Vector vector, IType[] collection) {
for (int i = 0; i < collection.length; i++) {
IType element = collection[i];
if (!vector.contains(element)) {
vector.addElement(element);
}
}
}
/**
* Adds the type to the collection of interfaces.
*/
protected void addInterface(IType type) {
fInterfaces.addElement(type);
}
/**
* Adds the type to the collection of root classes
* if the classes is not already present in the collection.
*/
protected void addRootClass(IType type) {
if (fRootClasses.contains(type)) return;
fRootClasses.add(type);
}
/**
* Adds the given subtype to the type.
*/
protected void addSubtype(IType type, IType subtype) {
TypeVector subtypes = (TypeVector)fTypeToSubtypes.get(type);
if (subtypes == null) {
subtypes = new TypeVector();
fTypeToSubtypes.put(type, subtypes);
}
if (!subtypes.contains(subtype)) {
subtypes.add(subtype);
}
}
/**
* @see ITypeHierarchy
*/
public void addTypeHierarchyChangedListener(ITypeHierarchyChangedListener listener) {
if (fChangeListeners == null) {
fChangeListeners= new Vector();
// fix for 1FW67PA
if (fExists) {
activate();
}
}
// add listener only if it is not already present
if (fChangeListeners.indexOf(listener) == -1) {
fChangeListeners.addElement(listener);
}
}
/**
* Caches the handle of the superclass for the specified type.
* As a side effect cache this type as a subtype of the superclass.
*/
protected void cacheSuperclass(IType type, IType superclass) {
if (superclass != null) {
fClassToSuperclass.put(type, superclass);
addSubtype(superclass, type);
}
}
/**
* Caches all of the superinterfaces that are specified for the
* type.
*/
protected void cacheSuperInterfaces(IType type, IType[] superinterfaces) {
fTypeToSuperInterfaces.put(type, superinterfaces);
for (int i = 0; i < superinterfaces.length; i++) {
IType superinterface = superinterfaces[i];
if (superinterface != null) {
addSubtype(superinterface, type);
}
}
}
/**
* Checks with the progress monitor to see whether the creation of the type hierarchy
* should be canceled. Should be regularly called
* so that the user can cancel.
*
* @exception OperationCanceledException if cancelling the operation has been requested
* @see IProgressMonitor#isCanceled
*/
protected void checkCanceled() {
if (fProgressMonitor != null && fProgressMonitor.isCanceled()) {
throw new OperationCanceledException();
}
}
/**
* Compute this type hierarchy.
*/
protected void compute() throws JavaModelException, CoreException {
if (JavaModelManager.ENABLE_INDEXING && fType != null) {
HierarchyBuilder builder =
new IndexBasedHierarchyBuilder(
this,
this.scope);
builder.build(this.computeSubtypes);
} // else a RegionBasedTypeHierarchy should be used
}
/**
* @see ITypeHierarchy
*/
public boolean contains(IType type) {
// classes
if (fClassToSuperclass.get(type) != null) {
return true;
}
// root classes
if (fRootClasses.contains(type)) return true;
// interfaces
for (Enumeration enum = fInterfaces.elements(); enum.hasMoreElements();) {
if (enum.nextElement().equals(type)) {
return true;
}
}
return false;
}
/**
* Deactivates this hierarchy for change listeners
*/
protected void deactivate() {
JavaModelManager.getJavaModelManager().removeElementChangedListener(this);
this.files= null;
fPackageRegion= null;
fRootRegion= null;
fProjectRegion= null;
fChangeListeners= null;
fIsActivated= false;
}
/**
* Empties this hierarchy.
*
* fix for 1FW67PA
*/
protected void destroy() {
fExists = false;
fClassToSuperclass = new Hashtable(1);
this.files = new Hashtable(5);
fInterfaces = new Vector(0);
fPackageRegion = new Region();
fProjectRegion = new Region();
fRootClasses = new TypeVector();
fRootRegion = new Region();
fTypeToSubtypes = new Hashtable(1);
fTypeToSuperInterfaces = new Hashtable(1);
JavaModelManager.getJavaModelManager().removeElementChangedListener(this);
}
/**
* Determines if the change effects this hierarchy, and fires
* change notification if required.
*/
public void elementChanged(ElementChangedEvent event) {
// fix for 1FW67PA
if (fExists) {
if (exists()) {
if (isAffected(event.getDelta())) {
fireChange();
}
} else {
destroy();
fireChange();
}
}
}
/**
* @see ITypeHierarchy
*
* fix for 1FW67PA
*/
public boolean exists() {
if (fExists) {
fExists = (fType == null || (fType != null && fType.exists())) && this.javaProject().exists();
if (!fExists) {
destroy();
}
}
return fExists;
}
/**
* Notifies listeners that this hierarchy has changed and needs
* refreshing. Note that listeners can be removed as we iterate
* through the list.
*/
protected void fireChange() {
if (fChangeListeners == null) {
return;
}
Vector listeners= (Vector)fChangeListeners.clone();
for (int i= 0; i < listeners.size(); i++) {
ITypeHierarchyChangedListener listener= (ITypeHierarchyChangedListener)listeners.elementAt(i);
// ensure the listener is still a listener
if (fChangeListeners != null && fChangeListeners.indexOf(listener) >= 0) {
listener.typeHierarchyChanged(this);
}
}
}
/**
* @see ITypeHierarchy
*/
public IType[] getAllClasses() {
Enumeration keys = fClassToSuperclass.keys();
TypeVector classes = fRootClasses.copy();
while (keys.hasMoreElements()) {
classes.add((IType)keys.nextElement());
}
return classes.elements();
}
/**
* @see ITypeHierarchy
*/
public IType[] getAllInterfaces() {
IType[] collection= new IType[fInterfaces.size()];
fInterfaces.copyInto(collection);
return collection;
}
/**
* @see ITypeHierarchy
*/
public IType[] getAllSubtypes(IType type) {
return getAllSubtypesForType(type);
}
/**
* @see getAllSubtypes(IType)
*/
private IType[] getAllSubtypesForType(IType type) {
Vector subTypes = new Vector();
getAllSubtypesForType0(type, subTypes);
IType[] subClasses = new IType[subTypes.size()];
subTypes.copyInto(subClasses);
return subClasses;
}
/**
*/
private void getAllSubtypesForType0(IType type, Vector subs) {
IType[] subTypes = getSubtypesForType(type);
if (subTypes.length != 0) {
for (int i = 0; i < subTypes.length; i++) {
IType subType = subTypes[i];
subs.addElement(subType);
getAllSubtypesForType0(subType, subs);
}
}
}
/**
* @see ITypeHierarchy
*/
public IType[] getAllSuperclasses(IType type) {
IType superclass = getSuperclass(type);
TypeVector supers = new TypeVector();
while (superclass != null) {
supers.add(superclass);
superclass = getSuperclass(superclass);
}
return supers.elements();
}
/**
* @see ITypeHierarchy
*/
public IType[] getAllSuperInterfaces(IType type) {
Vector supers = new Vector();
if (fTypeToSuperInterfaces.get(type) == null) {
return fgEmpty;
}
getAllSuperInterfaces0(type, supers);
IType[] superinterfaces = new IType[supers.size()];
supers.copyInto(superinterfaces);
return superinterfaces;
}
private void getAllSuperInterfaces0(IType type, Vector supers) {
IType[] superinterfaces = (IType[]) fTypeToSuperInterfaces.get(type);
if (superinterfaces != null && superinterfaces.length != 0) {
addAllCheckingDuplicates(supers, superinterfaces);
for (int i = 0; i < superinterfaces.length; i++) {
getAllSuperInterfaces0(superinterfaces[i], supers);
}
}
IType superclass = (IType) fClassToSuperclass.get(type);
if (superclass != null) {
getAllSuperInterfaces0(superclass, supers);
}
}
/**
* @see ITypeHierarchy
*/
public IType[] getAllSupertypes(IType type) {
Vector supers = new Vector();
if (fTypeToSuperInterfaces.get(type) == null) {
return fgEmpty;
}
getAllSupertypes0(type, supers);
IType[] supertypes = new IType[supers.size()];
supers.copyInto(supertypes);
return supertypes;
}
private void getAllSupertypes0(IType type, Vector supers) {
IType[] superinterfaces = (IType[]) fTypeToSuperInterfaces.get(type);
if (superinterfaces != null && superinterfaces.length != 0) {
addAllCheckingDuplicates(supers, superinterfaces);
for (int i = 0; i < superinterfaces.length; i++) {
getAllSuperInterfaces0(superinterfaces[i], supers);
}
}
IType superclass = (IType) fClassToSuperclass.get(type);
if (superclass != null) {
supers.addElement(superclass);
getAllSupertypes0(superclass, supers);
}
}
/**
* @see ITypeHierarchy
*/
public IType[] getAllTypes() {
IType[] classes = getAllClasses();
int classesLength = classes.length;
IType[] interfaces = getAllInterfaces();
int interfacesLength = interfaces.length;
IType[] all = new IType[classesLength + interfacesLength];
System.arraycopy(classes, 0, all, 0, classesLength);
System.arraycopy(interfaces, 0, all, classesLength, interfacesLength);
return all;
}
/**
* @see ITypeHierarchy
*/
public IType[] getExtendingInterfaces(IType type) {
try {
if (type.isClass()) {
return new IType[] {};
}
} catch (JavaModelException npe) {
return new IType[] {};
}
return getExtendingInterfaces0(type);
}
/**
* Assumes that the type is an interface
* @see getExtendingInterfaces
*/
private IType[] getExtendingInterfaces0(IType interfce) {
Enumeration keys = fTypeToSuperInterfaces.keys();
Vector xers = new Vector();
while (keys.hasMoreElements()) {
IType type = (IType) keys.nextElement();
try {
if (type.isClass()) {
continue;
}
} catch (JavaModelException npe) {
continue;
}
IType[] interfaces = (IType[]) fTypeToSuperInterfaces.get(type);
if (interfaces != null) {
for (int i = 0; i < interfaces.length; i++) {
IType iFace = interfaces[i];
if (iFace.equals(interfce)) {
xers.addElement(type);
}
}
}
}
IType[] extenders = new IType[xers.size()];
xers.copyInto(extenders);
return extenders;
}
/**
* @see ITypeHierarchy
*/
public IType[] getImplementingClasses(IType type) {
try {
if (type.isClass()) {
return fgEmpty;
}
} catch (JavaModelException npe) {
return fgEmpty;
}
return getImplementingClasses0(type);
}
/**
* Assumes that the type is an interface
* @see getImplementingClasses
*/
private IType[] getImplementingClasses0(IType interfce) {
Enumeration keys = fTypeToSuperInterfaces.keys();
Vector iMenters = new Vector();
while (keys.hasMoreElements()) {
IType type = (IType) keys.nextElement();
try {
if (type.isInterface()) {
continue;
}
} catch (JavaModelException npe) {
continue;
}
IType[] interfaces = (IType[]) fTypeToSuperInterfaces.get(type);
for (int i = 0; i < interfaces.length; i++) {
IType iFace = interfaces[i];
if (iFace.equals(interfce)) {
iMenters.addElement(type);
}
}
}
IType[] implementers = new IType[iMenters.size()];
iMenters.copyInto(implementers);
return implementers;
}
/**
* @see ITypeHierarchy
*/
public IType[] getRootClasses() {
return fRootClasses.elements();
}
/**
* @see ITypeHierarchy
*/
public IType[] getRootInterfaces() {
IType[] allInterfaces = getAllInterfaces();
IType[] roots = new IType[allInterfaces.length];
int rootNumber = 0;
for (int i = 0; i < allInterfaces.length; i++) {
IType[] superInterfaces = getSuperInterfaces(allInterfaces[i]);
if (superInterfaces == null || superInterfaces.length == 0) {
roots[rootNumber++] = allInterfaces[i];
}
}
IType[] result = new IType[rootNumber];
if (result.length > 0) {
System.arraycopy(roots, 0, result, 0, rootNumber);
}
return result;
}
/**
* @see ITypeHierarchy
*/
public IType[] getSubclasses(IType type) {
try {
if (type.isInterface()) {
return fgEmpty;
}
} catch (JavaModelException npe) {
return new IType[] {};
}
TypeVector vector = (TypeVector)fTypeToSubtypes.get(type);
if (vector == null)
return fgEmpty;
else
return vector.elements();
}
/**
* @see ITypeHierarchy
*/
public IType[] getSubtypes(IType type) {
return getSubtypesForType(type);
}
/**
* Returns an array of subtypes for the given type - will never return null.
*/
private IType[] getSubtypesForType(IType type) {
TypeVector vector = (TypeVector)fTypeToSubtypes.get(type);
if (vector == null)
return fgEmpty;
else
return vector.elements();
}
/**
* @see ITypeHierarchy
*/
public IType getSuperclass(IType type) {
try {
if (type.isInterface()) {
return null;
}
return (IType) fClassToSuperclass.get(type);
} catch (JavaModelException npe) {
return null;
}
}
/**
* @see ITypeHierarchy
*/
public IType[] getSuperInterfaces(IType type) {
IType[] interfaces = (IType[]) fTypeToSuperInterfaces.get(type);
if (interfaces == null) {
return fgEmpty;
}
return interfaces;
}
/**
* @see ITypeHierarchy
*/
public IType[] getSupertypes(IType type) {
IType superclass = getSuperclass(type);
if (superclass == null) {
return getSuperInterfaces(type);
} else {
TypeVector superTypes = new TypeVector(getSuperInterfaces(type));
superTypes.add(superclass);
return superTypes.elements();
}
}
/**
* @see ITypeHierarchy
*/
public IType getType() {
return fType;
}
/**
* Adds the new elements to a new array that contains all of the elements of the old array.
* Returns the new array.
*/
protected IType[] growAndAddToArray(IType[] array, IType[] additions) {
if (array == null || array.length == 0) {
return additions;
}
IType[] old = array;
array = new IType[old.length + additions.length];
System.arraycopy(old, 0, array, 0, old.length);
System.arraycopy(additions, 0, array, old.length, additions.length);
return array;
}
/**
* Adds the new element to a new array that contains all of the elements of the old array.
* Returns the new array.
*/
protected IType[] growAndAddToArray(IType[] array, IType addition) {
if (array == null || array.length == 0) {
return new IType[] {addition};
}
IType[] old = array;
array = new IType[old.length + 1];
System.arraycopy(old, 0, array, 0, old.length);
array[old.length] = addition;
return array;
}
/**
* Returns whether one of the subtypes in this hierarchy has the given simple name
* or this type has the given simple name.
*/
private boolean hasSubtypeNamed(String simpleName) {
if (fType.getElementName().equals(simpleName)) {
return true;
}
IType[] types = this.getAllSubtypes(fType);
for (int i = 0, length = types.length; i < length; i++) {
if (types[i].getElementName().equals(simpleName)) {
return true;
}
}
return false;
}
/**
* Returns whether the given delta (a compilation unit delta or a class file delta)
* indicates that one of the supertypes has changed or one of the imports has changed.
*/
private boolean hasSuperTypeOrImportChange(IJavaElementDelta delta) {
IJavaElementDelta[] children = delta.getAffectedChildren();
for (int i = 0, length = children.length; i < length; i++) {
IJavaElementDelta child = children[i];
if ((child.getFlags() & IJavaElementDelta.F_SUPER_TYPES) > 0) {
return true;
}
if (child.getElement() instanceof ImportContainer) {
return true;
}
}
return false;
}
/**
* Returns whether one of the types in this hierarchy has the given simple name.
*/
private boolean hasTypeNamed(String simpleName) {
IType[] types = this.getAllTypes();
for (int i = 0, length = types.length; i < length; i++) {
if (types[i].getElementName().equals(simpleName)) {
return true;
}
}
return false;
}
/**
* Returns whether the given delta (a compilation unit delta or a class file delta)
* indicates that one of its types has a visibility change.
*/
private boolean hasVisibilityChange(IJavaElementDelta delta) {
IJavaElementDelta[] children = delta.getAffectedChildren();
for (int i = 0, length = children.length; i < length; i++) {
IJavaElementDelta child = children[i];
if ((child.getFlags() & IJavaElementDelta.F_MODIFIERS) > 0) {
return true;
}
}
return false;
}
/**
* Returns whether the simple name of a supertype of the given type is
* the simple name of one of the types in this hierarchy.
*/
private boolean includesSupertypeOf(IType type) {
IType[] supertypes = getSupertypes(type);
for (int i = 0, length = supertypes.length; i < length; i++) {
if (hasTypeNamed(supertypes[i].getElementName())) {
return true;
}
}
return false;
}
/**
* Initializes this hierarchy's internal tables with the given size.
*/
protected void initialize(int size) {
if (size < 10) {
size = 10;
}
int smallSize = (size / 2);
fClassToSuperclass = new Hashtable(size);
fTypeToSubtypes = new Hashtable(smallSize);
fTypeToSuperInterfaces = new Hashtable(smallSize);
}
/**
* Returns true if this hierarchy is actively tracking changes
* in the Java Model.
*/
protected boolean isActivated() {
return fIsActivated;
}
/**
* Returns true if the given delta could change this type hierarchy
*/
private boolean isAffected(IJavaElementDelta delta) {
IJavaElement element= delta.getElement();
switch (element.getElementType()) {
case IJavaElement.JAVA_MODEL:
return isAffectedByJavaModel(delta, element);
case IJavaElement.JAVA_PROJECT:
return isAffectedByJavaProject(delta, element);
case IJavaElement.PACKAGE_FRAGMENT_ROOT:
return isAffectedByPackageFragmentRoot(delta, element);
case IJavaElement.PACKAGE_FRAGMENT:
return isAffectedByPackageFragment(delta, element);
case IJavaElement.CLASS_FILE:
case IJavaElement.COMPILATION_UNIT:
return isAffectedByType(delta, element);
}
return false;
}
/**
* Returns true if any of the children of a project, package
* fragment root, or package fragment have changed in a way that
* effects this type hierarchy.
*/
private boolean isAffectedByChildren(IJavaElementDelta delta) {
if ((delta.getFlags() & IJavaElementDelta.F_CHILDREN) > 0) {
IJavaElementDelta[] children= delta.getAffectedChildren();
for (int i= 0; i < children.length; i++) {
if (isAffected(children[i])) {
return true;
}
}
}
return false;
}
/**
* Returns true if the given java model delta could affect this type hierarchy
*/
private boolean isAffectedByJavaModel(IJavaElementDelta delta, IJavaElement element) {
switch (delta.getKind()) {
case IJavaElementDelta.ADDED :
case IJavaElementDelta.REMOVED :
return element.equals(this.javaProject().getJavaModel());
case IJavaElementDelta.CHANGED :
return isAffectedByChildren(delta);
}
return false;
}
/**
* Returns true if the given java project delta could affect this type hierarchy
*/
private boolean isAffectedByJavaProject(IJavaElementDelta delta, IJavaElement element) {
switch (delta.getKind()) {
case IJavaElementDelta.ADDED :
try {
// if the added project is on the classpath, then the hierarchy has changed
IClasspathEntry[] classpath = this.javaProject().getResolvedClasspath(true);
for (int i = 0; i < classpath.length; i++) {
if (classpath[i].getEntryKind() == IClasspathEntry.CPE_PROJECT
&& classpath[i].getPath().equals(element.getUnderlyingResource().getFullPath())) {
return true;
}
}
return false;
} catch (JavaModelException e) {
return false;
}
case IJavaElementDelta.REMOVED :
// removed project - if it contains packages we are interested in
// then the type hierarchy has changed
IJavaElement[] pkgs = fPackageRegion.getElements();
for (int i = 0; i < pkgs.length; i++) {
IJavaProject project = pkgs[i].getJavaProject();
if (project != null && project.equals(element)) {
return true;
}
}
return false;
case IJavaElementDelta.CHANGED :
return isAffectedByChildren(delta);
}
return false;
}
/**
* Returns true if the given package fragment delta could affect this type hierarchy
*/
private boolean isAffectedByPackageFragment(IJavaElementDelta delta, IJavaElement element) {
switch (delta.getKind()) {
case IJavaElementDelta.ADDED :
// if the package fragment is in the projects being considered, this could
// introduce new types, changing the hierarchy
return fProjectRegion.contains(element);
case IJavaElementDelta.REMOVED :
// is a change if the package fragment contains types in this hierarchy
return packageRegionContainsSamePackageFragment(element);
case IJavaElementDelta.CHANGED :
// look at the files in the package fragment
return isAffectedByChildren(delta);
}
return false;
}
/**
* Returns true if the given package fragment root delta could affect this type hierarchy
*/
private boolean isAffectedByPackageFragmentRoot(IJavaElementDelta delta, IJavaElement element) {
switch (delta.getKind()) {
case IJavaElementDelta.ADDED :
return fProjectRegion.contains(element);
case IJavaElementDelta.REMOVED :
case IJavaElementDelta.CHANGED :
if ((delta.getFlags() & IJavaElementDelta.F_REMOVED_FROM_CLASSPATH) > 0 || (delta.getFlags() & IJavaElementDelta.F_CONTENT) > 0) {
// 1. removed from classpath - if it contains packages we are interested in
// the the type hierarchy has changed
// 2. content of a jar changed - if it contains packages we are interested in
// the the type hierarchy has changed
IJavaElement[] pkgs = fPackageRegion.getElements();
for (int i = 0; i < pkgs.length; i++) {
if (pkgs[i].getParent().equals(element)) {
return true;
}
}
return false;
}
}
return isAffectedByChildren(delta);
}
/**
* Returns true if the given type delta (a compilation unit delta or a class file delta)
* could affect this type hierarchy.
*
* The rules are:
* - if the delta is an added type X, then the hierarchy is changed
* . if one of the types in this hierarchy has a supertype whose simple name is the
* simple name of X
* . if the simple name of a supertype of X is the simple name of one of
* the subtypes in this hierarchy (X will be added as one of the subtypes)
* - if the delta is a changed type X, then the hierarchy is changed
* . if the visibility of X has changed and if one of the types in this hierarchy has a
* supertype whose simple name is the simple name of X
* . if one of the supertypes of X has changed or one of the imports has changed,
* and if the simple name of a supertype of X is the simple name of one of
* the types in this hierarchy
* - if the delta is a removed type X, then the hierarchy is changed
* . if the given element is part of this hierarchy (note we cannot acces the types
* because the element has been removed)
*/
protected boolean isAffectedByType(IJavaElementDelta delta, IJavaElement element) {
// ignore changes to working copies
if (element instanceof CompilationUnit && ((CompilationUnit)element).isWorkingCopy()) {
return false;
}
int kind = delta.getKind();
if (kind == IJavaElementDelta.REMOVED) {
return this.files.get(element) != null;
} else {
IType[] types = null;
try {
types = (element instanceof CompilationUnit) ?
((CompilationUnit)element).getAllTypes() :
new IType[] {((org.eclipse.jdt.internal.core.ClassFile)element).getType()};
} catch (JavaModelException e) {
e.printStackTrace();
return false;
}
if (kind == IJavaElementDelta.ADDED) {
for (int i = 0, length = types.length; i < length; i++) {
IType type = types[i];
if (typeHasSupertype(type) || subtypesIncludeSupertypeOf(type)) {
return true;
}
}
} else { // kind == IJavaElementDelta.CHANGED :
boolean hasSupertypeChange = hasSuperTypeOrImportChange(delta);
boolean hasVisibilityChange = hasVisibilityChange(delta);
for (int i = 0, length = types.length; i < length; i++) {
IType type = types[i];
if ((hasVisibilityChange && typeHasSupertype(type))
|| (hasSupertypeChange && includesSupertypeOf(type))) {
return true;
}
}
}
}
return false;
}
/**
* Returns the java project this hierarchy was created in.
*/
public IJavaProject javaProject() {
return fType.getJavaProject();
}
/**
* Returns <code>true</code> if an equivalent package fragment is included in the package
* region. Package fragments are equivalent if they both have the same name.
*/
protected boolean packageRegionContainsSamePackageFragment(IJavaElement element) {
IJavaElement[] pkgs = fPackageRegion.getElements();
for (int i = 0; i < pkgs.length; i++) {
if (pkgs[i].getElementName().equals(element.getElementName())) {
return true;
}
}
return false;
}
/**
* Prunes this type hierarchy to only contain the branch to the given type,
* and its subtree. Pruning is only done for classes.
*/
protected void pruneTypeHierarchy(IType type, IProgressMonitor monitor) throws JavaModelException {
if (type.isClass()) {
IType[] supers= getAllSuperclasses(type);
if (supers.length == 0) {
// nothing to prune if this is a root - unless there are other roots
return;
}
IType[] branch= new IType[supers.length + 1];
System.arraycopy(supers, 0, branch, 1, supers.length);
branch[0]= type;
// Branch is a list from the root to our type
// Walk the branch pruning all other subtrees
for (int i= branch.length - 1; i > 0; i--) {
IType[] subtrees= getSubtypes(branch[i]);
for (int j= 0; j < subtrees.length; j++) {
if (!subtrees[j].equals(branch[i - 1])) {
removeType(subtrees[j]);
}
this.worked(1);
}
fTypeToSubtypes.put(branch[i], new TypeVector(branch[i - 1]));
}
}
}
/**
* @see ITypeHierarchy
*/
public void refresh(IProgressMonitor monitor) throws JavaModelException {
try {
boolean reactivate = isActivated();
Vector listeners = fChangeListeners;
if (reactivate) {
deactivate();
}
fProgressMonitor = monitor;
if (monitor != null) {
monitor.beginTask(Util.bind("hierarchy.creating"), IProgressMonitor.UNKNOWN); //$NON-NLS-1$
}
compute();
if (fType != null) {
//prune the hierarchy tree to only include branch and subtree for the type
pruneTypeHierarchy(fType, monitor);
}
if (reactivate) {
activate();
fChangeListeners = listeners;
}
if (monitor != null) {
monitor.done();
}
fProgressMonitor = null;
} catch (JavaModelException e) {
fProgressMonitor = null;
throw e;
} catch (CoreException e) {
fProgressMonitor = null;
throw new JavaModelException(e);
} catch (OperationCanceledException oce) {
refreshCancelled(oce);
}
}
/**
* The refresh of this type hierarchy has been cancelled.
* Cleanup the state of this now invalid type hierarchy.
*/
protected void refreshCancelled(OperationCanceledException oce) throws JavaModelException {
destroy();
fProgressMonitor = null;
throw oce;
}
/**
* Removes all the subtypes of the given type from the type hierarchy,
* and removes its superclass entry.
*/
protected void removeType(IType type) throws JavaModelException {
IType[] subtypes= getSubtypes(type);
fTypeToSubtypes.remove(type);
fClassToSuperclass.remove(type);
if (subtypes != null) {
for (int i= 0; i < subtypes.length; i++) {
removeType(subtypes[i]);
}
}
}
/**
* @see ITypeHierarchy
*/
public void removeTypeHierarchyChangedListener(ITypeHierarchyChangedListener listener) {
if (fChangeListeners == null) {
return;
}
fChangeListeners.removeElement(listener);
if (fChangeListeners.isEmpty()) {
deactivate();
}
}
/**
* Returns whether the simple name of a supertype of the given type is
* the simple name of one of the subtypes in this hierarchy or the
* simple name of this type.
*/
private boolean subtypesIncludeSupertypeOf(IType type) {
// look for superclass
String superclassName = null;
try {
superclassName = type.getSuperclassName();
} catch (JavaModelException e) {
e.printStackTrace();
return false;
}
if (superclassName == null) {
superclassName = "Object"; //$NON-NLS-1$
}
int dot = -1;
String simpleSuper = (dot = superclassName.lastIndexOf('.')) > -1 ?
superclassName.substring(dot + 1) :
superclassName;
if (hasSubtypeNamed(simpleSuper)) {
return true;
}
// look for super interfaces
String[] interfaceNames = null;
try {
interfaceNames = type.getSuperInterfaceNames();
} catch (JavaModelException e) {
e.printStackTrace();
return false;
}
for (int i = 0, length = interfaceNames.length; i < length; i++) {
dot = -1;
String interfaceName = interfaceNames[i];
String simpleInterface = (dot = interfaceName.lastIndexOf('.')) > -1 ?
interfaceName.substring(dot) :
interfaceName;
if (hasSubtypeNamed(simpleInterface)) {
return true;
}
}
return false;
}
/**
* @see ITypeHierarchy
*/
public String toString() {
StringBuffer buffer = new StringBuffer();
buffer.append("Focus: "); //$NON-NLS-1$
buffer.append(fType == null ? "<NONE>" : fType.getFullyQualifiedName()); //$NON-NLS-1$
buffer.append("\n"); //$NON-NLS-1$
if (exists()) {
if (fType != null) {
buffer.append("Super types:\n"); //$NON-NLS-1$
toString(buffer, fType, 1, true);
buffer.append("Sub types:\n"); //$NON-NLS-1$
toString(buffer, fType, 1, false);
} else {
buffer.append("Sub types of root classes:\n"); //$NON-NLS-1$
IType[] roots= getRootClasses();
for (int i= 0; i < roots.length; i++) {
toString(buffer, roots[i], 1, false);
}
}
} else {
buffer.append("(Hierarchy became stale)"); //$NON-NLS-1$
}
return buffer.toString();
}
/**
* Append a String to the given buffer representing the hierarchy for the type,
* beginning with the specified indentation level.
* If ascendant, shows the super types, otherwise show the sub types.
*/
private void toString(StringBuffer buffer, IType type, int indent, boolean ascendant) {
for (int i= 0; i < indent; i++) {
buffer.append(" "); //$NON-NLS-1$
}
buffer.append(type.getFullyQualifiedName());
buffer.append('\n');
IType[] types= ascendant ? getSupertypes(type) : getSubtypes(type);
for (int i= 0; i < types.length; i++) {
toString(buffer, types[i], indent + 1, ascendant);
}
}
/**
* Returns whether one of the types in this hierarchy has a supertype whose simple
* name is the simple name of the given type.
*/
private boolean typeHasSupertype(IType type) {
String simpleName = type.getElementName();
Enumeration enum = fClassToSuperclass.elements();
while (enum.hasMoreElements()) {
IType superType = (IType)enum.nextElement();
if (superType.getElementName().equals(simpleName)) {
return true;
}
}
return false;
}
/**
* @see IProgressMonitor
*/
protected void worked(int work) {
if (fProgressMonitor != null) {
fProgressMonitor.worked(work);
checkCanceled();
}
}
}