org.eclipse.jpt.jpa.ui.internal.navigator.JpaNavigatorContentProvider.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.jpt.jpa.ui.internal.navigator.JpaNavigatorContentProvider.java

Source

/*******************************************************************************
 * Copyright (c) 2007, 2013 Oracle. 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:
 *     Oracle - initial API and implementation
 ******************************************************************************/
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.ProjectTools;
import org.eclipse.jpt.common.core.internal.utility.ResourceChangeAdapter;
import org.eclipse.jpt.common.ui.internal.WorkbenchTools;
import org.eclipse.jpt.common.ui.internal.jface.NavigatorContentProvider;
import org.eclipse.jpt.common.ui.jface.ItemExtendedLabelProvider;
import org.eclipse.jpt.common.ui.jface.ItemTreeContentProvider;
import org.eclipse.jpt.jpa.core.JpaProject;
import org.eclipse.jpt.jpa.core.JpaProjectManager;
import org.eclipse.jpt.jpa.ui.JpaContextModelRootModel;
import org.eclipse.jpt.jpa.ui.JpaWorkbench;
import org.eclipse.ui.navigator.ICommonContentExtensionSite;

/**
 * This provider is invoked for:<ul>
 * <li>Eclipse projects with a JPA facet
 * <li>JPA root context node models
 * <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, JpaContextModelRootModel[]> projectChildren = new HashMap<IProject, JpaContextModelRootModel[]>();

    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 ItemTreeContentProvider.Factory buildItemContentProviderFactory() {
        return new JpaNavigatorItemContentProviderFactory();
    }

    @Override
    protected ItemExtendedLabelProvider.Factory buildItemLabelProviderFactory() {
        return new JpaNavigatorItemLabelProviderFactory();
    }

    @Override
    protected ResourceManager buildResourceManager() {
        JpaWorkbench jpaWorkbench = this.getJpaWorkbench();
        return (jpaWorkbench != null) ? jpaWorkbench.buildLocalResourceManager() : this.buildResourceManager_();
    }

    private ResourceManager buildResourceManager_() {
        return new LocalResourceManager(JFaceResources.getResources(WorkbenchTools.getDisplay()));
    }

    // ********** children **********

    /**
     * @see #getChildren_(Object)
     */
    @Override
    protected boolean hasChildren_(Object element) {
        return this.getChildren_(element) != null;
    }

    /**
     * We handle the children for an {@link IProject} here and allow the
     * superclass to delegate all others.
     */
    @Override
    protected Object[] getChildren_(Object element) {
        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 JpaContextModelRootModel[] getChildren_(IProject project) {
        JpaContextModelRootModel[] children = this.projectChildren.get(project);
        if (children == null) {
            children = this.buildChildren(project);
            this.projectChildren.put(project, children);
        }
        return children;
    }

    private JpaContextModelRootModel[] buildChildren(IProject project) {
        return new JpaContextModelRootModel[] { 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 <em>any</em>
     * project passed in.
     * <p>
     * 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 JpaContextModelRootModel buildChild(IProject project) {
        return (JpaContextModelRootModel) project.getAdapter(JpaContextModelRootModel.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) {
        JpaContextModelRootModel[] 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) {
        JpaContextModelRootModel[] children = this.projectChildren.remove(project);
        if (children != null) {
            for (JpaContextModelRootModel 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 WorkbenchTools.getAdapter(JpaWorkbench.class);
    }

    // ********** 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;
            }
        }
    }
}