blob: b6002a6f249d53bd32862e0169fa36ac280a3309 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2004 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Common Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.jdt.internal.debug.ui.actions;
import java.text.MessageFormat;
import java.util.Iterator;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IStatus;
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.ISourceLocator;
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.IJavaElement;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.debug.core.IJavaArray;
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.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.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.Region;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionProvider;
import org.eclipse.jface.viewers.IStructuredSelection;
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.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 = (IDebugView)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 && !(value instanceof IJavaArray)) {
return (IJavaObject)value;
}
}
} catch (DebugException e) {
JDIDebugUIPlugin.log(e);
}
} else if (selection instanceof JavaInspectExpression) {
IValue value= ((JavaInspectExpression)selection).getValue();
if (value instanceof IJavaObject && !(value instanceof IJavaArray)) {
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)
*/
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
IJavaObject object = getObjectContext();
IJavaStackFrame stackFrame= getStackFrameContext();
if (stackFrame == null) {
reportError(ActionMessages.getString("Evaluate.error.message.stack_frame_context")); //$NON-NLS-1$
return;
}
// check for nested evaluation
IJavaThread thread = (IJavaThread)stackFrame.getThread();
if (thread.isPerformingEvaluation()) {
reportError(ActionMessages.getString("EvaluateAction.Cannot_perform_nested_evaluations._1")); //$NON-NLS-1$
return;
}
setNewTargetPart(getTargetPart());
if (stackFrame.isSuspended()) {
IJavaElement javaElement= getJavaElement(stackFrame);
if (javaElement != null) {
IJavaProject project = javaElement.getJavaProject();
IEvaluationEngine engine = null;
try {
Object selection= getSelectedObject();
if (!(selection instanceof String)) {
return;
}
String expression= (String)selection;
engine = JDIDebugUIPlugin.getDefault().getEvaluationEngine(project, (IJavaDebugTarget)stackFrame.getDebugTarget());
setEvaluating(true);
boolean hitBreakpoints= JDIDebugModel.getPreferences().getBoolean(JDIDebugModel.PREF_SUSPEND_FOR_BREAKPOINTS_DURING_EVALUATION);
if (object == null) {
engine.evaluate(expression, stackFrame, this, DebugEvent.EVALUATION, hitBreakpoints);
} else {
engine.evaluate(expression, object, (IJavaThread)stackFrame.getThread(), this, DebugEvent.EVALUATION, hitBreakpoints);
}
return;
} catch (CoreException e) {
reportError(getExceptionMessage(e));
}
} else {
reportError(ActionMessages.getString("Evaluate.error.message.src_context")); //$NON-NLS-1$
}
} else {
// thread not suspended
reportError(ActionMessages.getString("EvaluateAction.Thread_not_suspended_-_unable_to_perform_evaluation._1")); //$NON-NLS-1$
}
evaluationCleanup();
}
protected IJavaElement getJavaElement(IStackFrame stackFrame) {
// Get the corresponding element.
ILaunch launch = stackFrame.getLaunch();
if (launch == null) {
return null;
}
ISourceLocator locator= launch.getSourceLocator();
if (locator == null)
return null;
Object sourceElement = locator.getSourceElement(stackFrame);
if (sourceElement instanceof IJavaElement) {
return (IJavaElement) sourceElement;
} else if (sourceElement instanceof IResource) {
IJavaProject project = JavaCore.create(((IResource)sourceElement).getProject());
if (project.exists()) {
return project;
}
}
return null;
}
/**
* 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;
}
ISourceLocator locator= launch.getSourceLocator();
if (locator == null) {
return false;
}
Object sourceElement = locator.getSourceElement(stackFrame);
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.errorDialog(ActionMessages.getString("EvaluateAction.Cannot_open_Display_view"), e); //$NON-NLS-1$
} finally {
page.activate(activePart);
}
}
if (view != null) {
page.bringToTop(view);
return (IDataDisplay)view.getAdapter(IDataDisplay.class);
}
}
}
return null;
}
protected IDataDisplay getDirectDataDisplay() {
IWorkbenchPart part= getTargetPart();
if (part != null) {
IDataDisplay display= (IDataDisplay)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= (IDataDisplay)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(MessageFormat.format(ActionMessages.getString("EvaluateAction.(evaluation_failed)_Reason"), new String[] {format(message)})); //$NON-NLS-1$
} else {
dataDisplay.displayExpressionValue(ActionMessages.getString("EvaluateAction.(evaluation_failed)_1")); //$NON-NLS-1$
}
} else {
Status status= new Status(IStatus.ERROR, JDIDebugUIPlugin.getUniqueIdentifier(), IStatus.ERROR, message, null);
ErrorDialog.openError(getShell(), ActionMessages.getString("Evaluate.error.title.eval_problems"), null, status); //$NON-NLS-1$
}
}
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();
}
protected 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= MessageFormat.format(ActionMessages.getString("Evaluate.error.message.direct_exception"), new Object[] { exception.getClass() }); //$NON-NLS-1$
if (exception.getMessage() != null) {
message= MessageFormat.format(ActionMessages.getString("Evaluate.error.message.exception.pattern"), new Object[] { message, exception.getMessage() }); //$NON-NLS-1$
}
return message;
}
/**
* Returns a message for the exception wrapped in an invocation exception
*/
protected String getInvocationExceptionMessage(com.sun.jdi.InvocationException exception) {
InvocationException ie= exception;
ObjectReference ref= ie.exception();
return MessageFormat.format(ActionMessages.getString("Evaluate.error.message.wrapped_exception"), new Object[] { ref.referenceType().name() }); //$NON-NLS-1$
}
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= MessageFormat.format(ActionMessages.getString("Evaluate.error.problem_append_pattern"), new Object[] { message, msg }); //$NON-NLS-1$
}
}
return message;
}
/* (non-Javadoc)
* @see org.eclipse.ui.IActionDelegate#run(IAction)
*/
public void run(IAction action) {
update();
run();
}
/* (non-Javadoc)
* @see org.eclipse.ui.IActionDelegate#selectionChanged(IAction, ISelection)
*/
public void selectionChanged(IAction action, ISelection selection) {
setAction(action);
}
/**
* @see IWorkbenchWindowActionDelegate#dispose()
*/
public void dispose() {
disposeDebugModelPresentation();
IWorkbenchWindow win = getWindow();
if (win != null) {
win.getPartService().removePartListener(this);
}
}
/**
* @see IWorkbenchWindowActionDelegate#init(IWorkbenchWindow)
*/
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)
*/
public void setActiveEditor(IAction action, IEditorPart targetEditor) {
setAction(action);
setTargetPart(targetEditor);
}
/**
* @see IPartListener#partActivated(IWorkbenchPart)
*/
public void partActivated(IWorkbenchPart part) {
setTargetPart(part);
}
/**
* @see IPartListener#partBroughtToTop(IWorkbenchPart)
*/
public void partBroughtToTop(IWorkbenchPart part) {
}
/**
* @see IPartListener#partClosed(IWorkbenchPart)
*/
public void partClosed(IWorkbenchPart part) {
if (part == getTargetPart()) {
setTargetPart(null);
}
if (part == getNewTargetPart()) {
setNewTargetPart(null);
}
}
/**
* @see IPartListener#partDeactivated(IWorkbenchPart)
*/
public void partDeactivated(IWorkbenchPart part) {
}
/**
* @see IPartListener#partOpened(IWorkbenchPart)
*/
public void partOpened(IWorkbenchPart part) {
}
/**
* @see IViewActionDelegate#init(IViewPart)
*/
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)
*/
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)
*/
public void snippetStateChanged(JavaSnippetEditor editor) {
if (editor != null && !editor.isEvaluating() && editor.getFile() != null) {
update();
} 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;
}
}