/******************************************************************************* * Copyright (c) 2000, 2009 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.resources.IFile; import org.eclipse.core.resources.IResource; 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.ast.TypeDeclaration; 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.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.PackageFragment; import org.eclipse.jdt.internal.core.ResolvedBinaryType; import org.eclipse.jdt.internal.core.SearchableEnvironment; import org.eclipse.jdt.internal.core.SourceTypeElementInfo; import org.eclipse.jdt.internal.core.util.ResourceCompilationUnit; 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; } if (project != null) { 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= 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 (TypeDeclaration.kind(type.getModifiers())) { case TypeDeclaration.CLASS_DECL: case TypeDeclaration.ENUM_DECL: if (superclassHandle == null) { this.hierarchy.addRootClass(typeHandle); } else { this.hierarchy.cacheSuperclass(typeHandle, superclassHandle); } break; case TypeDeclaration.INTERFACE_DECL: case TypeDeclaration.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 (TypeDeclaration.kind(typeInfo.getModifiers())) { case TypeDeclaration.CLASS_DECL: flag= NameLookup.ACCEPT_CLASSES; break; case TypeDeclaration.INTERFACE_DECL: flag= NameLookup.ACCEPT_INTERFACES; break; case TypeDeclaration.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(); NameLookup.Answer answer= this.nameLookup.findType(qualifiedName, false, flag, true/* consider secondary types */, false/* do NOT wait for indexes */, false/*don't check restrictions*/, null); return answer == null || answer.type == null || !answer.type.isBinary() ? null : answer.type; } 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, IFile file) { final char[] elementName= handle.getElementName().toCharArray(); return new ResourceCompilationUnit(file, file.getLocationURI()) { 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, IResource file) { IBinaryType info= null; try { info= Util.newClassFileReader(file); } 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; } 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; } }