package org.erlide.debug.ui.views;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.debug.core.DebugEvent;
import org.eclipse.debug.core.DebugPlugin;
import org.eclipse.debug.core.IDebugEventSetListener;
import org.eclipse.debug.core.ILaunch;
import org.eclipse.debug.core.model.IDebugTarget;
import org.eclipse.debug.ui.AbstractDebugView;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.IToolBarManager;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.viewers.CellLabelProvider;
import org.eclipse.jface.viewers.ColumnPixelData;
import org.eclipse.jface.viewers.IContentProvider;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.TableLayout;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.TreeViewerColumn;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerCell;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.FontMetrics;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.TreeColumn;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PartInitException;
import org.erlide.backend.debug.model.ErlangDebugTarget;
import org.erlide.backend.debug.model.TraceChangedEventData;
import org.erlide.debug.ui.tracing.DebugTraceEvent;
import org.erlide.engine.ErlangEngine;
import org.erlide.engine.model.ErlModelException;
import org.erlide.engine.model.root.IErlElementLocator;
import org.erlide.engine.model.root.IErlModule;
import org.erlide.ui.editors.erl.ErlangEditor;
import org.erlide.ui.editors.util.EditorUtility;
import org.erlide.ui.internal.ErlideUIPlugin;
import org.erlide.ui.util.DisplayUtils;
import org.erlide.util.ErlLogger;
import com.ericsson.otp.erlang.OtpErlangAtom;
import com.ericsson.otp.erlang.OtpErlangLong;
import com.ericsson.otp.erlang.OtpErlangObject;
import com.ericsson.otp.erlang.OtpErlangPid;
import com.ericsson.otp.erlang.OtpErlangRangeException;
import com.ericsson.otp.erlang.OtpErlangTuple;
public class DebuggerTraceView extends AbstractDebugView
implements IDebugEventSetListener {
// Tree:
// Launch
// Node
// Process
// Tuples with ieval (or trace info)
// -record(ieval,
// {level = 1, % Current call level
// line = -1, % Current source code line (of module)
// module, % MFA which called the currently
// function, % interpreted function
// arguments, %
// last_call = false % True if current expression is
// }). % the VERY last to be evaluated
// % (ie at all, not only in a clause)
private final class TreeContentProvider implements ITreeContentProvider {
@Override
public void dispose() {
}
// public void updateChildCount(Object element, int
// currentChildCount) {
//
// int length;
// if (element instanceof MarkerItem)
// length = ((MarkerItem) element).getChildren().length;
// else
// // If it is not a MarkerItem it is the root
// length = ((CachedMarkerBuilder) element).getElements().length;
//
// int markerLimit = MarkerSupportInternalUtilities
// .getMarkerLimit();
// length = markerLimit > 0 ? Math.min(length, markerLimit)
// : length;
// if (currentChildCount == length)
// return;
// viewer.setChildCount(element, length);
//
// }
// public void updateElement(Object parent, int index) {
// MarkerItem newItem;
//
// if (parent instanceof MarkerItem)
// newItem = ((MarkerItem) parent).getChildren()[index];
// else
// newItem = ((CachedMarkerBuilder) parent).getElements()[index];
//
// viewer.replace(parent, index, newItem);
// updateChildCount(newItem, -1);
//
// if (!newItem.isConcrete()
// && categoriesToExpand
// .contains(((MarkerCategory) newItem).getName())) {
// viewer.expandToLevel(newItem, 1);
// categoriesToExpand.remove(newItem);
// }
//
// }
@Override
public Object[] getChildren(final Object parentElement) {
if (parentElement instanceof ILaunch) {
final ILaunch launch = (ILaunch) parentElement;
final List<IDebugTarget> nodes = nodeMap.get(launch);
return nodes.toArray();
} else if (parentElement instanceof IDebugTarget) {
final IDebugTarget node = (IDebugTarget) parentElement;
final List<DebugTraceEvent> events = eventMap.get(node);
return events.toArray();
}
return NO_CHILDREN;
}
@Override
public Object[] getElements(final Object inputElement) {
// if (debugTarget == null) {
// return NO_CHILDREN;
// }
return launches.toArray();
// final List<OtpErlangTuple> traceList =
// debugTarget.getTraceList();
// if (traceList == null) {
// return NO_CHILDREN;
// }
// return traceList.toArray(new Object[traceList.size()]);
}
@Override
public Object getParent(final Object element) {
return parentMap.get(element);
}
@Override
public boolean hasChildren(final Object element) {
if (element instanceof DebugTraceEvent) {
return false;
}
return true;
}
@Override
public void inputChanged(final Viewer theViewer, final Object oldInput,
final Object newInput) {
}
}
public static class ColumnLabelProvider extends CellLabelProvider {
@Override
public void update(final ViewerCell cell) {
final Object element = cell.getElement();
final int columnIndex = cell.getColumnIndex();
String s = null;
if (element instanceof ILaunch) {
switch (columnIndex) {
case 0:
s = "launch";
break;
case 1:
final ILaunch l = (ILaunch) element;
s = l.toString();
break;
default:
s = "";
break;
}
}
if (element instanceof IDebugTarget) {
switch (columnIndex) {
case 0:
s = "node";
break;
case 1:
final IDebugTarget target = (IDebugTarget) element;
s = target.toString();
break;
default:
s = "";
break;
}
}
if (element instanceof DebugTraceEvent) {
final DebugTraceEvent dt = (DebugTraceEvent) element;
final OtpErlangTuple t = dt.getTuple();
final OtpErlangTuple t2 = (OtpErlangTuple) t.elementAt(1);
switch (columnIndex) {
case 0:
final OtpErlangAtom w = (OtpErlangAtom) t.elementAt(0);
final String what = w.atomValue();
s = what;
break;
case 1:
final OtpErlangTuple ieval = (OtpErlangTuple) t2.elementAt(0);
final OtpErlangAtom mod = (OtpErlangAtom) ieval.elementAt(3);
final String module = mod.atomValue();
final OtpErlangLong lin = (OtpErlangLong) ieval.elementAt(2);
s = module;
try {
final int line = lin.intValue();
s += ":" + line; //$NON-NLS-1$
} catch (final OtpErlangRangeException e) {
}
break;
case 2:
default:
final OtpErlangObject o = t2.elementAt(1);
s = o.toString();
break;
}
}
if (s != null) {
cell.setText(s);
}
}
}
protected static final Object[] NO_CHILDREN = new Object[0];
// private static final String DEBUG_TRACE_AS_LAUNCH = "DebugTraceAsLaunch";
// //$NON-NLS-1$
private TreeViewer viewer;
private final List<ILaunch> launches = new ArrayList<>();
private final Map<ILaunch, List<IDebugTarget>> nodeMap = new HashMap<>();
private final Map<IDebugTarget, List<DebugTraceEvent>> eventMap = new HashMap<>();
private final Map<Object, Object> parentMap = new HashMap<>();
// private ErlangDebugTarget debugTarget;
public DebuggerTraceView() {
}
@Override
protected Viewer createViewer(final Composite parent) {
viewer = new TreeViewer(new Tree(parent,
SWT.H_SCROLL | SWT.V_SCROLL | SWT.MULTI | SWT.FULL_SELECTION));
// setViewer(viewer);
// super.createPartControl(parent);
// parent.setLayout(new FillLayout());
viewer.getTree().setLinesVisible(true);
viewer.setUseHashlookup(true);
createColumns();
viewer.setContentProvider(getContentProvider());
viewer.setLabelProvider(new ColumnLabelProvider());
getSite().setSelectionProvider(viewer);
viewer.setInput(this);
DebugPlugin.getDefault().addDebugEventListener(this);
// viewer.getTree().addTreeListener(new TreeAdapter() {
// @Override
// public void treeCollapsed(final TreeEvent e) {
// removeExpandedCategory((MarkerCategory) e.item.getData());
// }
//
// @Override
// public void treeExpanded(final TreeEvent e) {
// addExpandedCategory((MarkerCategory) e.item.getData());
// }
// });
// // Set help on the view itself
// viewer.getControl().addHelpListener(new HelpListener() {
// public void helpRequested(HelpEvent e) {
// Object provider = getAdapter(IContextProvider.class);
// if (provider == null) {
// return;
// }
//
// IContext context = ((IContextProvider) provider)
// .getContext(viewer.getControl());
// PlatformUI.getWorkbench().getHelpSystem().displayHelp(context);
// }
//
// });
viewer.getTree().addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(final SelectionEvent e) {
final Object o = getSelectedInTree();
final String msg = o == null ? "" : o.toString(); //$NON-NLS-1$
getViewSite().getActionBars().getStatusLineManager().setMessage(msg);
}
});
viewer.getTree().addMouseListener(new MouseListener() {
@Override
public void mouseDoubleClick(final MouseEvent e) {
final Object o = getSelectedInTree();
if (o instanceof OtpErlangTuple) {
final OtpErlangTuple t = (OtpErlangTuple) o;
final OtpErlangTuple t2 = (OtpErlangTuple) t.elementAt(1);
final OtpErlangTuple ieval = (OtpErlangTuple) t2.elementAt(0);
final OtpErlangAtom mod = (OtpErlangAtom) ieval.elementAt(3);
final String module = mod.atomValue();
final OtpErlangLong lin = (OtpErlangLong) ieval.elementAt(2);
try {
final int line = lin.intValue();
gotoModuleLine(module, line);
} catch (final OtpErlangRangeException e1) {
}
}
}
@Override
public void mouseDown(final MouseEvent e) {
}
@Override
public void mouseUp(final MouseEvent e) {
}
});
// PlatformUI.getWorkbench().getWorkingSetManager()
// .addPropertyChangeListener(getWorkingSetListener());
return viewer;
// registerContextMenu();
// initDragAndDrop();
}
// @Override
// public void createPartControl(final Composite parent) {
// viewer = new TreeViewer(new Tree(parent, SWT.H_SCROLL | SWT.V_SCROLL
// | SWT.MULTI | SWT.FULL_SELECTION));
// setViewer(viewer);
// super.createPartControl(parent);
// // parent.setLayout(new FillLayout());
//
// viewer.getTree().setLinesVisible(true);
// viewer.setUseHashlookup(true);
//
// createColumns();
//
// viewer.setContentProvider(getContentProvider());
// viewer.setLabelProvider(new ColumnLabelProvider());
// getSite().setSelectionProvider(viewer);
//
// viewer.setInput(this);
// DebugPlugin.getDefault().addDebugEventListener(this);
//
// // viewer.getTree().addTreeListener(new TreeAdapter() {
// // @Override
// // public void treeCollapsed(final TreeEvent e) {
// // removeExpandedCategory((MarkerCategory) e.item.getData());
// // }
// //
// // @Override
// // public void treeExpanded(final TreeEvent e) {
// // addExpandedCategory((MarkerCategory) e.item.getData());
// // }
// // });
//
// // // Set help on the view itself
// // viewer.getControl().addHelpListener(new HelpListener() {
// // public void helpRequested(HelpEvent e) {
// // Object provider = getAdapter(IContextProvider.class);
// // if (provider == null) {
// // return;
// // }
// //
// // IContext context = ((IContextProvider) provider)
// // .getContext(viewer.getControl());
// // PlatformUI.getWorkbench().getHelpSystem().displayHelp(context);
// // }
// //
// // });
//
// viewer.getTree().addSelectionListener(new SelectionAdapter() {
// @Override
// public void widgetSelected(final SelectionEvent e) {
// final Object o = getSelectedInTree();
// final String msg = o == null ? "" : o.toString(); //$NON-NLS-1$
// getViewSite().getActionBars().getStatusLineManager()
// .setMessage(msg);
//
// }
// });
//
// viewer.getTree().addMouseListener(new MouseListener() {
//
// public void mouseDoubleClick(final MouseEvent e) {
// final Object o = getSelectedInTree();
// if (o instanceof OtpErlangTuple) {
// final OtpErlangTuple t = (OtpErlangTuple) o;
// final OtpErlangTuple t2 = (OtpErlangTuple) t.elementAt(1);
// final OtpErlangTuple ieval = (OtpErlangTuple) t2
// .elementAt(0);
// final OtpErlangAtom mod = (OtpErlangAtom) ieval
// .elementAt(3);
// final String module = mod.atomValue();
// final OtpErlangLong lin = (OtpErlangLong) ieval
// .elementAt(2);
// try {
// final int line = lin.intValue();
// gotoModuleLine(module, line);
// } catch (final OtpErlangRangeException e1) {
// }
//
// }
// }
//
// public void mouseDown(final MouseEvent e) {
// }
//
// public void mouseUp(final MouseEvent e) {
// }
//
// });
// // PlatformUI.getWorkbench().getWorkingSetManager()
// // .addPropertyChangeListener(getWorkingSetListener());
//
// // registerContextMenu();
// // initDragAndDrop();
//
// }
protected void gotoModuleLine(final String moduleName, final int line) {
final IWorkbenchWindow window = ErlideUIPlugin.getActiveWorkbenchWindow();
if (window == null) {
return;
}
final IWorkbenchPage page = window.getActivePage();
if (page == null) {
return;
}
IEditorPart part = null;
final IErlElementLocator model = ErlangEngine.getInstance().getModel();
IErlModule module;
try {
module = model.findModule(moduleName);
} catch (final ErlModelException e) {
ErlLogger.error(e);
return;
}
IEditorInput input = null;
input = EditorUtility.getEditorInput(module);
if (input != null) {
final String editorId = EditorUtility.getEditorID(input, module);
if (editorId != null) {
try {
part = page.openEditor(input, editorId);
} catch (final PartInitException e) {
ErlideUIPlugin.errorDialog(window.getShell(), "Go to File",
"Exception occurred", e); //
}
}
}
if (part instanceof ErlangEditor) {
part.setFocus();
final ErlangEditor ee = (ErlangEditor) part;
final IDocument d = ee.getDocument();
int lineStart, lineLength;
try {
lineStart = d.getLineOffset(line - 1);
lineLength = d.getLineLength(line - 1);
EditorUtility.revealInEditor(ee, lineStart, lineLength - 1);
} catch (final BadLocationException e) {
ErlLogger.error(e);
}
}
}
private IContentProvider getContentProvider() {
return new TreeContentProvider();
}
private void createColumns() {
final Tree tree = viewer.getTree();
final TableLayout layout = new TableLayout();
TreeViewerColumn column;
final String[] names = { "Kind", "Function", //$NON-NLS-1$ //$NON-NLS-2$
org.erlide.debug.ui.views.ActionMessages
.getString("DebuggerTraceView.5") }; //$NON-NLS-1$
for (final String name : names) {
column = new TreeViewerColumn(viewer, SWT.NONE);
final TreeColumn treeColumn = column.getColumn();
treeColumn.setResizable(true);
treeColumn.setMoveable(true);
treeColumn.addSelectionListener(new SelectionListener() {
@Override
public void widgetDefaultSelected(final SelectionEvent e) {
}
@Override
public void widgetSelected(final SelectionEvent e) {
}
});
// column.getColumn().setData(MARKER_FIELD, markerField);
// Show the help in the first column
column.setLabelProvider(new ColumnLabelProvider());
treeColumn.setText(name);
treeColumn.setToolTipText(name);
}
// column = new TreeViewerColumn(viewer, SWT.NONE);
// treeColumn = column.getColumn();
// treeColumn.setResizable(true);
// treeColumn.setMoveable(true);
// column.setLabelProvider(new ColumnLabelProvider());
// column.getColumn().setImage(markerField.getColumnHeaderImage());
// final EditingSupport support = markerField
// .getEditingSupport(viewer);
// if (support != null) {
// column.setEditingSupport(support);
// }
// if (builder.getPrimarySortField().equals(markerField)) {
// updateDirectionIndicator(column.getColumn(), markerField);
// }
int columnWidth = -1;
for (int i = 0; i < names.length; ++i) {
if (i == 0) {
// Compute and store a font metric
final GC gc = new GC(tree);
gc.setFont(tree.getFont());
final FontMetrics fontMetrics = gc.getFontMetrics();
gc.dispose();
columnWidth = Math.max(100, fontMetrics.getAverageCharWidth() * 20);
}
// if (columnWidths != null) {
// final Integer value = columnWidths.getInteger(getFieldId(column
// .getColumn()));
//
// // Make sure we get a useful value
// if (value != null && value.intValue() > 0) {
// columnWidth = value.intValue();
// }
// }
// // Take trim into account if we are using the default value, but
// not
// // if it is restored.
// if (columnWidth < 0) {
// layout.addColumnData(new ColumnPixelData(markerField
// .getDefaultColumnWidth(tree), true, true));
// } else {
layout.addColumnData(new ColumnPixelData(columnWidth, true));
// }
}
// }
// Remove extra columns
// if (currentColumns.length > fields.length) {
// for (int i = fields.length; i < currentColumns.length; i++) {
// currentColumns[i].dispose();
//
// }
// }
viewer.getTree().setLayout(layout);
tree.setLinesVisible(true);
tree.setHeaderVisible(true);
tree.layout(true);
}
@Override
public void setFocus() {
}
@Override
public void handleDebugEvents(final DebugEvent[] events) {
for (final DebugEvent event : events) {
if (event.getKind() == DebugEvent.MODEL_SPECIFIC
&& event.getDetail() == ErlangDebugTarget.TRACE_CHANGED) {
final Object source = event.getSource();
if (source instanceof ErlangDebugTarget) {
final TraceChangedEventData data = (TraceChangedEventData) event
.getData();
traceChanged(data, source);
}
}
}
}
private void traceChanged(final TraceChangedEventData data, final Object source) {
if (viewer == null || viewer.getControl().isDisposed()) {
return;
}
DisplayUtils.asyncExec(new Runnable() {
@Override
public void run() {
if (viewer == null || viewer.getControl().isDisposed()) {
return;
}
// if (viewer.getInput() != source) {
// viewer.setInput(source);
// viewer.refresh();
// } else {
if (data.getWhat() == TraceChangedEventData.ADDED) {
final ILaunch launch = data.getLaunch();
if (!launches.contains(launch)) {
launches.add(launch);
viewer.add(viewer.getInput(), launch);
parentMap.put(launch, viewer.getInput());
}
List<IDebugTarget> nodes = nodeMap.get(launch);
if (nodes == null) {
nodes = new ArrayList<>(1);
nodeMap.put(launch, nodes);
}
final IDebugTarget node = data.getNode();
if (!nodes.contains(node)) {
nodes.add(node);
viewer.add(launch, node);
parentMap.put(node, launch);
}
List<DebugTraceEvent> events = eventMap.get(node);
if (events == null) {
events = new ArrayList<>(data.getEvents().length);
eventMap.put(node, events);
}
final OtpErlangPid pid = data.getPid();
for (final OtpErlangTuple t : data.getEvents()) {
events.add(new DebugTraceEvent(pid, t));
parentMap.put(t, node);
}
viewer.add(node, data.getEvents());
}
// }
}
});
}
Object getSelectedInTree() {
final ISelection selection = viewer.getSelection();
if (selection instanceof IStructuredSelection) {
final IStructuredSelection ss = (IStructuredSelection) selection;
if (ss.size() == 1) {
return ss.getFirstElement();
}
}
return null;
}
@Override
protected void configureToolBar(final IToolBarManager tbm) {
}
@Override
protected void createActions() {
}
@Override
protected void fillContextMenu(final IMenuManager menu) {
}
@Override
protected String getHelpContextId() {
return null;
}
public List<DebugTraceEvent> getEventsForLaunch(final IDebugTarget target) {
return eventMap.get(target);
}
}