blob: c0a7541fd28d88d4d6c5660b7cc55d0686b230a9 [file] [log] [blame]
package org.eclipse.jdt.launching.sourcelookup;
/*
* (c) Copyright IBM Corp. 2000, 2001.
* All Rights Reserved.
*/
import java.io.File;
import java.io.IOException;
import java.io.StringReader;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.apache.xerces.dom.DocumentImpl;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.debug.core.ILaunchConfiguration;
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.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.JavaLaunchConfigurationUtils;
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.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
/**
* Locates source for a Java debug session by searching
* a configurable set of source locations.
* <p>
* This class may be instantiated; it is not intended to be subclassed.
* </p>
* @see org.eclipse.debug.core.model.ISourceLocator
* @since 2.0
*/
public class JavaSourceLocator implements IPersistableSourceLocator {
/**
* 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
*/
private IJavaSourceLocation[] fLocations;
/**
* 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
* <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
*/
public JavaSourceLocator(IJavaProject[] projects, boolean includeRequired) throws JavaModelException {
ArrayList 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]);
}
}
}
IJavaSourceLocation[] locations = new IJavaSourceLocation[requiredProjects.size()];
for (int i = 0; i < requiredProjects.size(); i++) {
locations[i] = new JavaProjectSourceLocation((IJavaProject)requiredProjects.get(i));
}
setSourceLocations(locations);
}
/**
* 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.
*
* @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.
*
* @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.
*
* @return the locations that this source locator is currently
* searching, in the order that they are searched
*/
public IJavaSourceLocation[] getSourceLocations() {
return fLocations;
}
/**
* @see org.eclipse.debug.core.model.ISourceLocator#getSourceElement(IStackFrame)
*/
public Object getSourceElement(IStackFrame stackFrame) {
if (stackFrame instanceof IJavaStackFrame) {
IJavaStackFrame frame = (IJavaStackFrame)stackFrame;
String name = null;
try {
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) ;
}
}
IJavaSourceLocation[] locations = getSourceLocations();
for (int i = 0; i < locations.length; i++) {
Object sourceElement = locations[i].findSourceElement(name);
if (sourceElement != null) {
return sourceElement;
}
}
} 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;
}
/**
* 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
*/
protected static void collectRequiredProjects(IJavaProject proj, ArrayList 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 .
*
* @param project Java project
* @return a collection of source locations for all required
* projects
* @exception CoreException if an exception occurs reading
* the classpath of the given or any required project
*/
public static IJavaSourceLocation[] getDefaultSourceLocations(IJavaProject project) throws CoreException {
ArrayList list = new ArrayList();
collectRequiredProjects(project, list);
IJavaSourceLocation[] locations = new IJavaSourceLocation[list.size()];
for (int i = 0; i < list.size(); i++) {
locations[i] = new JavaProjectSourceLocation((IJavaProject)list.get(i));
}
return locations;
}
/**
* @see IPersistableSourceLocator#getMemento()
*/
public String getMemento() throws CoreException {
Document doc = new DocumentImpl();
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);
}
try {
return JavaLaunchConfigurationUtils.serializeDocument(doc);
} catch (IOException e) {
abort(LaunchingMessages.getString("JavaSourceLocator.Unable_to_create_memento_for_Java_source_locator._4"), e); //$NON-NLS-1$
}
// execution will not reach here
return null;
}
/**
* @see IPersistableSourceLocator#initializeDefaults(ILaunchConfiguration)
*/
public void initializeDefaults(ILaunchConfiguration configuration) throws CoreException {
IRuntimeClasspathEntry[] entries = JavaRuntime.computeUnresolvedSourceLookupPath(configuration);
IRuntimeClasspathEntry[] resolved = JavaRuntime.resolveSourceLookupPath(entries, configuration);
setSourceLocations(getSourceLocations(resolved));
}
/**
* @see IPersistableSourceLocator#initializeFromMemento(String)
*/
public void initializeFromMemento(String memento) throws CoreException {
Exception ex = null;
try {
Element root = null;
DocumentBuilder parser =
DocumentBuilderFactory.newInstance().newDocumentBuilder();
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.getString("JavaSourceLocator.Unable_to_restore_Java_source_locator_-_invalid_format._6"), null); //$NON-NLS-1$
}
List sourceLocations = new ArrayList();
ClassLoader classLoader = LaunchingPlugin.getDefault().getDescriptor().getPluginClassLoader();
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.getString("JavaSourceLocator.Unable_to_restore_Java_source_locator_-_invalid_format._10"), null); //$NON-NLS-1$
}
Class clazz = null;
try {
clazz = classLoader.loadClass(className);
} catch (ClassNotFoundException e) {
abort(MessageFormat.format(LaunchingMessages.getString("JavaSourceLocator.Unable_to_restore_source_location_-_class_not_found__{0}_11"), new String[] {className}), e); //$NON-NLS-1$
}
IJavaSourceLocation location = null;
try {
location = (IJavaSourceLocation)clazz.newInstance();
} catch (IllegalAccessException e) {
abort(LaunchingMessages.getString("JavaSourceLocator.Unable_to_restore_source_location._12"), e); //$NON-NLS-1$
} catch (InstantiationException e) {
abort(LaunchingMessages.getString("JavaSourceLocator.Unable_to_restore_source_location._12"), e); //$NON-NLS-1$
}
location.initializeFrom(data);
sourceLocations.add(location);
} else {
abort(LaunchingMessages.getString("JavaSourceLocator.Unable_to_restore_Java_source_locator_-_invalid_format._14"), null); //$NON-NLS-1$
}
}
}
setSourceLocations((IJavaSourceLocation[])sourceLocations.toArray(new IJavaSourceLocation[sourceLocations.size()]));
return;
} catch (ParserConfigurationException e) {
ex = e;
} catch (SAXException e) {
ex = e;
} catch (IOException e) {
ex = e;
}
abort(LaunchingMessages.getString("JavaSourceLocator.Exception_occurred_initializing_source_locator._15"), ex); //$NON-NLS-1$
}
/**
* Returns source locations that are associted with the given runtime classpath
* entries.
*/
private static IJavaSourceLocation[] getSourceLocations(IRuntimeClasspathEntry[] entries) {
List 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.exists() && project.isOpen()) {
location = new JavaProjectSourceLocation(JavaCore.create(project));
}
break;
case IRuntimeClasspathEntry.ARCHIVE:
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.getString("JavaSourceLocator.Illegal_to_have_a_container_resolved_to_a_container_1")); //$NON-NLS-1$
}
if (location != null) {
locations.add(location);
}
}
return (IJavaSourceLocation[])locations.toArray(new IJavaSourceLocation[locations.size()]);
}
private boolean isEmpty(String string) {
return string == null || string.length() == 0;
}
/**
* Throws an internal error exception
*/
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);
}
}