| /******************************************************************************* |
| * Copyright (c) 2000, 2003 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Common Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/cpl-v10.html |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.jdt.internal.core.hierarchy; |
| |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.OutputStream; |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.Hashtable; |
| import java.util.Iterator; |
| import java.util.Map; |
| |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IPath; |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.core.runtime.ISafeRunnable; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.core.runtime.OperationCanceledException; |
| import org.eclipse.core.runtime.Platform; |
| import org.eclipse.jdt.core.*; |
| 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.IPackageFragmentRoot; |
| import org.eclipse.jdt.core.IType; |
| import org.eclipse.jdt.core.ITypeHierarchy; |
| import org.eclipse.jdt.core.ITypeHierarchyChangedListener; |
| import org.eclipse.jdt.core.JavaCore; |
| import org.eclipse.jdt.core.JavaModelException; |
| import org.eclipse.jdt.core.search.IJavaSearchScope; |
| import org.eclipse.jdt.core.search.SearchEngine; |
| import org.eclipse.jdt.internal.core.*; |
| import org.eclipse.jdt.internal.core.CompilationUnit; |
| import org.eclipse.jdt.internal.core.JavaElement; |
| import org.eclipse.jdt.internal.core.JavaModelStatus; |
| import org.eclipse.jdt.internal.core.JavaProject; |
| import org.eclipse.jdt.internal.core.Openable; |
| import org.eclipse.jdt.internal.core.Region; |
| import org.eclipse.jdt.internal.core.TypeVector; |
| import org.eclipse.jdt.internal.core.util.Util; |
| |
| /** |
| * @see ITypeHierarchy |
| */ |
| public class TypeHierarchy implements ITypeHierarchy, IElementChangedListener { |
| |
| public static boolean DEBUG = false; |
| |
| static final byte VERSION = 0x0000; |
| // SEPARATOR |
| static final byte SEPARATOR1 = '\n'; |
| static final byte SEPARATOR2 = ','; |
| static final byte SEPARATOR3 = '>'; |
| static final byte SEPARATOR4 = '\r'; |
| // general info |
| static final byte COMPUTE_SUBTYPES = 0x0001; |
| |
| // type info |
| static final byte CLASS = 0x0000; |
| static final byte INTERFACE = 0x0001; |
| static final byte COMPUTED_FOR = 0x0002; |
| static final byte ROOT = 0x0004; |
| |
| // cst |
| static final byte[] NO_FLAGS = new byte[]{}; |
| static final int SIZE = 10; |
| |
| /** |
| * The Java Project in which the hierarchy is being built - this |
| * provides the context for determining a classpath and namelookup rules. |
| * Possibly null. |
| */ |
| protected IJavaProject project; |
| /** |
| * The type the hierarchy was specifically computed for, |
| * possibly null. |
| */ |
| protected IType focusType; |
| |
| /* |
| * The working copies that take precedence over original compilation units |
| */ |
| protected ICompilationUnit[] workingCopies; |
| |
| protected Map classToSuperclass; |
| protected Map typeToSuperInterfaces; |
| protected Map typeToSubtypes; |
| protected Map typeFlags; |
| protected TypeVector rootClasses = new TypeVector(); |
| protected ArrayList interfaces = new ArrayList(10); |
| public ArrayList missingTypes = new ArrayList(4); |
| |
| protected static final IType[] NO_TYPE = new IType[0]; |
| |
| /** |
| * The progress monitor to report work completed too. |
| */ |
| protected IProgressMonitor progressMonitor = null; |
| |
| /** |
| * Change listeners - null if no one is listening. |
| */ |
| protected ArrayList changeListeners = null; |
| |
| /* |
| * A map from Openables to ArrayLists of ITypes |
| */ |
| public Map files = null; |
| |
| /** |
| * A region describing the packages considered by this |
| * hierarchy. Null if not activated. |
| */ |
| protected Region packageRegion = null; |
| |
| /** |
| * A region describing the projects considered by this |
| * hierarchy. Null if not activated. |
| */ |
| protected Region projectRegion = null; |
| |
| /** |
| * Whether this hierarchy should contains subtypes. |
| */ |
| protected boolean computeSubtypes; |
| |
| /** |
| * The scope this hierarchy should restrain itsef in. |
| */ |
| IJavaSearchScope scope; |
| |
| /* |
| * Whether this hierarchy needs refresh |
| */ |
| public boolean needsRefresh = true; |
| |
| /* |
| * Collects changes to types |
| */ |
| protected ChangeCollector changeCollector; |
| |
| /** |
| * Creates an empty TypeHierarchy |
| */ |
| public TypeHierarchy() { |
| // Creates an empty TypeHierarchy |
| } |
| /** |
| * Creates a TypeHierarchy on the given type. |
| */ |
| public TypeHierarchy(IType type, ICompilationUnit[] workingCopies, IJavaProject project, boolean computeSubtypes) { |
| this(type, workingCopies, SearchEngine.createJavaSearchScope(new IJavaElement[] {project}), computeSubtypes); |
| this.project = project; |
| } |
| /** |
| * Creates a TypeHierarchy on the given type. |
| */ |
| public TypeHierarchy(IType type, ICompilationUnit[] workingCopies, IJavaSearchScope scope, boolean computeSubtypes) { |
| this.focusType = type; |
| this.workingCopies = workingCopies; |
| this.computeSubtypes = computeSubtypes; |
| this.scope = scope; |
| } |
| /** |
| * Initializes the file, package and project regions |
| */ |
| protected void initializeRegions() { |
| |
| IType[] allTypes = getAllTypes(); |
| for (int i = 0; i < allTypes.length; i++) { |
| IType type = allTypes[i]; |
| Openable o = (Openable) ((JavaElement) type).getOpenableParent(); |
| if (o != null) { |
| ArrayList types = (ArrayList)this.files.get(o); |
| if (types == null) { |
| types = new ArrayList(); |
| this.files.put(o, types); |
| } |
| types.add(type); |
| } |
| IPackageFragment pkg = type.getPackageFragment(); |
| this.packageRegion.add(pkg); |
| IJavaProject declaringProject = type.getJavaProject(); |
| if (declaringProject != null) { |
| this.projectRegion.add(declaringProject); |
| } |
| checkCanceled(); |
| } |
| } |
| /** |
| * Adds all of the elements in the collection to the list if the |
| * element is not already in the list. |
| */ |
| private void addAllCheckingDuplicates(ArrayList list, IType[] collection) { |
| for (int i = 0; i < collection.length; i++) { |
| IType element = collection[i]; |
| if (!list.contains(element)) { |
| list.add(element); |
| } |
| } |
| } |
| /** |
| * Adds the type to the collection of interfaces. |
| */ |
| protected void addInterface(IType type) { |
| this.interfaces.add(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 (this.rootClasses.contains(type)) return; |
| this.rootClasses.add(type); |
| } |
| /** |
| * Adds the given subtype to the type. |
| */ |
| protected void addSubtype(IType type, IType subtype) { |
| TypeVector subtypes = (TypeVector)this.typeToSubtypes.get(type); |
| if (subtypes == null) { |
| subtypes = new TypeVector(); |
| this.typeToSubtypes.put(type, subtypes); |
| } |
| if (!subtypes.contains(subtype)) { |
| subtypes.add(subtype); |
| } |
| } |
| /** |
| * @see ITypeHierarchy |
| */ |
| public synchronized void addTypeHierarchyChangedListener(ITypeHierarchyChangedListener listener) { |
| ArrayList listeners = this.changeListeners; |
| if (listeners == null) { |
| this.changeListeners = listeners = new ArrayList(); |
| } |
| |
| // register with JavaCore to get Java element delta on first listener added |
| if (listeners.size() == 0) { |
| JavaCore.addElementChangedListener(this); |
| } |
| |
| // add listener only if it is not already present |
| if (listeners.indexOf(listener) == -1) { |
| listeners.add(listener); |
| } |
| } |
| private static Integer bytesToFlags(byte[] bytes){ |
| if(bytes != null && bytes.length > 0) { |
| return new Integer(new String(bytes)); |
| } else { |
| return null; |
| } |
| } |
| /** |
| * cacheFlags. |
| */ |
| public void cacheFlags(IType type, int flags) { |
| this.typeFlags.put(type, new Integer(flags)); |
| } |
| /** |
| * 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) { |
| this.classToSuperclass.put(type, superclass); |
| addSubtype(superclass, type); |
| } |
| } |
| /** |
| * Caches all of the superinterfaces that are specified for the |
| * type. |
| */ |
| protected void cacheSuperInterfaces(IType type, IType[] superinterfaces) { |
| this.typeToSuperInterfaces.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 (this.progressMonitor != null && this.progressMonitor.isCanceled()) { |
| throw new OperationCanceledException(); |
| } |
| } |
| /** |
| * Compute this type hierarchy. |
| */ |
| protected void compute() throws JavaModelException, CoreException { |
| if (this.focusType != 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 (this.classToSuperclass.get(type) != null) { |
| return true; |
| } |
| |
| // root classes |
| if (this.rootClasses.contains(type)) return true; |
| |
| // interfaces |
| if (this.interfaces.contains(type)) return true; |
| |
| return false; |
| } |
| /** |
| * Determines if the change effects this hierarchy, and fires |
| * change notification if required. |
| */ |
| public void elementChanged(ElementChangedEvent event) { |
| // type hierarchy change has already been fired |
| if (this.needsRefresh) return; |
| |
| if (isAffected(event.getDelta())) { |
| this.needsRefresh = true; |
| fireChange(); |
| } |
| } |
| /** |
| * @see ITypeHierarchy |
| */ |
| public boolean exists() { |
| if (!this.needsRefresh) return true; |
| |
| return (this.focusType == null || this.focusType.exists()) && this.javaProject().exists(); |
| } |
| /** |
| * Notifies listeners that this hierarchy has changed and needs |
| * refreshing. Note that listeners can be removed as we iterate |
| * through the list. |
| */ |
| public void fireChange() { |
| ArrayList listeners = this.changeListeners; |
| if (listeners == null) { |
| return; |
| } |
| if (DEBUG) { |
| System.out.println("FIRING hierarchy change ["+Thread.currentThread()+"]"); //$NON-NLS-1$ //$NON-NLS-2$ |
| if (this.focusType != null) { |
| System.out.println(" for hierarchy focused on " + ((JavaElement)this.focusType).toStringWithAncestors()); //$NON-NLS-1$ |
| } |
| } |
| // clone so that a listener cannot have a side-effect on this list when being notified |
| listeners = (ArrayList)listeners.clone(); |
| for (int i= 0; i < listeners.size(); i++) { |
| final ITypeHierarchyChangedListener listener= (ITypeHierarchyChangedListener)listeners.get(i); |
| Platform.run(new ISafeRunnable() { |
| public void handleException(Throwable exception) { |
| Util.log(exception, "Exception occurred in listener of Type hierarchy change notification"); //$NON-NLS-1$ |
| } |
| public void run() throws Exception { |
| listener.typeHierarchyChanged(TypeHierarchy.this); |
| } |
| }); |
| } |
| } |
| private static byte[] flagsToBytes(Integer flags){ |
| if(flags != null) { |
| return flags.toString().getBytes(); |
| } else { |
| return NO_FLAGS; |
| } |
| } |
| /** |
| * @see ITypeHierarchy |
| */ |
| public IType[] getAllClasses() { |
| |
| TypeVector classes = this.rootClasses.copy(); |
| for (Iterator iter = this.classToSuperclass.keySet().iterator(); iter.hasNext();){ |
| classes.add((IType)iter.next()); |
| } |
| return classes.elements(); |
| } |
| /** |
| * @see ITypeHierarchy |
| */ |
| public IType[] getAllInterfaces() { |
| IType[] collection= new IType[this.interfaces.size()]; |
| this.interfaces.toArray(collection); |
| return collection; |
| } |
| /** |
| * @see ITypeHierarchy |
| */ |
| public IType[] getAllSubtypes(IType type) { |
| return getAllSubtypesForType(type); |
| } |
| /** |
| * @see getAllSubtypes(IType) |
| */ |
| private IType[] getAllSubtypesForType(IType type) { |
| ArrayList subTypes = new ArrayList(); |
| getAllSubtypesForType0(type, subTypes); |
| IType[] subClasses = new IType[subTypes.size()]; |
| subTypes.toArray(subClasses); |
| return subClasses; |
| } |
| /** |
| */ |
| private void getAllSubtypesForType0(IType type, ArrayList subs) { |
| IType[] subTypes = getSubtypesForType(type); |
| if (subTypes.length != 0) { |
| for (int i = 0; i < subTypes.length; i++) { |
| IType subType = subTypes[i]; |
| subs.add(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) { |
| ArrayList supers = new ArrayList(); |
| if (this.typeToSuperInterfaces.get(type) == null) { |
| return NO_TYPE; |
| } |
| getAllSuperInterfaces0(type, supers); |
| IType[] superinterfaces = new IType[supers.size()]; |
| supers.toArray(superinterfaces); |
| return superinterfaces; |
| } |
| private void getAllSuperInterfaces0(IType type, ArrayList supers) { |
| IType[] superinterfaces = (IType[]) this.typeToSuperInterfaces.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) this.classToSuperclass.get(type); |
| if (superclass != null) { |
| getAllSuperInterfaces0(superclass, supers); |
| } |
| } |
| /** |
| * @see ITypeHierarchy |
| */ |
| public IType[] getAllSupertypes(IType type) { |
| ArrayList supers = new ArrayList(); |
| if (this.typeToSuperInterfaces.get(type) == null) { |
| return NO_TYPE; |
| } |
| getAllSupertypes0(type, supers); |
| IType[] supertypes = new IType[supers.size()]; |
| supers.toArray(supertypes); |
| return supertypes; |
| } |
| private void getAllSupertypes0(IType type, ArrayList supers) { |
| IType[] superinterfaces = (IType[]) this.typeToSuperInterfaces.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) this.classToSuperclass.get(type); |
| if (superclass != null) { |
| supers.add(superclass); |
| getAllSupertypes0(superclass, supers); |
| } |
| } |
| /** |
| * @see ITypeHierarchy |
| */ |
| public IType[] getAllTypes() { |
| IType[] classes = getAllClasses(); |
| int classesLength = classes.length; |
| IType[] allInterfaces = getAllInterfaces(); |
| int interfacesLength = allInterfaces.length; |
| IType[] all = new IType[classesLength + interfacesLength]; |
| System.arraycopy(classes, 0, all, 0, classesLength); |
| System.arraycopy(allInterfaces, 0, all, classesLength, interfacesLength); |
| return all; |
| } |
| |
| /** |
| * @see ITypeHierarchy#getCachedFlags(IType) |
| */ |
| public int getCachedFlags(IType type) { |
| Integer flagObject = (Integer) this.typeFlags.get(type); |
| if (flagObject != null){ |
| return flagObject.intValue(); |
| } |
| return -1; |
| } |
| |
| /** |
| * @see ITypeHierarchy |
| */ |
| public IType[] getExtendingInterfaces(IType type) { |
| if (!this.isInterface(type)) return NO_TYPE; |
| return getExtendingInterfaces0(type); |
| } |
| /** |
| * Assumes that the type is an interface |
| * @see getExtendingInterfaces |
| */ |
| private IType[] getExtendingInterfaces0(IType extendedInterface) { |
| Iterator iter = this.typeToSuperInterfaces.keySet().iterator(); |
| ArrayList interfaceList = new ArrayList(); |
| while (iter.hasNext()) { |
| IType type = (IType) iter.next(); |
| if (!this.isInterface(type)) { |
| continue; |
| } |
| IType[] superInterfaces = (IType[]) this.typeToSuperInterfaces.get(type); |
| if (superInterfaces != null) { |
| for (int i = 0; i < superInterfaces.length; i++) { |
| IType superInterface = superInterfaces[i]; |
| if (superInterface.equals(extendedInterface)) { |
| interfaceList.add(type); |
| } |
| } |
| } |
| } |
| IType[] extendingInterfaces = new IType[interfaceList.size()]; |
| interfaceList.toArray(extendingInterfaces); |
| return extendingInterfaces; |
| } |
| /** |
| * @see ITypeHierarchy |
| */ |
| public IType[] getImplementingClasses(IType type) { |
| if (!this.isInterface(type)) { |
| return NO_TYPE; |
| } |
| return getImplementingClasses0(type); |
| } |
| /** |
| * Assumes that the type is an interface |
| * @see getImplementingClasses |
| */ |
| private IType[] getImplementingClasses0(IType interfce) { |
| |
| Iterator iter = this.typeToSuperInterfaces.keySet().iterator(); |
| ArrayList iMenters = new ArrayList(); |
| while (iter.hasNext()) { |
| IType type = (IType) iter.next(); |
| if (this.isInterface(type)) { |
| continue; |
| } |
| IType[] types = (IType[]) this.typeToSuperInterfaces.get(type); |
| for (int i = 0; i < types.length; i++) { |
| IType iFace = types[i]; |
| if (iFace.equals(interfce)) { |
| iMenters.add(type); |
| } |
| } |
| } |
| IType[] implementers = new IType[iMenters.size()]; |
| iMenters.toArray(implementers); |
| return implementers; |
| } |
| /** |
| * @see ITypeHierarchy |
| */ |
| public IType[] getRootClasses() { |
| return this.rootClasses.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) { |
| if (this.isInterface(type)) { |
| return NO_TYPE; |
| } |
| TypeVector vector = (TypeVector)this.typeToSubtypes.get(type); |
| if (vector == null) |
| return NO_TYPE; |
| 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)this.typeToSubtypes.get(type); |
| if (vector == null) |
| return NO_TYPE; |
| else |
| return vector.elements(); |
| } |
| /** |
| * @see ITypeHierarchy |
| */ |
| public IType getSuperclass(IType type) { |
| if (this.isInterface(type)) { |
| return null; |
| } |
| return (IType) this.classToSuperclass.get(type); |
| } |
| /** |
| * @see ITypeHierarchy |
| */ |
| public IType[] getSuperInterfaces(IType type) { |
| IType[] types = (IType[]) this.typeToSuperInterfaces.get(type); |
| if (types == null) { |
| return NO_TYPE; |
| } |
| return types; |
| } |
| /** |
| * @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 this.focusType; |
| } |
| /** |
| * 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; |
| } |
| /* |
| * Whether fine-grained deltas where collected and affects this hierarchy. |
| */ |
| public boolean hasFineGrainChanges() { |
| return this.changeCollector != null && this.changeCollector.needsRefresh(); |
| } |
| /** |
| * 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 (this.focusType != null && this.focusType.getElementName().equals(simpleName)) { |
| return true; |
| } |
| IType[] types = this.getAllSubtypes(this.focusType); |
| for (int i = 0, length = types.length; i < length; i++) { |
| if (types[i].getElementName().equals(simpleName)) { |
| 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 simple name of the given type or one of its supertypes is |
| * the simple name of one of the types in this hierarchy. |
| */ |
| boolean includesTypeOrSupertype(IType type) { |
| try { |
| // check type |
| if (hasTypeNamed(type.getElementName())) return true; |
| |
| // check superclass |
| String superclassName = type.getSuperclassName(); |
| if (superclassName != null) { |
| int lastSeparator = superclassName.lastIndexOf('.'); |
| String simpleName = (lastSeparator > -1) ? superclassName.substring(lastSeparator) : superclassName; |
| if (hasTypeNamed(simpleName)) return true; |
| } |
| |
| // check superinterfaces |
| String[] superinterfaceNames = type.getSuperInterfaceNames(); |
| if (superinterfaceNames != null) { |
| for (int i = 0, length = superinterfaceNames.length; i < length; i++) { |
| String superinterfaceName = superinterfaceNames[i]; |
| int lastSeparator = superinterfaceName.lastIndexOf('.'); |
| String simpleName = (lastSeparator > -1) ? superinterfaceName.substring(lastSeparator) : superinterfaceName; |
| if (hasTypeNamed(simpleName)) return true; |
| } |
| } |
| } catch (JavaModelException e) { |
| // ignore |
| } |
| 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); |
| this.classToSuperclass = new HashMap(size); |
| this.interfaces = new ArrayList(smallSize); |
| this.missingTypes = new ArrayList(smallSize); |
| this.rootClasses = new TypeVector(); |
| this.typeToSubtypes = new HashMap(smallSize); |
| this.typeToSuperInterfaces = new HashMap(smallSize); |
| this.typeFlags = new HashMap(smallSize); |
| |
| this.projectRegion = new Region(); |
| this.packageRegion = new Region(); |
| this.files = new HashMap(5); |
| } |
| /** |
| * Returns true if the given delta could change this type hierarchy |
| */ |
| public synchronized 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 isAffectedByOpenable(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 = ((JavaProject)this.javaProject()).getExpandedClasspath(true); |
| for (int i = 0; i < classpath.length; i++) { |
| if (classpath[i].getEntryKind() == IClasspathEntry.CPE_PROJECT |
| && classpath[i].getPath().equals(element.getPath())) { |
| 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 = this.packageRegion.getElements(); |
| for (int i = 0; i < pkgs.length; i++) { |
| IJavaProject javaProject = pkgs[i].getJavaProject(); |
| if (javaProject != null && javaProject.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 this.projectRegion.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 this.projectRegion.contains(element); |
| case IJavaElementDelta.REMOVED : |
| case IJavaElementDelta.CHANGED : |
| int flags = delta.getFlags(); |
| if ((flags & IJavaElementDelta.F_ADDED_TO_CLASSPATH) > 0) { |
| // check if the root is in the classpath of one of the projects of this hierarchy |
| if (this.projectRegion != null) { |
| IPackageFragmentRoot root = (IPackageFragmentRoot)element; |
| IPath rootPath = root.getPath(); |
| IJavaElement[] elements = this.projectRegion.getElements(); |
| for (int i = 0; i < elements.length; i++) { |
| IJavaProject javaProject = (IJavaProject)elements[i]; |
| try { |
| IClasspathEntry[] classpath = javaProject.getResolvedClasspath(true); |
| for (int j = 0; j < classpath.length; j++) { |
| IClasspathEntry entry = classpath[j]; |
| if (entry.getPath().equals(rootPath)) { |
| return true; |
| } |
| } |
| } catch (JavaModelException e) { |
| // igmore this project |
| } |
| } |
| } |
| } |
| if ((flags & IJavaElementDelta.F_REMOVED_FROM_CLASSPATH) > 0 || (flags & 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 = this.packageRegion.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. |
| */ |
| protected boolean isAffectedByOpenable(IJavaElementDelta delta, IJavaElement element) { |
| if (element instanceof CompilationUnit) { |
| CompilationUnit cu = (CompilationUnit)element; |
| ChangeCollector collector = this.changeCollector == null ? new ChangeCollector(this) : this.changeCollector; |
| try { |
| collector.addChange(cu, delta); |
| } catch (JavaModelException e) { |
| e.printStackTrace(); |
| } |
| if (cu.isWorkingCopy()) { |
| // changes to working copies are batched |
| this.changeCollector = collector; |
| return false; |
| } else { |
| return collector.needsRefresh(); |
| } |
| } else if (element instanceof ClassFile) { |
| switch (delta.getKind()) { |
| case IJavaElementDelta.REMOVED: |
| return this.files.get(element) != null; |
| case IJavaElementDelta.ADDED: |
| IType type = ((ClassFile)element).getType(); |
| String typeName = type.getElementName(); |
| if (hasSupertype(typeName) |
| || subtypesIncludeSupertypeOf(type) |
| || this.missingTypes.contains(typeName)) { |
| |
| return true; |
| } |
| break; |
| case IJavaElementDelta.CHANGED: |
| IJavaElementDelta[] children = delta.getAffectedChildren(); |
| for (int i = 0, length = children.length; i < length; i++) { |
| IJavaElementDelta child = children[i]; |
| IJavaElement childElement = child.getElement(); |
| if (childElement instanceof IType) { |
| type = (IType)childElement; |
| boolean hasVisibilityChange = (delta.getFlags() & IJavaElementDelta.F_MODIFIERS) > 0; |
| boolean hasSupertypeChange = (delta.getFlags() & IJavaElementDelta.F_SUPER_TYPES) > 0; |
| if ((hasVisibilityChange && hasSupertype(type.getElementName())) |
| || (hasSupertypeChange && includesTypeOrSupertype(type))) { |
| return true; |
| } |
| } |
| } |
| break; |
| } |
| } |
| return false; |
| } |
| private boolean isInterface(IType type) { |
| int flags = this.getCachedFlags(type); |
| if (flags == -1) { |
| try { |
| return type.isInterface(); |
| } catch (JavaModelException e) { |
| return false; |
| } |
| } else { |
| return Flags.isInterface(flags); |
| } |
| } |
| /** |
| * Returns the java project this hierarchy was created in. |
| */ |
| public IJavaProject javaProject() { |
| return this.focusType.getJavaProject(); |
| } |
| protected static byte[] readUntil(InputStream input, byte separator) throws JavaModelException, IOException{ |
| return readUntil(input, separator, 0); |
| } |
| protected static byte[] readUntil(InputStream input, byte separator, int offset) throws IOException, JavaModelException{ |
| int length = 0; |
| byte[] bytes = new byte[SIZE]; |
| byte b; |
| while((b = (byte)input.read()) != separator && b != -1) { |
| if(bytes.length == length) { |
| System.arraycopy(bytes, 0, bytes = new byte[length*2], 0, length); |
| } |
| bytes[length++] = b; |
| } |
| if(b == -1) { |
| throw new JavaModelException(new JavaModelStatus(IStatus.ERROR)); |
| } |
| System.arraycopy(bytes, 0, bytes = new byte[length + offset], offset, length); |
| return bytes; |
| } |
| public static ITypeHierarchy load(IType type, InputStream input, WorkingCopyOwner owner) throws JavaModelException { |
| try { |
| TypeHierarchy typeHierarchy = new TypeHierarchy(); |
| typeHierarchy.initialize(1); |
| |
| IType[] types = new IType[SIZE]; |
| int typeCount = 0; |
| |
| byte version = (byte)input.read(); |
| |
| if(version != VERSION) { |
| throw new JavaModelException(new JavaModelStatus(IStatus.ERROR)); |
| } |
| byte generalInfo = (byte)input.read(); |
| if((generalInfo & COMPUTE_SUBTYPES) != 0) { |
| typeHierarchy.computeSubtypes = true; |
| } |
| |
| byte b; |
| byte[] bytes; |
| |
| // read project |
| bytes = readUntil(input, SEPARATOR1); |
| if(bytes.length > 0) { |
| typeHierarchy.project = (IJavaProject)JavaCore.create(new String(bytes)); |
| typeHierarchy.scope = SearchEngine.createJavaSearchScope(new IJavaElement[] {typeHierarchy.project}); |
| } else { |
| typeHierarchy.project = null; |
| typeHierarchy.scope = SearchEngine.createWorkspaceScope(); |
| } |
| |
| // read missing type |
| { |
| bytes = readUntil(input, SEPARATOR1); |
| byte[] missing; |
| int j = 0; |
| int length = bytes.length; |
| for (int i = 0; i < length; i++) { |
| b = bytes[i]; |
| if(b == SEPARATOR2) { |
| missing = new byte[i - j]; |
| System.arraycopy(bytes, j, missing, 0, i - j); |
| typeHierarchy.missingTypes.add(new String(missing)); |
| j = i + 1; |
| } |
| } |
| System.arraycopy(bytes, j, missing = new byte[length - j], 0, length - j); |
| typeHierarchy.missingTypes.add(new String(missing)); |
| } |
| |
| // read types |
| while((b = (byte)input.read()) != SEPARATOR1 && b != -1) { |
| bytes = readUntil(input, SEPARATOR4, 1); |
| bytes[0] = b; |
| IType element = (IType)JavaCore.create(new String(bytes), owner); |
| |
| if(types.length == typeCount) { |
| System.arraycopy(types, 0, types = new IType[typeCount * 2], 0, typeCount); |
| } |
| types[typeCount++] = element; |
| |
| // read flags |
| bytes = readUntil(input, SEPARATOR4); |
| Integer flags = bytesToFlags(bytes); |
| if(flags != null) { |
| typeHierarchy.cacheFlags(element, flags.intValue()); |
| } |
| |
| // read info |
| byte info = (byte)input.read(); |
| |
| if((info & INTERFACE) != 0) { |
| typeHierarchy.addInterface(element); |
| } |
| if((info & COMPUTED_FOR) != 0) { |
| if(!element.equals(type)) { |
| throw new JavaModelException(new JavaModelStatus(IStatus.ERROR)); |
| } |
| typeHierarchy.focusType = element; |
| } |
| if((info & ROOT) != 0) { |
| typeHierarchy.addRootClass(element); |
| } |
| } |
| |
| // read super class |
| while((b = (byte)input.read()) != SEPARATOR1 && b != -1) { |
| bytes = readUntil(input, SEPARATOR3, 1); |
| bytes[0] = b; |
| int subClass = new Integer(new String(bytes)).intValue(); |
| |
| // read super type |
| bytes = readUntil(input, SEPARATOR1); |
| int superClass = new Integer(new String(bytes)).intValue(); |
| |
| typeHierarchy.cacheSuperclass( |
| types[subClass], |
| types[superClass]); |
| } |
| |
| // read super interface |
| while((b = (byte)input.read()) != SEPARATOR1 && b != -1) { |
| bytes = readUntil(input, SEPARATOR3, 1); |
| bytes[0] = b; |
| int subClass = new Integer(new String(bytes)).intValue(); |
| |
| // read super interface |
| bytes = readUntil(input, SEPARATOR1); |
| IType[] superInterfaces = new IType[(bytes.length / 2) + 1]; |
| int interfaceCount = 0; |
| |
| int j = 0; |
| byte[] b2; |
| for (int i = 0; i < bytes.length; i++) { |
| if(bytes[i] == SEPARATOR2){ |
| b2 = new byte[i - j]; |
| System.arraycopy(bytes, j, b2, 0, i - j); |
| j = i + 1; |
| superInterfaces[interfaceCount++] = types[new Integer(new String(b2)).intValue()]; |
| } |
| } |
| b2 = new byte[bytes.length - j]; |
| System.arraycopy(bytes, j, b2, 0, bytes.length - j); |
| superInterfaces[interfaceCount++] = types[new Integer(new String(b2)).intValue()]; |
| System.arraycopy(superInterfaces, 0, superInterfaces = new IType[interfaceCount], 0, interfaceCount); |
| |
| typeHierarchy.cacheSuperInterfaces( |
| types[subClass], |
| superInterfaces); |
| } |
| if(b == -1) { |
| throw new JavaModelException(new JavaModelStatus(IStatus.ERROR)); |
| } |
| return typeHierarchy; |
| } catch(IOException e){ |
| throw new JavaModelException(e, IJavaModelStatusConstants.IO_EXCEPTION); |
| } |
| } |
| /** |
| * 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 = this.packageRegion.getElements(); |
| for (int i = 0; i < pkgs.length; i++) { |
| if (pkgs[i].getElementName().equals(element.getElementName())) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| /** |
| * @see ITypeHierarchy |
| * TODO (jerome) should use a PerThreadObject to build the hierarchy instead of synchronizing |
| * (see also isAffected(IJavaElementDelta)) |
| */ |
| public synchronized void refresh(IProgressMonitor monitor) throws JavaModelException { |
| try { |
| this.progressMonitor = monitor; |
| if (monitor != null) { |
| if (this.focusType != null) { |
| monitor.beginTask(Util.bind("hierarchy.creatingOnType", focusType.getFullyQualifiedName()), 100); //$NON-NLS-1$ |
| } else { |
| monitor.beginTask(Util.bind("hierarchy.creating"), 100); //$NON-NLS-1$ |
| } |
| } |
| long start = -1; |
| if (DEBUG) { |
| start = System.currentTimeMillis(); |
| if (this.computeSubtypes) { |
| System.out.println("CREATING TYPE HIERARCHY [" + Thread.currentThread() + "]"); //$NON-NLS-1$ //$NON-NLS-2$ |
| } else { |
| System.out.println("CREATING SUPER TYPE HIERARCHY [" + Thread.currentThread() + "]"); //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| if (this.focusType != null) { |
| System.out.println(" on type " + ((JavaElement)this.focusType).toStringWithAncestors()); //$NON-NLS-1$ |
| } |
| } |
| |
| compute(); |
| initializeRegions(); |
| this.needsRefresh = false; |
| this.changeCollector = null; |
| |
| if (DEBUG) { |
| if (this.computeSubtypes) { |
| System.out.println("CREATED TYPE HIERARCHY in " + (System.currentTimeMillis() - start) + "ms"); //$NON-NLS-1$ //$NON-NLS-2$ |
| } else { |
| System.out.println("CREATED SUPER TYPE HIERARCHY in " + (System.currentTimeMillis() - start) + "ms"); //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| System.out.println(this.toString()); |
| } |
| } catch (JavaModelException e) { |
| throw e; |
| } catch (CoreException e) { |
| throw new JavaModelException(e); |
| } finally { |
| if (monitor != null) { |
| monitor.done(); |
| } |
| this.progressMonitor = null; |
| } |
| } |
| |
| /** |
| * @see ITypeHierarchy |
| */ |
| public synchronized void removeTypeHierarchyChangedListener(ITypeHierarchyChangedListener listener) { |
| ArrayList listeners = this.changeListeners; |
| if (listeners == null) { |
| return; |
| } |
| listeners.remove(listener); |
| |
| // deregister from JavaCore on last listener removed |
| if (listeners.isEmpty()) { |
| JavaCore.removeElementChangedListener(this); |
| } |
| } |
| /** |
| * @see ITypeHierarchy |
| */ |
| public void store(OutputStream output, IProgressMonitor monitor) throws JavaModelException { |
| try { |
| // compute types in hierarchy |
| Hashtable hashtable = new Hashtable(); |
| Hashtable hashtable2 = new Hashtable(); |
| int count = 0; |
| |
| if(focusType != null) { |
| Integer index = new Integer(count++); |
| hashtable.put(focusType, index); |
| hashtable2.put(index, focusType); |
| } |
| Object[] types = classToSuperclass.keySet().toArray(); |
| for (int i = 0; i < types.length; i++) { |
| Object t = types[i]; |
| if(hashtable.get(t) == null) { |
| Integer index = new Integer(count++); |
| hashtable.put(t, index); |
| hashtable2.put(index, t); |
| } |
| Object superClass = classToSuperclass.get(t); |
| if(superClass != null && hashtable.get(superClass) == null) { |
| Integer index = new Integer(count++); |
| hashtable.put(superClass, index); |
| hashtable2.put(index, superClass); |
| } |
| } |
| types = typeToSuperInterfaces.keySet().toArray(); |
| for (int i = 0; i < types.length; i++) { |
| Object t = types[i]; |
| if(hashtable.get(t) == null) { |
| Integer index = new Integer(count++); |
| hashtable.put(t, index); |
| hashtable2.put(index, t); |
| } |
| Object[] sp = (Object[])typeToSuperInterfaces.get(t); |
| if(sp != null) { |
| for (int j = 0; j < sp.length; j++) { |
| Object superInterface = sp[j]; |
| if(sp[j] != null && hashtable.get(superInterface) == null) { |
| Integer index = new Integer(count++); |
| hashtable.put(superInterface, index); |
| hashtable2.put(index, superInterface); |
| } |
| } |
| } |
| } |
| // save version of the hierarchy format |
| output.write(VERSION); |
| |
| // save general info |
| byte generalInfo = 0; |
| if(computeSubtypes) { |
| generalInfo |= COMPUTE_SUBTYPES; |
| } |
| output.write(generalInfo); |
| |
| // save project |
| if(project != null) { |
| output.write(project.getHandleIdentifier().getBytes()); |
| } |
| output.write(SEPARATOR1); |
| |
| // save missing types |
| for (int i = 0; i < missingTypes.size(); i++) { |
| if(i != 0) { |
| output.write(SEPARATOR2); |
| } |
| output.write(((String)missingTypes.get(i)).getBytes()); |
| |
| } |
| output.write(SEPARATOR1); |
| |
| // save types |
| for (int i = 0; i < count ; i++) { |
| IType t = (IType)hashtable2.get(new Integer(i)); |
| |
| // n bytes |
| output.write(t.getHandleIdentifier().getBytes()); |
| output.write(SEPARATOR4); |
| output.write(flagsToBytes((Integer)typeFlags.get(t))); |
| output.write(SEPARATOR4); |
| byte info = CLASS; |
| if(focusType != null && focusType.equals(t)) { |
| info |= COMPUTED_FOR; |
| } |
| if(interfaces.contains(t)) { |
| info |= INTERFACE; |
| } |
| if(rootClasses.contains(t)) { |
| info |= ROOT; |
| } |
| output.write(info); |
| } |
| output.write(SEPARATOR1); |
| |
| // save superclasses |
| types = classToSuperclass.keySet().toArray(); |
| for (int i = 0; i < types.length; i++) { |
| IJavaElement key = (IJavaElement)types[i]; |
| IJavaElement value = (IJavaElement)classToSuperclass.get(key); |
| |
| output.write(((Integer)hashtable.get(key)).toString().getBytes()); |
| output.write('>'); |
| output.write(((Integer)hashtable.get(value)).toString().getBytes()); |
| output.write(SEPARATOR1); |
| } |
| output.write(SEPARATOR1); |
| |
| // save superinterfaces |
| types = typeToSuperInterfaces.keySet().toArray(); |
| for (int i = 0; i < types.length; i++) { |
| IJavaElement key = (IJavaElement)types[i]; |
| IJavaElement[] values = (IJavaElement[])typeToSuperInterfaces.get(key); |
| |
| if(values.length > 0) { |
| output.write(((Integer)hashtable.get(key)).toString().getBytes()); |
| output.write(SEPARATOR3); |
| for (int j = 0; j < values.length; j++) { |
| IJavaElement value = values[j]; |
| if(j != 0) output.write(SEPARATOR2); |
| output.write(((Integer)hashtable.get(value)).toString().getBytes()); |
| } |
| output.write(SEPARATOR1); |
| } |
| } |
| output.write(SEPARATOR1); |
| } catch(IOException e) { |
| throw new JavaModelException(e, IJavaModelStatusConstants.IO_EXCEPTION); |
| } |
| } |
| /** |
| * 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. |
| */ |
| boolean subtypesIncludeSupertypeOf(IType type) { |
| // look for superclass |
| String superclassName = null; |
| try { |
| superclassName = type.getSuperclassName(); |
| } catch (JavaModelException e) { |
| if (DEBUG) { |
| 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(this.focusType == null ? "<NONE>" : ((JavaElement)this.focusType).toStringWithAncestors()); //$NON-NLS-1$ |
| buffer.append("\n"); //$NON-NLS-1$ |
| if (exists()) { |
| if (this.focusType != null) { |
| buffer.append("Super types:\n"); //$NON-NLS-1$ |
| toString(buffer, this.focusType, 1, true); |
| buffer.append("Sub types:\n"); //$NON-NLS-1$ |
| toString(buffer, this.focusType, 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++) { |
| buffer.append(" "); //$NON-NLS-1$ |
| JavaElement element = (JavaElement)roots[i]; |
| buffer.append(element.toStringWithAncestors()); |
| buffer.append('\n'); |
| toString(buffer, roots[i], 1, false); |
| } |
| } |
| if (this.rootClasses.size > 1) { |
| buffer.append("Root classes:\n"); //$NON-NLS-1$ |
| IType[] roots = this.getRootClasses(); |
| for (int i = 0, length = roots.length; i < length; i++) { |
| IType type = roots[i]; |
| toString(buffer, type, 1, false); |
| } |
| } else if (this.rootClasses.size == 0) { |
| // see http://bugs.eclipse.org/bugs/show_bug.cgi?id=24691 |
| buffer.append("No root classes"); //$NON-NLS-1$ |
| } |
| } 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) { |
| IType[] types= ascendant ? getSupertypes(type) : getSubtypes(type); |
| for (int i= 0; i < types.length; i++) { |
| for (int j= 0; j < indent; j++) { |
| buffer.append(" "); //$NON-NLS-1$ |
| } |
| JavaElement element = (JavaElement)types[i]; |
| buffer.append(element.toStringWithAncestors()); |
| buffer.append('\n'); |
| toString(buffer, types[i], indent + 1, ascendant); |
| } |
| } |
| /** |
| * Returns whether one of the types in this hierarchy has a supertype whose simple |
| * name is the given simple name. |
| */ |
| boolean hasSupertype(String simpleName) { |
| for(Iterator iter = this.classToSuperclass.values().iterator(); iter.hasNext();){ |
| IType superType = (IType)iter.next(); |
| if (superType.getElementName().equals(simpleName)) { |
| return true; |
| } |
| } |
| return false; |
| } |
| /** |
| * @see IProgressMonitor |
| */ |
| protected void worked(int work) { |
| if (this.progressMonitor != null) { |
| this.progressMonitor.worked(work); |
| checkCanceled(); |
| } |
| } |
| } |