org.eclipse.jst.common.jdt.internal.classpath.FlexibleProjectContainer.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.jst.common.jdt.internal.classpath.FlexibleProjectContainer.java

Source

/******************************************************************************
 * Copyright (c) 2005, 2006 BEA Systems, Inc.
 * 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:
 *    Konstantin Komissarchik - initial API and implementation
 ******************************************************************************/

package org.eclipse.jst.common.jdt.internal.classpath;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.eclipse.core.resources.IContainer;
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.IResourceDeltaVisitor;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.jdt.core.IAccessRule;
import org.eclipse.jdt.core.IClasspathAttribute;
import org.eclipse.jdt.core.IClasspathContainer;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jst.common.frameworks.CommonFrameworksPlugin;
import org.eclipse.jst.common.internal.modulecore.IClasspathDependencyComponent;
import org.eclipse.wst.common.componentcore.ComponentCore;
import org.eclipse.wst.common.componentcore.ModuleCoreNature;
import org.eclipse.wst.common.componentcore.internal.util.IModuleConstants;
import org.eclipse.wst.common.componentcore.resources.IVirtualComponent;
import org.eclipse.wst.common.componentcore.resources.IVirtualFolder;
import org.eclipse.wst.common.componentcore.resources.IVirtualReference;
import org.eclipse.wst.common.componentcore.resources.IVirtualResource;

/**
 * @author <a href="mailto:kosta@bea.com">Konstantin Komissarchik</a>
 */

public abstract class FlexibleProjectContainer

        implements IClasspathContainer

{
    protected static final class PathType {
        public static final PathType LIB_DIRECTORY = new PathType(), CLASSES_DIRECTORY = new PathType();
    }

    private static ClasspathDecorationsManager decorations;

    static {
        // Register the resource listener that will listen for changes to
        // resources relevant to flexible project containers across the
        // workspace and refresh them as necessary.

        Listener.register();

        // Read the decorations from the workspace metadata.

        final String plugin = CommonFrameworksPlugin.PLUGIN_ID;
        decorations = new ClasspathDecorationsManager(plugin);
    }

    private static final String SEPARATOR = "!"; //$NON-NLS-1$

    public static String getDecorationManagerKey(IProject project, String container) {
        return project.getName() + SEPARATOR + container;
    }

    protected final IPath path;
    protected final IJavaProject owner;
    protected final IProject project;
    private final IPath[] paths;
    private final PathType[] pathTypes;
    protected final List entries;
    private final IClasspathEntry[] cpentries;
    private static final Set containerTypes = new HashSet();

    public FlexibleProjectContainer(final IPath path, final IJavaProject owner, final IProject project,
            final IPath[] paths, final PathType[] types) {
        this.path = path;
        this.owner = owner;
        this.project = project;
        this.paths = paths;
        this.pathTypes = types;

        if (!isFlexibleProject(this.project)) {
            // Silently noop if the referenced project is not a flexible
            // project. Should I be doing something else here?

            this.entries = Collections.EMPTY_LIST;
            this.cpentries = new IClasspathEntry[0];

            return;
        }

        addFlexibleProjectContainerType(path.segment(0));

        this.entries = computeClasspathEntries();
        this.cpentries = new IClasspathEntry[this.entries.size()];

        for (int i = 0, n = this.entries.size(); i < n; i++) {
            IPath entryPath = (IPath) this.entries.get(i);
            IResource resource = ResourcesPlugin.getWorkspace().getRoot().findMember(entryPath);
            if (null != resource && resource.getType() == IResource.PROJECT)
                this.cpentries[i] = JavaCore.newProjectEntry(entryPath, false);
            else
                this.cpentries[i] = newLibraryEntry(entryPath);
        }
    }

    public int getKind() {
        return K_APPLICATION;
    }

    public IPath getPath() {
        return this.path;
    }

    public IClasspathEntry[] getClasspathEntries() {
        return this.cpentries;
    }

    public boolean isOutOfDate(final IResourceDelta delta) {
        if (delta == null) {
            return false;
        }
        return isOutOfDate();
    }

    public boolean isOutOfDate() {
        final List currentEntries = computeClasspathEntries();
        return !this.entries.equals(currentEntries);
    }

    public abstract void refresh();

    /**
     * If forceUpdate is <code>false</code> then a refresh is made only if
     * {@link #isOutOfDate()} returns <code>true</code>. If forceUpdate is
     * <code>true</code> then a refresh is immediately made without checking
     * {@link #isOutOfDate()}.
     * 
     * @param forceUpdate
     */
    public void refresh(boolean forceUpdate) {
        if (forceUpdate || isOutOfDate()) {
            refresh();
        }
    }

    static ClasspathDecorationsManager getDecorationsManager() {
        return decorations;
    }

    protected IVirtualReference[] computeReferences(IVirtualComponent vc) {
        return vc.getReferences();
    }

    protected List computeClasspathEntries() {
        final List entries = new ArrayList();

        final IVirtualComponent vc = ComponentCore.createComponent(this.project);

        if (vc == null) {
            return entries;
        }

        IVirtualReference[] refs = computeReferences(vc);
        IVirtualComponent comp = null;
        Set jarsHandled = new HashSet();
        String jarName = null;
        for (int i = 0; i < refs.length; i++) {
            comp = refs[i].getReferencedComponent();
            if (!refs[i].getRuntimePath().equals(paths[0].makeAbsolute()))
                continue;
            jarName = refs[i].getArchiveName();
            if (null != jarName) {
                jarsHandled.add(jarName);
            }
            IPath newPath = null;
            if (comp.isBinary()) {
                if (comp instanceof IClasspathDependencyComponent)
                    continue;
                newPath = (IPath) comp.getAdapter(IPath.class);
            } else
                newPath = comp.getProject().getFullPath();

            if (newPath != null && !entries.contains(newPath))
                entries.add(newPath);
        }

        for (int i = 0; i < this.paths.length; i++) {
            final IVirtualFolder rootFolder = vc.getRootFolder();
            final IVirtualFolder vf = rootFolder.getFolder(paths[i]);

            if (this.pathTypes[i] == PathType.LIB_DIRECTORY) {
                final IVirtualResource[] contents;

                try {
                    contents = members(vf);
                } catch (CoreException e) {
                    CommonFrameworksPlugin.log(e);
                    continue;
                }

                for (int j = 0; j < contents.length; j++) {
                    final IResource r = contents[j].getUnderlyingResource();
                    final IPath p = r.getLocation();

                    if (!jarsHandled.contains(p.lastSegment()) && isJarFile(r)) {
                        jarsHandled.add(p.lastSegment());
                        entries.add(p);
                    }
                }
            } else {
                final IContainer[] uf = vf.getUnderlyingFolders();

                for (int j = 0; j < uf.length; j++) {
                    final IPath p = uf[j].getFullPath();

                    if (!jarsHandled.contains(p.lastSegment()) && !isSourceOrOutputDirectory(p)) {
                        jarsHandled.add(p.lastSegment());
                        entries.add(p);
                    }
                }
            }
        }

        return entries;
    }

    // TODO: This method was created to provide a safe last-minute workaround
    // for the issue described in https://bugs.eclipse.org/bugs/show_bug.cgi?id=162974.
    // This code needs to be revisited in a future release to find a more
    // permanent solution.

    protected IVirtualResource[] members(final IVirtualFolder vf)

            throws CoreException

    {
        return vf.members();
    }

    private IClasspathEntry newLibraryEntry(final IPath p) {
        IPath srcpath = null;
        IPath srcrootpath = null;
        IClasspathAttribute[] attrs = {};
        IAccessRule[] access = {};

        final ClasspathDecorations dec = decorations
                .getDecorations(getDecorationManagerKey(project, getPath().toString()), p.toString());

        if (dec != null) {
            srcpath = dec.getSourceAttachmentPath();
            srcrootpath = dec.getSourceAttachmentRootPath();
            attrs = dec.getExtraAttributes();
        }

        return JavaCore.newLibraryEntry(p, srcpath, srcrootpath, access, attrs, false);

    }

    private boolean isSourceOrOutputDirectory(final IPath aPath) {
        if (isJavaProject(this.project)) {
            try {
                final IJavaProject jproject = JavaCore.create(this.project);
                final IClasspathEntry[] cp = jproject.getRawClasspath();

                for (int i = 0; i < cp.length; i++) {
                    final IClasspathEntry cpe = cp[i];

                    if (cpe.getEntryKind() == IClasspathEntry.CPE_SOURCE) {
                        final IPath src = cpe.getPath();
                        final IPath output = cpe.getOutputLocation();

                        if (src.equals(aPath) || output != null && output.equals(aPath)) {
                            return true;
                        }
                    }
                }

                if (jproject.getOutputLocation().equals(aPath)) {
                    return true;
                }
            } catch (JavaModelException e) {
                CommonFrameworksPlugin.log(e);
            }
        }

        return false;
    }

    private static boolean isJavaProject(final IProject pj) {
        try {
            return pj.getNature(JavaCore.NATURE_ID) != null;
        } catch (CoreException e) {
            return false;
        }
    }

    private static boolean isFlexibleProject(final IProject pj) {
        try {
            return pj.getNature(IModuleConstants.MODULE_NATURE_ID) != null;
        } catch (CoreException e) {
            return false;
        }
    }

    private static boolean isJarFile(final IResource f) {
        if (f.getType() == IResource.FILE) {
            final String fname = f.getName();

            if (fname.endsWith(".jar") || fname.endsWith(".zip")) //$NON-NLS-1$ //$NON-NLS-2$
            {
                return true;
            }
        }

        return false;
    }

    private static boolean isMetadataFile(final IResource f) {
        if (f.getType() == IResource.FILE) {
            final String fname = f.getName();

            if (fname.equals(".component") || //$NON-NLS-1$
                    fname.equals("org.eclipse.wst.common.component") || //$NON-NLS-1$
                    fname.equals(".classpath")) //$NON-NLS-1$
            {
                return true;
            }
        }

        return false;
    }

    private static boolean isFlexibleProjectContainer(final IPath path) {
        synchronized (containerTypes) {
            return containerTypes.contains(path.segment(0));
        }
    }

    private static void addFlexibleProjectContainerType(final String type) {
        synchronized (containerTypes) {
            containerTypes.add(type);
        }
    }

    private static final class Listener

            implements IResourceChangeListener

    {
        public static void register() {
            final Listener listener = new Listener();
            final IWorkspace ws = ResourcesPlugin.getWorkspace();
            ws.addResourceChangeListener(listener, IResourceChangeEvent.PRE_BUILD);
        }

        public void resourceChanged(final IResourceChangeEvent event) {
            // Screen the delta before going any further. 

            if (!isInterestingEvent(event)) {
                return;
            }

            // Locate all of the flexible project containers.

            final ArrayList containers = new ArrayList();

            final IProject[] projects = ResourcesPlugin.getWorkspace().getRoot().getProjects();

            for (int i = 0; i < projects.length; i++) {
                final IProject project = projects[i];

                try {
                    if (isJavaProject(project)) {
                        final IJavaProject jproj = JavaCore.create(project);
                        final IClasspathEntry[] cpes = jproj.getRawClasspath();

                        for (int j = 0; j < cpes.length; j++) {
                            final IClasspathEntry cpe = cpes[j];
                            final IPath path = cpe.getPath();

                            if (cpe.getEntryKind() == IClasspathEntry.CPE_CONTAINER
                                    && isFlexibleProjectContainer(path)) {
                                final IClasspathContainer cont = JavaCore.getClasspathContainer(path, jproj);

                                containers.add(cont);
                            }
                        }
                    }
                } catch (JavaModelException e) {
                    CommonFrameworksPlugin.log(e);
                }
            }

            // Refresh the containers that are out of date.

            final IResourceDelta delta = event.getDelta();

            for (int i = 0, n = containers.size(); i < n; i++) {
                final FlexibleProjectContainer c = (FlexibleProjectContainer) containers.get(i);

                if (c.isOutOfDate(delta)) {
                    c.refresh();
                }
            }
        }

        private static boolean isInterestingEvent(final IResourceChangeEvent event) {
            final boolean[] result = new boolean[1];

            final IResourceDeltaVisitor visitor = new IResourceDeltaVisitor() {
                public boolean visit(final IResourceDelta delta) {
                    final IResource r = delta.getResource();

                    switch (r.getType()) {
                    case IResource.ROOT: {
                        return true;
                    }
                    case IResource.PROJECT: {
                        return ModuleCoreNature.isFlexibleProject((IProject) r);
                    }
                    case IResource.FOLDER: {
                        final int kind = delta.getKind();

                        if (kind == IResourceDelta.ADDED || kind == IResourceDelta.REMOVED) {
                            result[0] = true;
                            return false;
                        }
                        return true;
                    }
                    case IResource.FILE: {
                        if (isJarFile(r) || isMetadataFile(r)) {
                            result[0] = true;
                        }

                        return false;
                    }
                    default: {
                        return false;
                    }
                    }
                }
            };

            try {
                event.getDelta().accept(visitor, false);
            } catch (CoreException e) {
                CommonFrameworksPlugin.log(e);
            }

            return result[0];
        }
    }

}