/******************************************************************************* * Copyright (c) 2000, 2015 IBM Corporation 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: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.jdt.internal.launching; import java.util.ArrayList; 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.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.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; /** * Default user classpath entries for a Java project */ @SuppressWarnings("deprecation") public class DefaultProjectClasspathEntry extends AbstractRuntimeClasspathEntry { public static final String TYPE_ID = "org.eclipse.jdt.launching.classpathentry.defaultClasspath"; //$NON-NLS-1$ /** * Whether only exported entries should be on the runtime classpath. * By default all entries are on the runtime classpath. */ private boolean fExportedEntriesOnly = false; /** * Default constructor need to instantiate extensions */ public DefaultProjectClasspathEntry() { } /** * Constructs a new classpath entry for the given project. * * @param project Java project */ public DefaultProjectClasspathEntry(IJavaProject project) { setJavaProject(project); } /* (non-Javadoc) * @see org.eclipse.jdt.internal.launching.AbstractRuntimeClasspathEntry#buildMemento(org.w3c.dom.Document, org.w3c.dom.Element) */ @Override protected void buildMemento(Document document, Element memento) throws CoreException { memento.setAttribute("project", getJavaProject().getElementName()); //$NON-NLS-1$ memento.setAttribute("exportedEntriesOnly", Boolean.toString(fExportedEntriesOnly)); //$NON-NLS-1$ } /* (non-Javadoc) * @see org.eclipse.jdt.launching.IRuntimeClasspathEntry2#initializeFrom(org.w3c.dom.Element) */ @Override public void initializeFrom(Element memento) throws CoreException { String name = memento.getAttribute("project"); //$NON-NLS-1$ if (name == null) { abort(LaunchingMessages.DefaultProjectClasspathEntry_3, null); } IJavaProject project = JavaCore.create(ResourcesPlugin.getWorkspace().getRoot().getProject(name)); setJavaProject(project); name = memento.getAttribute("exportedEntriesOnly"); //$NON-NLS-1$ if (name != null) { fExportedEntriesOnly = Boolean.valueOf(name).booleanValue(); } } /* (non-Javadoc) * @see org.eclipse.jdt.launching.IRuntimeClasspathEntry2#getTypeId() */ @Override public String getTypeId() { return TYPE_ID; } /* (non-Javadoc) * @see org.eclipse.jdt.launching.IRuntimeClasspathEntry#getType() */ @Override public int getType() { return OTHER; } protected IProject getProject() { return getJavaProject().getProject(); } /* (non-Javadoc) * @see org.eclipse.jdt.launching.IRuntimeClasspathEntry#getLocation() */ @Override public String getLocation() { return getProject().getLocation().toOSString(); } /* (non-Javadoc) * @see org.eclipse.jdt.launching.IRuntimeClasspathEntry#getPath() */ @Override public IPath getPath() { return getProject().getFullPath(); } /* (non-Javadoc) * @see org.eclipse.jdt.launching.IRuntimeClasspathEntry#getResource() */ @Override public IResource getResource() { return getProject(); } /* (non-Javadoc) * @see org.eclipse.jdt.launching.IRuntimeClasspathEntry2#getRuntimeClasspathEntries(org.eclipse.debug.core.ILaunchConfiguration) */ @Override public IRuntimeClasspathEntry[] getRuntimeClasspathEntries(ILaunchConfiguration configuration) 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); 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; } } // remove bootpath entries - this is a default user classpath 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()]); } /** * Returns the transitive closure of classpath entries for the * given project entry. * * @param projectEntry project classpath entry * @param expandedPath a list of entries already expanded, should be empty * to begin, and contains the result * @param expanding a list of projects that have been or are currently being * expanded (to detect cycles) * @exception CoreException if unable to expand the classpath */ private void expandProject(IClasspathEntry projectEntry, List<Object> expandedPath, List<IClasspathEntry> expanding) throws CoreException { expanding.add(projectEntry); // 1. Get the raw classpath // 2. Replace source folder entries with a project entry IPath projectPath = projectEntry.getPath(); IResource res = ResourcesPlugin.getWorkspace().getRoot().findMember(projectPath.lastSegment()); if (res == null) { // add project entry and return expandedPath.add(projectEntry); return; } IJavaProject project = (IJavaProject)JavaCore.create(res); if (project == null || !project.getProject().isOpen() || !project.exists()) { // add project entry and return 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 (classpathEntry.getEntryKind() == IClasspathEntry.CPE_SOURCE) { if (!projectAdded) { projectAdded = true; unexpandedPath.add(projectEntry); } } else { // add exported entries, as configured if (classpathEntry.isExported()) { unexpandedPath.add(classpathEntry); } else if (!isExportedEntriesOnly() || project.equals(getJavaProject())) { // add non exported entries from root project or if we are including all entries unexpandedPath.add(classpathEntry); } } } // 3. expand each project entry (except for the root project) // 4. replace each container entry with a runtime entry associated with the project 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); } 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: 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); // check for duplicate/redundant entries 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()); r.setSourceAttachmentPath(entry.getSourceAttachmentPath()); r.setSourceAttachmentRootPath(entry.getSourceAttachmentRootPath()); if (!expandedPath.contains(r)) { expandedPath.add(r); } break; default: if (!expandedPath.contains(entry)) { // resolve project relative paths - @see bug 57732 & bug 248466 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()); r.setSourceAttachmentPath(entry.getSourceAttachmentPath()); r.setSourceAttachmentRootPath(entry.getSourceAttachmentRootPath()); if (!expandedPath.contains(r)) { expandedPath.add(r); } } } else { expandedPath.add(entry); } } break; } } } return; } /* (non-Javadoc) * @see org.eclipse.jdt.launching.IRuntimeClasspathEntry2#isComposite() */ @Override public boolean isComposite() { return true; } /* (non-Javadoc) * @see org.eclipse.jdt.launching.IRuntimeClasspathEntry2#getName() */ @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()}); } /* (non-Javadoc) * @see java.lang.Object#equals(java.lang.Object) */ @Override public boolean equals(Object obj) { if (obj instanceof DefaultProjectClasspathEntry) { DefaultProjectClasspathEntry entry = (DefaultProjectClasspathEntry) obj; return entry.getJavaProject().equals(getJavaProject()) && entry.isExportedEntriesOnly() == isExportedEntriesOnly(); } return false; } /* (non-Javadoc) * @see java.lang.Object#hashCode() */ @Override public int hashCode() { return getJavaProject().hashCode(); } /** * Sets whether the runtime classpath computation should only * include exported entries in referenced projects. * * @param exportedOnly if the runtime classpath computation should only * include exported entries in referenced projects. * @since 3.2 */ public void setExportedEntriesOnly(boolean exportedOnly) { fExportedEntriesOnly = exportedOnly; } /** * Returns whether the classpath computation only includes exported * entries in referenced projects. * * @return if the classpath computation only includes exported * entries in referenced projects. * @since 3.2 */ public boolean isExportedEntriesOnly() { return fExportedEntriesOnly | Platform.getPreferencesService().getBoolean( LaunchingPlugin.ID_PLUGIN, JavaRuntime.PREF_ONLY_INCLUDE_EXPORTED_CLASSPATH_ENTRIES, false, null); } }