/******************************************************************************* * Copyright (c) 2000, 2016 IBM Corporation 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: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.jdt.internal.debug.ui.actions; import java.lang.reflect.InvocationTargetException; import java.util.Iterator; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.Status; import org.eclipse.debug.core.DebugEvent; import org.eclipse.debug.core.DebugException; import org.eclipse.debug.core.ILaunch; import org.eclipse.debug.core.model.IStackFrame; import org.eclipse.debug.core.model.IValue; import org.eclipse.debug.ui.DebugUITools; import org.eclipse.debug.ui.IDebugModelPresentation; import org.eclipse.debug.ui.IDebugUIConstants; import org.eclipse.debug.ui.IDebugView; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.debug.core.IJavaDebugTarget; import org.eclipse.jdt.debug.core.IJavaObject; import org.eclipse.jdt.debug.core.IJavaStackFrame; import org.eclipse.jdt.debug.core.IJavaThread; import org.eclipse.jdt.debug.core.IJavaValue; import org.eclipse.jdt.debug.core.IJavaVariable; import org.eclipse.jdt.debug.core.JDIDebugModel; import org.eclipse.jdt.debug.eval.IEvaluationEngine; import org.eclipse.jdt.debug.eval.IEvaluationListener; import org.eclipse.jdt.debug.eval.IEvaluationResult; import org.eclipse.jdt.debug.ui.IJavaDebugUIConstants; import org.eclipse.jdt.internal.debug.core.JDIDebugPlugin; import org.eclipse.jdt.internal.debug.core.JavaDebugUtils; import org.eclipse.jdt.internal.debug.ui.EvaluationContextManager; import org.eclipse.jdt.internal.debug.ui.JDIDebugUIPlugin; import org.eclipse.jdt.internal.debug.ui.JavaWordFinder; import org.eclipse.jdt.internal.debug.ui.display.IDataDisplay; import org.eclipse.jdt.internal.debug.ui.display.JavaInspectExpression; import org.eclipse.jdt.internal.debug.ui.snippeteditor.ISnippetStateChangedListener; import org.eclipse.jdt.internal.debug.ui.snippeteditor.JavaSnippetEditor; import org.eclipse.jface.action.IAction; import org.eclipse.jface.dialogs.ErrorDialog; import org.eclipse.jface.operation.IRunnableWithProgress; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.IRegion; import org.eclipse.jface.text.ITextSelection; import org.eclipse.jface.text.ITextViewer; import org.eclipse.jface.text.Region; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.ISelectionProvider; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.osgi.util.NLS; import org.eclipse.swt.custom.StyledText; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Shell; import org.eclipse.ui.IEditorActionDelegate; import org.eclipse.ui.IEditorInput; import org.eclipse.ui.IEditorPart; import org.eclipse.ui.IObjectActionDelegate; import org.eclipse.ui.IPartListener; import org.eclipse.ui.IViewActionDelegate; import org.eclipse.ui.IViewPart; import org.eclipse.ui.IWorkbench; import org.eclipse.ui.IWorkbenchPage; import org.eclipse.ui.IWorkbenchPart; import org.eclipse.ui.IWorkbenchWindow; import org.eclipse.ui.IWorkbenchWindowActionDelegate; import org.eclipse.ui.PartInitException; import org.eclipse.ui.texteditor.ITextEditor; import com.sun.jdi.InvocationException; import com.sun.jdi.ObjectReference; /** * Action to do simple code evaluation. The evaluation * is done in the UI thread and the expression and result are * displayed using the IDataDisplay. */ public abstract class EvaluateAction implements IEvaluationListener, IWorkbenchWindowActionDelegate, IObjectActionDelegate, IEditorActionDelegate, IPartListener, IViewActionDelegate, ISnippetStateChangedListener { private IAction fAction; private IWorkbenchPart fTargetPart; private IWorkbenchWindow fWindow; private Object fSelection; private IRegion fRegion; /** * Is the action waiting for an evaluation. */ private boolean fEvaluating; /** * The new target part to use with the evaluation completes. */ private IWorkbenchPart fNewTargetPart= null; /** * Used to resolve editor input for selected stack frame */ private IDebugModelPresentation fPresentation; public EvaluateAction() { super(); } /** * Returns the 'object' context for this evaluation, * or <code>null</code> if none. If the evaluation is being performed * in the context of the variables view/inspector. Then * perform the evaluation in the context of the * selected value. * * @return Java object or <code>null</code> */ protected IJavaObject getObjectContext() { IWorkbenchPage page= JDIDebugUIPlugin.getActivePage(); if (page != null) { IWorkbenchPart activePart= page.getActivePart(); if (activePart != null) { IDebugView a = activePart.getAdapter(IDebugView.class); if (a != null) { if (a.getViewer() != null) { ISelection s = a.getViewer().getSelection(); if (s instanceof IStructuredSelection) { IStructuredSelection structuredSelection = (IStructuredSelection)s; if (structuredSelection.size() == 1) { Object selection= structuredSelection.getFirstElement(); if (selection instanceof IJavaVariable) { IJavaVariable var = (IJavaVariable)selection; // if 'this' is selected, use stack frame context try { if (!var.getName().equals("this")) { //$NON-NLS-1$ IValue value= var.getValue(); if (value instanceof IJavaObject) { return (IJavaObject)value; } } } catch (DebugException e) { JDIDebugUIPlugin.log(e); } } else if (selection instanceof JavaInspectExpression) { IValue value= ((JavaInspectExpression)selection).getValue(); if (value instanceof IJavaObject) { return (IJavaObject)value; } } } } } } } } return null; } /** * Finds the currently selected stack frame in the UI. * Stack frames from a scrapbook launch are ignored. */ protected IJavaStackFrame getStackFrameContext() { IWorkbenchPart part = getTargetPart(); IJavaStackFrame frame = null; if (part == null) { frame = EvaluationContextManager.getEvaluationContext(getWindow()); } else { frame = EvaluationContextManager.getEvaluationContext(part); } return frame; } /** * @see IEvaluationListener#evaluationComplete(IEvaluationResult) */ @Override public void evaluationComplete(final IEvaluationResult result) { // if plug-in has shutdown, ignore - see bug# 8693 if (JDIDebugUIPlugin.getDefault() == null) { return; } final IJavaValue value= result.getValue(); if (result.hasErrors() || value != null) { final Display display= JDIDebugUIPlugin.getStandardDisplay(); if (display.isDisposed()) { return; } displayResult(result); } } protected void evaluationCleanup() { setEvaluating(false); setTargetPart(fNewTargetPart); } /** * Display the given evaluation result. */ abstract protected void displayResult(IEvaluationResult result); protected void run() { // eval in context of object or stack frame final IJavaObject object = getObjectContext(); final IJavaStackFrame stackFrame= getStackFrameContext(); if (stackFrame == null) { reportError(ActionMessages.Evaluate_error_message_stack_frame_context); return; } // check for nested evaluation IJavaThread thread = (IJavaThread)stackFrame.getThread(); if (thread.isPerformingEvaluation()) { reportError(ActionMessages.EvaluateAction_Cannot_perform_nested_evaluations__1); return; } setNewTargetPart(getTargetPart()); IRunnableWithProgress runnable = new IRunnableWithProgress() { @Override public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException { if (stackFrame.isSuspended()) { IJavaProject project = getJavaProject(stackFrame); if (project != null) { IEvaluationEngine engine = null; try { Object selection= getSelectedObject(); if (!(selection instanceof String)) { return; } String expression= (String)selection; engine = JDIDebugPlugin.getDefault().getEvaluationEngine(project, (IJavaDebugTarget)stackFrame.getDebugTarget()); setEvaluating(true); boolean hitBreakpoints= Platform.getPreferencesService().getBoolean( JDIDebugPlugin.getUniqueIdentifier(), JDIDebugModel.PREF_SUSPEND_FOR_BREAKPOINTS_DURING_EVALUATION, true, null); if (object == null) { engine.evaluate(expression, stackFrame, EvaluateAction.this, DebugEvent.EVALUATION, hitBreakpoints); } else { engine.evaluate(expression, object, (IJavaThread)stackFrame.getThread(), EvaluateAction.this, DebugEvent.EVALUATION, hitBreakpoints); } return; } catch (CoreException e) { throw new InvocationTargetException(e, getExceptionMessage(e)); } } throw new InvocationTargetException(null, ActionMessages.Evaluate_error_message_src_context); } // thread not suspended throw new InvocationTargetException(null, ActionMessages.EvaluateAction_Thread_not_suspended___unable_to_perform_evaluation__1); } }; IWorkbench workbench = JDIDebugUIPlugin.getDefault().getWorkbench(); try { workbench.getProgressService().busyCursorWhile(runnable); } catch (InvocationTargetException e) { evaluationCleanup(); String message = e.getMessage(); if (message == null) { message = e.getClass().getName(); if (e.getCause() != null) { message = e.getCause().getClass().getName(); if (e.getCause().getMessage() != null) { message = e.getCause().getMessage(); } } } reportError(message); } catch (InterruptedException e) { } } protected IJavaProject getJavaProject(IStackFrame stackFrame) { // Get the corresponding element. ILaunch launch = stackFrame.getLaunch(); if (launch == null) { return null; } IJavaProject javaProject = null; if (stackFrame instanceof IJavaStackFrame) { javaProject = JavaDebugUtils.resolveJavaProject((IJavaStackFrame) stackFrame); } return javaProject; } /** * Updates the enabled state of the action that this is a * delegate for. */ protected void update() { IAction action= getAction(); if (action != null) { resolveSelectedObject(); } } /** * Resolves the selected object in the target part, or <code>null</code> * if there is no selection. */ protected void resolveSelectedObject() { Object selectedObject= null; fRegion = null; ISelection selection= getTargetSelection(); if (selection instanceof ITextSelection) { ITextSelection ts = (ITextSelection)selection; String text= ts.getText(); if (textHasContent(text)) { selectedObject= text; fRegion = new Region(ts.getOffset(), ts.getLength()); } else if (getTargetPart() instanceof IEditorPart) { IEditorPart editor= (IEditorPart)getTargetPart(); if (editor instanceof ITextEditor) { selectedObject = resolveSelectedObjectUsingToken(selectedObject, ts, editor); } } } else if (selection instanceof IStructuredSelection) { if (!selection.isEmpty()) { if (getTargetPart().getSite().getId().equals(IDebugUIConstants.ID_DEBUG_VIEW)) { //work on the editor selection IEditorPart editor= getTargetPart().getSite().getPage().getActiveEditor(); setTargetPart(editor); selection= getTargetSelection(); if (selection instanceof ITextSelection) { ITextSelection ts = (ITextSelection)selection; String text= ts.getText(); if (textHasContent(text)) { selectedObject= text; } else if (editor instanceof ITextEditor) { selectedObject= resolveSelectedObjectUsingToken(selectedObject, ts, editor); } } } else { IStructuredSelection ss= (IStructuredSelection)selection; Iterator<?> elements = ss.iterator(); while (elements.hasNext()) { if (!(elements.next() instanceof IJavaVariable)) { setSelectedObject(null); return; } } selectedObject= ss; } } } setSelectedObject(selectedObject); } private Object resolveSelectedObjectUsingToken(Object selectedObject, ITextSelection ts, IEditorPart editor) { ITextEditor textEditor= (ITextEditor) editor; IDocument doc= textEditor.getDocumentProvider().getDocument(editor.getEditorInput()); fRegion= JavaWordFinder.findWord(doc, ts.getOffset()); if (fRegion != null) { try { selectedObject= doc.get(fRegion.getOffset(), fRegion.getLength()); } catch (BadLocationException e) { } } return selectedObject; } protected ISelection getTargetSelection() { IWorkbenchPart part = getTargetPart(); if (part != null) { ISelectionProvider provider = part.getSite().getSelectionProvider(); if (provider != null) { return provider.getSelection(); } } return null; } /** * Resolve an editor input from the source element of the stack frame * argument, and return whether it's equal to the editor input for the * editor that owns this action. */ protected boolean compareToEditorInput(IStackFrame stackFrame) { ILaunch launch = stackFrame.getLaunch(); if (launch == null) { return false; } Object sourceElement; try { sourceElement = JavaDebugUtils.resolveSourceElement(stackFrame, launch); } catch (CoreException e) { return false; } if (sourceElement == null) { return false; } IEditorInput sfEditorInput= getDebugModelPresentation().getEditorInput(sourceElement); if (getTargetPart() instanceof IEditorPart) { return ((IEditorPart)getTargetPart()).getEditorInput().equals(sfEditorInput); } return false; } protected Shell getShell() { if (getTargetPart() != null) { return getTargetPart().getSite().getShell(); } return JDIDebugUIPlugin.getActiveWorkbenchShell(); } protected IDataDisplay getDataDisplay() { IDataDisplay display= getDirectDataDisplay(); if (display != null) { return display; } IWorkbenchPage page= JDIDebugUIPlugin.getActivePage(); if (page != null) { IWorkbenchPart activePart= page.getActivePart(); if (activePart != null) { IViewPart view = page.findView(IJavaDebugUIConstants.ID_DISPLAY_VIEW); if (view == null) { try { view= page.showView(IJavaDebugUIConstants.ID_DISPLAY_VIEW); } catch (PartInitException e) { JDIDebugUIPlugin.statusDialog(ActionMessages.EvaluateAction_Cannot_open_Display_view, e.getStatus()); } finally { page.activate(activePart); } } if (view != null) { page.bringToTop(view); return view.getAdapter(IDataDisplay.class); } } } return null; } protected IDataDisplay getDirectDataDisplay() { IWorkbenchPart part= getTargetPart(); if (part != null) { IDataDisplay display= part.getAdapter(IDataDisplay.class); if (display != null) { IWorkbenchPage page= JDIDebugUIPlugin.getActivePage(); if (page != null) { IWorkbenchPart activePart= page.getActivePart(); if (activePart != null) { if (activePart != part) { page.activate(part); } } } return display; } } IWorkbenchPage page= JDIDebugUIPlugin.getActivePage(); if (page != null) { IWorkbenchPart activePart= page.getActivePart(); if (activePart != null) { IDataDisplay display= activePart.getAdapter(IDataDisplay.class); if (display != null) { return display; } } } return null; } protected boolean textHasContent(String text) { if (text != null) { int length= text.length(); if (length > 0) { for (int i= 0; i < length; i++) { if (Character.isLetterOrDigit(text.charAt(i))) { return true; } } } } return false; } /** * Displays a failed evaluation message in the data display. */ protected void reportErrors(IEvaluationResult result) { String message= getErrorMessage(result); reportError(message); } protected void reportError(String message) { IDataDisplay dataDisplay= getDirectDataDisplay(); if (dataDisplay != null) { if (message.length() != 0) { dataDisplay.displayExpressionValue(NLS.bind(ActionMessages.EvaluateAction__evaluation_failed__Reason, new String[] {format(message)})); } else { dataDisplay.displayExpressionValue(ActionMessages.EvaluateAction__evaluation_failed__1); } } else { Status status= new Status(IStatus.ERROR, JDIDebugUIPlugin.getUniqueIdentifier(), IStatus.ERROR, message, null); ErrorDialog.openError(getShell(), ActionMessages.Evaluate_error_title_eval_problems, null, status); } } private String format(String message) { StringBuffer result= new StringBuffer(); int index= 0, pos; while ((pos= message.indexOf('\n', index)) != -1) { result.append("\t\t").append(message.substring(index, index= pos + 1)); //$NON-NLS-1$ } if (index < message.length()) { result.append("\t\t").append(message.substring(index)); //$NON-NLS-1$ } return result.toString(); } public static String getExceptionMessage(Throwable exception) { if (exception instanceof CoreException) { CoreException ce = (CoreException)exception; Throwable throwable= ce.getStatus().getException(); if (throwable instanceof com.sun.jdi.InvocationException) { return getInvocationExceptionMessage((com.sun.jdi.InvocationException)throwable); } else if (throwable instanceof CoreException) { // Traverse nested CoreExceptions return getExceptionMessage(throwable); } return ce.getStatus().getMessage(); } String message= NLS.bind(ActionMessages.Evaluate_error_message_direct_exception, new Object[] { exception.getClass() }); if (exception.getMessage() != null) { message= NLS.bind(ActionMessages.Evaluate_error_message_exception_pattern, new Object[] { message, exception.getMessage() }); } return message; } /** * Returns a message for the exception wrapped in an invocation exception */ protected static String getInvocationExceptionMessage(com.sun.jdi.InvocationException exception) { InvocationException ie= exception; ObjectReference ref= ie.exception(); return NLS.bind(ActionMessages.Evaluate_error_message_wrapped_exception, new Object[] { ref.referenceType().name() }); } protected String getErrorMessage(IEvaluationResult result) { String[] errors= result.getErrorMessages(); if (errors.length == 0) { return getExceptionMessage(result.getException()); } return getErrorMessage(errors); } protected String getErrorMessage(String[] errors) { String message= ""; //$NON-NLS-1$ for (int i= 0; i < errors.length; i++) { String msg= errors[i]; if (i == 0) { message= msg; } else { message= NLS.bind(ActionMessages.Evaluate_error_problem_append_pattern, new Object[] { message, msg }); } } return message; } /* (non-Javadoc) * @see org.eclipse.ui.IActionDelegate#run(IAction) */ @Override public void run(IAction action) { update(); run(); } /* (non-Javadoc) * @see org.eclipse.ui.IActionDelegate#selectionChanged(IAction, ISelection) */ @Override public void selectionChanged(IAction action, ISelection selection) { setAction(action); } /** * @see IWorkbenchWindowActionDelegate#dispose() */ @Override public void dispose() { disposeDebugModelPresentation(); IWorkbenchWindow win = getWindow(); if (win != null) { win.getPartService().removePartListener(this); } } /** * @see IWorkbenchWindowActionDelegate#init(IWorkbenchWindow) */ @Override public void init(IWorkbenchWindow window) { setWindow(window); IWorkbenchPage page= window.getActivePage(); if (page != null) { setTargetPart(page.getActivePart()); } window.getPartService().addPartListener(this); update(); } protected IAction getAction() { return fAction; } protected void setAction(IAction action) { fAction = action; } /** * Returns a debug model presentation (creating one * if necessary). * * @return debug model presentation */ protected IDebugModelPresentation getDebugModelPresentation() { if (fPresentation == null) { fPresentation = DebugUITools.newDebugModelPresentation(JDIDebugModel.getPluginIdentifier()); } return fPresentation; } /** * Disposes this action's debug model presentation, if * one was created. */ protected void disposeDebugModelPresentation() { if (fPresentation != null) { fPresentation.dispose(); } } /** * @see IEditorActionDelegate#setActiveEditor(IAction, IEditorPart) */ @Override public void setActiveEditor(IAction action, IEditorPart targetEditor) { setAction(action); setTargetPart(targetEditor); } /** * @see IPartListener#partActivated(IWorkbenchPart) */ @Override public void partActivated(IWorkbenchPart part) { setTargetPart(part); } /** * @see IPartListener#partBroughtToTop(IWorkbenchPart) */ @Override public void partBroughtToTop(IWorkbenchPart part) { } /** * @see IPartListener#partClosed(IWorkbenchPart) */ @Override public void partClosed(IWorkbenchPart part) { if (part == getTargetPart()) { setTargetPart(null); } if (part == getNewTargetPart()) { setNewTargetPart(null); } } /** * @see IPartListener#partDeactivated(IWorkbenchPart) */ @Override public void partDeactivated(IWorkbenchPart part) { } /** * @see IPartListener#partOpened(IWorkbenchPart) */ @Override public void partOpened(IWorkbenchPart part) { } /** * @see IViewActionDelegate#init(IViewPart) */ @Override public void init(IViewPart view) { setTargetPart(view); } protected IWorkbenchPart getTargetPart() { return fTargetPart; } protected void setTargetPart(IWorkbenchPart part) { if (isEvaluating()) { //do not want to change the target part while evaluating //see bug 8334 setNewTargetPart(part); } else { if (getTargetPart() instanceof JavaSnippetEditor) { ((JavaSnippetEditor)getTargetPart()).removeSnippetStateChangedListener(this); } fTargetPart= part; if (part instanceof JavaSnippetEditor) { ((JavaSnippetEditor)part).addSnippetStateChangedListener(this); } } } protected IWorkbenchWindow getWindow() { return fWindow; } protected void setWindow(IWorkbenchWindow window) { fWindow = window; } /** * @see IObjectActionDelegate#setActivePart(IAction, IWorkbenchPart) */ @Override public void setActivePart(IAction action, IWorkbenchPart targetPart) { setAction(action); setTargetPart(targetPart); update(); } protected Object getSelectedObject() { return fSelection; } protected void setSelectedObject(Object selection) { fSelection = selection; } /** * @see ISnippetStateChangedListener#snippetStateChanged(JavaSnippetEditor) */ @Override public void snippetStateChanged(JavaSnippetEditor editor) { if (editor != null && !editor.isEvaluating() && editor.getFile() != null) { update(); getAction().setEnabled(getSelectedObject() != null); } else { getAction().setEnabled(false); } } protected IWorkbenchPart getNewTargetPart() { return fNewTargetPart; } protected void setNewTargetPart(IWorkbenchPart newTargetPart) { fNewTargetPart = newTargetPart; } protected boolean isEvaluating() { return fEvaluating; } protected void setEvaluating(boolean evaluating) { fEvaluating = evaluating; } /** * Returns the selected text region, or <code>null</code> if none. * * @return */ protected IRegion getRegion() { return fRegion; } /** * Returns the styled text widget associated with the given part * or <code>null</code> if none. * * @param part workbench part * @return associated style text widget or <code>null</code> */ public static StyledText getStyledText(IWorkbenchPart part) { ITextViewer viewer = part.getAdapter(ITextViewer.class); StyledText textWidget = null; if (viewer == null) { Control control = part.getAdapter(Control.class); if (control instanceof StyledText) { textWidget = (StyledText) control; } } else { textWidget = viewer.getTextWidget(); } return textWidget; } /** * Returns an anchor point for a popup dialog on top of a styled text * or <code>null</code> if none. * * @param part or <code>null</code> * @return anchor point or <code>null</code> */ public static Point getPopupAnchor(StyledText textWidget) { if (textWidget != null) { Point docRange = textWidget.getSelectionRange(); int midOffset = docRange.x + (docRange.y / 2); Point point = textWidget.getLocationAtOffset(midOffset); point = textWidget.toDisplay(point); GC gc = new GC(textWidget); gc.setFont(textWidget.getFont()); int height = gc.getFontMetrics().getHeight(); gc.dispose(); point.y += height; return point; } return null; } }