package ca.ubc.cs.spl.vebugger.ui.views.variables.details; import org.eclipse.debug.core.DebugException; import org.eclipse.debug.core.DebugPlugin; import org.eclipse.debug.core.model.IDebugElement; import org.eclipse.debug.core.model.IExpression; import org.eclipse.debug.core.model.IThread; import org.eclipse.debug.core.model.IValue; import org.eclipse.debug.core.model.IVariable; import org.eclipse.debug.core.model.IWatchExpressionDelegate; import org.eclipse.debug.core.model.IWatchExpressionListener; import org.eclipse.debug.core.model.IWatchExpressionResult; import org.eclipse.debug.ui.IDetailPane; import org.eclipse.jdt.debug.core.IJavaFieldVariable; import org.eclipse.jdt.debug.core.IJavaObject; import org.eclipse.jdt.debug.core.IJavaPrimitiveValue; import org.eclipse.jdt.debug.core.IJavaVariable; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.TreePath; import org.eclipse.jface.viewers.TreeSelection; import org.eclipse.swt.SWT; import org.eclipse.swt.browser.Browser; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Display; import org.eclipse.ui.IWorkbenchPartSite; import org.omg.CORBA.portable.UnknownException; public class VisualDetailPane implements IDetailPane, IWatchExpressionListener { public static final String VISUAL_DETAIL_PANE_ID = "VisualDetailPane"; private static final String VEBUGGER_RENDERING_METHOD_EXPRESSION = "vebugger.VisualDebuggerAid.toString"; // TODO externalize strings public static final String VISUAL_DETAIL_PANE_NAME = "Visual Debugger"; public static final String VISUAL_DETAIL_PANE_DESCRIPTION = "A visual debugger that allows developers to provide short scripts to visuals objects."; private Browser browser; @Override public void init(IWorkbenchPartSite partSite) { } @Override public Control createControl(Composite parent) { browser = new Browser(parent, SWT.H_SCROLL | SWT.V_SCROLL); browser.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); return browser; } @Override public void dispose() { browser.dispose(); browser = null; } @Override public void display(IStructuredSelection selection) { try { // Clear the text for now browser.setText(""); if (!selection.isEmpty() || !(selection instanceof TreeSelection)) { Object firstElement = selection.getFirstElement(); if (firstElement != null && firstElement instanceof IDebugElement) { IDebugElement element = (IDebugElement) firstElement; if (element instanceof IVariable) { displayVariable(selection, element); } else if (element instanceof IExpression) { displayExpression(element); } else { // FIXME we don't treat other selection types yet throw new DebugException(null); } } } } catch (DebugException e) { setBrowserTextToString("Could not parse " + selection + "\n\n<pre>" + e.getMessage() + "</pre>", "Error"); e.printStackTrace(); } } @Override public boolean setFocus() { return false; } @Override public String getID() { return VISUAL_DETAIL_PANE_ID; } @Override public String getName() { return VISUAL_DETAIL_PANE_NAME; } @Override public String getDescription() { return VISUAL_DETAIL_PANE_DESCRIPTION; } @Override public void watchEvaluationFinished(final IWatchExpressionResult result) { if (result != null) { Display.getDefault().asyncExec(new Runnable() { @SuppressWarnings("synthetic-access") @Override public void run() { try { if (result.hasErrors()) { DebugException e = result.getException(); if (e != null) { throw e; } throw new UnknownException(null); } browser.setText(result.getValue().getValueString()); } catch (DebugException | UnknownException e) { setBrowserTextToString(e.toString(), "Error!"); } } }); } } /** * Render a selected variable. * * @param selection * the selection object * @param element * the variable element * @throws DebugException */ private void displayVariable(IStructuredSelection selection, IDebugElement element) throws DebugException { IValue value = ((IVariable) element).getValue(); if (value instanceof IJavaPrimitiveValue) { setBrowserTextToPrimitive((IJavaPrimitiveValue) value); } else { TreePath firstElementTreePath = ((TreeSelection) selection).getPaths()[0]; String watchExpression = generateWatchExpression(firstElementTreePath); String messageExpression = generateMessageExpression(watchExpression); // Iterate all threads and run our rendering // expression in them in the hopes that we can find // the relevant selection in only one thread // FIXME find a better way to derive the correct thread! IWatchExpressionDelegate delegate = DebugPlugin.getDefault().getExpressionManager() .newWatchExpressionDelegate(element.getModelIdentifier()); for (IThread thread : element.getDebugTarget().getThreads()) { delegate.evaluateExpression(messageExpression, thread, this); } } } /** * Render a watch expression. * * FIXME we don't treat watch expression yet, this will just render their * {@link String} value. * * @param element * the expression element */ private void displayExpression(IDebugElement element) { IValue value = ((IExpression) element).getValue(); if (value instanceof IJavaPrimitiveValue) { setBrowserTextToPrimitive((IJavaPrimitiveValue) value); } else if (value instanceof IJavaObject) { setBrowserTextToString(value.toString()); } else if (value != null) { setBrowserTextToString(value.toString(), "Error!"); } } private void setBrowserTextToString(String text) { browser.setText("<html><body><p>" + text + "</p></body></html>"); } private void setBrowserTextToString(String text, String title) { browser.setText("<html><body><h1>" + title + "</h1><p>" + text + "</p></body></html>"); } private void setBrowserTextToPrimitive(IJavaPrimitiveValue value) { setBrowserTextToString("<pre>" + value + "</pre>"); } /** * Generate a Java expression that represents the selection. * * e.g. if we selected the "foo" field of the 3rd object in an array called * "bar" inside an object called "baz", build "baz.bar[2].foo" * * @param treePath * the selection {@link TreePath} object * @return a Java expression that represents the selection * @throws DebugException */ private String generateWatchExpression(TreePath treePath) throws DebugException { StringBuilder sb = new StringBuilder(); for (int i = 0; i < treePath.getSegmentCount(); i++) { Object part = treePath.getSegment(i); if (part instanceof IJavaFieldVariable) { sb.append('.'); sb.append(((IJavaFieldVariable) part).getName()); } else if (part instanceof IJavaVariable) { sb.append(((IJavaVariable) part).getName()); } } return sb.toString(); } /** * Generate a Java expression that represents a call to the Vebugger * rendering aid. * * e.g. for the expression "foo" generate * "vebugger.VisualDebuggerAid.toString(foo)" * * @param watchExpression * the expression to render * @return a Java expression that represents a call to the Vebugger * rendering aid */ private String generateMessageExpression(String watchExpression) { StringBuilder sb = new StringBuilder(VEBUGGER_RENDERING_METHOD_EXPRESSION).append('(').append(watchExpression) .append(')'); return sb.toString(); } }