| /******************************************************************************* |
| * Copyright (c) 2000, 2004 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.jdt.internal.core.hierarchy; |
| |
| import java.util.HashMap; |
| import java.util.Map; |
| |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.core.runtime.OperationCanceledException; |
| import org.eclipse.jdt.core.IType; |
| import org.eclipse.jdt.core.JavaModelException; |
| import org.eclipse.jdt.internal.compiler.env.IBinaryType; |
| import org.eclipse.jdt.internal.compiler.env.ICompilationUnit; |
| import org.eclipse.jdt.internal.compiler.env.IGenericType; |
| import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding; |
| import org.eclipse.jdt.internal.compiler.problem.DefaultProblemFactory; |
| import org.eclipse.jdt.internal.core.*; |
| import org.eclipse.jdt.internal.core.BasicCompilationUnit; |
| import org.eclipse.jdt.internal.core.ClassFile; |
| import org.eclipse.jdt.internal.core.JarPackageFragmentRoot; |
| import org.eclipse.jdt.internal.core.JavaElement; |
| import org.eclipse.jdt.internal.core.JavaModelManager; |
| import org.eclipse.jdt.internal.core.JavaProject; |
| import org.eclipse.jdt.internal.core.NameLookup; |
| import org.eclipse.jdt.internal.core.Openable; |
| import org.eclipse.jdt.internal.core.SearchableEnvironment; |
| import org.eclipse.jdt.internal.core.SourceTypeElementInfo; |
| import org.eclipse.jdt.internal.core.util.Util; |
| |
| public abstract class HierarchyBuilder { |
| /** |
| * The hierarchy being built. |
| */ |
| protected TypeHierarchy hierarchy; |
| /** |
| * @see NameLookup |
| */ |
| protected NameLookup nameLookup; |
| /** |
| * The resolver used to resolve type hierarchies |
| * @see HierarchyResolver |
| */ |
| protected HierarchyResolver hierarchyResolver; |
| /** |
| * A temporary cache of infos to handles to speed info |
| * to handle translation - it only contains the entries |
| * for the types in the region (in other words, it contains |
| * no supertypes outside the region). |
| */ |
| protected Map infoToHandle; |
| /* |
| * The dot-separated fully qualified name of the focus type, or null of none. |
| */ |
| protected String focusQualifiedName; |
| |
| public HierarchyBuilder(TypeHierarchy hierarchy) throws JavaModelException { |
| |
| this.hierarchy = hierarchy; |
| JavaProject project = (JavaProject) hierarchy.javaProject(); |
| |
| IType focusType = hierarchy.getType(); |
| org.eclipse.jdt.core.ICompilationUnit unitToLookInside = focusType == null ? null : focusType.getCompilationUnit(); |
| org.eclipse.jdt.core.ICompilationUnit[] workingCopies = this.hierarchy.workingCopies; |
| org.eclipse.jdt.core.ICompilationUnit[] unitsToLookInside; |
| if (unitToLookInside != null) { |
| int wcLength = workingCopies == null ? 0 : workingCopies.length; |
| if (wcLength == 0) { |
| unitsToLookInside = new org.eclipse.jdt.core.ICompilationUnit[] {unitToLookInside}; |
| } else { |
| unitsToLookInside = new org.eclipse.jdt.core.ICompilationUnit[wcLength+1]; |
| unitsToLookInside[0] = unitToLookInside; |
| System.arraycopy(workingCopies, 0, unitsToLookInside, 1, wcLength); |
| } |
| } else { |
| unitsToLookInside = workingCopies; |
| } |
| SearchableEnvironment searchableEnvironment = project.newSearchableNameEnvironment(unitsToLookInside); |
| this.nameLookup = searchableEnvironment.nameLookup; |
| this.hierarchyResolver = |
| new HierarchyResolver( |
| searchableEnvironment, |
| project.getOptions(true), |
| this, |
| new DefaultProblemFactory()); |
| this.infoToHandle = new HashMap(5); |
| this.focusQualifiedName = focusType == null ? null : focusType.getFullyQualifiedName(); |
| } |
| |
| public abstract void build(boolean computeSubtypes) |
| throws JavaModelException, CoreException; |
| /** |
| * Configure this type hierarchy by computing the supertypes only. |
| */ |
| protected void buildSupertypes() { |
| IType focusType = this.getType(); |
| if (focusType == null) |
| return; |
| // get generic type from focus type |
| IGenericType type; |
| try { |
| type = (IGenericType) ((JavaElement) focusType).getElementInfo(); |
| } catch (JavaModelException e) { |
| // if the focus type is not present, or if cannot get workbench path |
| // we cannot create the hierarchy |
| return; |
| } |
| //NB: no need to set focus type on hierarchy resolver since no other type is injected |
| // in the hierarchy resolver, thus there is no need to check that a type is |
| // a sub or super type of the focus type. |
| this.hierarchyResolver.resolve(type); |
| |
| // Add focus if not already in (case of a type with no explicit super type) |
| if (!this.hierarchy.contains(focusType)) { |
| this.hierarchy.addRootClass(focusType); |
| } |
| } |
| /** |
| * Connect the supplied type to its superclass & superinterfaces. |
| * The superclass & superinterfaces are the identical binary or source types as |
| * supplied by the name environment. |
| */ |
| public void connect( |
| IGenericType type, |
| IType typeHandle, |
| IType superclassHandle, |
| IType[] superinterfaceHandles) { |
| |
| /* |
| * Temporary workaround for 1G2O5WK: ITPJCORE:WINNT - NullPointerException when selecting "Show in Type Hierarchy" for a inner class |
| */ |
| if (typeHandle == null) |
| return; |
| if (TypeHierarchy.DEBUG) { |
| System.out.println( |
| "Connecting: " + ((JavaElement) typeHandle).toStringWithAncestors()); //$NON-NLS-1$ |
| System.out.println( |
| " to superclass: " //$NON-NLS-1$ |
| + (superclassHandle == null |
| ? "<None>" //$NON-NLS-1$ |
| : ((JavaElement) superclassHandle).toStringWithAncestors())); |
| System.out.print(" and superinterfaces:"); //$NON-NLS-1$ |
| if (superinterfaceHandles == null || superinterfaceHandles.length == 0) { |
| System.out.println(" <None>"); //$NON-NLS-1$ |
| } else { |
| System.out.println(); |
| for (int i = 0, length = superinterfaceHandles.length; i < length; i++) { |
| if (superinterfaceHandles[i] == null) continue; |
| System.out.println( |
| " " + ((JavaElement) superinterfaceHandles[i]).toStringWithAncestors()); //$NON-NLS-1$ |
| } |
| } |
| } |
| // now do the caching |
| switch (type.getKind()) { |
| case IGenericType.CLASS_DECL : |
| case IGenericType.ENUM_DECL : |
| if (superclassHandle == null) { |
| this.hierarchy.addRootClass(typeHandle); |
| } else { |
| this.hierarchy.cacheSuperclass(typeHandle, superclassHandle); |
| } |
| break; |
| case IGenericType.INTERFACE_DECL : |
| case IGenericType.ANNOTATION_TYPE_DECL : |
| this.hierarchy.addInterface(typeHandle); |
| break; |
| } |
| if (superinterfaceHandles == null) { |
| superinterfaceHandles = TypeHierarchy.NO_TYPE; |
| } |
| this.hierarchy.cacheSuperInterfaces(typeHandle, superinterfaceHandles); |
| |
| // record flags |
| this.hierarchy.cacheFlags(typeHandle, type.getModifiers()); |
| } |
| /** |
| * Returns a handle for the given generic type or null if not found. |
| */ |
| protected IType getHandle(IGenericType genericType, ReferenceBinding binding) { |
| if (genericType == null) |
| return null; |
| if (genericType instanceof HierarchyType) { |
| IType handle = (IType)this.infoToHandle.get(genericType); |
| if (handle == null) { |
| handle = ((HierarchyType)genericType).typeHandle; |
| handle = (IType) ((JavaElement) handle).resolved(binding); |
| this.infoToHandle.put(genericType, handle); |
| } |
| return handle; |
| } else if (genericType.isBinaryType()) { |
| ClassFile classFile = (ClassFile) this.infoToHandle.get(genericType); |
| // if it's null, it's from outside the region, so do lookup |
| if (classFile == null) { |
| IType handle = lookupBinaryHandle((IBinaryType) genericType); |
| if (handle == null) |
| return null; |
| // case of an anonymous type (see 1G2O5WK: ITPJCORE:WINNT - NullPointerException when selecting "Show in Type Hierarchy" for a inner class) |
| // optimization: remember the handle for next call (case of java.io.Serializable that a lot of classes implement) |
| classFile = (ClassFile) handle.getParent(); |
| this.infoToHandle.put(genericType, classFile); |
| } |
| return new ResolvedBinaryType(classFile, classFile.getTypeName(), new String(binding.computeUniqueKey())); |
| } else if (genericType instanceof SourceTypeElementInfo) { |
| IType handle = ((SourceTypeElementInfo) genericType).getHandle(); |
| return (IType) ((JavaElement) handle).resolved(binding); |
| } else |
| return null; |
| } |
| protected IType getType() { |
| return this.hierarchy.getType(); |
| } |
| /** |
| * Looks up and returns a handle for the given binary info. |
| */ |
| protected IType lookupBinaryHandle(IBinaryType typeInfo) { |
| int flag; |
| String qualifiedName; |
| switch (typeInfo.getKind()) { |
| case IGenericType.CLASS_DECL : |
| flag = NameLookup.ACCEPT_CLASSES; |
| break; |
| case IGenericType.INTERFACE_DECL : |
| flag = NameLookup.ACCEPT_INTERFACES; |
| break; |
| case IGenericType.ENUM_DECL : |
| flag = NameLookup.ACCEPT_ENUMS; |
| break; |
| default: |
| //case IGenericType.ANNOTATION : |
| flag = NameLookup.ACCEPT_ANNOTATIONS; |
| break; |
| } |
| char[] bName = typeInfo.getName(); |
| qualifiedName = new String(ClassFile.translatedName(bName)); |
| if (qualifiedName.equals(this.focusQualifiedName)) return getType(); |
| return this.nameLookup.findType(qualifiedName, false, flag); |
| } |
| protected void worked(IProgressMonitor monitor, int work) { |
| if (monitor != null) { |
| if (monitor.isCanceled()) { |
| throw new OperationCanceledException(); |
| } else { |
| monitor.worked(work); |
| } |
| } |
| } |
| /** |
| * Create an ICompilationUnit info from the given compilation unit on disk. |
| */ |
| protected ICompilationUnit createCompilationUnitFromPath(Openable handle, String osPath) { |
| final char[] elementName = handle.getElementName().toCharArray(); |
| return new BasicCompilationUnit(null/* no source*/, null/* no package */, osPath, handle) { |
| public char[] getFileName() { |
| return elementName; |
| } |
| }; |
| } |
| /** |
| * Creates the type info from the given class file on disk and |
| * adds it to the given list of infos. |
| */ |
| protected IBinaryType createInfoFromClassFile(Openable handle, String osPath) { |
| IBinaryType info = null; |
| try { |
| info = org.eclipse.jdt.internal.compiler.classfmt.ClassFileReader.read(osPath); |
| } catch (org.eclipse.jdt.internal.compiler.classfmt.ClassFormatException e) { |
| if (TypeHierarchy.DEBUG) { |
| e.printStackTrace(); |
| } |
| return null; |
| } catch (java.io.IOException e) { |
| if (TypeHierarchy.DEBUG) { |
| e.printStackTrace(); |
| } |
| return null; |
| } |
| this.infoToHandle.put(info, handle); |
| return info; |
| } |
| /** |
| * Create a type info from the given class file in a jar and adds it to the given list of infos. |
| */ |
| protected IBinaryType createInfoFromClassFileInJar(Openable classFile) { |
| PackageFragment pkg = (PackageFragment) classFile.getParent(); |
| String classFilePath = Util.concatWith(pkg.names, classFile.getElementName(), '/'); |
| IBinaryType info = null; |
| java.util.zip.ZipFile zipFile = null; |
| try { |
| zipFile = ((JarPackageFragmentRoot)pkg.getParent()).getJar(); |
| info = org.eclipse.jdt.internal.compiler.classfmt.ClassFileReader.read( |
| zipFile, |
| classFilePath); |
| } catch (org.eclipse.jdt.internal.compiler.classfmt.ClassFormatException e) { |
| if (TypeHierarchy.DEBUG) { |
| e.printStackTrace(); |
| } |
| return null; |
| } catch (java.io.IOException e) { |
| if (TypeHierarchy.DEBUG) { |
| e.printStackTrace(); |
| } |
| return null; |
| } catch (CoreException e) { |
| if (TypeHierarchy.DEBUG) { |
| e.printStackTrace(); |
| } |
| return null; |
| } finally { |
| JavaModelManager.getJavaModelManager().closeZipFile(zipFile); |
| } |
| this.infoToHandle.put(info, classFile); |
| return info; |
| } |
| |
| } |