blob: 8b6a5bcfef8568cdf8afef23797afe2f9908b8be [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2009, 2019 STMicroelectronics and others.
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Xavier Raynaud <xavier.raynaud@st.com> - initial API and implementation
* Red Hat Inc. - ongoing maintenance
*******************************************************************************/
package org.eclipse.linuxtools.binutils.link2source;
import java.io.File;
import java.net.URI;
import java.util.HashSet;
import java.util.Set;
import org.eclipse.cdt.core.IBinaryParser.IBinaryObject;
import org.eclipse.cdt.core.model.CModelException;
import org.eclipse.cdt.core.model.CoreModel;
import org.eclipse.cdt.core.model.ICProject;
import org.eclipse.cdt.core.model.IOutputEntry;
import org.eclipse.cdt.core.model.ISourceRoot;
import org.eclipse.cdt.debug.core.CDebugCorePlugin;
import org.eclipse.cdt.ui.CUIPlugin;
import org.eclipse.core.filesystem.EFS;
import org.eclipse.core.filesystem.IFileStore;
import org.eclipse.core.filesystem.URIUtil;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceProxy;
import org.eclipse.core.resources.IResourceProxyVisitor;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
import org.eclipse.debug.core.sourcelookup.AbstractSourceLookupDirector;
import org.eclipse.debug.core.sourcelookup.ISourceContainer;
import org.eclipse.debug.core.sourcelookup.containers.LocalFileStorage;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.linuxtools.binutils.utils.STSymbolManager;
import org.eclipse.linuxtools.internal.Activator;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.ide.FileStoreEditorInput;
import org.eclipse.ui.part.FileEditorInput;
import org.eclipse.ui.texteditor.IDocumentProvider;
import org.eclipse.ui.texteditor.ITextEditor;
/**
* This class provides a support for link-to-source
*
*/
public final class STLink2SourceSupport {
private STLink2SourceSupport() {
}
/**
* Open a C Editor at the given location.
*
* @param binaryLoc A path to a binary file.
* @param sourceLoc The location of the source file.
* @param lineNumber The line to open at.
* @return <code>true</code> if the link-to-source was successful,
* <code>false</code> otherwise
*/
private static boolean openSourceFileAtLocation(IPath binaryLoc, IPath sourceLoc, int lineNumber) {
IFile binary = STSymbolManager.sharedInstance.findFileFromPath(binaryLoc);
IProject project = null;
if (binary != null) {
project = binary.getProject();
}
return openFileImpl(project, sourceLoc, lineNumber);
}
/**
* Open a C Editor at the given location.
*
* @param project The parent project.
* @param sourceLoc The location of the source file.
* @param lineNumber The line to open at.
* @return <code>true</code> if the link-to-source was successful, <code>false</code> otherwise
*/
public static boolean openSourceFileAtLocation(IProject project, IPath sourceLoc, int lineNumber) {
return openFileImpl(project, sourceLoc, lineNumber);
}
/**
* Open a C Editor at the given location.
*
* @param binary A binary file.
* @param sourceLoc The location of the source file.
* @param lineNumber The line to open at.
* @return <code>true</code> if the link-to-source was successful, <code>false</code> otherwise
*/
public static boolean openSourceFileAtLocation(IBinaryObject binary, String sourceLoc, int lineNumber) {
if (sourceLoc == null) {
return false;
}
IPath p = new Path(sourceLoc);
return openSourceFileAtLocation(binary.getPath(), p, lineNumber);
}
private static boolean openFileImpl(IProject project, IPath sourceLoc, int lineNumber) {
if (sourceLoc == null || "??".equals(sourceLoc.toString())) { //$NON-NLS-1$
return false;
}
try {
IEditorInput editorInput = getEditorInput(sourceLoc, project);
IWorkbenchPage p = CUIPlugin.getActivePage();
if (p != null) {
if (editorInput == null) {
p.openEditor(new STCSourceNotFoundEditorInput(project, sourceLoc, lineNumber),
STCSourceNotFoundEditor.ID, true);
} else {
IEditorPart editor = p.openEditor(editorInput, CUIPlugin.EDITOR_ID, true);
if (lineNumber > 0 && editor instanceof ITextEditor) {
IDocumentProvider provider = ((ITextEditor) editor).getDocumentProvider();
IDocument document = provider.getDocument(editor.getEditorInput());
try {
int start = document.getLineOffset(lineNumber - 1);
((ITextEditor) editor).selectAndReveal(start, 0);
IWorkbenchPage page = editor.getSite().getPage();
page.activate(editor);
return true;
} catch (BadLocationException x) {
// ignore
}
}
}
}
} catch (PartInitException e) {
}
return false;
}
public static IEditorInput getEditorInput(IPath p, IProject project) {
IFile f = getFileForPath(p, project);
if (f != null && f.exists()) {
return new FileEditorInput(f);
}
if (p.isAbsolute()) {
File file = p.toFile();
if (file.exists()) {
try {
IFileStore ifs = EFS.getStore(file.toURI());
return new FileStoreEditorInput(ifs);
} catch (CoreException e) {
Activator.getDefault().getLog().log(e.getStatus());
}
}
}
return findFileInCommonSourceLookup(p);
}
private static IEditorInput findFileInCommonSourceLookup(IPath path) {
try {
AbstractSourceLookupDirector director = CDebugCorePlugin.getDefault().getCommonSourceLookupDirector();
ISourceContainer[] c = director.getSourceContainers();
for (ISourceContainer sourceContainer : c) {
Object[] o = sourceContainer.findSourceElements(path.toOSString());
for (Object object : o) {
if (object instanceof IFile) {
return new FileEditorInput((IFile) object);
} else if (object instanceof LocalFileStorage) {
LocalFileStorage storage = (LocalFileStorage) object;
IFileStore ifs = EFS.getStore(storage.getFile().toURI());
return new FileStoreEditorInput(ifs);
}
}
}
} catch (CoreException e) {
// do nothing
}
return null;
}
/**
* @param path The path of the file.
* @param project The project to look into.
* @return The file if found, null otherwise.
* @since 5.0
*/
public static IFile getFileForPath(IPath path, IProject project) {
IFile f = getFileForPathImpl(path, project);
if (f == null) {
Set<IProject> allProjects = new HashSet<>();
try {
getAllReferencedProjects(allProjects, project);
} catch (CoreException e) {
Activator.getDefault().getLog().log(e.getStatus());
}
if (allProjects != null) {
for (IProject project2 : allProjects) {
f = getFileForPathImpl(path, project2);
if (f != null) {
break;
}
}
}
}
return f;
}
// Private resource proxy visitor to run through a project's resources to see if
// it contains a link to an element. This allows us to locate the
// project (and it's binary) that has gcov data for a particular resource that has been linked into
// the project. We can't just query the resource for it's project in such a case. This
// is part of the fix for bug: 447554
private static class FindLinkedResourceVisitor implements IResourceProxyVisitor {
final private URI element;
private boolean keepSearching = true;
private boolean found;
private IResource resource;
private String lastLinkPath;
public FindLinkedResourceVisitor(URI element) {
this.element = element;
}
public boolean foundElement() {
return found;
}
public IResource getResource() {
return resource;
}
@Override
public boolean visit(IResourceProxy proxy) {
// To correctly find a file in a linked directory, we cannot just look at the isLinked() attribute
// which is not set for the file but is set for one of its parent directories. So, we keep track
// of linked directories and use them to determine if we should bother getting the resource to compare with.
if (proxy.isLinked()) {
lastLinkPath = proxy.requestFullPath().toString();
}
if (lastLinkPath != null && proxy.requestFullPath().toString().startsWith(lastLinkPath) && proxy.requestResource().getLocationURI().equals(element)) {
found = true;
resource = proxy.requestResource();
keepSearching = false;
}
return keepSearching;
}
}
private static IFile getFileForPathImpl(IPath path, IProject project) {
IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
if (path.isAbsolute()) {
//FIXME EK-LINUXTOOLS: return root.getFileForLocation(path);
return STSymbolManager.sharedInstance.findFileFromPath(path);
}
if (project != null && project.exists()) {
ICProject cproject = CoreModel.getDefault().create(project);
if (cproject != null) {
try {
ISourceRoot[] roots = cproject.getAllSourceRoots();
for (ISourceRoot sourceRoot : roots) {
IContainer r = sourceRoot.getResource();
IResource res = r.findMember(path);
if (res != null && res.exists() && res instanceof IFile) {
return (IFile) res;
}
}
IOutputEntry entries[] = cproject.getOutputEntries();
for (IOutputEntry pathEntry : entries) {
IPath p = pathEntry.getPath();
IResource r = root.findMember(p);
if (r instanceof IContainer) {
IContainer parent = (IContainer) r;
IResource res = parent.findMember(path);
if (res != null && res.exists() && res instanceof IFile) {
return (IFile) res;
}
}
}
} catch (CModelException e) {
Activator.getDefault().getLog().log(e.getStatus());
}
}
}
// no match found...try and see if we are dealing with a link
IPath realPath = project.getLocation().append(path).makeAbsolute();
URI realURI = URIUtil.toURI(realPath.toString());
try {
FindLinkedResourceVisitor visitor = new STLink2SourceSupport.FindLinkedResourceVisitor(realURI);
project.accept(visitor, IResource.DEPTH_INFINITE);
// If we find a match, make note of the target and the real C project.
if (visitor.foundElement()) {
return (IFile) visitor.getResource();
}
} catch (CoreException e) {
}
return null;
}
private static void getAllReferencedProjects(Set<IProject> all, IProject project) throws CoreException {
if (project != null) {
IProject[] refs = project.getReferencedProjects();
for (IProject ref : refs) {
if (!all.contains(ref) && ref.exists() && ref.isOpen()) {
all.add(ref);
getAllReferencedProjects(all, ref);
}
}
}
}
}