blob: e96a0cd0291a10e0d3b8d706cfe28c6a9ba620f1 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2004, 2013 Richard Hoefter and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* 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<>());
exclusionLists.add(new ArrayList<>());
}
}
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<>());
exclusionLists.add(new ArrayList<>());
}
}
/**
* 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;
}
}
}