org.eclipse.ant.internal.ui.datatransfer.EclipseClasspath.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.ant.internal.ui.datatransfer.EclipseClasspath.java

Source

/*******************************************************************************
 * Copyright (c) 2004, 2013 Richard Hoefter and others.
 * 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:
 *     Richard Hoefter (richard.hoefter@web.de) - initial API and implementation, bug 95298, bug 192726, bug 201180
 *     IBM Corporation - nlsing and incorporating into Eclipse, bug 108276
 *******************************************************************************/

package org.eclipse.ant.internal.ui.datatransfer;

import java.io.File;
import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import org.eclipse.ant.internal.core.IAntCoreConstants;
import org.eclipse.ant.internal.ui.AntUIPlugin;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.jdt.core.IClasspathContainer;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IPackageFragmentRoot;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.launching.IRuntimeClasspathEntry;
import org.eclipse.jdt.launching.JavaRuntime;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

/**
 * Class to inspect classpath of an Eclipse project.
 */
public class EclipseClasspath {

    protected List<String> srcDirs = new ArrayList<>();
    protected List<String> classDirs = new ArrayList<>();
    protected List<List<String>> inclusionLists = new ArrayList<>();
    protected List<List<String>> exclusionLists = new ArrayList<>();

    protected Map<String, String> variable2valueMap = new LinkedHashMap<>();
    protected List<String> rawClassPathEntries = new ArrayList<>();
    protected List<String> rawClassPathEntriesAbsolute = new ArrayList<>();

    private IJavaProject project;

    private static Map<String, IClasspathContainer> userLibraryCache = new HashMap<>();

    /**
     * Initialize object with classpath of given project.
     */
    public EclipseClasspath(IJavaProject project) throws JavaModelException {
        this.project = project;
        handle(project.getRawClasspath());
    }

    /**
     * Initialize object with runtime classpath of given launch configuration.
     * 
     * @param project
     *            project that contains given launch configuration conf
     * @param conf
     *            launch configuration
     * @param bootstrap
     *            if true only bootstrap entries are added, if false only non-bootstrap entries are added
     */
    public EclipseClasspath(IJavaProject project, ILaunchConfiguration conf, boolean bootstrap)
            throws CoreException {
        this.project = project;

        // convert IRuntimeClasspathEntry to IClasspathEntry
        IRuntimeClasspathEntry[] runtimeEntries;
        // see AbstractJavaLaunchConfigurationDelegate
        runtimeEntries = JavaRuntime.computeUnresolvedRuntimeClasspath(conf);
        List<IClasspathEntry> classpathEntries = new ArrayList<>(runtimeEntries.length);
        for (int i = 0; i < runtimeEntries.length; i++) {
            IRuntimeClasspathEntry entry = runtimeEntries[i];
            if (bootstrap && (entry.getClasspathProperty() == IRuntimeClasspathEntry.BOOTSTRAP_CLASSES)
                    || !bootstrap && (entry.getClasspathProperty() != IRuntimeClasspathEntry.BOOTSTRAP_CLASSES)) {
                // NOTE: See AbstractJavaLaunchConfigurationDelegate.getBootpathExt()
                // for an alternate bootclasspath detection
                if (entry.getClass().getName().equals("org.eclipse.jdt.internal.launching.VariableClasspathEntry")) //$NON-NLS-1$
                {
                    IClasspathEntry e = convertVariableClasspathEntry(entry);
                    if (e != null) {
                        classpathEntries.add(e);
                    }
                } else if (entry.getClass().getName()
                        .equals("org.eclipse.jdt.internal.launching.DefaultProjectClasspathEntry")) //$NON-NLS-1$
                {
                    IClasspathEntry e = JavaCore.newProjectEntry(entry.getPath());
                    classpathEntries.add(e);
                } else if (entry.getClasspathEntry() != null) {
                    classpathEntries.add(entry.getClasspathEntry());
                }
            } else if (bootstrap && entry.toString().startsWith(JavaRuntime.JRE_CONTAINER)) {
                classpathEntries.add(entry.getClasspathEntry());
            } else if (bootstrap && entry.toString().startsWith(JavaCore.USER_LIBRARY_CONTAINER_ID)) {
                classpathEntries.add(entry.getClasspathEntry());
            }
        }
        IClasspathEntry[] entries = classpathEntries.toArray(new IClasspathEntry[classpathEntries.size()]);

        handle(entries);
    }

    private void handle(IClasspathEntry[] entries) throws JavaModelException {
        for (int i = 0; i < entries.length; i++) {
            handleSources(entries[i]);
            handleVariables(entries[i]);
            handleJars(entries[i]);
            handleLibraries(entries[i]);
            handleProjects(entries[i]);
        }
    }

    private void handleSources(IClasspathEntry entry) throws JavaModelException {
        String projectRoot = ExportUtil.getProjectRoot(project);
        String defaultClassDir = project.getOutputLocation().toString();
        String defaultClassDirAbsolute = ExportUtil.resolve(project.getOutputLocation());

        if (entry.getContentKind() == IPackageFragmentRoot.K_SOURCE
                && entry.getEntryKind() == IClasspathEntry.CPE_SOURCE) {
            // found source path
            IPath srcDirPath = entry.getPath();
            IPath classDirPath = entry.getOutputLocation();
            String srcDir = handleLinkedResource(srcDirPath);
            ExportUtil.removeProjectRoot((srcDirPath != null) ? srcDirPath.toString() : projectRoot,
                    project.getProject());
            String classDir = ExportUtil.removeProjectRoot(
                    (classDirPath != null) ? classDirPath.toString() : defaultClassDir, project.getProject());
            srcDirs.add(srcDir);
            classDirs.add(classDir);
            String classDirAbsolute = (classDirPath != null) ? ExportUtil.resolve(classDirPath)
                    : defaultClassDirAbsolute;
            rawClassPathEntries.add(classDir);
            rawClassPathEntriesAbsolute.add(classDirAbsolute);
            IPath[] inclusions = entry.getInclusionPatterns();
            List<String> inclusionList = new ArrayList<>();
            for (int j = 0; j < inclusions.length; j++) {
                if (inclusions[j] != null) {
                    inclusionList.add(ExportUtil.removeProjectRoot(inclusions[j].toString(), project.getProject()));
                }
            }
            inclusionLists.add(inclusionList);
            IPath[] exclusions = entry.getExclusionPatterns();
            List<String> exclusionList = new ArrayList<>();
            for (int j = 0; j < exclusions.length; j++) {
                if (exclusions[j] != null) {
                    exclusionList.add(ExportUtil.removeProjectRoot(exclusions[j].toString(), project.getProject()));
                }
            }
            exclusionLists.add(exclusionList);
        }
    }

    /**
     * Check if given source path is a linked resource. Add values to {@link #variable2valueMap} accordingly.
     * 
     * @param srcDirPath
     *            source dir as IPath
     * @return source directory with reference, e.g. ${MYPATH}/src, if it is no link, orginal source dir is returned
     */
    private String handleLinkedResource(IPath srcDirPath) {
        String projectRoot = ExportUtil.getProjectRoot(project);
        String srcDir = ExportUtil.removeProjectRoot((srcDirPath != null) ? srcDirPath.toString() : projectRoot,
                project.getProject());
        if (srcDirPath == null) {
            return srcDir;
        }
        IFile file;
        try {
            file = ResourcesPlugin.getWorkspace().getRoot().getFile(srcDirPath);
        } catch (IllegalArgumentException e) {
            return srcDir;
        }
        if (file.isLinked()) {
            String pathVariable = file.getRawLocation().segment(0).toString();
            URI pathVariableValue = file.getWorkspace().getPathVariableManager().getURIValue(pathVariable);
            if (pathVariableValue != null) {
                // path variable was used
                String pathVariableExtension = file.getRawLocation().removeFirstSegments(1).toString(); // Bug 192726
                String relativePath = ExportUtil.getRelativePath(pathVariableValue.toString(), projectRoot);
                variable2valueMap.put(pathVariable + ".pathvariable", relativePath); //$NON-NLS-1$
                variable2valueMap.put(srcDir + ".link", //$NON-NLS-1$
                        "${" + pathVariable + ".pathvariable}/" + pathVariableExtension); //$NON-NLS-1$  //$NON-NLS-2$
            } else {
                String relativePath = ExportUtil
                        .getRelativePath(file.getLocation() + IAntCoreConstants.EMPTY_STRING, projectRoot);
                variable2valueMap.put(srcDir + ".link", relativePath); //$NON-NLS-1$
            }
            srcDir = "${" + srcDir + ".link}"; //$NON-NLS-1$ //$NON-NLS-2$
        }
        return srcDir;
    }

    private void handleJars(IClasspathEntry entry) {
        if (entry.getContentKind() == IPackageFragmentRoot.K_BINARY
                && entry.getEntryKind() == IClasspathEntry.CPE_LIBRARY) {
            String jarFile = entry.getPath().toString();
            StringBuffer jarFileBuffer = new StringBuffer();
            StringBuffer jarFileAbsoluteBuffer = new StringBuffer();
            String jarFileAbsolute = ExportUtil.resolve(entry.getPath());
            if (jarFileAbsolute == null) {
                jarFileAbsolute = jarFile; // jarFile was already absolute
                if (handleSubProjectClassesDirectory(jarFile, jarFileBuffer, jarFileAbsoluteBuffer)) {
                    jarFile = jarFileBuffer.toString();
                    jarFileAbsolute = jarFileAbsoluteBuffer.toString();
                }
            }
            String jarFileOld = jarFile;
            jarFile = ExportUtil.removeProjectRoot(jarFile, project.getProject());
            if (jarFile.equals(jarFileOld)) {
                if (handleSubProjectClassesDirectory(jarFile, jarFileBuffer, jarFileAbsoluteBuffer)) {
                    jarFile = jarFileBuffer.toString();
                    jarFileAbsolute = jarFileAbsoluteBuffer.toString();
                }
            }
            rawClassPathEntries.add(jarFile);
            rawClassPathEntriesAbsolute.add(jarFileAbsolute);
        }
    }

    /**
     * Checks if file is a class directory of a subproject and fills string buffers with resolved values.
     * 
     * @param file
     *            file to check
     * @param jarFile
     *            filled with file location with variable reference ${project.location}, which is also added to variable2valueMap
     * @param jarFileAbsolute
     *            filled with absolute file location
     * @return true if file is a classes directory
     */
    private boolean handleSubProjectClassesDirectory(String file, StringBuffer jarFile,
            StringBuffer jarFileAbsolute) {
        // class directory of a subproject?
        if (file != null && file.indexOf('/') == 0) {
            int i = file.indexOf('/', 1);
            i = (i != -1) ? i : file.length();
            String subproject = file.substring(1, i);
            IJavaProject javaproject = ExportUtil.getJavaProjectByName(subproject);
            if (javaproject != null) {
                jarFile.setLength(0);
                jarFileAbsolute.setLength(0);
                String location = javaproject.getProject().getName() + ".location"; //$NON-NLS-1$
                jarFileAbsolute.append(ExportUtil.replaceProjectRoot(file, javaproject.getProject(),
                        ExportUtil.getProjectRoot(javaproject)));
                jarFile.append(
                        ExportUtil.replaceProjectRoot(file, javaproject.getProject(), "${" + location + "}")); //$NON-NLS-1$ //$NON-NLS-2$
                String projectRoot = ExportUtil.getProjectRoot(project);
                String relativePath = ExportUtil.getRelativePath(ExportUtil.getProjectRoot(javaproject),
                        projectRoot);
                variable2valueMap.put(location, relativePath);
                return true;
            }
        }
        return false;
    }

    private void handleVariables(IClasspathEntry entry) {
        if (entry.getContentKind() == IPackageFragmentRoot.K_SOURCE
                && entry.getEntryKind() == IClasspathEntry.CPE_VARIABLE) {
            // found variable
            String e = entry.getPath().toString();
            int index = e.indexOf('/');
            if (index == -1) {
                index = e.indexOf('\\');
            }
            String variable = e;
            String path = IAntCoreConstants.EMPTY_STRING;
            if (index != -1) {
                variable = e.substring(0, index);
                path = e.substring(index);
            }
            IPath value = JavaCore.getClasspathVariable(variable);
            if (value != null) {
                String projectRoot = ExportUtil.getProjectRoot(project);
                String relativePath = ExportUtil.getRelativePath(value.toString(), projectRoot);
                variable2valueMap.put(variable, relativePath);
            } else if (variable2valueMap.get(variable) == null) {
                // only add empty value, if variable is new
                variable2valueMap.put(variable, IAntCoreConstants.EMPTY_STRING);
            }
            rawClassPathEntriesAbsolute.add(value + path);
            rawClassPathEntries.add("${" + variable + "}" + path); //$NON-NLS-1$ //$NON-NLS-2$
        }
    }

    private void handleLibraries(IClasspathEntry entry) throws JavaModelException {
        if (entry.getContentKind() == IPackageFragmentRoot.K_SOURCE
                && entry.getEntryKind() == IClasspathEntry.CPE_CONTAINER) {
            // found library
            IClasspathContainer container = JavaCore.getClasspathContainer(entry.getPath(), project);
            if (container == null) {
                // jar missing (project not compile clean)
                return;
            }
            String jar = entry.getPath().toString();
            String refName;
            if (jar.startsWith(JavaRuntime.JRE_CONTAINER)) {
                // JRE System Library
                refName = "${jre.container}"; //$NON-NLS-1$
            } else if (jar.startsWith(JavaCore.USER_LIBRARY_CONTAINER_ID)) {
                // User Library
                String libraryName = container.getDescription();
                refName = "${" + libraryName + ".userclasspath}"; //$NON-NLS-1$ //$NON-NLS-2$
                if (container.getKind() == IClasspathContainer.K_SYSTEM) {
                    refName = "${" + libraryName + ".bootclasspath}"; //$NON-NLS-1$ //$NON-NLS-2$
                }
            } else {
                // Library dependencies: e.g. Plug-in Dependencies
                String libraryName = container.getDescription();
                refName = "${" + libraryName + ".libraryclasspath}"; //$NON-NLS-1$ //$NON-NLS-2$
            }
            userLibraryCache.put(refName, container);
            srcDirs.add(refName);
            classDirs.add(refName);
            rawClassPathEntries.add(refName);
            rawClassPathEntriesAbsolute.add(refName);
            inclusionLists.add(new ArrayList<String>());
            exclusionLists.add(new ArrayList<String>());
        }
    }

    private void handleProjects(IClasspathEntry entry) {
        if (entry.getContentKind() == IPackageFragmentRoot.K_SOURCE
                && entry.getEntryKind() == IClasspathEntry.CPE_PROJECT) {
            // found required project on build path
            String subProjectRoot = entry.getPath().toString();
            IJavaProject subProject = ExportUtil.getJavaProject(subProjectRoot);
            if (subProject == null) {
                // project was not loaded in workspace
                AntUIPlugin.log("project is not loaded in workspace: " + subProjectRoot, null); //$NON-NLS-1$
                return;
            }
            // only add an indicator that this is a project reference
            String classpathRef = "${" + subProject.getProject().getName() + ".classpath}"; //$NON-NLS-1$ //$NON-NLS-2$
            srcDirs.add(classpathRef);
            classDirs.add(classpathRef);
            rawClassPathEntries.add(classpathRef);
            rawClassPathEntriesAbsolute.add(classpathRef);
            inclusionLists.add(new ArrayList<String>());
            exclusionLists.add(new ArrayList<String>());
        }
    }

    /**
     * Get runtime classpath items for given project separated with path separator.
     */
    public static String getClasspath(IJavaProject project) throws CoreException {
        List<String> items = getClasspathList(project);
        return ExportUtil.toString(items, File.pathSeparator);
    }

    /**
     * Get runtime classpath items for given project.
     */
    public static List<String> getClasspathList(IJavaProject project) throws CoreException {
        String[] classpath = JavaRuntime.computeDefaultRuntimeClassPath(project);
        return Arrays.asList(classpath);
    }

    /**
     * Check if given string is a reference.
     */
    public static boolean isReference(String s) {
        return isProjectReference(s) || isUserLibraryReference(s) || isUserSystemLibraryReference(s)
                || isLibraryReference(s) || isJreReference(s);
        // NOTE: A linked resource is no reference
    }

    /**
     * Check if given string is a project reference.
     */
    public static boolean isProjectReference(String s) {
        return s.startsWith("${") && s.endsWith(".classpath}"); //$NON-NLS-1$ //$NON-NLS-2$ 
    }

    /**
     * Resolves given project reference to a project.
     * 
     * @return <code>null</code> if project is not resolvable
     */
    public static IJavaProject resolveProjectReference(String s) {
        String name = ExportUtil.removePrefixAndSuffix(s, "${", ".classpath}"); //$NON-NLS-1$ //$NON-NLS-2$
        return ExportUtil.getJavaProjectByName(name);
    }

    /**
     * Check if given string is a user library reference.
     */
    public static boolean isUserLibraryReference(String s) {
        return s.startsWith("${") && s.endsWith(".userclasspath}"); //$NON-NLS-1$ //$NON-NLS-2$ 
    }

    /**
     * Check if given string is a user system library reference. This library is added to the compiler boot classpath.
     */
    public static boolean isUserSystemLibraryReference(String s) {
        return s.startsWith("${") && s.endsWith(".bootclasspath}"); //$NON-NLS-1$ //$NON-NLS-2$ 
    }

    /**
     * Check if given string is a library reference. e.g. Plug-in dependencies are library references.
     * 
     */
    public static boolean isLibraryReference(String s) {
        return s.startsWith("${") && s.endsWith(".libraryclasspath}"); //$NON-NLS-1$ //$NON-NLS-2$ 
    }

    /**
     * Check if given string is a JRE reference.
     */
    public static boolean isJreReference(String s) {
        return s.equals("${jre.container}"); //$NON-NLS-1$
    }

    /**
     * Resolves given user (system) library or plugin reference to its container.
     * 
     * <p>
     * NOTE: The library can only be resolved if an EclipseClasspath object was created which had a reference to this library. The class holds an
     * internal cache to circumvent that UserLibraryManager is an internal class.
     * 
     * @return null if library is not resolvable
     */
    public static IClasspathContainer resolveUserLibraryReference(String s) {
        return userLibraryCache.get(s);
    }

    /**
     * Check if given string is a linked resource.
     * 
     */
    public static boolean isLinkedResource(String s) {
        return s.startsWith("${") && s.endsWith(".link}"); //$NON-NLS-1$ //$NON-NLS-2$ 
    }

    /**
     * Get source folder name of a linked resource.
     * 
     * @see #isLinkedResource(String)
     */
    public static String getLinkedResourceName(String s) {
        return ExportUtil.removePrefixAndSuffix(s, "${", ".link}"); //$NON-NLS-1$ //$NON-NLS-2$ 
    }

    /**
     * Resolves given linked resource to an absolute file location.
     */
    public String resolveLinkedResource(String s) {
        String name = ExportUtil.removePrefixAndSuffix(s, "${", "}"); //$NON-NLS-1$ //$NON-NLS-2$
        String value = variable2valueMap.get(name);
        String suffix = ".pathvariable}"; //$NON-NLS-1$
        int i = value.indexOf(suffix);
        if (i != -1) {
            // path variable
            String pathVariable = value.substring(0, i + suffix.length() - 1);
            pathVariable = ExportUtil.removePrefix(pathVariable, "${"); //$NON-NLS-1$
            return variable2valueMap.get(pathVariable) + value.substring(i + suffix.length());
        }
        return value;
    }

    /**
     * Convert a VariableClasspathEntry to a IClasspathEntry.
     * 
     * <p>
     * This is a workaround as entry.getClasspathEntry() returns null.
     */
    private IClasspathEntry convertVariableClasspathEntry(IRuntimeClasspathEntry entry) {
        try {
            Document doc = ExportUtil.parseXmlString(entry.getMemento());
            Element element = (Element) doc.getElementsByTagName("memento").item(0); //$NON-NLS-1$
            String variableString = element.getAttribute("variableString"); //$NON-NLS-1$
            ExportUtil.addVariable(variable2valueMap, variableString, ExportUtil.getProjectRoot(project));
            // remove ${...} from string to be conform for handleVariables()
            variableString = ExportUtil.removePrefix(variableString, "${");//$NON-NLS-1$
            int i = variableString.indexOf('}');
            if (i != -1) {
                variableString = variableString.substring(0, i) + variableString.substring(i + 1);
            }
            IPath path = new Path(variableString);
            return JavaCore.newVariableEntry(path, null, null);
        } catch (Exception e) {
            AntUIPlugin.log(e);
            return null;
        }

    }
}