0001: /*******************************************************************************
0002: * Copyright (c) 2000, 2007 IBM Corporation and others.
0003: * All rights reserved. This program and the accompanying materials
0004: * are made available under the terms of the Eclipse Public License v1.0
0005: * which accompanies this distribution, and is available at
0006: * http://www.eclipse.org/legal/epl-v10.html
0007: *
0008: * Contributors:
0009: * IBM Corporation - initial API and implementation
0010: *******************************************************************************/package org.eclipse.jdt.internal.core.hierarchy;
0011:
0012: import java.io.IOException;
0013: import java.io.InputStream;
0014: import java.io.OutputStream;
0015: import java.util.ArrayList;
0016: import java.util.HashMap;
0017: import java.util.Hashtable;
0018: import java.util.Iterator;
0019: import java.util.Map;
0020:
0021: import org.eclipse.core.runtime.CoreException;
0022: import org.eclipse.core.runtime.IPath;
0023: import org.eclipse.core.runtime.IProgressMonitor;
0024: import org.eclipse.core.runtime.ISafeRunnable;
0025: import org.eclipse.core.runtime.IStatus;
0026: import org.eclipse.core.runtime.OperationCanceledException;
0027: import org.eclipse.core.runtime.SafeRunner;
0028: import org.eclipse.jdt.core.*;
0029: import org.eclipse.jdt.core.search.IJavaSearchScope;
0030: import org.eclipse.jdt.core.search.SearchEngine;
0031: import org.eclipse.jdt.internal.core.*;
0032: import org.eclipse.jdt.internal.core.util.Messages;
0033: import org.eclipse.jdt.internal.core.util.Util;
0034:
0035: /**
0036: * @see ITypeHierarchy
0037: */
0038: public class TypeHierarchy implements ITypeHierarchy,
0039: IElementChangedListener {
0040:
0041: public static boolean DEBUG = false;
0042:
0043: static final byte VERSION = 0x0000;
0044: // SEPARATOR
0045: static final byte SEPARATOR1 = '\n';
0046: static final byte SEPARATOR2 = ',';
0047: static final byte SEPARATOR3 = '>';
0048: static final byte SEPARATOR4 = '\r';
0049: // general info
0050: static final byte COMPUTE_SUBTYPES = 0x0001;
0051:
0052: // type info
0053: static final byte CLASS = 0x0000;
0054: static final byte INTERFACE = 0x0001;
0055: static final byte COMPUTED_FOR = 0x0002;
0056: static final byte ROOT = 0x0004;
0057:
0058: // cst
0059: static final byte[] NO_FLAGS = new byte[] {};
0060: static final int SIZE = 10;
0061:
0062: /**
0063: * The Java Project in which the hierarchy is being built - this
0064: * provides the context for determining a classpath and namelookup rules.
0065: * Possibly null.
0066: */
0067: protected IJavaProject project;
0068: /**
0069: * The type the hierarchy was specifically computed for,
0070: * possibly null.
0071: */
0072: protected IType focusType;
0073:
0074: /*
0075: * The working copies that take precedence over original compilation units
0076: */
0077: protected ICompilationUnit[] workingCopies;
0078:
0079: protected Map classToSuperclass;
0080: protected Map typeToSuperInterfaces;
0081: protected Map typeToSubtypes;
0082: protected Map typeFlags;
0083: protected TypeVector rootClasses = new TypeVector();
0084: protected ArrayList interfaces = new ArrayList(10);
0085: public ArrayList missingTypes = new ArrayList(4);
0086:
0087: protected static final IType[] NO_TYPE = new IType[0];
0088:
0089: /**
0090: * The progress monitor to report work completed too.
0091: */
0092: protected IProgressMonitor progressMonitor = null;
0093:
0094: /**
0095: * Change listeners - null if no one is listening.
0096: */
0097: protected ArrayList changeListeners = null;
0098:
0099: /*
0100: * A map from Openables to ArrayLists of ITypes
0101: */
0102: public Map files = null;
0103:
0104: /**
0105: * A region describing the packages considered by this
0106: * hierarchy. Null if not activated.
0107: */
0108: protected Region packageRegion = null;
0109:
0110: /**
0111: * A region describing the projects considered by this
0112: * hierarchy. Null if not activated.
0113: */
0114: protected Region projectRegion = null;
0115:
0116: /**
0117: * Whether this hierarchy should contains subtypes.
0118: */
0119: protected boolean computeSubtypes;
0120:
0121: /**
0122: * The scope this hierarchy should restrain itsef in.
0123: */
0124: IJavaSearchScope scope;
0125:
0126: /*
0127: * Whether this hierarchy needs refresh
0128: */
0129: public boolean needsRefresh = true;
0130:
0131: /*
0132: * Collects changes to types
0133: */
0134: protected ChangeCollector changeCollector;
0135:
0136: /**
0137: * Creates an empty TypeHierarchy
0138: */
0139: public TypeHierarchy() {
0140: // Creates an empty TypeHierarchy
0141: }
0142:
0143: /**
0144: * Creates a TypeHierarchy on the given type.
0145: */
0146: public TypeHierarchy(IType type, ICompilationUnit[] workingCopies,
0147: IJavaProject project, boolean computeSubtypes) {
0148: this (type, workingCopies, SearchEngine
0149: .createJavaSearchScope(new IJavaElement[] { project }),
0150: computeSubtypes);
0151: this .project = project;
0152: }
0153:
0154: /**
0155: * Creates a TypeHierarchy on the given type.
0156: */
0157: public TypeHierarchy(IType type, ICompilationUnit[] workingCopies,
0158: IJavaSearchScope scope, boolean computeSubtypes) {
0159: this .focusType = type == null ? null
0160: : (IType) ((JavaElement) type).unresolved(); // unsure the focus type is unresolved (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=92357)
0161: this .workingCopies = workingCopies;
0162: this .computeSubtypes = computeSubtypes;
0163: this .scope = scope;
0164: }
0165:
0166: /**
0167: * Initializes the file, package and project regions
0168: */
0169: protected void initializeRegions() {
0170:
0171: IType[] allTypes = getAllTypes();
0172: for (int i = 0; i < allTypes.length; i++) {
0173: IType type = allTypes[i];
0174: Openable o = (Openable) ((JavaElement) type)
0175: .getOpenableParent();
0176: if (o != null) {
0177: ArrayList types = (ArrayList) this .files.get(o);
0178: if (types == null) {
0179: types = new ArrayList();
0180: this .files.put(o, types);
0181: }
0182: types.add(type);
0183: }
0184: IPackageFragment pkg = type.getPackageFragment();
0185: this .packageRegion.add(pkg);
0186: IJavaProject declaringProject = type.getJavaProject();
0187: if (declaringProject != null) {
0188: this .projectRegion.add(declaringProject);
0189: }
0190: checkCanceled();
0191: }
0192: }
0193:
0194: /**
0195: * Adds all of the elements in the collection to the list if the
0196: * element is not already in the list.
0197: */
0198: private void addAllCheckingDuplicates(ArrayList list,
0199: IType[] collection) {
0200: for (int i = 0; i < collection.length; i++) {
0201: IType element = collection[i];
0202: if (!list.contains(element)) {
0203: list.add(element);
0204: }
0205: }
0206: }
0207:
0208: /**
0209: * Adds the type to the collection of interfaces.
0210: */
0211: protected void addInterface(IType type) {
0212: this .interfaces.add(type);
0213: }
0214:
0215: /**
0216: * Adds the type to the collection of root classes
0217: * if the classes is not already present in the collection.
0218: */
0219: protected void addRootClass(IType type) {
0220: if (this .rootClasses.contains(type))
0221: return;
0222: this .rootClasses.add(type);
0223: }
0224:
0225: /**
0226: * Adds the given subtype to the type.
0227: */
0228: protected void addSubtype(IType type, IType subtype) {
0229: TypeVector subtypes = (TypeVector) this .typeToSubtypes
0230: .get(type);
0231: if (subtypes == null) {
0232: subtypes = new TypeVector();
0233: this .typeToSubtypes.put(type, subtypes);
0234: }
0235: if (!subtypes.contains(subtype)) {
0236: subtypes.add(subtype);
0237: }
0238: }
0239:
0240: /**
0241: * @see ITypeHierarchy
0242: */
0243: public synchronized void addTypeHierarchyChangedListener(
0244: ITypeHierarchyChangedListener listener) {
0245: ArrayList listeners = this .changeListeners;
0246: if (listeners == null) {
0247: this .changeListeners = listeners = new ArrayList();
0248: }
0249:
0250: // register with JavaCore to get Java element delta on first listener added
0251: if (listeners.size() == 0) {
0252: JavaCore.addElementChangedListener(this );
0253: }
0254:
0255: // add listener only if it is not already present
0256: if (listeners.indexOf(listener) == -1) {
0257: listeners.add(listener);
0258: }
0259: }
0260:
0261: private static Integer bytesToFlags(byte[] bytes) {
0262: if (bytes != null && bytes.length > 0) {
0263: return new Integer(new String(bytes));
0264: } else {
0265: return null;
0266: }
0267: }
0268:
0269: /**
0270: * cacheFlags.
0271: */
0272: public void cacheFlags(IType type, int flags) {
0273: this .typeFlags.put(type, new Integer(flags));
0274: }
0275:
0276: /**
0277: * Caches the handle of the superclass for the specified type.
0278: * As a side effect cache this type as a subtype of the superclass.
0279: */
0280: protected void cacheSuperclass(IType type, IType super class) {
0281: if (super class != null) {
0282: this .classToSuperclass.put(type, super class);
0283: addSubtype(super class, type);
0284: }
0285: }
0286:
0287: /**
0288: * Caches all of the superinterfaces that are specified for the
0289: * type.
0290: */
0291: protected void cacheSuperInterfaces(IType type,
0292: IType[] super interfaces) {
0293: this .typeToSuperInterfaces.put(type, super interfaces);
0294: for (int i = 0; i < super interfaces.length; i++) {
0295: IType super interface = super interfaces[i];
0296: if (super interface != null) {
0297: addSubtype(super interface, type);
0298: }
0299: }
0300: }
0301:
0302: /**
0303: * Checks with the progress monitor to see whether the creation of the type hierarchy
0304: * should be canceled. Should be regularly called
0305: * so that the user can cancel.
0306: *
0307: * @exception OperationCanceledException if cancelling the operation has been requested
0308: * @see IProgressMonitor#isCanceled
0309: */
0310: protected void checkCanceled() {
0311: if (this .progressMonitor != null
0312: && this .progressMonitor.isCanceled()) {
0313: throw new OperationCanceledException();
0314: }
0315: }
0316:
0317: /**
0318: * Compute this type hierarchy.
0319: */
0320: protected void compute() throws JavaModelException, CoreException {
0321: if (this .focusType != null) {
0322: HierarchyBuilder builder = new IndexBasedHierarchyBuilder(
0323: this , this .scope);
0324: builder.build(this .computeSubtypes);
0325: } // else a RegionBasedTypeHierarchy should be used
0326: }
0327:
0328: /**
0329: * @see ITypeHierarchy
0330: */
0331: public boolean contains(IType type) {
0332: // classes
0333: if (this .classToSuperclass.get(type) != null) {
0334: return true;
0335: }
0336:
0337: // root classes
0338: if (this .rootClasses.contains(type))
0339: return true;
0340:
0341: // interfaces
0342: if (this .interfaces.contains(type))
0343: return true;
0344:
0345: return false;
0346: }
0347:
0348: /**
0349: * Determines if the change effects this hierarchy, and fires
0350: * change notification if required.
0351: */
0352: public void elementChanged(ElementChangedEvent event) {
0353: // type hierarchy change has already been fired
0354: if (this .needsRefresh)
0355: return;
0356:
0357: if (isAffected(event.getDelta())) {
0358: this .needsRefresh = true;
0359: fireChange();
0360: }
0361: }
0362:
0363: /**
0364: * @see ITypeHierarchy
0365: */
0366: public boolean exists() {
0367: if (!this .needsRefresh)
0368: return true;
0369:
0370: return (this .focusType == null || this .focusType.exists())
0371: && this .javaProject().exists();
0372: }
0373:
0374: /**
0375: * Notifies listeners that this hierarchy has changed and needs
0376: * refreshing. Note that listeners can be removed as we iterate
0377: * through the list.
0378: */
0379: public void fireChange() {
0380: ArrayList listeners = this .changeListeners;
0381: if (listeners == null) {
0382: return;
0383: }
0384: if (DEBUG) {
0385: System.out
0386: .println("FIRING hierarchy change [" + Thread.currentThread() + "]"); //$NON-NLS-1$ //$NON-NLS-2$
0387: if (this .focusType != null) {
0388: System.out
0389: .println(" for hierarchy focused on " + ((JavaElement) this .focusType).toStringWithAncestors()); //$NON-NLS-1$
0390: }
0391: }
0392: // clone so that a listener cannot have a side-effect on this list when being notified
0393: listeners = (ArrayList) listeners.clone();
0394: for (int i = 0; i < listeners.size(); i++) {
0395: final ITypeHierarchyChangedListener listener = (ITypeHierarchyChangedListener) listeners
0396: .get(i);
0397: SafeRunner.run(new ISafeRunnable() {
0398: public void handleException(Throwable exception) {
0399: Util
0400: .log(exception,
0401: "Exception occurred in listener of Type hierarchy change notification"); //$NON-NLS-1$
0402: }
0403:
0404: public void run() throws Exception {
0405: listener.typeHierarchyChanged(TypeHierarchy.this );
0406: }
0407: });
0408: }
0409: }
0410:
0411: private static byte[] flagsToBytes(Integer flags) {
0412: if (flags != null) {
0413: return flags.toString().getBytes();
0414: } else {
0415: return NO_FLAGS;
0416: }
0417: }
0418:
0419: /**
0420: * @see ITypeHierarchy
0421: */
0422: public IType[] getAllClasses() {
0423:
0424: TypeVector classes = this .rootClasses.copy();
0425: for (Iterator iter = this .classToSuperclass.keySet().iterator(); iter
0426: .hasNext();) {
0427: classes.add((IType) iter.next());
0428: }
0429: return classes.elements();
0430: }
0431:
0432: /**
0433: * @see ITypeHierarchy
0434: */
0435: public IType[] getAllInterfaces() {
0436: IType[] collection = new IType[this .interfaces.size()];
0437: this .interfaces.toArray(collection);
0438: return collection;
0439: }
0440:
0441: /**
0442: * @see ITypeHierarchy
0443: */
0444: public IType[] getAllSubtypes(IType type) {
0445: return getAllSubtypesForType(type);
0446: }
0447:
0448: /**
0449: * @see #getAllSubtypes(IType)
0450: */
0451: private IType[] getAllSubtypesForType(IType type) {
0452: ArrayList subTypes = new ArrayList();
0453: getAllSubtypesForType0(type, subTypes);
0454: IType[] subClasses = new IType[subTypes.size()];
0455: subTypes.toArray(subClasses);
0456: return subClasses;
0457: }
0458:
0459: /**
0460: */
0461: private void getAllSubtypesForType0(IType type, ArrayList subs) {
0462: IType[] subTypes = getSubtypesForType(type);
0463: if (subTypes.length != 0) {
0464: for (int i = 0; i < subTypes.length; i++) {
0465: IType subType = subTypes[i];
0466: subs.add(subType);
0467: getAllSubtypesForType0(subType, subs);
0468: }
0469: }
0470: }
0471:
0472: /**
0473: * @see ITypeHierarchy
0474: */
0475: public IType[] getAllSuperclasses(IType type) {
0476: IType super class = getSuperclass(type);
0477: TypeVector super s = new TypeVector();
0478: while (super class != null) {
0479: super s.add(super class);
0480: super class = getSuperclass(super class);
0481: }
0482: return super s.elements();
0483: }
0484:
0485: /**
0486: * @see ITypeHierarchy
0487: */
0488: public IType[] getAllSuperInterfaces(IType type) {
0489: ArrayList super s = new ArrayList();
0490: if (this .typeToSuperInterfaces.get(type) == null) {
0491: return NO_TYPE;
0492: }
0493: getAllSuperInterfaces0(type, super s);
0494: IType[] super interfaces = new IType[super s.size()];
0495: super s.toArray(super interfaces);
0496: return super interfaces;
0497: }
0498:
0499: private void getAllSuperInterfaces0(IType type, ArrayList super s) {
0500: IType[] super interfaces = (IType[]) this .typeToSuperInterfaces
0501: .get(type);
0502: if (super interfaces != null && super interfaces.length != 0) {
0503: addAllCheckingDuplicates(super s, super interfaces);
0504: for (int i = 0; i < super interfaces.length; i++) {
0505: getAllSuperInterfaces0(super interfaces[i], super s);
0506: }
0507: }
0508: IType super class = (IType) this .classToSuperclass.get(type);
0509: if (super class != null) {
0510: getAllSuperInterfaces0(super class, super s);
0511: }
0512: }
0513:
0514: /**
0515: * @see ITypeHierarchy
0516: */
0517: public IType[] getAllSupertypes(IType type) {
0518: ArrayList super s = new ArrayList();
0519: if (this .typeToSuperInterfaces.get(type) == null) {
0520: return NO_TYPE;
0521: }
0522: getAllSupertypes0(type, super s);
0523: IType[] super types = new IType[super s.size()];
0524: super s.toArray(super types);
0525: return super types;
0526: }
0527:
0528: private void getAllSupertypes0(IType type, ArrayList super s) {
0529: IType[] super interfaces = (IType[]) this .typeToSuperInterfaces
0530: .get(type);
0531: if (super interfaces != null && super interfaces.length != 0) {
0532: addAllCheckingDuplicates(super s, super interfaces);
0533: for (int i = 0; i < super interfaces.length; i++) {
0534: getAllSuperInterfaces0(super interfaces[i], super s);
0535: }
0536: }
0537: IType super class = (IType) this .classToSuperclass.get(type);
0538: if (super class != null) {
0539: super s.add(super class);
0540: getAllSupertypes0(super class, super s);
0541: }
0542: }
0543:
0544: /**
0545: * @see ITypeHierarchy
0546: */
0547: public IType[] getAllTypes() {
0548: IType[] classes = getAllClasses();
0549: int classesLength = classes.length;
0550: IType[] allInterfaces = getAllInterfaces();
0551: int interfacesLength = allInterfaces.length;
0552: IType[] all = new IType[classesLength + interfacesLength];
0553: System.arraycopy(classes, 0, all, 0, classesLength);
0554: System.arraycopy(allInterfaces, 0, all, classesLength,
0555: interfacesLength);
0556: return all;
0557: }
0558:
0559: /**
0560: * @see ITypeHierarchy#getCachedFlags(IType)
0561: */
0562: public int getCachedFlags(IType type) {
0563: Integer flagObject = (Integer) this .typeFlags.get(type);
0564: if (flagObject != null) {
0565: return flagObject.intValue();
0566: }
0567: return -1;
0568: }
0569:
0570: /**
0571: * @see ITypeHierarchy
0572: */
0573: public IType[] getExtendingInterfaces(IType type) {
0574: if (!this .isInterface(type))
0575: return NO_TYPE;
0576: return getExtendingInterfaces0(type);
0577: }
0578:
0579: /**
0580: * Assumes that the type is an interface
0581: * @see #getExtendingInterfaces
0582: */
0583: private IType[] getExtendingInterfaces0(IType extendedInterface) {
0584: Iterator iter = this .typeToSuperInterfaces.entrySet()
0585: .iterator();
0586: ArrayList interfaceList = new ArrayList();
0587: while (iter.hasNext()) {
0588: Map.Entry entry = (Map.Entry) iter.next();
0589: IType type = (IType) entry.getKey();
0590: if (!this .isInterface(type)) {
0591: continue;
0592: }
0593: IType[] super Interfaces = (IType[]) entry.getValue();
0594: if (super Interfaces != null) {
0595: for (int i = 0; i < super Interfaces.length; i++) {
0596: IType super Interface = super Interfaces[i];
0597: if (super Interface.equals(extendedInterface)) {
0598: interfaceList.add(type);
0599: }
0600: }
0601: }
0602: }
0603: IType[] extendingInterfaces = new IType[interfaceList.size()];
0604: interfaceList.toArray(extendingInterfaces);
0605: return extendingInterfaces;
0606: }
0607:
0608: /**
0609: * @see ITypeHierarchy
0610: */
0611: public IType[] getImplementingClasses(IType type) {
0612: if (!this .isInterface(type)) {
0613: return NO_TYPE;
0614: }
0615: return getImplementingClasses0(type);
0616: }
0617:
0618: /**
0619: * Assumes that the type is an interface
0620: * @see #getImplementingClasses
0621: */
0622: private IType[] getImplementingClasses0(IType interfce) {
0623:
0624: Iterator iter = this .typeToSuperInterfaces.entrySet()
0625: .iterator();
0626: ArrayList iMenters = new ArrayList();
0627: while (iter.hasNext()) {
0628: Map.Entry entry = (Map.Entry) iter.next();
0629: IType type = (IType) entry.getKey();
0630: if (this .isInterface(type)) {
0631: continue;
0632: }
0633: IType[] types = (IType[]) entry.getValue();
0634: for (int i = 0; i < types.length; i++) {
0635: IType iFace = types[i];
0636: if (iFace.equals(interfce)) {
0637: iMenters.add(type);
0638: }
0639: }
0640: }
0641: IType[] implementers = new IType[iMenters.size()];
0642: iMenters.toArray(implementers);
0643: return implementers;
0644: }
0645:
0646: /**
0647: * @see ITypeHierarchy
0648: */
0649: public IType[] getRootClasses() {
0650: return this .rootClasses.elements();
0651: }
0652:
0653: /**
0654: * @see ITypeHierarchy
0655: */
0656: public IType[] getRootInterfaces() {
0657: IType[] allInterfaces = getAllInterfaces();
0658: IType[] roots = new IType[allInterfaces.length];
0659: int rootNumber = 0;
0660: for (int i = 0; i < allInterfaces.length; i++) {
0661: IType[] super Interfaces = getSuperInterfaces(allInterfaces[i]);
0662: if (super Interfaces == null || super Interfaces.length == 0) {
0663: roots[rootNumber++] = allInterfaces[i];
0664: }
0665: }
0666: IType[] result = new IType[rootNumber];
0667: if (result.length > 0) {
0668: System.arraycopy(roots, 0, result, 0, rootNumber);
0669: }
0670: return result;
0671: }
0672:
0673: /**
0674: * @see ITypeHierarchy
0675: */
0676: public IType[] getSubclasses(IType type) {
0677: if (this .isInterface(type)) {
0678: return NO_TYPE;
0679: }
0680: TypeVector vector = (TypeVector) this .typeToSubtypes.get(type);
0681: if (vector == null)
0682: return NO_TYPE;
0683: else
0684: return vector.elements();
0685: }
0686:
0687: /**
0688: * @see ITypeHierarchy
0689: */
0690: public IType[] getSubtypes(IType type) {
0691: return getSubtypesForType(type);
0692: }
0693:
0694: /**
0695: * Returns an array of subtypes for the given type - will never return null.
0696: */
0697: private IType[] getSubtypesForType(IType type) {
0698: TypeVector vector = (TypeVector) this .typeToSubtypes.get(type);
0699: if (vector == null)
0700: return NO_TYPE;
0701: else
0702: return vector.elements();
0703: }
0704:
0705: /**
0706: * @see ITypeHierarchy
0707: */
0708: public IType getSuperclass(IType type) {
0709: if (this .isInterface(type)) {
0710: return null;
0711: }
0712: return (IType) this .classToSuperclass.get(type);
0713: }
0714:
0715: /**
0716: * @see ITypeHierarchy
0717: */
0718: public IType[] getSuperInterfaces(IType type) {
0719: IType[] types = (IType[]) this .typeToSuperInterfaces.get(type);
0720: if (types == null) {
0721: return NO_TYPE;
0722: }
0723: return types;
0724: }
0725:
0726: /**
0727: * @see ITypeHierarchy
0728: */
0729: public IType[] getSupertypes(IType type) {
0730: IType super class = getSuperclass(type);
0731: if (super class == null) {
0732: return getSuperInterfaces(type);
0733: } else {
0734: TypeVector super Types = new TypeVector(
0735: getSuperInterfaces(type));
0736: super Types.add(super class);
0737: return super Types.elements();
0738: }
0739: }
0740:
0741: /**
0742: * @see ITypeHierarchy
0743: */
0744: public IType getType() {
0745: return this .focusType;
0746: }
0747:
0748: /**
0749: * Adds the new elements to a new array that contains all of the elements of the old array.
0750: * Returns the new array.
0751: */
0752: protected IType[] growAndAddToArray(IType[] array, IType[] additions) {
0753: if (array == null || array.length == 0) {
0754: return additions;
0755: }
0756: IType[] old = array;
0757: array = new IType[old.length + additions.length];
0758: System.arraycopy(old, 0, array, 0, old.length);
0759: System.arraycopy(additions, 0, array, old.length,
0760: additions.length);
0761: return array;
0762: }
0763:
0764: /**
0765: * Adds the new element to a new array that contains all of the elements of the old array.
0766: * Returns the new array.
0767: */
0768: protected IType[] growAndAddToArray(IType[] array, IType addition) {
0769: if (array == null || array.length == 0) {
0770: return new IType[] { addition };
0771: }
0772: IType[] old = array;
0773: array = new IType[old.length + 1];
0774: System.arraycopy(old, 0, array, 0, old.length);
0775: array[old.length] = addition;
0776: return array;
0777: }
0778:
0779: /*
0780: * Whether fine-grained deltas where collected and affects this hierarchy.
0781: */
0782: public boolean hasFineGrainChanges() {
0783: ChangeCollector collector = this .changeCollector;
0784: return collector != null && collector.needsRefresh();
0785: }
0786:
0787: /**
0788: * Returns whether one of the subtypes in this hierarchy has the given simple name
0789: * or this type has the given simple name.
0790: */
0791: private boolean hasSubtypeNamed(String simpleName) {
0792: if (this .focusType != null
0793: && this .focusType.getElementName().equals(simpleName)) {
0794: return true;
0795: }
0796: IType[] types = this .focusType == null ? getAllTypes()
0797: : getAllSubtypes(this .focusType);
0798: for (int i = 0, length = types.length; i < length; i++) {
0799: if (types[i].getElementName().equals(simpleName)) {
0800: return true;
0801: }
0802: }
0803: return false;
0804: }
0805:
0806: /**
0807: * Returns whether one of the types in this hierarchy has the given simple name.
0808: */
0809: private boolean hasTypeNamed(String simpleName) {
0810: IType[] types = this .getAllTypes();
0811: for (int i = 0, length = types.length; i < length; i++) {
0812: if (types[i].getElementName().equals(simpleName)) {
0813: return true;
0814: }
0815: }
0816: return false;
0817: }
0818:
0819: /**
0820: * Returns whether the simple name of the given type or one of its supertypes is
0821: * the simple name of one of the types in this hierarchy.
0822: */
0823: boolean includesTypeOrSupertype(IType type) {
0824: try {
0825: // check type
0826: if (hasTypeNamed(type.getElementName()))
0827: return true;
0828:
0829: // check superclass
0830: String super className = type.getSuperclassName();
0831: if (super className != null) {
0832: int lastSeparator = super className.lastIndexOf('.');
0833: String simpleName = super className
0834: .substring(lastSeparator + 1);
0835: if (hasTypeNamed(simpleName))
0836: return true;
0837: }
0838:
0839: // check superinterfaces
0840: String[] super interfaceNames = type
0841: .getSuperInterfaceNames();
0842: if (super interfaceNames != null) {
0843: for (int i = 0, length = super interfaceNames.length; i < length; i++) {
0844: String super interfaceName = super interfaceNames[i];
0845: int lastSeparator = super interfaceName
0846: .lastIndexOf('.');
0847: String simpleName = super interfaceName
0848: .substring(lastSeparator + 1);
0849: if (hasTypeNamed(simpleName))
0850: return true;
0851: }
0852: }
0853: } catch (JavaModelException e) {
0854: // ignore
0855: }
0856: return false;
0857: }
0858:
0859: /**
0860: * Initializes this hierarchy's internal tables with the given size.
0861: */
0862: protected void initialize(int size) {
0863: if (size < 10) {
0864: size = 10;
0865: }
0866: int smallSize = (size / 2);
0867: this .classToSuperclass = new HashMap(size);
0868: this .interfaces = new ArrayList(smallSize);
0869: this .missingTypes = new ArrayList(smallSize);
0870: this .rootClasses = new TypeVector();
0871: this .typeToSubtypes = new HashMap(smallSize);
0872: this .typeToSuperInterfaces = new HashMap(smallSize);
0873: this .typeFlags = new HashMap(smallSize);
0874:
0875: this .projectRegion = new Region();
0876: this .packageRegion = new Region();
0877: this .files = new HashMap(5);
0878: }
0879:
0880: /**
0881: * Returns true if the given delta could change this type hierarchy
0882: */
0883: public synchronized boolean isAffected(IJavaElementDelta delta) {
0884: IJavaElement element = delta.getElement();
0885: switch (element.getElementType()) {
0886: case IJavaElement.JAVA_MODEL:
0887: return isAffectedByJavaModel(delta, element);
0888: case IJavaElement.JAVA_PROJECT:
0889: return isAffectedByJavaProject(delta, element);
0890: case IJavaElement.PACKAGE_FRAGMENT_ROOT:
0891: return isAffectedByPackageFragmentRoot(delta, element);
0892: case IJavaElement.PACKAGE_FRAGMENT:
0893: return isAffectedByPackageFragment(delta,
0894: (PackageFragment) element);
0895: case IJavaElement.CLASS_FILE:
0896: case IJavaElement.COMPILATION_UNIT:
0897: return isAffectedByOpenable(delta, element);
0898: }
0899: return false;
0900: }
0901:
0902: /**
0903: * Returns true if any of the children of a project, package
0904: * fragment root, or package fragment have changed in a way that
0905: * effects this type hierarchy.
0906: */
0907: private boolean isAffectedByChildren(IJavaElementDelta delta) {
0908: if ((delta.getFlags() & IJavaElementDelta.F_CHILDREN) > 0) {
0909: IJavaElementDelta[] children = delta.getAffectedChildren();
0910: for (int i = 0; i < children.length; i++) {
0911: if (isAffected(children[i])) {
0912: return true;
0913: }
0914: }
0915: }
0916: return false;
0917: }
0918:
0919: /**
0920: * Returns true if the given java model delta could affect this type hierarchy
0921: */
0922: private boolean isAffectedByJavaModel(IJavaElementDelta delta,
0923: IJavaElement element) {
0924: switch (delta.getKind()) {
0925: case IJavaElementDelta.ADDED:
0926: case IJavaElementDelta.REMOVED:
0927: return element.equals(this .javaProject().getJavaModel());
0928: case IJavaElementDelta.CHANGED:
0929: return isAffectedByChildren(delta);
0930: }
0931: return false;
0932: }
0933:
0934: /**
0935: * Returns true if the given java project delta could affect this type hierarchy
0936: */
0937: private boolean isAffectedByJavaProject(IJavaElementDelta delta,
0938: IJavaElement element) {
0939: int kind = delta.getKind();
0940: int flags = delta.getFlags();
0941: if ((flags & IJavaElementDelta.F_OPENED) != 0) {
0942: kind = IJavaElementDelta.ADDED; // affected in the same way
0943: }
0944: if ((flags & IJavaElementDelta.F_CLOSED) != 0) {
0945: kind = IJavaElementDelta.REMOVED; // affected in the same way
0946: }
0947: switch (kind) {
0948: case IJavaElementDelta.ADDED:
0949: try {
0950: // if the added project is on the classpath, then the hierarchy has changed
0951: IClasspathEntry[] classpath = ((JavaProject) this
0952: .javaProject()).getExpandedClasspath();
0953: for (int i = 0; i < classpath.length; i++) {
0954: if (classpath[i].getEntryKind() == IClasspathEntry.CPE_PROJECT
0955: && classpath[i].getPath().equals(
0956: element.getPath())) {
0957: return true;
0958: }
0959: }
0960: if (this .focusType != null) {
0961: // if the hierarchy's project is on the added project classpath, then the hierarchy has changed
0962: classpath = ((JavaProject) element)
0963: .getExpandedClasspath();
0964: IPath hierarchyProject = javaProject().getPath();
0965: for (int i = 0; i < classpath.length; i++) {
0966: if (classpath[i].getEntryKind() == IClasspathEntry.CPE_PROJECT
0967: && classpath[i].getPath().equals(
0968: hierarchyProject)) {
0969: return true;
0970: }
0971: }
0972: }
0973: return false;
0974: } catch (JavaModelException e) {
0975: return false;
0976: }
0977: case IJavaElementDelta.REMOVED:
0978: // removed project - if it contains packages we are interested in
0979: // then the type hierarchy has changed
0980: IJavaElement[] pkgs = this .packageRegion.getElements();
0981: for (int i = 0; i < pkgs.length; i++) {
0982: IJavaProject javaProject = pkgs[i].getJavaProject();
0983: if (javaProject != null && javaProject.equals(element)) {
0984: return true;
0985: }
0986: }
0987: return false;
0988: case IJavaElementDelta.CHANGED:
0989: return isAffectedByChildren(delta);
0990: }
0991: return false;
0992: }
0993:
0994: /**
0995: * Returns true if the given package fragment delta could affect this type hierarchy
0996: */
0997: private boolean isAffectedByPackageFragment(
0998: IJavaElementDelta delta, PackageFragment element) {
0999: switch (delta.getKind()) {
1000: case IJavaElementDelta.ADDED:
1001: // if the package fragment is in the projects being considered, this could
1002: // introduce new types, changing the hierarchy
1003: return this .projectRegion.contains(element);
1004: case IJavaElementDelta.REMOVED:
1005: // is a change if the package fragment contains types in this hierarchy
1006: return packageRegionContainsSamePackageFragment(element);
1007: case IJavaElementDelta.CHANGED:
1008: // look at the files in the package fragment
1009: return isAffectedByChildren(delta);
1010: }
1011: return false;
1012: }
1013:
1014: /**
1015: * Returns true if the given package fragment root delta could affect this type hierarchy
1016: */
1017: private boolean isAffectedByPackageFragmentRoot(
1018: IJavaElementDelta delta, IJavaElement element) {
1019: switch (delta.getKind()) {
1020: case IJavaElementDelta.ADDED:
1021: return this .projectRegion.contains(element);
1022: case IJavaElementDelta.REMOVED:
1023: case IJavaElementDelta.CHANGED:
1024: int flags = delta.getFlags();
1025: if ((flags & IJavaElementDelta.F_ADDED_TO_CLASSPATH) > 0) {
1026: // check if the root is in the classpath of one of the projects of this hierarchy
1027: if (this .projectRegion != null) {
1028: IPackageFragmentRoot root = (IPackageFragmentRoot) element;
1029: IPath rootPath = root.getPath();
1030: IJavaElement[] elements = this .projectRegion
1031: .getElements();
1032: for (int i = 0; i < elements.length; i++) {
1033: JavaProject javaProject = (JavaProject) elements[i];
1034: try {
1035: IClasspathEntry entry = javaProject
1036: .getClasspathEntryFor(rootPath);
1037: if (entry != null) {
1038: return true;
1039: }
1040: } catch (JavaModelException e) {
1041: // igmore this project
1042: }
1043: }
1044: }
1045: }
1046: if ((flags & IJavaElementDelta.F_REMOVED_FROM_CLASSPATH) > 0
1047: || (flags & IJavaElementDelta.F_CONTENT) > 0) {
1048: // 1. removed from classpath - if it contains packages we are interested in
1049: // the the type hierarchy has changed
1050: // 2. content of a jar changed - if it contains packages we are interested in
1051: // the the type hierarchy has changed
1052: IJavaElement[] pkgs = this .packageRegion.getElements();
1053: for (int i = 0; i < pkgs.length; i++) {
1054: if (pkgs[i].getParent().equals(element)) {
1055: return true;
1056: }
1057: }
1058: return false;
1059: }
1060: }
1061: return isAffectedByChildren(delta);
1062: }
1063:
1064: /**
1065: * Returns true if the given type delta (a compilation unit delta or a class file delta)
1066: * could affect this type hierarchy.
1067: */
1068: protected boolean isAffectedByOpenable(IJavaElementDelta delta,
1069: IJavaElement element) {
1070: if (element instanceof CompilationUnit) {
1071: CompilationUnit cu = (CompilationUnit) element;
1072: ChangeCollector collector = this .changeCollector;
1073: if (collector == null) {
1074: collector = new ChangeCollector(this );
1075: }
1076: try {
1077: collector.addChange(cu, delta);
1078: } catch (JavaModelException e) {
1079: if (DEBUG)
1080: e.printStackTrace();
1081: }
1082: if (cu.isWorkingCopy()) {
1083: // changes to working copies are batched
1084: this .changeCollector = collector;
1085: return false;
1086: } else {
1087: return collector.needsRefresh();
1088: }
1089: } else if (element instanceof ClassFile) {
1090: switch (delta.getKind()) {
1091: case IJavaElementDelta.REMOVED:
1092: return this .files.get(element) != null;
1093: case IJavaElementDelta.ADDED:
1094: IType type = ((ClassFile) element).getType();
1095: String typeName = type.getElementName();
1096: if (hasSupertype(typeName)
1097: || subtypesIncludeSupertypeOf(type)
1098: || this .missingTypes.contains(typeName)) {
1099:
1100: return true;
1101: }
1102: break;
1103: case IJavaElementDelta.CHANGED:
1104: IJavaElementDelta[] children = delta
1105: .getAffectedChildren();
1106: for (int i = 0, length = children.length; i < length; i++) {
1107: IJavaElementDelta child = children[i];
1108: IJavaElement childElement = child.getElement();
1109: if (childElement instanceof IType) {
1110: type = (IType) childElement;
1111: boolean hasVisibilityChange = (delta.getFlags() & IJavaElementDelta.F_MODIFIERS) > 0;
1112: boolean hasSupertypeChange = (delta.getFlags() & IJavaElementDelta.F_SUPER_TYPES) > 0;
1113: if ((hasVisibilityChange && hasSupertype(type
1114: .getElementName()))
1115: || (hasSupertypeChange && includesTypeOrSupertype(type))) {
1116: return true;
1117: }
1118: }
1119: }
1120: break;
1121: }
1122: }
1123: return false;
1124: }
1125:
1126: private boolean isInterface(IType type) {
1127: int flags = this .getCachedFlags(type);
1128: if (flags == -1) {
1129: try {
1130: return type.isInterface();
1131: } catch (JavaModelException e) {
1132: return false;
1133: }
1134: } else {
1135: return Flags.isInterface(flags);
1136: }
1137: }
1138:
1139: /**
1140: * Returns the java project this hierarchy was created in.
1141: */
1142: public IJavaProject javaProject() {
1143: return this .focusType.getJavaProject();
1144: }
1145:
1146: protected static byte[] readUntil(InputStream input, byte separator)
1147: throws JavaModelException, IOException {
1148: return readUntil(input, separator, 0);
1149: }
1150:
1151: protected static byte[] readUntil(InputStream input,
1152: byte separator, int offset) throws IOException,
1153: JavaModelException {
1154: int length = 0;
1155: byte[] bytes = new byte[SIZE];
1156: byte b;
1157: while ((b = (byte) input.read()) != separator && b != -1) {
1158: if (bytes.length == length) {
1159: System.arraycopy(bytes, 0,
1160: bytes = new byte[length * 2], 0, length);
1161: }
1162: bytes[length++] = b;
1163: }
1164: if (b == -1) {
1165: throw new JavaModelException(new JavaModelStatus(
1166: IStatus.ERROR));
1167: }
1168: System.arraycopy(bytes, 0, bytes = new byte[length + offset],
1169: offset, length);
1170: return bytes;
1171: }
1172:
1173: public static ITypeHierarchy load(IType type, InputStream input,
1174: WorkingCopyOwner owner) throws JavaModelException {
1175: try {
1176: TypeHierarchy typeHierarchy = new TypeHierarchy();
1177: typeHierarchy.initialize(1);
1178:
1179: IType[] types = new IType[SIZE];
1180: int typeCount = 0;
1181:
1182: byte version = (byte) input.read();
1183:
1184: if (version != VERSION) {
1185: throw new JavaModelException(new JavaModelStatus(
1186: IStatus.ERROR));
1187: }
1188: byte generalInfo = (byte) input.read();
1189: if ((generalInfo & COMPUTE_SUBTYPES) != 0) {
1190: typeHierarchy.computeSubtypes = true;
1191: }
1192:
1193: byte b;
1194: byte[] bytes;
1195:
1196: // read project
1197: bytes = readUntil(input, SEPARATOR1);
1198: if (bytes.length > 0) {
1199: typeHierarchy.project = (IJavaProject) JavaCore
1200: .create(new String(bytes));
1201: typeHierarchy.scope = SearchEngine
1202: .createJavaSearchScope(new IJavaElement[] { typeHierarchy.project });
1203: } else {
1204: typeHierarchy.project = null;
1205: typeHierarchy.scope = SearchEngine
1206: .createWorkspaceScope();
1207: }
1208:
1209: // read missing type
1210: {
1211: bytes = readUntil(input, SEPARATOR1);
1212: byte[] missing;
1213: int j = 0;
1214: int length = bytes.length;
1215: for (int i = 0; i < length; i++) {
1216: b = bytes[i];
1217: if (b == SEPARATOR2) {
1218: missing = new byte[i - j];
1219: System.arraycopy(bytes, j, missing, 0, i - j);
1220: typeHierarchy.missingTypes.add(new String(
1221: missing));
1222: j = i + 1;
1223: }
1224: }
1225: System.arraycopy(bytes, j, missing = new byte[length
1226: - j], 0, length - j);
1227: typeHierarchy.missingTypes.add(new String(missing));
1228: }
1229:
1230: // read types
1231: while ((b = (byte) input.read()) != SEPARATOR1 && b != -1) {
1232: bytes = readUntil(input, SEPARATOR4, 1);
1233: bytes[0] = b;
1234: IType element = (IType) JavaCore.create(new String(
1235: bytes), owner);
1236:
1237: if (types.length == typeCount) {
1238: System.arraycopy(types, 0,
1239: types = new IType[typeCount * 2], 0,
1240: typeCount);
1241: }
1242: types[typeCount++] = element;
1243:
1244: // read flags
1245: bytes = readUntil(input, SEPARATOR4);
1246: Integer flags = bytesToFlags(bytes);
1247: if (flags != null) {
1248: typeHierarchy.cacheFlags(element, flags.intValue());
1249: }
1250:
1251: // read info
1252: byte info = (byte) input.read();
1253:
1254: if ((info & INTERFACE) != 0) {
1255: typeHierarchy.addInterface(element);
1256: }
1257: if ((info & COMPUTED_FOR) != 0) {
1258: if (!element.equals(type)) {
1259: throw new JavaModelException(
1260: new JavaModelStatus(IStatus.ERROR));
1261: }
1262: typeHierarchy.focusType = element;
1263: }
1264: if ((info & ROOT) != 0) {
1265: typeHierarchy.addRootClass(element);
1266: }
1267: }
1268:
1269: // read super class
1270: while ((b = (byte) input.read()) != SEPARATOR1 && b != -1) {
1271: bytes = readUntil(input, SEPARATOR3, 1);
1272: bytes[0] = b;
1273: int subClass = new Integer(new String(bytes))
1274: .intValue();
1275:
1276: // read super type
1277: bytes = readUntil(input, SEPARATOR1);
1278: int super Class = new Integer(new String(bytes))
1279: .intValue();
1280:
1281: typeHierarchy.cacheSuperclass(types[subClass],
1282: types[super Class]);
1283: }
1284:
1285: // read super interface
1286: while ((b = (byte) input.read()) != SEPARATOR1 && b != -1) {
1287: bytes = readUntil(input, SEPARATOR3, 1);
1288: bytes[0] = b;
1289: int subClass = new Integer(new String(bytes))
1290: .intValue();
1291:
1292: // read super interface
1293: bytes = readUntil(input, SEPARATOR1);
1294: IType[] super Interfaces = new IType[(bytes.length / 2) + 1];
1295: int interfaceCount = 0;
1296:
1297: int j = 0;
1298: byte[] b2;
1299: for (int i = 0; i < bytes.length; i++) {
1300: if (bytes[i] == SEPARATOR2) {
1301: b2 = new byte[i - j];
1302: System.arraycopy(bytes, j, b2, 0, i - j);
1303: j = i + 1;
1304: super Interfaces[interfaceCount++] = types[new Integer(
1305: new String(b2)).intValue()];
1306: }
1307: }
1308: b2 = new byte[bytes.length - j];
1309: System.arraycopy(bytes, j, b2, 0, bytes.length - j);
1310: super Interfaces[interfaceCount++] = types[new Integer(
1311: new String(b2)).intValue()];
1312: System.arraycopy(super Interfaces, 0,
1313: super Interfaces = new IType[interfaceCount], 0,
1314: interfaceCount);
1315:
1316: typeHierarchy.cacheSuperInterfaces(types[subClass],
1317: super Interfaces);
1318: }
1319: if (b == -1) {
1320: throw new JavaModelException(new JavaModelStatus(
1321: IStatus.ERROR));
1322: }
1323: return typeHierarchy;
1324: } catch (IOException e) {
1325: throw new JavaModelException(e,
1326: IJavaModelStatusConstants.IO_EXCEPTION);
1327: }
1328: }
1329:
1330: /**
1331: * Returns <code>true</code> if an equivalent package fragment is included in the package
1332: * region. Package fragments are equivalent if they both have the same name.
1333: */
1334: protected boolean packageRegionContainsSamePackageFragment(
1335: PackageFragment element) {
1336: IJavaElement[] pkgs = this .packageRegion.getElements();
1337: for (int i = 0; i < pkgs.length; i++) {
1338: PackageFragment pkg = (PackageFragment) pkgs[i];
1339: if (Util.equalArraysOrNull(pkg.names, element.names))
1340: return true;
1341: }
1342: return false;
1343: }
1344:
1345: /**
1346: * @see ITypeHierarchy
1347: * TODO (jerome) should use a PerThreadObject to build the hierarchy instead of synchronizing
1348: * (see also isAffected(IJavaElementDelta))
1349: */
1350: public synchronized void refresh(IProgressMonitor monitor)
1351: throws JavaModelException {
1352: try {
1353: this .progressMonitor = monitor;
1354: if (monitor != null) {
1355: monitor.beginTask(this .focusType != null ? Messages
1356: .bind(Messages.hierarchy_creatingOnType,
1357: this .focusType.getFullyQualifiedName())
1358: : Messages.hierarchy_creating, 100);
1359: }
1360: long start = -1;
1361: if (DEBUG) {
1362: start = System.currentTimeMillis();
1363: if (this .computeSubtypes) {
1364: System.out
1365: .println("CREATING TYPE HIERARCHY [" + Thread.currentThread() + "]"); //$NON-NLS-1$ //$NON-NLS-2$
1366: } else {
1367: System.out
1368: .println("CREATING SUPER TYPE HIERARCHY [" + Thread.currentThread() + "]"); //$NON-NLS-1$ //$NON-NLS-2$
1369: }
1370: if (this .focusType != null) {
1371: System.out
1372: .println(" on type " + ((JavaElement) this .focusType).toStringWithAncestors()); //$NON-NLS-1$
1373: }
1374: }
1375:
1376: compute();
1377: initializeRegions();
1378: this .needsRefresh = false;
1379: this .changeCollector = null;
1380:
1381: if (DEBUG) {
1382: if (this .computeSubtypes) {
1383: System.out
1384: .println("CREATED TYPE HIERARCHY in " + (System.currentTimeMillis() - start) + "ms"); //$NON-NLS-1$ //$NON-NLS-2$
1385: } else {
1386: System.out
1387: .println("CREATED SUPER TYPE HIERARCHY in " + (System.currentTimeMillis() - start) + "ms"); //$NON-NLS-1$ //$NON-NLS-2$
1388: }
1389: System.out.println(this .toString());
1390: }
1391: } catch (JavaModelException e) {
1392: throw e;
1393: } catch (CoreException e) {
1394: throw new JavaModelException(e);
1395: } finally {
1396: if (monitor != null) {
1397: monitor.done();
1398: }
1399: this .progressMonitor = null;
1400: }
1401: }
1402:
1403: /**
1404: * @see ITypeHierarchy
1405: */
1406: public synchronized void removeTypeHierarchyChangedListener(
1407: ITypeHierarchyChangedListener listener) {
1408: ArrayList listeners = this .changeListeners;
1409: if (listeners == null) {
1410: return;
1411: }
1412: listeners.remove(listener);
1413:
1414: // deregister from JavaCore on last listener removed
1415: if (listeners.isEmpty()) {
1416: JavaCore.removeElementChangedListener(this );
1417: }
1418: }
1419:
1420: /**
1421: * @see ITypeHierarchy
1422: */
1423: public void store(OutputStream output, IProgressMonitor monitor)
1424: throws JavaModelException {
1425: try {
1426: // compute types in hierarchy
1427: Hashtable hashtable = new Hashtable();
1428: Hashtable hashtable2 = new Hashtable();
1429: int count = 0;
1430:
1431: if (this .focusType != null) {
1432: Integer index = new Integer(count++);
1433: hashtable.put(this .focusType, index);
1434: hashtable2.put(index, this .focusType);
1435: }
1436: Object[] types = this .classToSuperclass.entrySet()
1437: .toArray();
1438: for (int i = 0; i < types.length; i++) {
1439: Map.Entry entry = (Map.Entry) types[i];
1440: Object t = entry.getKey();
1441: if (hashtable.get(t) == null) {
1442: Integer index = new Integer(count++);
1443: hashtable.put(t, index);
1444: hashtable2.put(index, t);
1445: }
1446: Object super Class = entry.getValue();
1447: if (super Class != null
1448: && hashtable.get(super Class) == null) {
1449: Integer index = new Integer(count++);
1450: hashtable.put(super Class, index);
1451: hashtable2.put(index, super Class);
1452: }
1453: }
1454: types = this .typeToSuperInterfaces.entrySet().toArray();
1455: for (int i = 0; i < types.length; i++) {
1456: Map.Entry entry = (Map.Entry) types[i];
1457: Object t = entry.getKey();
1458: if (hashtable.get(t) == null) {
1459: Integer index = new Integer(count++);
1460: hashtable.put(t, index);
1461: hashtable2.put(index, t);
1462: }
1463: Object[] sp = (Object[]) entry.getValue();
1464: if (sp != null) {
1465: for (int j = 0; j < sp.length; j++) {
1466: Object super Interface = sp[j];
1467: if (sp[j] != null
1468: && hashtable.get(super Interface) == null) {
1469: Integer index = new Integer(count++);
1470: hashtable.put(super Interface, index);
1471: hashtable2.put(index, super Interface);
1472: }
1473: }
1474: }
1475: }
1476: // save version of the hierarchy format
1477: output.write(VERSION);
1478:
1479: // save general info
1480: byte generalInfo = 0;
1481: if (this .computeSubtypes) {
1482: generalInfo |= COMPUTE_SUBTYPES;
1483: }
1484: output.write(generalInfo);
1485:
1486: // save project
1487: if (this .project != null) {
1488: output.write(this .project.getHandleIdentifier()
1489: .getBytes());
1490: }
1491: output.write(SEPARATOR1);
1492:
1493: // save missing types
1494: for (int i = 0; i < this .missingTypes.size(); i++) {
1495: if (i != 0) {
1496: output.write(SEPARATOR2);
1497: }
1498: output.write(((String) this .missingTypes.get(i))
1499: .getBytes());
1500:
1501: }
1502: output.write(SEPARATOR1);
1503:
1504: // save types
1505: for (int i = 0; i < count; i++) {
1506: IType t = (IType) hashtable2.get(new Integer(i));
1507:
1508: // n bytes
1509: output.write(t.getHandleIdentifier().getBytes());
1510: output.write(SEPARATOR4);
1511: output.write(flagsToBytes((Integer) this .typeFlags
1512: .get(t)));
1513: output.write(SEPARATOR4);
1514: byte info = CLASS;
1515: if (this .focusType != null && this .focusType.equals(t)) {
1516: info |= COMPUTED_FOR;
1517: }
1518: if (this .interfaces.contains(t)) {
1519: info |= INTERFACE;
1520: }
1521: if (this .rootClasses.contains(t)) {
1522: info |= ROOT;
1523: }
1524: output.write(info);
1525: }
1526: output.write(SEPARATOR1);
1527:
1528: // save superclasses
1529: types = this .classToSuperclass.entrySet().toArray();
1530: for (int i = 0; i < types.length; i++) {
1531: Map.Entry entry = (Map.Entry) types[i];
1532: IJavaElement key = (IJavaElement) entry.getKey();
1533: IJavaElement value = (IJavaElement) entry.getValue();
1534:
1535: output.write(((Integer) hashtable.get(key)).toString()
1536: .getBytes());
1537: output.write('>');
1538: output.write(((Integer) hashtable.get(value))
1539: .toString().getBytes());
1540: output.write(SEPARATOR1);
1541: }
1542: output.write(SEPARATOR1);
1543:
1544: // save superinterfaces
1545: types = this .typeToSuperInterfaces.entrySet().toArray();
1546: for (int i = 0; i < types.length; i++) {
1547: Map.Entry entry = (Map.Entry) types[i];
1548: IJavaElement key = (IJavaElement) entry.getKey();
1549: IJavaElement[] values = (IJavaElement[]) entry
1550: .getValue();
1551:
1552: if (values.length > 0) {
1553: output.write(((Integer) hashtable.get(key))
1554: .toString().getBytes());
1555: output.write(SEPARATOR3);
1556: for (int j = 0; j < values.length; j++) {
1557: IJavaElement value = values[j];
1558: if (j != 0)
1559: output.write(SEPARATOR2);
1560: output.write(((Integer) hashtable.get(value))
1561: .toString().getBytes());
1562: }
1563: output.write(SEPARATOR1);
1564: }
1565: }
1566: output.write(SEPARATOR1);
1567: } catch (IOException e) {
1568: throw new JavaModelException(e,
1569: IJavaModelStatusConstants.IO_EXCEPTION);
1570: }
1571: }
1572:
1573: /**
1574: * Returns whether the simple name of a supertype of the given type is
1575: * the simple name of one of the subtypes in this hierarchy or the
1576: * simple name of this type.
1577: */
1578: boolean subtypesIncludeSupertypeOf(IType type) {
1579: // look for superclass
1580: String super className = null;
1581: try {
1582: super className = type.getSuperclassName();
1583: } catch (JavaModelException e) {
1584: if (DEBUG) {
1585: e.printStackTrace();
1586: }
1587: return false;
1588: }
1589: if (super className == null) {
1590: super className = "Object"; //$NON-NLS-1$
1591: }
1592: int dot = -1;
1593: String simpleSuper = (dot = super className.lastIndexOf('.')) > -1 ? super className
1594: .substring(dot + 1)
1595: : super className;
1596: if (hasSubtypeNamed(simpleSuper)) {
1597: return true;
1598: }
1599:
1600: // look for super interfaces
1601: String[] interfaceNames = null;
1602: try {
1603: interfaceNames = type.getSuperInterfaceNames();
1604: } catch (JavaModelException e) {
1605: if (DEBUG)
1606: e.printStackTrace();
1607: return false;
1608: }
1609: for (int i = 0, length = interfaceNames.length; i < length; i++) {
1610: dot = -1;
1611: String interfaceName = interfaceNames[i];
1612: String simpleInterface = (dot = interfaceName
1613: .lastIndexOf('.')) > -1 ? interfaceName
1614: .substring(dot) : interfaceName;
1615: if (hasSubtypeNamed(simpleInterface)) {
1616: return true;
1617: }
1618: }
1619:
1620: return false;
1621: }
1622:
1623: /**
1624: * @see ITypeHierarchy
1625: */
1626: public String toString() {
1627: StringBuffer buffer = new StringBuffer();
1628: buffer.append("Focus: "); //$NON-NLS-1$
1629: buffer
1630: .append(this .focusType == null ? "<NONE>" : ((JavaElement) this .focusType).toStringWithAncestors(false/*don't show key*/)); //$NON-NLS-1$
1631: buffer.append("\n"); //$NON-NLS-1$
1632: if (exists()) {
1633: if (this .focusType != null) {
1634: buffer.append("Super types:\n"); //$NON-NLS-1$
1635: toString(buffer, this .focusType, 1, true);
1636: buffer.append("Sub types:\n"); //$NON-NLS-1$
1637: toString(buffer, this .focusType, 1, false);
1638: } else {
1639: buffer.append("Sub types of root classes:\n"); //$NON-NLS-1$
1640: IJavaElement[] roots = Util.sortCopy(getRootClasses());
1641: for (int i = 0; i < roots.length; i++) {
1642: toString(buffer, (IType) roots[i], 1, false);
1643: }
1644: }
1645: if (this .rootClasses.size > 1) {
1646: buffer.append("Root classes:\n"); //$NON-NLS-1$
1647: IJavaElement[] roots = Util.sortCopy(getRootClasses());
1648: for (int i = 0, length = roots.length; i < length; i++) {
1649: toString(buffer, (IType) roots[i], 1, false);
1650: }
1651: } else if (this .rootClasses.size == 0) {
1652: // see http://bugs.eclipse.org/bugs/show_bug.cgi?id=24691
1653: buffer.append("No root classes"); //$NON-NLS-1$
1654: }
1655: } else {
1656: buffer.append("(Hierarchy became stale)"); //$NON-NLS-1$
1657: }
1658: return buffer.toString();
1659: }
1660:
1661: /**
1662: * Append a String to the given buffer representing the hierarchy for the type,
1663: * beginning with the specified indentation level.
1664: * If ascendant, shows the super types, otherwise show the sub types.
1665: */
1666: private void toString(StringBuffer buffer, IType type, int indent,
1667: boolean ascendant) {
1668: IType[] types = ascendant ? getSupertypes(type)
1669: : getSubtypes(type);
1670: IJavaElement[] sortedTypes = Util.sortCopy(types);
1671: for (int i = 0; i < sortedTypes.length; i++) {
1672: for (int j = 0; j < indent; j++) {
1673: buffer.append(" "); //$NON-NLS-1$
1674: }
1675: JavaElement element = (JavaElement) sortedTypes[i];
1676: buffer.append(element
1677: .toStringWithAncestors(false/*don't show key*/));
1678: buffer.append('\n');
1679: toString(buffer, types[i], indent + 1, ascendant);
1680: }
1681: }
1682:
1683: /**
1684: * Returns whether one of the types in this hierarchy has a supertype whose simple
1685: * name is the given simple name.
1686: */
1687: boolean hasSupertype(String simpleName) {
1688: for (Iterator iter = this .classToSuperclass.values().iterator(); iter
1689: .hasNext();) {
1690: IType super Type = (IType) iter.next();
1691: if (super Type.getElementName().equals(simpleName)) {
1692: return true;
1693: }
1694: }
1695: return false;
1696: }
1697:
1698: /**
1699: * @see IProgressMonitor
1700: */
1701: protected void worked(int work) {
1702: if (this.progressMonitor != null) {
1703: this.progressMonitor.worked(work);
1704: checkCanceled();
1705: }
1706: }
1707: }
|