/*******************************************************************************
* Copyright (c) 2000, 2010 QNX Software Systems 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:
* QNX Software Systems - Initial API and implementation
* Sergey Prigogin (Google)
*******************************************************************************/
package org.eclipse.cdt.debug.internal.core.sourcelookup;
import java.io.File;
import java.io.IOException;
import java.io.StringReader;
import com.ibm.icu.text.MessageFormat;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerException;
import org.eclipse.cdt.core.model.CoreModel;
import org.eclipse.cdt.core.model.ICProject;
import org.eclipse.cdt.core.model.ITranslationUnit;
import org.eclipse.cdt.debug.core.CDebugCorePlugin;
import org.eclipse.cdt.debug.core.CDebugUtils;
import org.eclipse.cdt.debug.core.ICDTLaunchConfigurationConstants;
import org.eclipse.cdt.debug.core.sourcelookup.CProjectSourceContainer;
import org.eclipse.cdt.debug.core.sourcelookup.ICSourceLocation;
import org.eclipse.cdt.debug.core.sourcelookup.IDirectorySourceLocation;
import org.eclipse.cdt.debug.core.sourcelookup.IMappingSourceContainer;
import org.eclipse.cdt.debug.core.sourcelookup.IProjectSourceLocation;
import org.eclipse.cdt.debug.core.sourcelookup.MappingSourceContainer;
import org.eclipse.cdt.internal.core.resources.ResourceLookup;
import org.eclipse.core.filesystem.URIUtil;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
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.ILaunchConfiguration;
import org.eclipse.debug.core.sourcelookup.ISourceContainer;
import org.eclipse.debug.core.sourcelookup.ISourceLookupDirector;
import org.eclipse.debug.core.sourcelookup.containers.DirectorySourceContainer;
import org.eclipse.debug.core.sourcelookup.containers.LocalFileStorage;
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;
public class SourceUtils {
private static final String NAME_COMMON_SOURCE_LOCATIONS = "commonSourceLocations"; //$NON-NLS-1$
private static final String NAME_SOURCE_LOCATION = "sourceLocation"; //$NON-NLS-1$
private static final String ATTR_CLASS = "class"; //$NON-NLS-1$
private static final String ATTR_MEMENTO = "memento"; //$NON-NLS-1$
public static String getCommonSourceLocationsMemento(ICSourceLocation[] locations) {
Document document = null;
Throwable ex = null;
try {
document = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
Element element = document.createElement(NAME_COMMON_SOURCE_LOCATIONS);
document.appendChild(element);
saveSourceLocations(document, element, locations);
return CDebugUtils.serializeDocument(document);
} catch (ParserConfigurationException e) {
ex = e;
} catch (IOException e) {
ex = e;
} catch (TransformerException e) {
ex = e;
}
CDebugCorePlugin.log(new Status(IStatus.ERROR, CDebugCorePlugin.getUniqueIdentifier(), 0,
"Error saving common source settings.", ex)); //$NON-NLS-1$
return null;
}
private static void saveSourceLocations(Document doc, Element node, ICSourceLocation[] locations) {
for(int i = 0; i < locations.length; i++) {
Element child = doc.createElement(NAME_SOURCE_LOCATION);
child.setAttribute(ATTR_CLASS, locations[i].getClass().getName());
try {
child.setAttribute(ATTR_MEMENTO, locations[i].getMemento());
} catch (CoreException e) {
CDebugCorePlugin.log(e);
continue;
}
node.appendChild(child);
}
}
public static ICSourceLocation[] getCommonSourceLocationsFromMemento(String memento) {
ICSourceLocation[] result = new ICSourceLocation[0];
if (!isEmpty(memento)) {
try {
DocumentBuilder parser = DocumentBuilderFactory.newInstance().newDocumentBuilder();
StringReader reader = new StringReader(memento);
InputSource source = new InputSource(reader);
Element root = parser.parse(source).getDocumentElement();
if (root.getNodeName().equalsIgnoreCase(NAME_COMMON_SOURCE_LOCATIONS))
result = initializeSourceLocations(root);
} catch (ParserConfigurationException e) {
CDebugCorePlugin.log(new Status(IStatus.ERROR, CDebugCorePlugin.getUniqueIdentifier(), 0,
"Error initializing common source settings.", e)); //$NON-NLS-1$
} catch (SAXException e) {
CDebugCorePlugin.log(new Status(IStatus.ERROR, CDebugCorePlugin.getUniqueIdentifier(), 0,
"Error initializing common source settings.", e)); //$NON-NLS-1$
} catch (IOException e) {
CDebugCorePlugin.log(new Status(IStatus.ERROR, CDebugCorePlugin.getUniqueIdentifier(), 0,
"Error initializing common source settings.", e)); //$NON-NLS-1$
}
}
return result;
}
public static ICSourceLocation[] initializeSourceLocations(Element root) {
List<ICSourceLocation> sourceLocations = new LinkedList<ICSourceLocation>();
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(NAME_SOURCE_LOCATION)) {
String className = entry.getAttribute(ATTR_CLASS);
String data = entry.getAttribute(ATTR_MEMENTO);
if (className == null || className.trim().length() == 0) {
CDebugCorePlugin.log("Unable to restore common source locations - invalid format."); //$NON-NLS-1$
continue;
}
Class<?> clazz = null;
try {
clazz = CDebugCorePlugin.getDefault().getBundle().loadClass(className);
} catch (ClassNotFoundException e) {
CDebugCorePlugin.log(MessageFormat.format("Unable to restore source location - class not found {0}", //$NON-NLS-1$
(Object[]) new String[] { className }));
continue;
}
ICSourceLocation location = null;
try {
location = (ICSourceLocation)clazz.newInstance();
} catch (IllegalAccessException e) {
CDebugCorePlugin.log("Unable to restore source location: " + e.getMessage()); //$NON-NLS-1$
continue;
} catch (InstantiationException e) {
CDebugCorePlugin.log("Unable to restore source location: " + e.getMessage()); //$NON-NLS-1$
continue;
}
try {
location.initializeFrom(data);
sourceLocations.add(location);
} catch (CoreException e) {
CDebugCorePlugin.log("Unable to restore source location: " + e.getMessage()); //$NON-NLS-1$
}
}
}
}
return sourceLocations.toArray(new ICSourceLocation[sourceLocations.size()]);
}
private static boolean isEmpty(String string) {
return string == null || string.trim().length() == 0;
}
static public ISourceContainer[] convertSourceLocations(ICSourceLocation[] locations) {
ArrayList<ISourceContainer> containers = new ArrayList<ISourceContainer>(locations.length);
int mappingCount = 0;
for (ICSourceLocation location : locations) {
if (location instanceof IProjectSourceLocation) {
containers.add(new CProjectSourceContainer(((IProjectSourceLocation) location).getProject(), false));
} else if (location instanceof IDirectorySourceLocation) {
IDirectorySourceLocation d = (IDirectorySourceLocation) location;
IPath a = d.getAssociation();
if (a != null) {
MappingSourceContainer mapping = new MappingSourceContainer(InternalSourceLookupMessages.SourceUtils_0 + (++mappingCount));
mapping.addMapEntries(new MapEntrySourceContainer[] { new MapEntrySourceContainer(a, d.getDirectory()) });
containers.add(mapping);
}
containers.add(new DirectorySourceContainer(d.getDirectory(), d.searchSubfolders()));
}
}
return containers.toArray(new ISourceContainer[containers.size()]);
}
static IPath getCompilationPath(ISourceContainer container, String sourceName) {
if (container instanceof IMappingSourceContainer) {
return ((IMappingSourceContainer) container).getCompilationPath(sourceName);
}
try {
for (ISourceContainer cont : container.getSourceContainers()) {
IPath path = getCompilationPath(cont, sourceName);
if (path != null)
return path;
}
} catch (CoreException e) {
}
return null;
}
public static IProject[] getAllReferencedProjects(IProject project) throws CoreException {
Set<IProject> all = new HashSet<IProject>();
getAllReferencedProjects(all, project);
return all.toArray(new IProject[all.size()]);
}
private static void getAllReferencedProjects(Set<IProject> all, IProject project) throws CoreException {
for (IProject ref : project.getReferencedProjects()) {
if (!all.contains(ref) && ref.exists() && ref.isOpen()) {
all.add(ref);
getAllReferencedProjects(all, ref);
}
}
}
/**
* Returns the project from the launch configuration, or {@code null} if it's not available.
*/
public static IProject getLaunchConfigurationProject(ISourceLookupDirector director) {
String name = getLaunchConfigurationProjectName(director);
return name != null ? ResourcesPlugin.getWorkspace().getRoot().getProject(name) : null;
}
/**
* Returns the project name from the launch configuration, or {@code null} if it's not available.
*/
public static String getLaunchConfigurationProjectName(ISourceLookupDirector director) {
ILaunchConfiguration config = director.getLaunchConfiguration();
if (config != null) {
try {
String name = config.getAttribute(ICDTLaunchConfigurationConstants.ATTR_PROJECT_NAME, ""); //$NON-NLS-1$
if (name.length() > 0)
return name;
} catch (CoreException e) {
CDebugCorePlugin.log(e);
}
}
return null;
}
/**
* Returns source elements corresponding to a file.
* @param file A source or header file.
* @param director A source lookup director.
* @return An array of source elements sorted in relevance order. The elements of the array can
* be either instances of IFile, ITranslationUnit or LocalFileStorage. The returned array can be empty if
* no source elements match the given file.
*/
public static Object[] findSourceElements(File file, ISourceLookupDirector director) {
IFile[] wfiles = ResourceLookup.findFilesForLocation(new Path(file.getAbsolutePath()));
IProject lcProject = null;
if (director != null) {
lcProject = getLaunchConfigurationProject(director);
}
if (wfiles.length > 0) {
ResourceLookup.sortFilesByRelevance(wfiles, lcProject);
return updateUnavailableResources(wfiles, lcProject);
}
try {
// Check the canonical path as well to support case insensitive file
// systems like Windows.
wfiles = ResourceLookup.findFilesForLocation(new Path(file.getCanonicalPath()));
if (wfiles.length > 0) {
ResourceLookup.sortFilesByRelevance(wfiles, lcProject);
return updateUnavailableResources(wfiles, lcProject);
}
// The file is not already in the workspace so try to create an external translation unit for it.
if (lcProject != null) {
ICProject project = CoreModel.getDefault().create(lcProject);
if (project != null) {
return new ITranslationUnit[] { CoreModel.getDefault().createTranslationUnitFrom(project, URIUtil.toURI(file.getCanonicalPath(), true)) };
}
}
} catch (IOException e) { // ignore if getCanonicalPath throws
}
// If we can't create an ETU then fall back on LocalFileStorage.
return new LocalFileStorage[] { new LocalFileStorage(file) };
}
/**
* Check for IFile to be available in workspace ( see {@link IFile#isAccessible()} ).
* Unavailable resources are replaced with {@link ITranslationUnit}
* @param wfiles
* @param project
* @return
*/
private static Object[] updateUnavailableResources(IFile[] wfiles, IProject project){
// with no projects context we will not be able to create ITranslationUnits
if (project == null) {
return wfiles;
}
ICProject cProject = CoreModel.getDefault().create(project);
Object[] result = new Object[wfiles.length];
for (int i=0; i< wfiles.length; ++i) {
IFile wkspFile = wfiles[i];
if (wkspFile.isAccessible()) {
result[i] = wkspFile;
} else {
result[i] = CoreModel.getDefault().createTranslationUnitFrom(cProject, wkspFile.getLocationURI());
}
}
return result;
}
}