blob: 3001c007554a1d97d6c1f469770b84fb2b2658d8 [file] [log] [blame]
package org.eclipse.jdt.ui;/*
* Licensed Materials - Property of IBM,
* WebSphere Studio Workbench
* (c) Copyright IBM Corp 1999, 2000
*/
import java.util.ArrayList;
import java.util.List;
import org.eclipse.swt.widgets.Control;
import org.eclipse.jface.viewers.IBasicPropertyConstants;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceDelta;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
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.IJavaModel;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IPackageFragment;
import org.eclipse.jdt.core.IPackageFragmentRoot;
import org.eclipse.jdt.core.IParent;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.internal.ui.JavaPlugin;
import org.eclipse.jdt.internal.ui.util.ArrayUtility;
/**
* Standard tree content provider for Java elements.
* Use this class when you want to present the Java elements in a viewer.
* <p>
* The following Java element hierarchy is surfaced by this content provider:
* <p>
* <pre>
Java model (<code>IJavaModel</code>)
Java project (<code>IJavaProject</code>)
package fragment root (<code>IPackageFragmentRoot</code>)
package fragment (<code>IPackageFragment</code>)
compilation unit (<code>ICompilationUnit</code>)
binary class file (<code>IClassFile</code>)
* </pre>
* </p>
* <p>
* Note that when the entire Java project is declared to be package fragment root,
* the corresponding package fragment root element that normally appears between the
* Java project and the package fragments is automatically filtered out.
* </p>
* <p>
* This class may be instantiated; it is not intended to be subclassed.
* </p>
*/
public class JavaElementContentProvider implements ITreeContentProvider, IElementChangedListener {
private TreeViewer fViewer;
/**
* Creates a new content provider for Java elements.
*/
public JavaElementContentProvider() {
}
/* (non-Javadoc)
* Method declared on IContentProvider.
*/
public void dispose() {
JavaCore.removeElementChangedListener(this);
}
/* (non-Javadoc)
* Method declared on IContentProvider.
*/
public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
fViewer= (TreeViewer)viewer;
if (oldInput == null && newInput != null) {
JavaCore.addElementChangedListener(this);
} else if (oldInput != null && newInput == null) {
JavaCore.removeElementChangedListener(this);
}
}
/* (non-Javadoc)
* Method declared on IStructuredContentProvider.
*/
public Object[] getElements(Object parent) {
return getChildren(parent);
}
/* (non-Javadoc)
* Method declared on ITreeContentProvider.
*/
public Object[] getChildren(Object element) {
if (!exists(element))
return ArrayUtility.getEmptyArray();
try {
if (element instanceof IJavaModel)
return getJavaProjects((IJavaModel)element);
if (element instanceof IJavaProject)
return getNonProjectPackageFragmentRoots((IJavaProject)element);
if (element instanceof IPackageFragmentRoot)
return getPackageFragments((IPackageFragmentRoot)element);
if (element instanceof IPackageFragment)
return getPackageContents((IPackageFragment)element);
if (element instanceof IFolder)
return getResources((IFolder)element);
} catch (JavaModelException e) {
return ArrayUtility.getEmptyArray();
}
return ArrayUtility.getEmptyArray();
}
/* (non-Javadoc)
* Method declared on ITreeContentProvider.
*/
public Object getParent(Object element) {
if (!exists(element))
return null;
return internalGetParent(element);
}
/* (non-Javadoc)
* Method declared on IElementChangedListener.
*/
public void elementChanged(final ElementChangedEvent event) {
handleElementDelta(event.getDelta());
}
private void handleElementDelta(IJavaElementDelta delta) {
try {
processDelta(delta);
} catch(JavaModelException e) {
JavaPlugin.getDefault().logErrorStatus("Child non present", e.getStatus());
}
}
/**
* Processes a delta recursively. When more than two children are affected the
* tree is fully refreshed starting at this node. The delta is processed in the
* current thread but the viewer updates are posted to the UI thread.
*/
private void processDelta(IJavaElementDelta delta) throws JavaModelException {
int kind= delta.getKind();
int flags= delta.getFlags();
IJavaElement element= delta.getElement();
// handle open and closing of a solution or project
if (((flags & IJavaElementDelta.F_CLOSED) != 0) || ((flags & IJavaElementDelta.F_OPENED) != 0)) {
postRefresh(element);
return;
}
if (kind == IJavaElementDelta.REMOVED) {
Object parent= internalGetParent(element);
postRemove(element);
if (parent instanceof IPackageFragment)
updatePackageIcon((IPackageFragment)parent);
// we are filtering out empty subpackages, so we
// a package becomes empty we remove it from the viewer.
if (isPackageFragmentEmpty(element.getParent())) {
postRefresh(internalGetParent(parent));
}
return;
}
if (kind == IJavaElementDelta.ADDED) {
Object parent= internalGetParent(element);
// we are filtering out empty subpackages, so we
// have to handle additions to them specially.
if (parent instanceof IPackageFragment) {
postRefresh(internalGetParent(parent));
} else {
postAdd(parent, element);
}
}
// we don't show the contents of a compilation or IClassFile, so don't go any deeper
if ((element instanceof ICompilationUnit) || (element instanceof IClassFile))
return;
if (isClassPathChange(delta)) {
// throw the towel and do a full refresh of the affected java project.
postRefresh(element.getJavaProject());
}
if (delta.getResourceDeltas() != null) {
IResourceDelta[] rd= delta.getResourceDeltas();
IJavaProject project= element.getJavaProject();
for (int i= 0; i < rd.length; i++) {
processResourceDelta(rd[i], element, project.getOutputLocation());
}
}
IJavaElementDelta[] affectedChildren= delta.getAffectedChildren();
if (affectedChildren.length > 1) {
// a package fragment might become non empty refresh from the parent
if (element instanceof IPackageFragment) {
IJavaElement parent= (IJavaElement)internalGetParent(element);
postRefresh(parent);
return;
}
// more than one child changed, refresh from here downwards
postRefresh(fixupProjectPackageFragmentRoot(element));
return;
}
for (int i= 0; i < affectedChildren.length; i++) {
processDelta(affectedChildren[i]);
}
}
/**
* Updates the package icon
*/
private void updatePackageIcon(final IJavaElement element) {
postRunnable(new Runnable() {
public void run() {
fViewer.update(element, new String[]{IBasicPropertyConstants.P_IMAGE});
}
});
}
/**
* Process resource deltas
*/
private void processResourceDelta(IResourceDelta delta, Object parent, IPath output) {
int status= delta.getKind();
IResource resource= delta.getResource();
// filter out changes affecting the output folder
if (resource == null || output.isPrefixOf(resource.getFullPath()))
return;
// this could be optimized by handling all the added children in the parent
if ((status & IResourceDelta.REMOVED) != 0) {
if (parent instanceof IPackageFragment)
// refresh one level above to deal with empty package filtering properly
postRefresh(internalGetParent(parent));
else
postRemove(resource);
}
if ((status & IResourceDelta.ADDED) != 0) {
if (parent instanceof IPackageFragment)
// refresh one level above to deal with empty package filtering properly
postRefresh(internalGetParent(parent));
else
postAdd(parent, resource);
}
int changeFlags= delta.getFlags();
IResourceDelta[] affectedChildren= delta.getAffectedChildren();
if (affectedChildren.length > 1) {
// more than one child changed, refresh from here downwards
postRefresh(resource);
return;
}
for (int i= 0; i < affectedChildren.length; i++)
processResourceDelta(affectedChildren[i], resource, output);
}
private void postRefresh(final Object root) {
postRunnable(new Runnable() {
public void run() {
fViewer.refresh(root);
}
});
}
private void postAdd(final Object parent, final Object element) {
postRunnable(new Runnable() {
public void run() {
fViewer.add(parent, element);
}
});
}
private void postRemove(final Object element) {
postRunnable(new Runnable() {
public void run() {
fViewer.remove(element);
}
});
}
private void postRunnable(final Runnable r) {
Control ctrl= fViewer.getControl();
if (ctrl != null && !ctrl.isDisposed()) {
ctrl.getDisplay().asyncExec(r);
}
}
/* (non-Javadoc)
*
* @see ITreeContentProvider
*/
public boolean hasChildren(Object element) {
// don't allow to drill down into a compilation unit or class file
if (element instanceof ICompilationUnit ||
element instanceof IClassFile ||
element instanceof IFile)
return false;
if (element instanceof IJavaProject) {
IJavaProject jp= (IJavaProject)element;
if (!jp.getProject().isOpen()) {
System.out.println("Project not open");
return false;
}
}
if (element instanceof IParent) {
try {
// when we have Java children return true, else we fetch all the children
if (((IParent)element).hasChildren())
return true;
} catch(JavaModelException e) {
//ErrorDialog.openError(Utilities.getFocusShell(), "Children non present", null, e.getStatus());
return false;
}
}
Object[] children= getChildren(element);
return (children != null) && children.length > 0;
}
private Object[] getPackageFragments(IPackageFragmentRoot root) throws JavaModelException {
IJavaElement[] fragments= root.getChildren();
return ArrayUtility.merge(fragments, root.getNonJavaResources());
}
private Object[] getNonProjectPackageFragmentRoots(IJavaProject project) throws JavaModelException {
// return an empty enumeration when the project is closed
if (!project.getProject().isOpen())
return ArrayUtility.getEmptyArray();
IPackageFragmentRoot[] roots= project.getPackageFragmentRoots();
List list= new ArrayList(roots.length);
// filter out package fragments that correspond to projects and
// replace them with the package fragments directly
boolean projectIsRoot= false;
for (int i= 0; i < roots.length; i++) {
IPackageFragmentRoot root= (IPackageFragmentRoot)roots[i];
if (isProjectPackageFragmentRoot(root)) {
projectIsRoot= true;
Object[] children= getPackageFragments(root);
for (int k= 0; k < children.length; k++) {
list.add(children[k]);
}
if (list.size() == 0)
System.out.println("No children found");
}
else if (hasChildren(root)/*root.hasChildren()*/) {
list.add(root);
} else {
System.out.println("Root doesn't have children");
}
}
if (projectIsRoot)
return list.toArray();
return ArrayUtility.merge(list.toArray(), project.getNonJavaResources());
}
private Object[] getJavaProjects(IJavaModel jm) throws JavaModelException {
return jm.getJavaProjects();
}
private Object[] getPackageContents(IPackageFragment fragment) throws JavaModelException {
if (fragment.getKind() == IPackageFragmentRoot.K_SOURCE) {
return ArrayUtility.merge(fragment.getCompilationUnits(), fragment.getNonJavaResources());
}
return ArrayUtility.merge(fragment.getClassFiles(), fragment.getNonJavaResources());
}
private Object[] getResources(IFolder folder) {
try {
return folder.members();
} catch(CoreException e) {
return ArrayUtility.getEmptyArray();
}
}
private boolean isClassPathChange(IJavaElementDelta delta) {
int flags= delta.getFlags();
return (delta.getKind() == IJavaElementDelta.CHANGED &&
((flags & IJavaElementDelta.F_ADDED_TO_CLASSPATH) != 0) ||
((flags & IJavaElementDelta.F_REMOVED_FROM_CLASSPATH) != 0) ||
((flags & IJavaElementDelta.F_CLASSPATH_REORDER) != 0));
}
private Object fixupProjectPackageFragmentRoot(Object element) {
try {
if (element instanceof IPackageFragmentRoot) {
IPackageFragmentRoot root= (IPackageFragmentRoot)element;
if (isProjectPackageFragmentRoot(root))
return root.getParent();
}
return element;
} catch(JavaModelException e) {
return element;
}
}
private boolean isPackageFragmentEmpty(IJavaElement element) throws JavaModelException {
if (element instanceof IPackageFragment) {
IPackageFragment fragment= (IPackageFragment)element;
if (!(fragment.hasChildren() || fragment.getNonJavaResources().length > 0) && fragment.hasSubpackages())
return true;
}
return false;
}
private boolean isProjectPackageFragmentRoot(IPackageFragmentRoot root) throws JavaModelException {
IResource resource= root.getUnderlyingResource();
return (resource instanceof IProject);
}
private boolean exists(Object element) {
if (element == null) {
return false;
}
if (element instanceof IResource) {
return ((IResource)element).exists();
}
if (element instanceof IJavaElement) {
return ((IJavaElement)element).exists();
}
return true;
}
private Object internalGetParent(Object element) {
if (element instanceof IJavaProject) {
return ((IJavaProject)element).getJavaModel();
}
// try to map resources to the containing package fragment
if (element instanceof IResource) {
IResource parent= ((IResource)element).getParent();
Object packageFragment= JavaCore.create(parent);
if (packageFragment != null)
return packageFragment;
return parent;
}
// for package fragments that are contained in a project package fragment
// we have to skip the package fragment root as the parent.
if (element instanceof IPackageFragment) {
IPackageFragmentRoot parent= (IPackageFragmentRoot)((IPackageFragment)element).getParent();
return fixupProjectPackageFragmentRoot(parent);
}
if (element instanceof IJavaElement)
return ((IJavaElement)element).getParent();
return null;
}
}