blob: 3b9e894f031e76ec13abc41053bec9f24d66da16 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2019 Ericsson
*
* All rights reserved. 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
*******************************************************************************/
package org.eclipse.tracecompass.tmf.ui.actions;
import java.io.File;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import org.eclipse.core.filesystem.EFS;
import org.eclipse.core.filesystem.IFileStore;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceVisitor;
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.jface.action.Action;
import org.eclipse.jface.action.ActionContributionItem;
import org.eclipse.jface.action.IContributionItem;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.viewers.ArrayContentProvider;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.tracecompass.internal.tmf.ui.Messages;
import org.eclipse.tracecompass.tmf.core.event.lookup.ITmfCallsite;
import org.eclipse.tracecompass.tmf.core.event.lookup.ITmfSourceLookup;
import org.eclipse.tracecompass.tmf.ui.project.model.TraceUtils;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.dialogs.ListDialog;
import org.eclipse.ui.ide.IDE;
import org.eclipse.ui.texteditor.ITextEditor;
/**
* Open source code action. Original version was in events table, but it is more
* decoupled now to be able to be used in other views.
*
* @author Matthew Khouzam
* @since 5.1
*/
public class OpenSourceCodeAction extends Action {
private static final String EMPTY_STRING = ""; //$NON-NLS-1$
private final ITmfCallsite fCallsite;
private final Shell fShell;
/**
* Builder
*
* @param actionText
* "open" message recommended to be "lookup" if the location can be
* erroneous and open if it's accurate.
* @param sourceLookup
* the source code to lookup
* @param shell
* the parent shell for source file dialog
* @return an contribution item to open a callsite or null if invalid
*/
public static IContributionItem create(String actionText, ITmfSourceLookup sourceLookup, Shell shell) {
List<ITmfCallsite> cs = sourceLookup.getCallsites();
if (cs == null) {
return null;
}
List<ITmfCallsite> callsites = cs.stream().filter(callstack -> callstack.getLineNo() != null).collect(Collectors.toList());
if (callsites.isEmpty()) {
/* Not enough information to provide a full callsite */
return null;
}
if (callsites.size() == 1) {
return new ActionContributionItem(new OpenSourceCodeAction(actionText, callsites.get(0), shell));
}
MenuManager mgr = new MenuManager(actionText);
for (ITmfCallsite callsite : callsites) {
mgr.add(new OpenSourceCodeAction(callsite.toString(), callsite, shell));
}
return mgr;
}
/**
* Open Source Code Action Constructor
*
* @param text
* text to display
* @param callsite
* the callsite
* @param shell
* the shell
* @since 5.2
*/
public OpenSourceCodeAction(String text, ITmfCallsite callsite, Shell shell) {
super(text);
fCallsite = callsite;
fShell = shell;
}
@Override
public void run() {
ITmfCallsite cs = fCallsite;
if (cs == null) {
return;
}
String fileName = cs.getFileName();
Long lineNo = cs.getLineNo();
if (lineNo == null) {
/* Not enough information to provide a full callsite */
return;
}
final String trimmedPath = fileName.replaceAll("\\.\\./", EMPTY_STRING); //$NON-NLS-1$
File fileToOpen = new File(trimmedPath);
try {
if (fileToOpen.exists() && fileToOpen.isFile()) {
/*
* The path points to a "real" file, attempt to open that
*/
IFileStore fileStore = EFS.getLocalFileSystem().getStore(fileToOpen.toURI());
IWorkbenchPage page = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();
IEditorPart editor = IDE.openEditorOnFileStore(page, fileStore);
if (editor instanceof ITextEditor) {
/*
* Calculate the "document offset" corresponding to the line
* number, then seek there.
*/
ITextEditor textEditor = (ITextEditor) editor;
int lineNumber = lineNo.intValue();
IDocument document = textEditor.getDocumentProvider().getDocument(textEditor.getEditorInput());
IRegion region = document.getLineInformation(lineNumber - 1);
if (region != null) {
textEditor.selectAndReveal(region.getOffset(), region.getLength());
}
}
} else {
/*
* The file was not found on disk, attempt to find it in the
* workspace instead.
*/
IMarker marker = null;
final ArrayList<IFile> files = new ArrayList<>();
IPath p = new Path(trimmedPath);
ResourcesPlugin.getWorkspace().getRoot().accept(new IResourceVisitor() {
@Override
public boolean visit(IResource resource) throws CoreException {
if (resource instanceof IFile && resource.getFullPath().toString().endsWith(p.lastSegment())) {
files.add((IFile) resource);
}
return true;
}
});
IFile file = null;
if (files.size() > 1) {
ListDialog dialog = new ListDialog(fShell);
dialog.setContentProvider(ArrayContentProvider.getInstance());
dialog.setLabelProvider(new LabelProvider() {
@Override
public String getText(Object element) {
return ((IFile) element).getFullPath().toString();
}
});
dialog.setInput(files);
dialog.setTitle(Messages.TmfSourceLookup_OpenSourceCodeSelectFileDialogTitle);
dialog.setMessage(Messages.TmfSourceLookup_OpenSourceCodeSelectFileDialogTitle + '\n' + cs.toString());
dialog.open();
Object[] result = dialog.getResult();
if (result != null && result.length > 0) {
file = (IFile) result[0];
}
} else if (files.size() == 1) {
file = files.get(0);
}
if (file != null) {
marker = file.createMarker(IMarker.MARKER);
marker.setAttribute(IMarker.LINE_NUMBER, lineNo.intValue());
IDE.openEditor(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage(), marker);
marker.delete();
} else if (files.isEmpty()) {
TraceUtils.displayWarningMsg(new FileNotFoundException('\'' + cs.toString() + '\'' + '\n' + Messages.TmfSourceLookup_OpenSourceCodeNotFound));
}
}
} catch (BadLocationException | CoreException e) {
TraceUtils.displayErrorMsg(e);
}
}
}