Skip to main content
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'jpa/plugins/org.eclipse.jpt.jpa.ui/src/org/eclipse/jpt/jpa/ui/internal/navigator/JpaNavigatorContentProvider.java')
-rw-r--r--jpa/plugins/org.eclipse.jpt.jpa.ui/src/org/eclipse/jpt/jpa/ui/internal/navigator/JpaNavigatorContentProvider.java245
1 files changed, 229 insertions, 16 deletions
diff --git a/jpa/plugins/org.eclipse.jpt.jpa.ui/src/org/eclipse/jpt/jpa/ui/internal/navigator/JpaNavigatorContentProvider.java b/jpa/plugins/org.eclipse.jpt.jpa.ui/src/org/eclipse/jpt/jpa/ui/internal/navigator/JpaNavigatorContentProvider.java
index b22087aa48..fbd66487eb 100644
--- a/jpa/plugins/org.eclipse.jpt.jpa.ui/src/org/eclipse/jpt/jpa/ui/internal/navigator/JpaNavigatorContentProvider.java
+++ b/jpa/plugins/org.eclipse.jpt.jpa.ui/src/org/eclipse/jpt/jpa/ui/internal/navigator/JpaNavigatorContentProvider.java
@@ -9,17 +9,31 @@
******************************************************************************/
package org.eclipse.jpt.jpa.ui.internal.navigator;
+import java.util.HashMap;
+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.IResourceChangeEvent;
+import org.eclipse.core.resources.IResourceChangeListener;
+import org.eclipse.core.resources.IResourceDelta;
+import org.eclipse.core.resources.IWorkspace;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.resource.LocalResourceManager;
import org.eclipse.jface.resource.ResourceManager;
import org.eclipse.jpt.common.core.internal.utility.PlatformTools;
+import org.eclipse.jpt.common.core.internal.utility.ProjectTools;
+import org.eclipse.jpt.common.core.internal.utility.ResourceChangeAdapter;
import org.eclipse.jpt.common.ui.internal.jface.NavigatorContentProvider;
import org.eclipse.jpt.common.ui.jface.ItemExtendedLabelProviderFactory;
import org.eclipse.jpt.common.ui.jface.ItemTreeContentProviderFactory;
+import org.eclipse.jpt.jpa.core.JpaProject;
+import org.eclipse.jpt.jpa.core.JpaProjectManager;
import org.eclipse.jpt.jpa.ui.JpaRootContextNodeModel;
import org.eclipse.jpt.jpa.ui.JpaWorkbench;
+import org.eclipse.ui.IWorkbench;
import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.navigator.ICommonContentExtensionSite;
/**
* This provider is invoked for:<ul>
@@ -28,14 +42,40 @@ import org.eclipse.ui.PlatformUI;
* <li>JPA context nodes
* </ul>
* See <code>org.eclipse.jpt.jpa.ui/plugin.xml</code>.
+ * <p>
+ * <strong>NB:</strong> We can only refresh the navigator once the navigator
+ * has instantiated and invoked this provider for the first time. At that point
+ * we are listening for resource changes and can force a refresh of the
+ * navigator as the JPA facet is added or removed from the projects.
+ * In particular: If a project is already present and expanded in the navigator,
+ * and this provider has never been invoked (i.e. there are no JPA projects in
+ * the workspace yet), adding the JPA facet to the project will <em>not</em>
+ * result in the "JPA Content" node showing up under the project's node in the
+ * navigator tree. The navigator must be explicitly refreshed,
+ * forcing it to re-examine the provider trigger points and invoking this
+ * provider as a result.
*/
public class JpaNavigatorContentProvider
extends NavigatorContentProvider
{
+ private volatile IResourceChangeListener resourceChangeListener;
+ private final HashMap<IProject, JpaRootContextNodeModel[]> projectChildren = new HashMap<IProject, JpaRootContextNodeModel[]>();
+
+
public JpaNavigatorContentProvider() {
super();
}
+
+ // ********** initialization **********
+
+ @Override
+ public void init(ICommonContentExtensionSite config) {
+ super.init(config);
+ this.resourceChangeListener = new ResourceChangeListener();
+ this.getWorkspace().addResourceChangeListener(this.resourceChangeListener, IResourceChangeEvent.POST_CHANGE);
+ }
+
@Override
protected ItemTreeContentProviderFactory buildItemContentProviderFactory() {
return new JpaNavigatorItemContentProviderFactory();
@@ -49,40 +89,213 @@ public class JpaNavigatorContentProvider
@Override
protected ResourceManager buildResourceManager() {
JpaWorkbench jpaWorkbench = this.getJpaWorkbench();
- return (jpaWorkbench != null) ? jpaWorkbench.buildLocalResourceManager() : new LocalResourceManager(JFaceResources.getResources(PlatformUI.getWorkbench().getDisplay()));
+ return (jpaWorkbench != null) ? jpaWorkbench.buildLocalResourceManager() : this.buildResourceManager_();
}
- private JpaWorkbench getJpaWorkbench() {
- return PlatformTools.getAdapter(PlatformUI.getWorkbench(), JpaWorkbench.class);
+ private ResourceManager buildResourceManager_() {
+ return new LocalResourceManager(JFaceResources.getResources(this.getWorkbench().getDisplay()));
}
+
+ // ********** children **********
+
+ /**
+ * @see #getChildren_(Object)
+ */
@Override
protected boolean hasChildren_(Object element) {
- return this.getRootContextNodeModel(element) != null;
+ return this.getChildren_(element) != null;
}
/**
- * We handle the children for an {@link IProject} here and delegate
- * all others.
+ * We handle the children for an {@link IProject} here and allow the
+ * superclass to delegate all others.
*/
@Override
protected Object[] getChildren_(Object element) {
- JpaRootContextNodeModel child = this.getRootContextNodeModel(element);
- return (child == null) ? null : new Object[] {child};
+ return (element instanceof IProject) ?
+ this.getChildren((IProject) element) :
+ null;
+ }
+
+ private Object[] getChildren(IProject project) {
+ synchronized (this.projectChildren) {
+ return this.getChildren_(project);
+ }
+ }
+
+ /**
+ * Pre-condition: {@link #projectChildren} is <code>synchronized</code>
+ */
+ private JpaRootContextNodeModel[] getChildren_(IProject project) {
+ JpaRootContextNodeModel[] children = this.projectChildren.get(project);
+ if (children == null) {
+ children = this.buildChildren(project);
+ this.projectChildren.put(project, children);
+ }
+ return children;
+ }
+
+ private JpaRootContextNodeModel[] buildChildren(IProject project) {
+ return new JpaRootContextNodeModel[] {this.buildChild(project)};
}
/**
* This provider should only be invoked for projects that have the
- * JPA facet; so we return a JPA root context node model for any
+ * JPA facet; so we return a JPA root context node model for <em>any</em>
* project passed in.
* <p>
- * <strong>NB:</strong> There is no way to refresh the navigator when the
- * JPA facet is added to a project. The view must be explicitly refreshed,
- * forcing it to re-examine the provider trigger points.
+ * We return a JPA root context node <em>model</em> (as opposed to simply a
+ * JPA root context node) so a node appears in the navigator tree even if
+ * there is not yet a JPA project. Since this node is returned for any
+ * project with a JPA facet, we must listen for the JPA facet being removed
+ * so, when the JPA facet is removed, we can tell the delegate to
+ * dispose the node's content and label providers (which are listening to
+ * the node for changes etc.).
*/
- private JpaRootContextNodeModel getRootContextNodeModel(Object element) {
- return (element instanceof IProject) ?
- (JpaRootContextNodeModel) ((IProject) element).getAdapter(JpaRootContextNodeModel.class) :
- null;
+ private JpaRootContextNodeModel buildChild(IProject project) {
+ return (JpaRootContextNodeModel) project.getAdapter(JpaRootContextNodeModel.class);
+ }
+
+
+ // ********** facet file changes **********
+
+ /* CU private */ void facetFileChanged(IProject project) {
+ if (ProjectTools.hasFacet(project, JpaProject.FACET)) {
+ this.jpaFacetIsPresent(project);
+ } else {
+ this.jpaFacetIsAbsent(project);
+ }
+ }
+
+ private void jpaFacetIsPresent(IProject project) {
+ synchronized (this.projectChildren) {
+ this.jpaFacetIsPresent_(project);
+ }
+ }
+
+ /**
+ * The specified project <em>has</em> the JPA facet;
+ * refresh the view if necessary.
+ * <p>
+ * Pre-condition: {@link #projectChildren} is <code>synchronized</code>
+ */
+ private void jpaFacetIsPresent_(IProject project) {
+ JpaRootContextNodeModel[] children = this.projectChildren.get(project);
+ if (children == null) {
+ this.delegate.updateChildren(project); // force the viewer to refresh the project
+ }
+ }
+
+ private void jpaFacetIsAbsent(IProject project) {
+ synchronized (this.projectChildren) {
+ this.jpaFacetIsAbsent_(project);
+ }
+ }
+
+ /**
+ * The specified project does <em>not</em> have the JPA facet;
+ * remove the JPA root context node model if necessary.
+ * <p>
+ * Pre-condition: {@link #projectChildren} is <code>synchronized</code>
+ */
+ private void jpaFacetIsAbsent_(IProject project) {
+ JpaRootContextNodeModel[] children = this.projectChildren.remove(project);
+ if (children != null) {
+ for (JpaRootContextNodeModel child : children) { // should be only one...
+ this.delegate.dispose(child);
+ }
+ this.delegate.updateChildren(project); // force the viewer to refresh the project
+ }
+ }
+
+
+ // ********** disposal **********
+
+ @Override
+ public void dispose() {
+ // the delegate will dispose everything, including the JPA root context node models
+ this.getWorkspace().removeResourceChangeListener(this.resourceChangeListener);
+ super.dispose();
+ }
+
+
+ // ********** misc **********
+
+ private IWorkspace getWorkspace() {
+ return this.getJpaWorkbench().getJpaWorkspace().getWorkspace();
+ }
+
+ private JpaWorkbench getJpaWorkbench() {
+ return PlatformTools.getAdapter(this.getWorkbench(), JpaWorkbench.class);
+ }
+
+ private IWorkbench getWorkbench() {
+ return PlatformUI.getWorkbench();
+ }
+
+
+ // ********** resource change listener **********
+
+ /**
+ * Listen for changes to the facet settings file.
+ * @see org.eclipse.jpt.jpa.core.internal.InternalJpaProjectManager#FACETED_PROJECT_FRAMEWORK_SETTINGS_FILE_NAME
+ */
+ /* CU private */ class ResourceChangeListener
+ extends ResourceChangeAdapter
+ {
+ @Override
+ public void resourceChanged(IResourceChangeEvent event) {
+ switch (event.getType()) {
+ case IResourceChangeEvent.POST_CHANGE :
+ this.processPostChangeDelta(event.getDelta());
+ break;
+ default :
+ break;
+ }
+ }
+
+ private void processPostChangeDelta(IResourceDelta delta) {
+ IResource resource = delta.getResource();
+ switch (resource.getType()) {
+ case IResource.ROOT :
+ this.processPostChangeDeltaChildren(delta);
+ break;
+ case IResource.PROJECT :
+ this.processPostChangeDeltaChildren(delta);
+ break;
+ case IResource.FOLDER :
+ if (((IFolder) resource).getName().equals(".settings")) { //$NON-NLS-1$
+ this.processPostChangeDeltaChildren(delta);
+ }
+ break;
+ case IResource.FILE :
+ IFile file = (IFile) resource;
+ if (file.getName().equals(JpaProjectManager.FACETED_PROJECT_FRAMEWORK_SETTINGS_FILE_NAME)) {
+ this.checkForFacetFileChanges(file, delta);
+ }
+ break;
+ default :
+ break;
+ }
+ }
+
+ private void processPostChangeDeltaChildren(IResourceDelta delta) {
+ for (IResourceDelta child : delta.getAffectedChildren()) {
+ this.processPostChangeDelta(child); // recurse
+ }
+ }
+
+ private void checkForFacetFileChanges(IFile file, IResourceDelta delta) {
+ switch (delta.getKind()) {
+ case IResourceDelta.ADDED :
+ case IResourceDelta.REMOVED :
+ case IResourceDelta.CHANGED :
+ JpaNavigatorContentProvider.this.facetFileChanged(file.getProject());
+ break;
+ default :
+ break;
+ }
+ }
}
}

Back to the top