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
/******************************************************************************* * 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.sourcelookup; import java.io.File; import java.io.IOException; import java.io.StringReader; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; 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.IStatus; import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.Status; import org.eclipse.debug.core.DebugPlugin; import org.eclipse.debug.core.ILaunchConfiguration; import org.eclipse.debug.core.ILaunchConfigurationType; import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy; import org.eclipse.debug.core.model.IPersistableSourceLocator; import org.eclipse.debug.core.model.IStackFrame; import org.eclipse.jdt.core.IClasspathEntry; import org.eclipse.jdt.core.IJavaModel; 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.debug.core.IJavaStackFrame; import org.eclipse.jdt.debug.core.IJavaThread; import org.eclipse.jdt.internal.launching.LaunchingMessages; import org.eclipse.jdt.internal.launching.LaunchingPlugin; import org.eclipse.jdt.launching.IJavaLaunchConfigurationConstants; import org.eclipse.jdt.launching.IRuntimeClasspathEntry; import org.eclipse.jdt.launching.JavaRuntime; import org.eclipse.osgi.util.NLS; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.osgi.framework.Bundle; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import org.xml.sax.helpers.DefaultHandler;
Locates source for a Java debug session by searching a configurable set of source locations.

This class may be instantiated.

See Also:
  • ISourceLocator
Since:2.0
Deprecated:In 3.0, the debug platform provides source lookup facilities that should be used in place of the Java source lookup support provided in 2.0. The new facilities provide a source lookup director that coordinates source lookup among a set of participants, searching a set of source containers. See the following packages: org.eclipse.debug.core.sourcelookup and org.eclipse.debug.core.sourcelookup.containers. This class has been replaced by a Java source lookup director and Java source lookup participant. To migrate to the new source lookup support clients should add two new attributes to their launch configuration type extensions:
  • sourceLocatorId="org.eclipse.jdt.launching.sourceLocator.JavaSourceLookupDirector"
  • sourcePathComputerId="org.eclipse.jdt.launching.sourceLookup.javaSourcePathComputer"
The source locator id attribute specifies to use the Java source lookup director for launch configurations of the associated type, and the source path computer id attribute specifies the class to use when computing a default source lookup path for a launch configuration. The path computer referenced/provided (by the above id), computes a default source lookup path based on the support provided in the 2.0 release - i.e. a configuration's ATTR_SOURCE_PATH_PROVIDER attribute (if present), or a default source lookup path based on a configuration's runtime classpath. This class has been replaced by the Java source lookup director which is an internal class, but can be used via the sourceLocatorId attribute on a launch configuration type extension.
@noextendThis class is not intended to be sub-classed by clients.
/** * Locates source for a Java debug session by searching * a configurable set of source locations. * <p> * This class may be instantiated. * </p> * @see org.eclipse.debug.core.model.ISourceLocator * @since 2.0 * @deprecated In 3.0, the debug platform provides source lookup facilities that * should be used in place of the Java source lookup support provided in 2.0. * The new facilities provide a source lookup director that coordinates source * lookup among a set of participants, searching a set of source containers. * See the following packages: <code>org.eclipse.debug.core.sourcelookup</code> * and <code>org.eclipse.debug.core.sourcelookup.containers</code>. This class * has been replaced by a Java source lookup director and Java source lookup * participant. To migrate to the new source lookup support clients should * add two new attributes to their launch configuration type extensions:<ul> * <li>sourceLocatorId="org.eclipse.jdt.launching.sourceLocator.JavaSourceLookupDirector"</li> * <li>sourcePathComputerId="org.eclipse.jdt.launching.sourceLookup.javaSourcePathComputer"</li> * </ul> * The source locator id attribute specifies to use the Java source lookup director * for launch configurations of the associated type, and the source path computer id * attribute specifies the class to use when computing a default source lookup * path for a launch configuration. The path computer referenced/provided (by the * above id), computes a default source lookup path based on the support provided in * the 2.0 release - i.e. a configuration's <code>ATTR_SOURCE_PATH_PROVIDER</code> * attribute (if present), or a default source lookup path based on a configuration's * runtime classpath. This class has been replaced by the Java source lookup * director which is an internal class, but can be used via the * <code>sourceLocatorId</code> attribute on a launch configuration type extension. * @noextend This class is not intended to be sub-classed by clients. */
@Deprecated public class JavaSourceLocator implements IPersistableSourceLocator {
Identifier for the 'Java Source Locator' extension (value "org.eclipse.jdt.launching.javaSourceLocator").
/** * Identifier for the 'Java Source Locator' extension * (value <code>"org.eclipse.jdt.launching.javaSourceLocator"</code>). */
public static final String ID_JAVA_SOURCE_LOCATOR = LaunchingPlugin.getUniqueIdentifier() + ".javaSourceLocator"; //$NON-NLS-1$
A collection of the source locations to search
/** * A collection of the source locations to search */
private IJavaSourceLocation[] fLocations;
Constructs a new empty JavaSourceLocator.
/** * Constructs a new empty JavaSourceLocator. */
public JavaSourceLocator() { setSourceLocations(new IJavaSourceLocation[0]); }
Constructs a new Java source locator that looks in the specified project for source, and required projects, if includeRequired is true.
Params:
  • projects – the projects in which to look for source
  • includeRequired – whether to look in required projects as well
Throws:
/** * Constructs a new Java source locator that looks in the * specified project for source, and required projects, if * <code>includeRequired</code> is <code>true</code>. * * @param projects the projects in which to look for source * @param includeRequired whether to look in required projects * as well * @throws CoreException if a new locator fails to be created */
public JavaSourceLocator(IJavaProject[] projects, boolean includeRequired) throws CoreException { ArrayList<IJavaProject> requiredProjects = new ArrayList<>(); for (int i= 0; i < projects.length; i++) { if (includeRequired) { collectRequiredProjects(projects[i], requiredProjects); } else { if (!requiredProjects.contains(projects[i])) { requiredProjects.add(projects[i]); } } } // only add external entries with the same location once HashMap<IPath, IPath> external = new HashMap<>(); ArrayList<PackageFragmentRootSourceLocation> list = new ArrayList<>(); // compute the default locations for each project, and add unique ones Iterator<IJavaProject> iter = requiredProjects.iterator(); while (iter.hasNext()) { IJavaProject p = iter.next(); IPackageFragmentRoot[] roots = p.getPackageFragmentRoots(); for (int i = 0; i < roots.length; i++) { if (roots[i].isExternal()) { IPath location = roots[i].getPath(); if (external.get(location) == null) { external.put(location, location); list.add(new PackageFragmentRootSourceLocation(roots[i])); } } else { list.add(new PackageFragmentRootSourceLocation(roots[i])); } } } IJavaSourceLocation[] locations = list.toArray(new IJavaSourceLocation[list.size()]); setSourceLocations(locations); }
Constructs a new JavaSourceLocator that searches the specified set of source locations for source elements.
Params:
  • locations – the source locations to search for source, in the order they should be searched
/** * Constructs a new JavaSourceLocator that searches the * specified set of source locations for source elements. * * @param locations the source locations to search for * source, in the order they should be searched */
public JavaSourceLocator(IJavaSourceLocation[] locations) { setSourceLocations(locations); }
Constructs a new JavaSourceLocator that searches the default set of source locations for the given Java project.
Params:
  • project – Java project
Throws:
  • CoreException – if an exception occurs reading the classpath of the given or any required project
/** * Constructs a new JavaSourceLocator that searches the * default set of source locations for the given Java project. * * @param project Java project * @exception CoreException if an exception occurs reading * the classpath of the given or any required project */
public JavaSourceLocator(IJavaProject project) throws CoreException { setSourceLocations(getDefaultSourceLocations(project)); }
Sets the locations that will be searched, in the order to be searched.
Params:
  • locations – the locations that will be searched, in the order to be searched
/** * Sets the locations that will be searched, in the order * to be searched. * * @param locations the locations that will be searched, in the order * to be searched */
public void setSourceLocations(IJavaSourceLocation[] locations) { fLocations = locations; }
Returns the locations that this source locator is currently searching, in the order that they are searched.
Returns:the locations that this source locator is currently searching, in the order that they are searched
/** * Returns the locations that this source locator is currently * searching, in the order that they are searched. * * @return the locations that this source locator is currently * searching, in the order that they are searched */
public IJavaSourceLocation[] getSourceLocations() { return fLocations; }
Returns all source elements that correspond to the type associated with the given stack frame, or null if none.
Params:
  • stackFrame – stack frame
Returns:all source elements that correspond to the type associated with the given stack frame, or null if none
Since:2.1
/** * Returns all source elements that correspond to the type associated with * the given stack frame, or <code>null</code> if none. * * @param stackFrame stack frame * @return all source elements that correspond to the type associated with * the given stack frame, or <code>null</code> if none * @since 2.1 */
public Object[] getSourceElements(IStackFrame stackFrame) { if (stackFrame instanceof IJavaStackFrame) { IJavaStackFrame frame = (IJavaStackFrame)stackFrame; String name = null; try { name = getFullyQualfiedName(frame); if (name == null) { return null; } } catch (CoreException e) { // if the thread has since resumed, return null if (e.getStatus().getCode() != IJavaThread.ERR_THREAD_NOT_SUSPENDED) { LaunchingPlugin.log(e); } return null; } List<Object> list = new ArrayList<>(); IJavaSourceLocation[] locations = getSourceLocations(); for (int i = 0; i < locations.length; i++) { try { Object sourceElement = locations[i].findSourceElement(name); if (sourceElement != null) { list.add(sourceElement); } } catch (CoreException e) { // log the error and try the next source location LaunchingPlugin.log(e); } } return list.toArray(); } return null; } /* (non-Javadoc) * @see org.eclipse.debug.core.model.ISourceLocator#getSourceElement(org.eclipse.debug.core.model.IStackFrame) */ @Override public Object getSourceElement(IStackFrame stackFrame) { if (stackFrame instanceof IJavaStackFrame) { IJavaStackFrame frame = (IJavaStackFrame)stackFrame; String name = null; try { name = getFullyQualfiedName(frame); if (name == null) { return null; } } catch (CoreException e) { // if the thread has since resumed, return null if (e.getStatus().getCode() != IJavaThread.ERR_THREAD_NOT_SUSPENDED) { LaunchingPlugin.log(e); } return null; } IJavaSourceLocation[] locations = getSourceLocations(); for (int i = 0; i < locations.length; i++) { try { Object sourceElement = locations[i].findSourceElement(name); if (sourceElement != null) { return sourceElement; } } catch (CoreException e) { // log the error and try the next source location LaunchingPlugin.log(e); } } } return null; } private String getFullyQualfiedName(IJavaStackFrame frame) throws CoreException { String name = null; if (frame.isObsolete()) { return null; } String sourceName = frame.getSourceName(); if (sourceName == null) { // no debug attributes, guess at source name name = frame.getDeclaringTypeName(); } else { // build source name from debug attributes using // the source file name and the package of the declaring // type // @see bug# 21518 - remove absolute path prefix int index = sourceName.lastIndexOf('\\'); if (index == -1) { index = sourceName.lastIndexOf('/'); } if (index >= 0) { sourceName = sourceName.substring(index + 1); } String declName= frame.getDeclaringTypeName(); index = declName.lastIndexOf('.'); if (index >= 0) { name = declName.substring(0, index + 1); } else { name = ""; //$NON-NLS-1$ } index = sourceName.lastIndexOf('.'); if (index >= 0) { name += sourceName.substring(0, index) ; } } return name; }
Adds all projects required by proj to the list res
Params:
  • proj – the project for which to compute required projects
  • res – the list to add all required projects too
Throws:
/** * Adds all projects required by <code>proj</code> to the list * <code>res</code> * * @param proj the project for which to compute required * projects * @param res the list to add all required projects too * @throws JavaModelException if there is a problem with the backing Java model */
protected static void collectRequiredProjects(IJavaProject proj, ArrayList<IJavaProject> res) throws JavaModelException { if (!res.contains(proj)) { res.add(proj); IJavaModel model= proj.getJavaModel(); IClasspathEntry[] entries= proj.getRawClasspath(); for (int i= 0; i < entries.length; i++) { IClasspathEntry curr= entries[i]; if (curr.getEntryKind() == IClasspathEntry.CPE_PROJECT) { IJavaProject ref= model.getJavaProject(curr.getPath().segment(0)); if (ref.exists()) { collectRequiredProjects(ref, res); } } } } }
Returns a default collection of source locations for the given Java project. Default source locations consist of the given project and all of its required projects .
Params:
  • project – Java project
Throws:
  • CoreException – if an exception occurs reading computing the default locations
Returns:a collection of source locations for all required projects
/** * Returns a default collection of source locations for * the given Java project. Default source locations consist * of the given project and all of its required projects . * * @param project Java project * @return a collection of source locations for all required * projects * @exception CoreException if an exception occurs reading * computing the default locations */
public static IJavaSourceLocation[] getDefaultSourceLocations(IJavaProject project) throws CoreException { // create a temporary launch config ILaunchConfigurationType type = DebugPlugin.getDefault().getLaunchManager().getLaunchConfigurationType(IJavaLaunchConfigurationConstants.ID_JAVA_APPLICATION); ILaunchConfigurationWorkingCopy config = type.newInstance(null, project.getElementName()); config.setAttribute(IJavaLaunchConfigurationConstants.ATTR_PROJECT_NAME, project.getElementName()); JavaSourceLocator locator = new JavaSourceLocator(); locator.initializeDefaults(config); return locator.getSourceLocations(); } /* (non-Javadoc) * @see org.eclipse.debug.core.model.IPersistableSourceLocator#getMemento() */ @Override public String getMemento() throws CoreException { Document doc = DebugPlugin.newDocument(); Element node = doc.createElement("javaSourceLocator"); //$NON-NLS-1$ doc.appendChild(node); IJavaSourceLocation[] locations = getSourceLocations(); for (int i = 0; i < locations.length; i++) { Element child = doc.createElement("javaSourceLocation"); //$NON-NLS-1$ child.setAttribute("class", locations[i].getClass().getName()); //$NON-NLS-1$ child.setAttribute("memento", locations[i].getMemento()); //$NON-NLS-1$ node.appendChild(child); } return DebugPlugin.serializeDocument(doc); } /* (non-Javadoc) * @see org.eclipse.debug.core.model.IPersistableSourceLocator#initializeDefaults(org.eclipse.debug.core.ILaunchConfiguration) */ @Override public void initializeDefaults(ILaunchConfiguration configuration) throws CoreException { IRuntimeClasspathEntry[] entries = JavaRuntime.computeUnresolvedSourceLookupPath(configuration); IRuntimeClasspathEntry[] resolved = JavaRuntime.resolveSourceLookupPath(entries, configuration); setSourceLocations(getSourceLocations(resolved)); } /* (non-Javadoc) * @see org.eclipse.debug.core.model.IPersistableSourceLocator#initializeFromMemento(java.lang.String) */ @Override public void initializeFromMemento(String memento) throws CoreException { Exception ex = null; try { Element root = null; DocumentBuilder parser = DocumentBuilderFactory.newInstance().newDocumentBuilder(); parser.setErrorHandler(new DefaultHandler()); StringReader reader = new StringReader(memento); InputSource source = new InputSource(reader); root = parser.parse(source).getDocumentElement(); if (!root.getNodeName().equalsIgnoreCase("javaSourceLocator")) { //$NON-NLS-1$ abort(LaunchingMessages.JavaSourceLocator_Unable_to_restore_Java_source_locator___invalid_format__6, null); } List<IJavaSourceLocation> sourceLocations = new ArrayList<>(); Bundle bundle = LaunchingPlugin.getDefault().getBundle(); NodeList list = root.getChildNodes(); int length = list.getLength(); for (int i = 0; i < length; ++i) { Node node = list.item(i); short type = node.getNodeType(); if (type == Node.ELEMENT_NODE) { Element entry = (Element) node; if (entry.getNodeName().equalsIgnoreCase("javaSourceLocation")) { //$NON-NLS-1$ String className = entry.getAttribute("class"); //$NON-NLS-1$ String data = entry.getAttribute("memento"); //$NON-NLS-1$ if (isEmpty(className)) { abort(LaunchingMessages.JavaSourceLocator_Unable_to_restore_Java_source_locator___invalid_format__10, null); } Class<?> clazz = null; try { clazz = bundle.loadClass(className); } catch (ClassNotFoundException e) { abort(NLS.bind(LaunchingMessages.JavaSourceLocator_Unable_to_restore_source_location___class_not_found___0__11, new String[] {className}), e); } IJavaSourceLocation location = null; try { location = (IJavaSourceLocation)clazz.newInstance(); } catch (IllegalAccessException e) { abort(LaunchingMessages.JavaSourceLocator_Unable_to_restore_source_location__12, e); } catch (InstantiationException e) { abort(LaunchingMessages.JavaSourceLocator_Unable_to_restore_source_location__12, e); } location.initializeFrom(data); sourceLocations.add(location); } else { abort(LaunchingMessages.JavaSourceLocator_Unable_to_restore_Java_source_locator___invalid_format__14, null); } } } setSourceLocations(sourceLocations.toArray(new IJavaSourceLocation[sourceLocations.size()])); return; } catch (ParserConfigurationException e) { ex = e; } catch (SAXException e) { ex = e; } catch (IOException e) { ex = e; } abort(LaunchingMessages.JavaSourceLocator_Exception_occurred_initializing_source_locator__15, ex); }
Returns source locations that are associated with the given runtime classpath entries.
Params:
  • entries – the entries to compute source locations for
Returns:the array of IJavaSourceLocation
/** * Returns source locations that are associated with the given runtime classpath * entries. * @param entries the entries to compute source locations for * @return the array of {@link IJavaSourceLocation} */
private static IJavaSourceLocation[] getSourceLocations(IRuntimeClasspathEntry[] entries) { List<IJavaSourceLocation> locations = new ArrayList<>(entries.length); for (int i = 0; i < entries.length; i++) { IRuntimeClasspathEntry entry = entries[i]; IJavaSourceLocation location = null; switch (entry.getType()) { case IRuntimeClasspathEntry.PROJECT: IProject project = (IProject)entry.getResource(); if (project != null && project.exists() && project.isOpen()) { location = new JavaProjectSourceLocation(JavaCore.create(project)); } break; case IRuntimeClasspathEntry.ARCHIVE: // check if the archive is in the workspace as a package fragment root location = getArchiveSourceLocation(entry); if (location == null) { String path = entry.getSourceAttachmentLocation(); if (path == null) { // if there is no source attachment, look in the archive itself path = entry.getLocation(); } if (path != null) { File file = new File(path); if (file.exists()) { if (file.isDirectory()) { location = new DirectorySourceLocation(file); } else { location = new ArchiveSourceLocation(path, entry.getSourceAttachmentRootLocation()); } } } } break; case IRuntimeClasspathEntry.VARIABLE: String source = entry.getSourceAttachmentLocation(); if (source != null) { location = new ArchiveSourceLocation(source, entry.getSourceAttachmentRootLocation()); } break; case IRuntimeClasspathEntry.CONTAINER: throw new IllegalArgumentException(LaunchingMessages.JavaSourceLocator_Illegal_to_have_a_container_resolved_to_a_container_1); } if (location != null) { locations.add(location); } } return locations.toArray(new IJavaSourceLocation[locations.size()]); } private boolean isEmpty(String string) { return string == null || string.length() == 0; }
Throws an internal error exception
Params:
  • message – the message
  • e – the error
Throws:
/** * Throws an internal error exception * @param message the message * @param e the error * @throws CoreException the new {@link CoreException} */
private void abort(String message, Throwable e) throws CoreException { IStatus s = new Status(IStatus.ERROR, LaunchingPlugin.getUniqueIdentifier(), IJavaLaunchConfigurationConstants.ERR_INTERNAL_ERROR, message, e); throw new CoreException(s); }
Returns whether the given objects are equal, allowing for null.
Params:
  • a – the first item
  • b – the item to compare
Returns:whether the given objects are equal, allowing for null
/** * Returns whether the given objects are equal, allowing * for <code>null</code>. * * @param a the first item * @param b the item to compare * @return whether the given objects are equal, allowing * for <code>null</code> */
private static boolean equalOrNull(Object a, Object b) { if (a == null) { return b == null; } if (b == null) { return false; } return a.equals(b); }
Returns whether the source attachments of the given package fragment root and runtime classpath entry are equal.
Params:
  • root – package fragment root
  • entry – runtime classpath entry
Throws:
Returns:whether the source attachments of the given package fragment root and runtime classpath entry are equal
/** * Returns whether the source attachments of the given package fragment * root and runtime classpath entry are equal. * * @param root package fragment root * @param entry runtime classpath entry * @return whether the source attachments of the given package fragment * root and runtime classpath entry are equal * @throws JavaModelException if there is a problem with the backing Java model */
private static boolean isSourceAttachmentEqual(IPackageFragmentRoot root, IRuntimeClasspathEntry entry) throws JavaModelException { return equalOrNull(root.getSourceAttachmentPath(), entry.getSourceAttachmentPath()); }
Determines if the given archive runtime classpath entry exists in the workspace as a package fragment root. Returns the associated package fragment root source location if possible, otherwise null.
Params:
  • entry – archive runtime classpath entry
Returns:IJavaSourceLocation or null
/** * Determines if the given archive runtime classpath entry exists * in the workspace as a package fragment root. Returns the associated * package fragment root source location if possible, otherwise * <code>null</code>. * * @param entry archive runtime classpath entry * @return IJavaSourceLocation or <code>null</code> */
private static IJavaSourceLocation getArchiveSourceLocation(IRuntimeClasspathEntry entry) { IResource resource = entry.getResource(); if (resource == null) { // Check all package fragment roots for case of external archive. // External jars are shared, so it does not matter which project it // originates from IJavaModel model = JavaCore.create(ResourcesPlugin.getWorkspace().getRoot()); try { IJavaProject[] jps = model.getJavaProjects(); for (int i = 0; i < jps.length; i++) { IPackageFragmentRoot[] allRoots = jps[i].getPackageFragmentRoots(); for (int j = 0; j < allRoots.length; j++) { IPackageFragmentRoot root = allRoots[j]; if (root.isExternal() && root.getPath().equals(new Path(entry.getLocation()))) { if (isSourceAttachmentEqual(root, entry)) { // use package fragment root return new PackageFragmentRootSourceLocation(root); } } } } } catch (JavaModelException e) { LaunchingPlugin.log(e); } } else { // check if the archive is a package fragment root IProject project = resource.getProject(); IJavaProject jp = JavaCore.create(project); try { if (jp != null && jp.exists()) { IPackageFragmentRoot root = jp.getPackageFragmentRoot(resource); IPackageFragmentRoot[] allRoots = jp.getPackageFragmentRoots(); for (int j = 0; j < allRoots.length; j++) { if (allRoots[j].equals(root)) { // ensure source attachment paths match if (isSourceAttachmentEqual(root, entry)) { // use package fragment root return new PackageFragmentRootSourceLocation(root); } } } } // check all other java projects to see if another project references // the archive IJavaModel model = JavaCore.create(ResourcesPlugin.getWorkspace().getRoot()); IJavaProject[] jps = model.getJavaProjects(); for (int i = 0; i < jps.length; i++) { IPackageFragmentRoot[] allRoots = jps[i].getPackageFragmentRoots(); for (int j = 0; j < allRoots.length; j++) { IPackageFragmentRoot root = allRoots[j]; if (!root.isExternal() && root.getPath().equals(entry.getPath())) { if (isSourceAttachmentEqual(root, entry)) { // use package fragment root return new PackageFragmentRootSourceLocation(root); } } } } } catch (JavaModelException e) { LaunchingPlugin.log(e); } } return null; } }