/* | |
* (c) Copyright IBM Corp. 2000, 2001. | |
* All Rights Reserved. | |
*/ | |
package org.eclipse.jdt.internal.ui.typehierarchy; | |
import java.lang.reflect.InvocationTargetException; | |
import java.util.ArrayList; | |
import java.util.List; | |
import org.eclipse.core.runtime.IProgressMonitor; | |
import org.eclipse.core.runtime.IStatus; | |
import org.eclipse.jface.operation.IRunnableContext; | |
import org.eclipse.jface.operation.IRunnableWithProgress; | |
import org.eclipse.jdt.core.ElementChangedEvent; | |
import org.eclipse.jdt.core.IClassFile; | |
import org.eclipse.jdt.core.ICompilationUnit; | |
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.IRegion; | |
import org.eclipse.jdt.core.IType; | |
import org.eclipse.jdt.core.ITypeHierarchy; | |
import org.eclipse.jdt.core.ITypeHierarchyChangedListener; | |
import org.eclipse.jdt.core.IWorkingCopy; | |
import org.eclipse.jdt.core.JavaCore; | |
import org.eclipse.jdt.core.JavaModelException; | |
import org.eclipse.jdt.internal.corext.util.JavaModelUtil; | |
import org.eclipse.jdt.internal.ui.JavaPlugin; | |
/** | |
* Manages a type hierarchy, to keep it refreshed, and to allow it to be shared. | |
*/ | |
public class TypeHierarchyLifeCycle implements ITypeHierarchyChangedListener, IElementChangedListener { | |
private boolean fHierarchyRefreshNeeded; | |
private ITypeHierarchy fHierarchy; | |
private IJavaElement fInputElement; | |
private boolean fIsSuperTypesOnly; | |
private boolean fReconciled; | |
private List fChangeListeners; | |
public TypeHierarchyLifeCycle() { | |
this(false); | |
} | |
public TypeHierarchyLifeCycle(boolean isSuperTypesOnly) { | |
fHierarchy= null; | |
fInputElement= null; | |
fIsSuperTypesOnly= isSuperTypesOnly; | |
fChangeListeners= new ArrayList(2); | |
fReconciled= false; | |
} | |
public ITypeHierarchy getHierarchy() { | |
return fHierarchy; | |
} | |
public IJavaElement getInputElement() { | |
return fInputElement; | |
} | |
public void freeHierarchy() { | |
if (fHierarchy != null) { | |
fHierarchy.removeTypeHierarchyChangedListener(this); | |
JavaCore.removeElementChangedListener(this); | |
fHierarchy= null; | |
fInputElement= null; | |
} | |
} | |
public void removeChangedListener(ITypeHierarchyLifeCycleListener listener) { | |
fChangeListeners.remove(listener); | |
} | |
public void addChangedListener(ITypeHierarchyLifeCycleListener listener) { | |
if (!fChangeListeners.contains(listener)) { | |
fChangeListeners.add(listener); | |
} | |
} | |
private void fireChange(IType[] changedTypes) { | |
for (int i= fChangeListeners.size()-1; i>=0; i--) { | |
ITypeHierarchyLifeCycleListener curr= (ITypeHierarchyLifeCycleListener) fChangeListeners.get(i); | |
curr.typeHierarchyChanged(this, changedTypes); | |
} | |
} | |
public void ensureRefreshedTypeHierarchy(final IJavaElement element, IRunnableContext context) throws JavaModelException { | |
if (element == null || !element.exists()) { | |
freeHierarchy(); | |
return; | |
} | |
boolean hierachyCreationNeeded= (fHierarchy == null || !element.equals(fInputElement)); | |
if (hierachyCreationNeeded || fHierarchyRefreshNeeded) { | |
IRunnableWithProgress op= new IRunnableWithProgress() { | |
public void run(IProgressMonitor pm) throws InvocationTargetException { | |
try { | |
doHierarchyRefresh(element, pm); | |
} catch (JavaModelException e) { | |
throw new InvocationTargetException(e); | |
} | |
} | |
}; | |
try { | |
context.run(false, false, op); | |
} catch (InvocationTargetException e) { | |
Throwable th= e.getTargetException(); | |
if (th instanceof JavaModelException) { | |
throw (JavaModelException)th; | |
} else { | |
throw new JavaModelException(th, IStatus.ERROR); | |
} | |
} catch (InterruptedException e) { | |
// Not cancelable. | |
} | |
fHierarchyRefreshNeeded= false; | |
} | |
} | |
private void doHierarchyRefresh(IJavaElement element, IProgressMonitor pm) throws JavaModelException { | |
boolean hierachyCreationNeeded= (fHierarchy == null || !element.equals(fInputElement)); | |
// to ensore the order of the two listeners always remove / add listeners on operations | |
// on type hierarchies | |
if (fHierarchy != null) { | |
fHierarchy.removeTypeHierarchyChangedListener(this); | |
JavaCore.removeElementChangedListener(this); | |
} | |
if (hierachyCreationNeeded) { | |
fInputElement= element; | |
if (element.getElementType() == IJavaElement.TYPE) { | |
IType type= (IType) element; | |
if (fIsSuperTypesOnly) { | |
fHierarchy= type.newSupertypeHierarchy(pm); | |
} else { | |
fHierarchy= type.newTypeHierarchy(pm); | |
} | |
} else { | |
IRegion region= JavaCore.newRegion(); | |
if (element.getElementType() == IJavaElement.JAVA_PROJECT) { | |
// for projects only add the contained source folders | |
IPackageFragmentRoot[] roots= ((IJavaProject) element).getPackageFragmentRoots(); | |
for (int i= 0; i < roots.length; i++) { | |
if (!roots[i].isExternal()) { | |
region.add(roots[i]); | |
} | |
} | |
} else if (element.getElementType() == IJavaElement.PACKAGE_FRAGMENT) { | |
IPackageFragmentRoot[] roots= element.getJavaProject().getPackageFragmentRoots(); | |
String name= element.getElementName(); | |
for (int i= 0; i < roots.length; i++) { | |
IPackageFragment pack= roots[i].getPackageFragment(name); | |
if (pack.exists()) { | |
region.add(pack); | |
} | |
} | |
} else { | |
region.add(element); | |
} | |
IJavaProject jproject= element.getJavaProject(); | |
fHierarchy= jproject.newTypeHierarchy(region, pm); | |
} | |
} else { | |
fHierarchy.refresh(pm); | |
} | |
fHierarchy.addTypeHierarchyChangedListener(this); | |
JavaCore.addElementChangedListener(this); | |
} | |
/* | |
* @see ITypeHierarchyChangedListener#typeHierarchyChanged | |
*/ | |
public void typeHierarchyChanged(ITypeHierarchy typeHierarchy) { | |
fHierarchyRefreshNeeded= true; | |
} | |
/* | |
* @see IElementChangedListener#elementChanged(ElementChangedEvent) | |
*/ | |
public void elementChanged(ElementChangedEvent event) { | |
if (fChangeListeners.isEmpty()) { | |
return; | |
} | |
IJavaElement elem= event.getDelta().getElement(); | |
if (!isReconciled() && elem instanceof IWorkingCopy && ((IWorkingCopy)elem).isWorkingCopy()) { | |
return; | |
} | |
if (fHierarchyRefreshNeeded) { | |
fireChange(null); | |
} else { | |
ArrayList changedTypes= new ArrayList(); | |
processDelta(event.getDelta(), changedTypes); | |
if (changedTypes.size() > 0) { | |
fireChange((IType[]) changedTypes.toArray(new IType[changedTypes.size()])); | |
} | |
} | |
} | |
/* | |
* Assume that the hierarchy is intact (no refresh needed) | |
*/ | |
private void processDelta(IJavaElementDelta delta, ArrayList changedTypes) { | |
IJavaElement element= delta.getElement(); | |
switch (element.getElementType()) { | |
case IJavaElement.TYPE: | |
processTypeDelta((IType) element, changedTypes); | |
processChildrenDelta(delta, changedTypes); // (inner types) | |
break; | |
case IJavaElement.JAVA_MODEL: | |
case IJavaElement.JAVA_PROJECT: | |
case IJavaElement.PACKAGE_FRAGMENT_ROOT: | |
case IJavaElement.PACKAGE_FRAGMENT: | |
processChildrenDelta(delta, changedTypes); | |
break; | |
case IJavaElement.COMPILATION_UNIT: | |
ICompilationUnit cu= (ICompilationUnit)element; | |
boolean isWorkingCopyRemove= isWorkingCopyRemove(cu, delta.getKind()); | |
if (isWorkingCopyRemove || delta.getKind() == IJavaElementDelta.CHANGED && isPossibleStructuralChange(delta.getFlags())) { | |
try { | |
if (isWorkingCopyRemove) | |
cu= (ICompilationUnit)cu.getOriginalElement(); | |
if (cu.exists()) { | |
IType[] types= cu.getAllTypes(); | |
for (int i= 0; i < types.length; i++) { | |
processTypeDelta(types[i], changedTypes); | |
} | |
} | |
} catch (JavaModelException e) { | |
JavaPlugin.log(e); | |
} | |
} else { | |
processChildrenDelta(delta, changedTypes); | |
} | |
break; | |
case IJavaElement.CLASS_FILE: | |
if (delta.getKind() == IJavaElementDelta.CHANGED) { | |
try { | |
IType type= ((IClassFile) element).getType(); | |
processTypeDelta(type, changedTypes); | |
} catch (JavaModelException e) { | |
JavaPlugin.log(e); | |
} | |
} else { | |
processChildrenDelta(delta, changedTypes); | |
} | |
break; | |
} | |
} | |
private boolean isPossibleStructuralChange(int flags) { | |
return (flags & (IJavaElementDelta.F_CONTENT | IJavaElementDelta.F_FINE_GRAINED)) == IJavaElementDelta.F_CONTENT; | |
} | |
private boolean isWorkingCopyRemove(ICompilationUnit cu, int deltaKind) { | |
return isReconciled() && deltaKind == IJavaElementDelta.REMOVED && cu.isWorkingCopy(); | |
} | |
private void processTypeDelta(IType type, ArrayList changedTypes) { | |
type= JavaModelUtil.toOriginal(type); | |
if (getHierarchy().contains(type)) { | |
changedTypes.add(type); | |
} | |
} | |
private void processChildrenDelta(IJavaElementDelta delta, ArrayList changedTypes) { | |
IJavaElementDelta[] children= delta.getAffectedChildren(); | |
for (int i= 0; i < children.length; i++) { | |
processDelta(children[i], changedTypes); // recursive | |
} | |
} | |
public boolean isReconciled() { | |
return fReconciled; | |
} | |
public void setReconciled(boolean reconciled) { | |
fReconciled= reconciled; | |
} | |
} |