| /******************************************************************************* |
| * Copyright (c) 2000, 2015 IBM Corporation 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: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.jdt.launching; |
| |
| |
| import java.io.IOException; |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.jar.Attributes; |
| import java.util.jar.JarFile; |
| import java.util.jar.Manifest; |
| |
| 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.internal.launching.DefaultProjectClasspathEntry; |
| import org.eclipse.jdt.internal.launching.VariableClasspathEntry; |
| |
| /** |
| * Default implementation of source lookup path computation and resolution. |
| * <p> |
| * This class may be sub-classed. |
| * </p> |
| * @since 2.0 |
| */ |
| public class StandardSourcePathProvider extends StandardClasspathProvider { |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.jdt.launching.IRuntimeClasspathProvider#computeUnresolvedClasspath(org.eclipse.debug.core.ILaunchConfiguration) |
| */ |
| @Override |
| public IRuntimeClasspathEntry[] computeUnresolvedClasspath(ILaunchConfiguration configuration) throws CoreException { |
| boolean useDefault = configuration.getAttribute(IJavaLaunchConfigurationConstants.ATTR_DEFAULT_SOURCE_PATH, true); |
| IRuntimeClasspathEntry[] entries = null; |
| if (useDefault) { |
| // the default source lookup path is the same as the classpath |
| entries = super.computeUnresolvedClasspath(configuration); |
| } else { |
| // recover persisted source path |
| entries = recoverRuntimePath(configuration, IJavaLaunchConfigurationConstants.ATTR_SOURCE_PATH); |
| } |
| return entries; |
| |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.jdt.launching.IRuntimeClasspathProvider#resolveClasspath(org.eclipse.jdt.launching.IRuntimeClasspathEntry[], org.eclipse.debug.core.ILaunchConfiguration) |
| */ |
| @Override |
| public IRuntimeClasspathEntry[] resolveClasspath(IRuntimeClasspathEntry[] entries, ILaunchConfiguration configuration) throws CoreException { |
| List<IRuntimeClasspathEntry> all = new UniqueList(entries.length); |
| for (int i = 0; i < entries.length; i++) { |
| switch (entries[i].getType()) { |
| case IRuntimeClasspathEntry.PROJECT: |
| // a project resolves to itself for source lookup (rather than the class file output locations) |
| all.add(entries[i]); |
| break; |
| case IRuntimeClasspathEntry.OTHER: |
| IRuntimeClasspathEntry2 entry = (IRuntimeClasspathEntry2) entries[i]; |
| String typeId = entry.getTypeId(); |
| IRuntimeClasspathEntry[] res = null; |
| switch (typeId) { |
| case DefaultProjectClasspathEntry.TYPE_ID: |
| // add the resolved children of the project |
| IRuntimeClasspathEntry[] children = entry.getRuntimeClasspathEntries(configuration); |
| res = JavaRuntime.resolveSourceLookupPath(children, configuration); |
| break; |
| case VariableClasspathEntry.TYPE_ID: |
| // add the archive itself - we currently do not allow a source attachment |
| res = JavaRuntime.resolveRuntimeClasspathEntry(entry, configuration); |
| break; |
| default: |
| res = JavaRuntime.resolveRuntimeClasspathEntry(entry, configuration); |
| break; |
| } |
| if (res != null) { |
| for (int j = 0; j < res.length; j++) { |
| all.add(res[j]); |
| addManifestReferences(res[j], all); |
| } |
| } |
| break; |
| |
| default: |
| IRuntimeClasspathEntry[] resolved = JavaRuntime.resolveRuntimeClasspathEntry(entries[i], configuration); |
| for (int j = 0; j < resolved.length; j++) { |
| all.add(resolved[j]); |
| addManifestReferences(resolved[j], all); |
| } |
| break; |
| } |
| } |
| return all.toArray(new IRuntimeClasspathEntry[all.size()]); |
| } |
| |
| /** |
| * If the given entry is an archive, adds any archives referenced by the associated manifest. |
| * |
| * @param entry runtime classpath entry |
| * @param all list to add references to |
| */ |
| protected void addManifestReferences(IRuntimeClasspathEntry entry, List<IRuntimeClasspathEntry> all) { |
| if (entry.getType() == IRuntimeClasspathEntry.ARCHIVE) { |
| String location = entry.getLocation(); |
| if (location != null) { |
| try (JarFile jar = new JarFile(location, false);) { |
| Manifest manifest = jar.getManifest(); |
| if (manifest != null) { |
| Attributes mainAttributes = manifest.getMainAttributes(); |
| if (mainAttributes != null) { |
| String value = mainAttributes.getValue(Attributes.Name.CLASS_PATH); |
| if (value != null) { |
| String[] entries = value.split("\\s+"); //$NON-NLS-1$ |
| IPath base = new Path(location); |
| base = base.removeLastSegments(1); |
| for (int i = 0; i < entries.length; i++) { |
| IPath path = base.append(entries[i]); |
| if (path.toFile().exists()) { |
| IRuntimeClasspathEntry ref = JavaRuntime.newArchiveRuntimeClasspathEntry(path); |
| if (!all.contains(ref)) { |
| all.add(ref); |
| } |
| } |
| } |
| } |
| } |
| } |
| } catch (IOException e) { |
| } |
| } |
| } |
| } |
| |
| /* |
| * An ArrayList that acts like a set -i.e. does not allow duplicate items. |
| * hack for bug 112774 |
| */ |
| class UniqueList extends ArrayList<IRuntimeClasspathEntry> { |
| private static final long serialVersionUID = -7402160651027036270L; |
| HashSet<IRuntimeClasspathEntry> set; |
| |
| public UniqueList(int length) { |
| super(length); |
| set = new HashSet<>(length); |
| } |
| |
| @Override |
| public void add(int index, IRuntimeClasspathEntry element) { |
| if (set.add(element)) { |
| super.add(index, element); |
| } |
| } |
| |
| @Override |
| public boolean add(IRuntimeClasspathEntry o) { |
| if (set.add(o)) { |
| return super.add(o); |
| } |
| return false; |
| } |
| |
| @Override |
| public boolean addAll(Collection<? extends IRuntimeClasspathEntry> c) { |
| if (set.addAll(c)) { |
| return super.addAll(c); |
| } |
| return false; |
| } |
| |
| @Override |
| public boolean addAll(int index, Collection<? extends IRuntimeClasspathEntry> c) { |
| if (set.addAll(c)) { |
| return super.addAll(index, c); |
| } |
| return false; |
| } |
| |
| @Override |
| public void clear() { |
| set.clear(); |
| super.clear(); |
| } |
| |
| @Override |
| public boolean contains(Object elem) { |
| return set.contains(elem); |
| } |
| |
| @Override |
| public void ensureCapacity(int minCapacity) { |
| super.ensureCapacity(minCapacity); |
| } |
| |
| @Override |
| public IRuntimeClasspathEntry remove(int index) { |
| IRuntimeClasspathEntry object = super.remove(index); |
| set.remove(object); |
| return object; |
| } |
| |
| @Override |
| protected void removeRange(int fromIndex, int toIndex) { |
| for (int index = fromIndex; index<=toIndex; index++) { |
| remove(index); |
| } |
| } |
| |
| @Override |
| public IRuntimeClasspathEntry set(int index, IRuntimeClasspathEntry element) { |
| set.remove(element); |
| if (set.add(element)) { |
| return super.set(index, element); |
| } |
| return null; //should not happen. |
| } |
| } |
| |
| } |