package org.eclipse.jdt.internal.launching;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Platform;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.jdt.core.ClasspathContainerInitializer;
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.IPackageFragmentRoot;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.launching.IJavaLaunchConfigurationConstants;
import org.eclipse.jdt.launching.IRuntimeClasspathEntry;
import org.eclipse.jdt.launching.IRuntimeContainerComparator;
import org.eclipse.jdt.launching.JavaRuntime;
import org.eclipse.osgi.util.NLS;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
@SuppressWarnings("deprecation")
public class DefaultProjectClasspathEntry extends AbstractRuntimeClasspathEntry {
public static final String TYPE_ID = "org.eclipse.jdt.launching.classpathentry.defaultClasspath";
private boolean fExportedEntriesOnly = false;
public DefaultProjectClasspathEntry() {
}
public DefaultProjectClasspathEntry(IJavaProject project) {
setJavaProject(project);
}
@Override
protected void buildMemento(Document document, Element memento) throws CoreException {
memento.setAttribute("project", getJavaProject().getElementName());
memento.setAttribute("exportedEntriesOnly", Boolean.toString(fExportedEntriesOnly));
}
@Override
public void initializeFrom(Element memento) throws CoreException {
String name = memento.getAttribute("project");
if (name == null) {
abort(LaunchingMessages.DefaultProjectClasspathEntry_3, null);
}
IJavaProject project = JavaCore.create(ResourcesPlugin.getWorkspace().getRoot().getProject(name));
setJavaProject(project);
name = memento.getAttribute("exportedEntriesOnly");
if (name != null) {
fExportedEntriesOnly = Boolean.valueOf(name).booleanValue();
}
}
@Override
public String getTypeId() {
return TYPE_ID;
}
@Override
public int getType() {
return OTHER;
}
protected IProject getProject() {
return getJavaProject().getProject();
}
@Override
public String getLocation() {
return getProject().getLocation().toOSString();
}
@Override
public IPath getPath() {
return getProject().getFullPath();
}
@Override
public IResource getResource() {
return getProject();
}
@Override
public IRuntimeClasspathEntry[] getRuntimeClasspathEntries(ILaunchConfiguration configuration) throws CoreException {
boolean excludeTestCode = configuration != null
&& configuration.getAttribute(IJavaLaunchConfigurationConstants.ATTR_EXCLUDE_TEST_CODE, false);
return getRuntimeClasspathEntries(excludeTestCode);
}
@Override
public IRuntimeClasspathEntry[] getRuntimeClasspathEntries(boolean excludeTestCode) throws CoreException {
IClasspathEntry entry = JavaCore.newProjectEntry(getJavaProject().getProject().getFullPath());
List<Object> classpathEntries = new ArrayList<>(5);
List<IClasspathEntry> expanding = new ArrayList<>(5);
expandProject(entry, classpathEntries, expanding, excludeTestCode, isExportedEntriesOnly(), getJavaProject(), false);
IRuntimeClasspathEntry[] runtimeEntries = new IRuntimeClasspathEntry[classpathEntries.size()];
for (int i = 0; i < runtimeEntries.length; i++) {
Object e = classpathEntries.get(i);
if (e instanceof IClasspathEntry) {
IClasspathEntry cpe = (IClasspathEntry)e;
runtimeEntries[i] = new RuntimeClasspathEntry(cpe);
} else {
runtimeEntries[i] = (IRuntimeClasspathEntry)e;
}
}
List<IRuntimeClasspathEntry> ordered = new ArrayList<>(runtimeEntries.length);
for (int i = 0; i < runtimeEntries.length; i++) {
if (runtimeEntries[i].getClasspathProperty() == IRuntimeClasspathEntry.USER_CLASSES) {
ordered.add(runtimeEntries[i]);
}
}
return ordered.toArray(new IRuntimeClasspathEntry[ordered.size()]);
}
public static void expandProject(IClasspathEntry projectEntry, List<Object> expandedPath, List<IClasspathEntry> expanding, boolean excludeTestCode, boolean exportedEntriesOnly, IJavaProject rootProject, boolean isModularJVM) throws CoreException {
expanding.add(projectEntry);
IPath projectPath = projectEntry.getPath();
IResource res = ResourcesPlugin.getWorkspace().getRoot().findMember(projectPath.lastSegment());
if (res == null) {
expandedPath.add(projectEntry);
return;
}
IJavaProject project = (IJavaProject)JavaCore.create(res);
if (project == null || !project.getProject().isOpen() || !project.exists()) {
expandedPath.add(projectEntry);
return;
}
IClasspathEntry[] buildPath = project.getRawClasspath();
List<IClasspathEntry> unexpandedPath = new ArrayList<>(buildPath.length);
boolean projectAdded = false;
for (int i = 0; i < buildPath.length; i++) {
IClasspathEntry classpathEntry = buildPath[i];
if (excludeTestCode && classpathEntry.isTest()) {
continue;
}
if (classpathEntry.getEntryKind() == IClasspathEntry.CPE_SOURCE) {
if (!projectAdded) {
projectAdded = true;
unexpandedPath.add(projectEntry);
}
} else {
if (classpathEntry.isExported()) {
unexpandedPath.add(classpathEntry);
} else if (!exportedEntriesOnly || project.equals(rootProject)) {
unexpandedPath.add(classpathEntry);
}
}
}
Iterator<IClasspathEntry> iter = unexpandedPath.iterator();
while (iter.hasNext()) {
IClasspathEntry entry = iter.next();
if (entry == projectEntry) {
expandedPath.add(entry);
} else {
switch (entry.getEntryKind()) {
case IClasspathEntry.CPE_PROJECT:
if (!expanding.contains(entry)) {
expandProject(entry, expandedPath, expanding, excludeTestCode, exportedEntriesOnly, rootProject, isModularJVM);
}
break;
case IClasspathEntry.CPE_CONTAINER:
IClasspathContainer container = JavaCore.getClasspathContainer(entry.getPath(), project);
int property = -1;
if (container != null) {
switch (container.getKind()) {
case IClasspathContainer.K_APPLICATION:
if (isModularJVM) {
if (Arrays.stream(entry.getExtraAttributes()).anyMatch(attribute -> IClasspathAttribute.MODULE.equals(attribute.getName())
&& Boolean.TRUE.toString().equals(attribute.getValue()))) {
property = IRuntimeClasspathEntry.MODULE_PATH;
} else {
property = IRuntimeClasspathEntry.CLASS_PATH;
}
} else {
property = IRuntimeClasspathEntry.USER_CLASSES;
}
break;
case IClasspathContainer.K_DEFAULT_SYSTEM:
property = IRuntimeClasspathEntry.STANDARD_CLASSES;
break;
case IClasspathContainer.K_SYSTEM:
property = IRuntimeClasspathEntry.BOOTSTRAP_CLASSES;
break;
}
IRuntimeClasspathEntry r = JavaRuntime.newRuntimeContainerClasspathEntry(entry.getPath(), property, project);
boolean duplicate = false;
ClasspathContainerInitializer initializer = JavaCore.getClasspathContainerInitializer(r.getPath().segment(0));
for (int i = 0; i < expandedPath.size(); i++) {
Object o = expandedPath.get(i);
if (o instanceof IRuntimeClasspathEntry) {
IRuntimeClasspathEntry re = (IRuntimeClasspathEntry)o;
if (re.getType() == IRuntimeClasspathEntry.CONTAINER) {
if (container instanceof IRuntimeContainerComparator) {
duplicate = ((IRuntimeContainerComparator)container).isDuplicate(re.getPath());
} else {
ClasspathContainerInitializer initializer2 = JavaCore.getClasspathContainerInitializer(re.getPath().segment(0));
Object id1 = null;
Object id2 = null;
if (initializer == null) {
id1 = r.getPath().segment(0);
} else {
id1 = initializer.getComparisonID(r.getPath(), project);
}
if (initializer2 == null) {
id2 = re.getPath().segment(0);
} else {
IJavaProject context = re.getJavaProject();
if (context == null) {
context = project;
}
id2 = initializer2.getComparisonID(re.getPath(), context);
}
if (id1 == null) {
duplicate = id2 == null;
} else {
duplicate = id1.equals(id2);
}
}
if (duplicate) {
break;
}
}
}
}
if (!duplicate) {
expandedPath.add(r);
}
}
break;
case IClasspathEntry.CPE_VARIABLE:
IRuntimeClasspathEntry r = JavaRuntime.newVariableRuntimeClasspathEntry(entry.getPath());
if (isModularJVM) {
adjustClasspathProperty(r, entry);
}
r.setSourceAttachmentPath(entry.getSourceAttachmentPath());
r.setSourceAttachmentRootPath(entry.getSourceAttachmentRootPath());
if (!expandedPath.contains(r)) {
expandedPath.add(r);
}
break;
default:
if (!expandedPath.contains(entry)) {
if (entry.getEntryKind() != IClasspathEntry.CPE_SOURCE) {
IPackageFragmentRoot[] roots = project.findPackageFragmentRoots(entry);
for (int i = 0; i < roots.length; i++) {
IPackageFragmentRoot root = roots[i];
r = JavaRuntime.newArchiveRuntimeClasspathEntry(root.getPath(), entry.getSourceAttachmentPath(), entry.getSourceAttachmentRootPath(), entry.getAccessRules(), entry.getExtraAttributes(), entry.isExported());
if (isModularJVM) {
adjustClasspathProperty(r, entry);
}
r.setSourceAttachmentPath(entry.getSourceAttachmentPath());
r.setSourceAttachmentRootPath(entry.getSourceAttachmentRootPath());
if (!expandedPath.contains(r)) {
expandedPath.add(r);
}
}
} else {
expandedPath.add(entry);
}
}
break;
}
}
}
return;
}
public static void adjustClasspathProperty(IRuntimeClasspathEntry r, IClasspathEntry entry) {
if (r.getClasspathProperty() == IRuntimeClasspathEntry.USER_CLASSES) {
if (Arrays.stream(entry.getExtraAttributes()).anyMatch(attribute -> IClasspathAttribute.MODULE.equals(attribute.getName())
&& Boolean.TRUE.toString().equals(attribute.getValue()))) {
r.setClasspathProperty(IRuntimeClasspathEntry.MODULE_PATH);
} else {
r.setClasspathProperty(IRuntimeClasspathEntry.CLASS_PATH);
}
}
}
@Override
public boolean isComposite() {
return true;
}
@Override
public String getName() {
if (isExportedEntriesOnly()) {
return NLS.bind(LaunchingMessages.DefaultProjectClasspathEntry_2, new String[] {getJavaProject().getElementName()});
}
return NLS.bind(LaunchingMessages.DefaultProjectClasspathEntry_4, new String[] {getJavaProject().getElementName()});
}
@Override
public boolean equals(Object obj) {
if (obj instanceof DefaultProjectClasspathEntry) {
DefaultProjectClasspathEntry entry = (DefaultProjectClasspathEntry) obj;
return entry.getJavaProject().equals(getJavaProject()) &&
entry.isExportedEntriesOnly() == isExportedEntriesOnly();
}
return false;
}
@Override
public int hashCode() {
return getJavaProject().hashCode();
}
public void setExportedEntriesOnly(boolean exportedOnly) {
fExportedEntriesOnly = exportedOnly;
}
public boolean isExportedEntriesOnly() {
return fExportedEntriesOnly | Platform.getPreferencesService().getBoolean(
LaunchingPlugin.ID_PLUGIN,
JavaRuntime.PREF_ONLY_INCLUDE_EXPORTED_CLASSPATH_ENTRIES,
false,
null);
}
}