blob: 9ed9c1301024b1cbb084249c52e405c239d92058 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2015 IBM Corporation and others.
*
* 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
*
* Contributors:
* IBM Corporation - initial API and implementation
* Sebastian Davids <sdavids@gmx.de> - bug 38919
*******************************************************************************/
package org.eclipse.jdt.internal.debug.ui.snippeteditor;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.IncrementalProjectBuilder;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.QualifiedName;
import org.eclipse.core.runtime.Status;
import org.eclipse.debug.core.DebugEvent;
import org.eclipse.debug.core.DebugException;
import org.eclipse.debug.core.DebugPlugin;
import org.eclipse.debug.core.IDebugEventFilter;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.debug.core.model.IBreakpoint;
import org.eclipse.debug.core.model.IDebugElement;
import org.eclipse.debug.core.model.IDebugTarget;
import org.eclipse.debug.core.model.IStackFrame;
import org.eclipse.debug.core.model.IThread;
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.IValueDetailListener;
import org.eclipse.debug.ui.InspectPopupDialog;
import org.eclipse.jdt.core.CompletionRequestor;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.eval.IEvaluationContext;
import org.eclipse.jdt.debug.core.IJavaDebugTarget;
import org.eclipse.jdt.debug.core.IJavaStackFrame;
import org.eclipse.jdt.debug.core.IJavaThread;
import org.eclipse.jdt.debug.core.IJavaType;
import org.eclipse.jdt.debug.core.IJavaValue;
import org.eclipse.jdt.debug.core.JDIDebugModel;
import org.eclipse.jdt.debug.eval.EvaluationManager;
import org.eclipse.jdt.debug.eval.IClassFileEvaluationEngine;
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.ui.JDIContentAssistPreference;
import org.eclipse.jdt.internal.debug.ui.JDIDebugUIPlugin;
import org.eclipse.jdt.internal.debug.ui.JDISourceViewer;
import org.eclipse.jdt.internal.debug.ui.JavaDebugImages;
import org.eclipse.jdt.internal.debug.ui.JavaDebugOptionsManager;
import org.eclipse.jdt.internal.debug.ui.actions.DisplayAction;
import org.eclipse.jdt.internal.debug.ui.actions.EvaluateAction;
import org.eclipse.jdt.internal.debug.ui.actions.PopupInspectAction;
import org.eclipse.jdt.internal.debug.ui.display.JavaInspectExpression;
import org.eclipse.jdt.launching.IJavaLaunchConfigurationConstants;
import org.eclipse.jdt.launching.IVMInstall;
import org.eclipse.jdt.launching.JavaRuntime;
import org.eclipse.jdt.ui.IContextMenuConstants;
import org.eclipse.jdt.ui.JavaUI;
import org.eclipse.jdt.ui.PreferenceConstants;
import org.eclipse.jdt.ui.text.JavaSourceViewerConfiguration;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.dialogs.ErrorDialog;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.ITextSelection;
import org.eclipse.jface.text.contentassist.ContentAssistant;
import org.eclipse.jface.text.contentassist.IContentAssistant;
import org.eclipse.jface.text.source.ISourceViewer;
import org.eclipse.jface.text.source.IVerticalRuler;
import org.eclipse.jface.text.source.SourceViewerConfiguration;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.BusyIndicator;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorSite;
import org.eclipse.ui.IPageLayout;
import org.eclipse.ui.IPartListener2;
import org.eclipse.ui.IViewPart;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchPartReference;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.actions.WorkspaceModifyOperation;
import org.eclipse.ui.dialogs.SaveAsDialog;
import org.eclipse.ui.editors.text.EditorsUI;
import org.eclipse.ui.part.EditorActionBarContributor;
import org.eclipse.ui.part.FileEditorInput;
import org.eclipse.ui.part.IShowInTargetList;
import org.eclipse.ui.texteditor.AbstractDecoratedTextEditor;
import org.eclipse.ui.texteditor.ChainedPreferenceStore;
import org.eclipse.ui.texteditor.IDocumentProvider;
import org.eclipse.ui.texteditor.ITextEditorActionConstants;
import org.eclipse.ui.texteditor.ITextEditorActionDefinitionIds;
import org.eclipse.ui.texteditor.TextOperationAction;
import com.sun.jdi.InvocationException;
import com.sun.jdi.ObjectReference;
/**
* An editor for Java snippets.
*/
public class JavaSnippetEditor extends AbstractDecoratedTextEditor implements IDebugEventFilter, IEvaluationListener, IValueDetailListener {
public static final String IMPORTS_CONTEXT = "SnippetEditor.imports"; //$NON-NLS-1$
public final static int RESULT_DISPLAY= 1;
public final static int RESULT_RUN= 2;
public final static int RESULT_INSPECT= 3;
private int fResultMode; // one of the RESULT_* constants
private IJavaProject fJavaProject;
private IEvaluationContext fEvaluationContext;
private IDebugTarget fVM;
private String[] fLaunchedClassPath;
private String fLaunchedWorkingDir;
private String fLaunchedVMArgs;
private IVMInstall fLaunchedVM;
private List<ISnippetStateChangedListener> fSnippetStateListeners;
private boolean fEvaluating;
private IJavaThread fThread;
private boolean fStepFiltersSetting;
private int fSnippetStart;
private int fSnippetEnd;
private String[] fImports= null;
private Image fOldTitleImage= null;
private IClassFileEvaluationEngine fEngine= null;
/**
* The debug model presentation used for computing toString
*/
private IDebugModelPresentation fPresentation= DebugUITools.newDebugModelPresentation(JDIDebugModel.getPluginIdentifier());
/**
* The result of a toString evaluation returned asynchronously by the
* debug model.
*/
private String fResult;
/**
* A thread that waits to have a
* thread to perform an evaluation in.
*/
private static class WaitThread extends Thread {
/**
* The display used for event dispatching.
*/
private Display fDisplay;
/**
* Indicates whether to continue event queue dispatching.
*/
private volatile boolean fContinueEventDispatching = true;
private Object fLock;
/**
* Creates a "wait" thread
*
* @param display the display to be used to read and dispatch events
* @param lock the monitor to wait on
*/
private WaitThread(Display display, Object lock) {
super("Snippet Wait Thread"); //$NON-NLS-1$
setDaemon(true);
fDisplay = display;
fLock= lock;
}
@Override
public void run() {
try {
synchronized (fLock) {
//should be notified out of #setThread(IJavaThread)
fLock.wait(10000);
}
} catch (InterruptedException e) {
} finally {
// Make sure that all events in the asynchronous event queue
// are dispatched.
fDisplay.syncExec(new Runnable() {
@Override
public void run() {
// do nothing
}
});
// Stop event dispatching
fContinueEventDispatching= false;
// Force the event loop to return from sleep () so that
// it stops event dispatching.
fDisplay.asyncExec(null);
}
}
/**
* Processes events.
*/
protected void block() {
if (fDisplay == Display.getCurrent()) {
while (fContinueEventDispatching) {
if (!fDisplay.readAndDispatch()) {
fDisplay.sleep();
}
}
}
}
}
/**
* Listens for part activation to set scrapbook active system property
* for action enablement.
*/
private IPartListener2 fActivationListener = new IPartListener2() {
@Override
public void partActivated(IWorkbenchPartReference partRef) {
if ("org.eclipse.jdt.debug.ui.SnippetEditor".equals(partRef.getId())) { //$NON-NLS-1$
System.setProperty(JDIDebugUIPlugin.getUniqueIdentifier() + ".scrapbookActive", "true"); //$NON-NLS-1$ //$NON-NLS-2$
} else {
System.setProperty(JDIDebugUIPlugin.getUniqueIdentifier() + ".scrapbookActive", "false"); //$NON-NLS-1$ //$NON-NLS-2$
}
}
@Override
public void partBroughtToTop(IWorkbenchPartReference partRef) {
}
@Override
public void partClosed(IWorkbenchPartReference partRef) {
}
@Override
public void partDeactivated(IWorkbenchPartReference partRef) {
}
@Override
public void partHidden(IWorkbenchPartReference partRef) {
}
@Override
public void partInputChanged(IWorkbenchPartReference partRef) {
}
@Override
public void partOpened(IWorkbenchPartReference partRef) {
}
@Override
public void partVisible(IWorkbenchPartReference partRef) {
}
};
public JavaSnippetEditor() {
super();
setDocumentProvider(JDIDebugUIPlugin.getDefault().getSnippetDocumentProvider());
IPreferenceStore store = new ChainedPreferenceStore(new IPreferenceStore[] {
PreferenceConstants.getPreferenceStore(),
EditorsUI.getPreferenceStore()});
setSourceViewerConfiguration(new JavaSnippetViewerConfiguration(JDIDebugUIPlugin.getDefault().getJavaTextTools(), store, this));
fSnippetStateListeners = new ArrayList<>(4);
setPreferenceStore(store);
setEditorContextMenuId("#JavaSnippetEditorContext"); //$NON-NLS-1$
setRulerContextMenuId("#JavaSnippetRulerContext"); //$NON-NLS-1$
}
/* (non-Javadoc)
* @see org.eclipse.ui.texteditor.AbstractTextEditor#doSetInput(org.eclipse.ui.IEditorInput)
*/
@Override
protected void doSetInput(IEditorInput input) throws CoreException {
super.doSetInput(input);
IFile file= getFile();
if (file != null) {
String property= file.getPersistentProperty(new QualifiedName(JDIDebugUIPlugin.getUniqueIdentifier(), IMPORTS_CONTEXT));
if (property != null) {
fImports = JavaDebugOptionsManager.parseList(property);
}
}
}
@Override
public void init(IEditorSite site, IEditorInput input) throws PartInitException {
super.init(site, input);
site.getWorkbenchWindow().getPartService().addPartListener(fActivationListener);
}
/* (non-Javadoc)
* @see org.eclipse.ui.IWorkbenchPart#dispose()
*/
@Override
public void dispose() {
shutDownVM();
fPresentation.dispose();
fSnippetStateListeners = null;
ISourceViewer viewer = getSourceViewer();
if(viewer != null) {
((JDISourceViewer)viewer).dispose();
}
getSite().getWorkbenchWindow().getPartService().removePartListener(fActivationListener);
super.dispose();
}
/**
* Actions for the editor popup menu
* @see org.eclipse.ui.texteditor.AbstractTextEditor#createActions()
*/
@Override
protected void createActions() {
super.createActions();
if (getFile() != null) {
Action action = new TextOperationAction(SnippetMessages.getBundle(), "SnippetEditor.ContentAssistProposal.", this, ISourceViewer.CONTENTASSIST_PROPOSALS); //$NON-NLS-1$
action.setActionDefinitionId(ITextEditorActionDefinitionIds.CONTENT_ASSIST_PROPOSALS);
setAction("ContentAssistProposal", action);//$NON-NLS-1$
setAction("ShowInPackageView", new ShowInPackageViewAction(this)); //$NON-NLS-1$
setAction("Stop", new StopAction(this)); //$NON-NLS-1$
setAction("SelectImports", new SelectImportsAction(this)); //$NON-NLS-1$
}
}
/* (non-Javadoc)
* @see org.eclipse.ui.texteditor.AbstractTextEditor#editorContextMenuAboutToShow(org.eclipse.jface.action.IMenuManager)
*/
@Override
protected void editorContextMenuAboutToShow(IMenuManager menu) {
super.editorContextMenuAboutToShow(menu);
addGroup(menu, ITextEditorActionConstants.GROUP_EDIT, IContextMenuConstants.GROUP_GENERATE);
addGroup(menu, ITextEditorActionConstants.GROUP_FIND, IContextMenuConstants.GROUP_SEARCH);
addGroup(menu, IContextMenuConstants.GROUP_SEARCH, IContextMenuConstants.GROUP_SHOW);
if (getFile() != null) {
addAction(menu, IContextMenuConstants.GROUP_SHOW, "ShowInPackageView"); //$NON-NLS-1$
addAction(menu, IContextMenuConstants.GROUP_ADDITIONS, "Run"); //$NON-NLS-1$
addAction(menu, IContextMenuConstants.GROUP_ADDITIONS, "Stop"); //$NON-NLS-1$
addAction(menu, IContextMenuConstants.GROUP_ADDITIONS, "SelectImports"); //$NON-NLS-1$
}
}
protected boolean isVMLaunched() {
return fVM != null;
}
public boolean isEvaluating() {
return fEvaluating;
}
public void evalSelection(int resultMode) {
if (!isInJavaProject()) {
reportNotInJavaProjectError();
return;
}
if (isEvaluating()) {
return;
}
checkCurrentProject();
evaluationStarts();
fResultMode= resultMode;
buildAndLaunch();
if (fVM == null) {
evaluationEnds();
return;
}
fireEvalStateChanged();
ITextSelection selection= (ITextSelection) getSelectionProvider().getSelection();
String snippet= selection.getText();
fSnippetStart= selection.getOffset();
fSnippetEnd= fSnippetStart + selection.getLength();
evaluate(snippet);
}
/**
* Checks if the page has been copied/moved to a different project or the project has been renamed.
* Updates the launch configuration template if a copy/move/rename has occurred.
*/
protected void checkCurrentProject() {
IFile file= getFile();
if (file == null) {
return;
}
try {
ILaunchConfiguration config = ScrapbookLauncher.getLaunchConfigurationTemplate(file);
if (config != null) {
String projectName = config.getAttribute(IJavaLaunchConfigurationConstants.ATTR_PROJECT_NAME, (String)null);
IJavaProject pro = JavaCore.create(file.getProject());
if (!pro.getElementName().equals(projectName)) {
//the page has been moved to a "different" project
ScrapbookLauncher.setLaunchConfigMemento(file, null);
}
}
} catch (CoreException ce) {
JDIDebugUIPlugin.log(ce);
ErrorDialog.openError(getShell(), SnippetMessages.getString("SnippetEditor.error.evaluating"), null, ce.getStatus()); //$NON-NLS-1$
evaluationEnds();
return;
}
}
protected void buildAndLaunch() {
IJavaProject javaProject= getJavaProject();
if (javaProject == null) {
return;
}
boolean build = !javaProject.getProject().getWorkspace().isAutoBuilding()
|| !javaProject.hasBuildState();
if (build) {
if (!performIncrementalBuild()) {
return;
}
}
boolean changed= classPathHasChanged();
if (!changed) {
changed = workingDirHasChanged();
}
if (!changed) {
changed = vmHasChanged();
}
if (!changed) {
changed = vmArgsChanged();
}
boolean launch= fVM == null || changed;
if (changed) {
shutDownVM();
}
if (fVM == null) {
checkMultipleEditors();
}
if (launch && fVM == null) {
launchVM();
fVM= ScrapbookLauncher.getDefault().getDebugTarget(getFile());
}
}
protected boolean performIncrementalBuild() {
IRunnableWithProgress r= new IRunnableWithProgress() {
@Override
public void run(IProgressMonitor pm) throws InvocationTargetException {
try {
getJavaProject().getProject().build(IncrementalProjectBuilder.INCREMENTAL_BUILD, pm);
} catch (CoreException e) {
throw new InvocationTargetException(e);
}
}
};
try {
PlatformUI.getWorkbench().getProgressService().run(true, false, r);
} catch (InterruptedException e) {
JDIDebugUIPlugin.log(e);
evaluationEnds();
return false;
} catch (InvocationTargetException e) {
JDIDebugUIPlugin.log(e);
evaluationEnds();
return false;
}
return true;
}
protected void checkMultipleEditors() {
fVM= ScrapbookLauncher.getDefault().getDebugTarget(getFile());
//multiple editors are opened on the same page
if (fVM != null) {
DebugPlugin.getDefault().addDebugEventFilter(this);
try {
for (IThread thread : fVM.getThreads()) {
if (thread.isSuspended()) {
thread.resume();
}
}
} catch (DebugException de) {
JDIDebugUIPlugin.log(de);
}
}
}
protected void setImports(String[] imports) {
fImports= imports;
IFile file= getFile();
if (file == null) {
return;
}
String serialized= null;
if (imports != null) {
serialized= JavaDebugOptionsManager.serializeList(imports);
}
// persist
try {
file.setPersistentProperty(new QualifiedName(JDIDebugUIPlugin.getUniqueIdentifier(), IMPORTS_CONTEXT), serialized);
} catch (CoreException e) {
JDIDebugUIPlugin.log(e);
ErrorDialog.openError(getShell(), SnippetMessages.getString("SnippetEditor.error.imports"), null, e.getStatus()); //$NON-NLS-1$
}
}
protected String[] getImports() {
return fImports;
}
protected IEvaluationContext getEvaluationContext() {
if (fEvaluationContext == null) {
IJavaProject project= getJavaProject();
if (project != null) {
fEvaluationContext= project.newEvaluationContext();
}
}
if (fEvaluationContext != null) {
if (getImports() != null) {
fEvaluationContext.setImports(getImports());
} else {
fEvaluationContext.setImports(new String[]{});
}
}
return fEvaluationContext;
}
protected IJavaProject getJavaProject() {
if (fJavaProject == null) {
try {
fJavaProject = findJavaProject();
} catch (CoreException e) {
JDIDebugUIPlugin.log(e);
showError(e.getStatus());
}
}
return fJavaProject;
}
protected void shutDownVM() {
DebugPlugin.getDefault().removeDebugEventFilter(this);
// The real shut down
IDebugTarget target= fVM;
if (fVM != null) {
try {
IBreakpoint bp = ScrapbookLauncher.getDefault().getMagicBreakpoint(fVM);
if (bp != null) {
fVM.breakpointRemoved(bp, null);
}
if (getThread() != null) {
getThread().resume();
}
fVM.terminate();
} catch (DebugException e) {
JDIDebugUIPlugin.log(e);
ErrorDialog.openError(getShell(), SnippetMessages.getString("SnippetEditor.error.shutdown"), null, e.getStatus()); //$NON-NLS-1$
return;
}
vmTerminated();
ScrapbookLauncher.getDefault().cleanup(target);
}
}
/**
* The VM has terminated, update state
*/
protected void vmTerminated() {
fVM= null;
fThread= null;
fEvaluationContext= null;
fLaunchedClassPath= null;
if (fEngine != null) {
fEngine.dispose();
}
fEngine= null;
fireEvalStateChanged();
}
public void addSnippetStateChangedListener(ISnippetStateChangedListener listener) {
if (fSnippetStateListeners != null && !fSnippetStateListeners.contains(listener)) {
fSnippetStateListeners.add(listener);
}
}
public void removeSnippetStateChangedListener(ISnippetStateChangedListener listener) {
if (fSnippetStateListeners != null) {
fSnippetStateListeners.remove(listener);
}
}
protected void fireEvalStateChanged() {
Runnable r= new Runnable() {
@Override
public void run() {
Shell shell= getShell();
if (fSnippetStateListeners != null && shell != null && !shell.isDisposed()) {
for (ISnippetStateChangedListener listener : new ArrayList<>(fSnippetStateListeners)) {
listener.snippetStateChanged(JavaSnippetEditor.this);
}
}
}
};
Shell shell= getShell();
if (shell != null) {
getShell().getDisplay().asyncExec(r);
}
}
protected void evaluate(String snippet) {
if (getThread() == null) {
WaitThread eThread= new WaitThread(Display.getCurrent(), this);
eThread.start();
eThread.block();
}
if (getThread() == null) {
IStatus status = new Status(IStatus.ERROR, JDIDebugUIPlugin.getUniqueIdentifier(), IJavaDebugUIConstants.INTERNAL_ERROR, "Evaluation failed: internal error - unable to obtain an execution context.", null); //$NON-NLS-1$
ErrorDialog.openError(getShell(), SnippetMessages.getString("SnippetEditor.error.evaluating"), null, status); //$NON-NLS-1$
evaluationEnds();
return;
}
boolean hitBreakpoints = Platform.getPreferencesService().getBoolean(
JDIDebugPlugin.getUniqueIdentifier(),
JDIDebugModel.PREF_SUSPEND_FOR_BREAKPOINTS_DURING_EVALUATION,
true,
null);
try {
getEvaluationEngine().evaluate(snippet,getThread(), this, hitBreakpoints);
} catch (DebugException e) {
JDIDebugUIPlugin.log(e);
ErrorDialog.openError(getShell(), SnippetMessages.getString("SnippetEditor.error.evaluating"), null, e.getStatus()); //$NON-NLS-1$
evaluationEnds();
}
}
/* (non-Javadoc)
* @see org.eclipse.jdt.debug.eval.IEvaluationListener#evaluationComplete(org.eclipse.jdt.debug.eval.IEvaluationResult)
*/
@Override
public void evaluationComplete(IEvaluationResult result) {
boolean severeErrors = false;
if (result.hasErrors()) {
String[] errors = result.getErrorMessages();
severeErrors = errors.length > 0;
if (result.getException() != null) {
showException(result.getException());
}
showAllErrors(errors);
}
IJavaValue value= result.getValue();
if (value != null && !severeErrors) {
switch (fResultMode) {
case RESULT_DISPLAY:
displayResult(value);
break;
case RESULT_INSPECT:
JavaInspectExpression exp = new JavaInspectExpression(result.getSnippet().trim(), value);
showExpression(exp);
break;
case RESULT_RUN:
// no action
break;
}
}
evaluationEnds();
}
/**
* Make the expression view visible or open one
* if required.
*/
protected void showExpressionView() {
Runnable r = new Runnable() {
@Override
public void run() {
IWorkbenchPage page = JDIDebugUIPlugin.getActivePage();
if (page != null) {
IViewPart part = page.findView(IDebugUIConstants.ID_EXPRESSION_VIEW);
if (part == null) {
try {
page.showView(IDebugUIConstants.ID_EXPRESSION_VIEW);
} catch (PartInitException e) {
JDIDebugUIPlugin.log(e);
showError(e.getStatus());
}
} else {
page.bringToTop(part);
}
}
}
};
async(r);
}
protected void codeComplete(CompletionRequestor requestor) throws JavaModelException {
ITextSelection selection= (ITextSelection)getSelectionProvider().getSelection();
int start= selection.getOffset();
String snippet= getSourceViewer().getDocument().get();
IEvaluationContext e= getEvaluationContext();
if (e != null) {
e.codeComplete(snippet, start, requestor);
}
}
protected IJavaElement[] codeResolve() throws JavaModelException {
ISourceViewer viewer= getSourceViewer();
if (viewer == null) {
return null;
}
ITextSelection selection= (ITextSelection) getSelectionProvider().getSelection();
int start= selection.getOffset();
int len= selection.getLength();
String snippet= viewer.getDocument().get();
IEvaluationContext e= getEvaluationContext();
if (e != null) {
return e.codeSelect(snippet, start, len);
}
return null;
}
protected void showError(IStatus status) {
evaluationEnds();
if (!status.isOK()) {
ErrorDialog.openError(getShell(), SnippetMessages.getString("SnippetEditor.error.evaluating2"), null, status); //$NON-NLS-1$
}
}
protected void showError(String message) {
Status status= new Status(IStatus.ERROR, JDIDebugUIPlugin.getUniqueIdentifier(), IStatus.ERROR, message, null);
showError(status);
}
protected void displayResult(IJavaValue result) {
StringBuilder resultString= new StringBuilder();
try {
IJavaType type = result.getJavaType();
if (type != null) {
String sig= type.getSignature();
if ("V".equals(sig)) { //$NON-NLS-1$
resultString.append(SnippetMessages.getString("SnippetEditor.noreturnvalue")); //$NON-NLS-1$
} else {
if (sig != null) {
resultString.append(SnippetMessages.getFormattedString("SnippetEditor.typename", result.getReferenceTypeName())); //$NON-NLS-1$
} else {
resultString.append(" "); //$NON-NLS-1$
}
resultString.append(DisplayAction.trimDisplayResult(evaluateToString(result)));
}
} else {
resultString.append(DisplayAction.trimDisplayResult(result.getValueString()));
}
} catch(DebugException e) {
JDIDebugUIPlugin.log(e);
ErrorDialog.openError(getShell(), SnippetMessages.getString("SnippetEditor.error.toString"), null, e.getStatus()); //$NON-NLS-1$
}
final String message = resultString.toString();
Runnable r = new Runnable() {
@Override
public void run() {
try {
getSourceViewer().getDocument().replace(fSnippetEnd, 0, message);
selectAndReveal(fSnippetEnd, message.length());
} catch (BadLocationException e) {
}
}
};
async(r);
}
/**
* Returns the result of evaluating 'toString' on the given
* value.
*
* @param value object or primitive data type the 'toString'
* is required for
* @return the result of evaluating toString
* @exception DebugException if an exception occurs during the
* evaluation.
*/
protected synchronized String evaluateToString(IJavaValue value) {
fResult= null;
fPresentation.computeDetail(value, this);
if (fResult == null) {
try {
wait(10000);
} catch (InterruptedException e) {
return SnippetMessages.getString("SnippetEditor.error.interrupted"); //$NON-NLS-1$
}
}
return fResult;
}
/* (non-Javadoc)
* @see org.eclipse.debug.ui.IValueDetailListener#detailComputed(org.eclipse.debug.core.model.IValue, java.lang.String)
*/
@Override
public synchronized void detailComputed(IValue value, final String result) {
fResult= result;
this.notifyAll();
}
protected void showAllErrors(final String[] errors) {
IDocument document = getSourceViewer().getDocument();
String delimiter = document.getLegalLineDelimiters()[0];
final StringBuilder errorString = new StringBuilder();
for (String error : errors) {
errorString.append(error + delimiter);
}
Runnable r = new Runnable() {
@Override
public void run() {
try {
getSourceViewer().getDocument().replace(fSnippetStart, 0, errorString.toString());
selectAndReveal(fSnippetStart, errorString.length());
} catch (BadLocationException e) {
}
}
};
async(r);
}
private void showExpression(final JavaInspectExpression expression) {
Runnable r = new Runnable() {
@Override
public void run() {
new InspectPopupDialog(getShell(), EvaluateAction.getPopupAnchor(getSourceViewer().getTextWidget()), PopupInspectAction.ACTION_DEFININITION_ID, expression).open();
}
};
async(r);
}
protected void showException(Throwable exception) {
if (exception instanceof DebugException) {
DebugException de = (DebugException)exception;
Throwable t= de.getStatus().getException();
if (t != null) {
// show underlying exception
showUnderlyingException(t);
return;
}
}
ByteArrayOutputStream bos= new ByteArrayOutputStream();
PrintStream ps= new PrintStream(bos, true);
exception.printStackTrace(ps);
final String message = bos.toString();
Runnable r = new Runnable() {
@Override
public void run() {
try {
getSourceViewer().getDocument().replace(fSnippetEnd, 0, message);
selectAndReveal(fSnippetEnd, message.length());
} catch (BadLocationException e) {
}
}
};
async(r);
}
protected void showUnderlyingException(Throwable t) {
if (t instanceof InvocationException) {
InvocationException ie= (InvocationException)t;
ObjectReference ref= ie.exception();
String eName= ref.referenceType().name();
final String message= SnippetMessages.getFormattedString("SnippetEditor.exception", eName); //$NON-NLS-1$
Runnable r = new Runnable() {
@Override
public void run() {
try {
getSourceViewer().getDocument().replace(fSnippetEnd, 0, message);
selectAndReveal(fSnippetEnd, message.length());
} catch (BadLocationException e) {
}
}
};
async(r);
} else {
showException(t);
}
}
protected IJavaProject findJavaProject() throws CoreException {
IFile file = getFile();
if (file != null) {
IProject p= file.getProject();
if (p.getNature(JavaCore.NATURE_ID) != null) {
return JavaCore.create(p);
}
}
return null;
}
protected boolean classPathHasChanged() {
String[] classpath= getClassPath(getJavaProject());
if (fLaunchedClassPath != null && !classPathsEqual(fLaunchedClassPath, classpath)) {
MessageDialog.openWarning(getShell(), SnippetMessages.getString("SnippetEditor.warning"), SnippetMessages.getString("SnippetEditor.warning.cpchange")); //$NON-NLS-2$ //$NON-NLS-1$
return true;
}
return false;
}
protected boolean workingDirHasChanged() {
String wd = getWorkingDirectoryAttribute();
boolean changed = false;
if (wd == null || fLaunchedWorkingDir == null) {
if (wd != fLaunchedWorkingDir) {
changed = true;
}
} else {
if (!wd.equals(fLaunchedWorkingDir)) {
changed = true;
}
}
if (changed && fVM != null) {
MessageDialog.openWarning(getShell(), SnippetMessages.getString("SnippetEditor.Warning_1"), SnippetMessages.getString("SnippetEditor.The_working_directory_has_changed._Restarting_the_evaluation_context._2")); //$NON-NLS-1$ //$NON-NLS-2$
}
return changed;
}
protected boolean vmArgsChanged() {
String args = getVMArgsAttribute();
boolean changed = false;
if (args == null || fLaunchedVMArgs == null) {
if (args != fLaunchedVMArgs) {
changed = true;
}
} else {
if (!args.equals(fLaunchedVMArgs)) {
changed = true;
}
}
if (changed && fVM != null) {
MessageDialog.openWarning(getShell(), SnippetMessages.getString("SnippetEditor.Warning_1"), SnippetMessages.getString("SnippetEditor.1")); //$NON-NLS-1$ //$NON-NLS-2$
}
return changed;
}
protected boolean vmHasChanged() {
IVMInstall vm = getVMInstall();
boolean changed = false;
if (vm == null || fLaunchedVM == null) {
if (vm != fLaunchedVM) {
changed = true;
}
} else {
if (!vm.equals(fLaunchedVM)) {
changed = true;
}
}
if (changed && fVM != null) {
MessageDialog.openWarning(getShell(), SnippetMessages.getString("SnippetEditor.Warning_1"), SnippetMessages.getString("SnippetEditor.The_JRE_has_changed._Restarting_the_evaluation_context._2")); //$NON-NLS-1$ //$NON-NLS-2$
}
return changed;
}
protected boolean classPathsEqual(String[] path1, String[] path2) {
if (path1.length != path2.length) {
return false;
}
for (int i= 0; i < path1.length; i++) {
if (!path1[i].equals(path2[i])) {
return false;
}
}
return true;
}
protected synchronized void evaluationStarts() {
if (fThread != null) {
try {
IThread thread = fThread;
fThread = null;
thread.resume();
} catch (DebugException e) {
JDIDebugUIPlugin.log(e);
showException(e);
return;
}
}
fEvaluating = true;
setTitleImage();
fireEvalStateChanged();
showStatus(SnippetMessages.getString("SnippetEditor.evaluating")); //$NON-NLS-1$
getSourceViewer().setEditable(false);
}
/**
* Sets the tab image to indicate whether in the process of
* evaluating or not.
*/
protected void setTitleImage() {
Image image=null;
if (fEvaluating) {
fOldTitleImage= getTitleImage();
image= JavaDebugImages.get(JavaDebugImages.IMG_OBJS_SNIPPET_EVALUATING);
} else {
image= fOldTitleImage;
fOldTitleImage= null;
}
if (image != null) {
setTitleImage(image);
}
}
protected void evaluationEnds() {
Runnable r = new Runnable() {
@Override
public void run() {
fEvaluating= false;
setTitleImage();
fireEvalStateChanged();
showStatus(""); //$NON-NLS-1$
getSourceViewer().setEditable(true);
}
};
async(r);
}
protected void showStatus(String message) {
IEditorSite site=(IEditorSite)getSite();
EditorActionBarContributor contributor= (EditorActionBarContributor)site.getActionBarContributor();
contributor.getActionBars().getStatusLineManager().setMessage(message);
}
protected String[] getClassPath(IJavaProject project) {
try {
return JavaRuntime.computeDefaultRuntimeClassPath(project);
} catch (CoreException e) {
JDIDebugUIPlugin.log(e);
return new String[0];
}
}
protected Shell getShell() {
return getSite().getShell();
}
/**
* @see IDebugEventFilter#filterDebugEvents(DebugEvent[])
*/
@Override
public DebugEvent[] filterDebugEvents(DebugEvent[] events) {
for (DebugEvent e : events) {
Object source = e.getSource();
if (source instanceof IDebugElement) {
IDebugElement de = (IDebugElement)source;
if (de instanceof IDebugTarget) {
if (de.getDebugTarget().equals(fVM)) {
if (e.getKind() == DebugEvent.TERMINATE) {
setThread(null);
Runnable r = new Runnable() {
@Override
public void run() {
vmTerminated();
}
};
getShell().getDisplay().asyncExec(r);
}
}
} else if (de instanceof IJavaThread) {
if (e.getKind() == DebugEvent.SUSPEND) {
IJavaThread jt = (IJavaThread)de;
try {
if (jt.equals(getThread()) && e.getDetail() == DebugEvent.EVALUATION) {
return null;
}
IJavaStackFrame f= (IJavaStackFrame)jt.getTopStackFrame();
if (f != null) {
IJavaDebugTarget target = (IJavaDebugTarget) f.getDebugTarget();
IBreakpoint[] bps = jt.getBreakpoints();
//last line of the eval method in ScrapbookMain1?
int lineNumber = f.getLineNumber();
if (e.getDetail() == DebugEvent.STEP_END && (lineNumber == 28)
&& f.getDeclaringTypeName().equals("org.eclipse.jdt.internal.debug.ui.snippeteditor.ScrapbookMain1") //$NON-NLS-1$
&& jt.getDebugTarget() == fVM) {
// restore step filters
target.setStepFiltersEnabled(fStepFiltersSetting);
setThread(jt);
return null;
} else if (e.getDetail() == DebugEvent.BREAKPOINT && bps.length > 0 && bps[0].equals(ScrapbookLauncher.getDefault().getMagicBreakpoint(jt.getDebugTarget()))) {
// locate the 'eval' method and step over
for (IStackFrame stackframe : jt.getStackFrames()) {
IJavaStackFrame frame = (IJavaStackFrame) stackframe;
if (frame.getReceivingTypeName().equals("org.eclipse.jdt.internal.debug.ui.snippeteditor.ScrapbookMain1") && frame.getName().equals("eval")) { //$NON-NLS-1$ //$NON-NLS-2$
// ignore step filters for this step
fStepFiltersSetting = target.isStepFiltersEnabled();
target.setStepFiltersEnabled(false);
frame.stepOver();
return null;
}
}
}
}
} catch (DebugException ex) {
JDIDebugUIPlugin.log(ex);
}
}
}
}
}
return events;
}
/* (non-Javadoc)
* @see org.eclipse.ui.texteditor.AbstractTextEditor#affectsTextPresentation(org.eclipse.jface.util.PropertyChangeEvent)
*/
@Override
protected boolean affectsTextPresentation(PropertyChangeEvent event) {
JavaSourceViewerConfiguration sourceViewerConfiguration = (JavaSourceViewerConfiguration) getSourceViewerConfiguration();
return sourceViewerConfiguration.affectsTextPresentation(event);
}
/* (non-Javadoc)
* @see org.eclipse.ui.texteditor.AbstractTextEditor#handlePreferenceStoreChanged(org.eclipse.jface.util.PropertyChangeEvent)
*/
@Override
protected void handlePreferenceStoreChanged(PropertyChangeEvent event) {
JDISourceViewer isv= (JDISourceViewer) getSourceViewer();
if (isv != null) {
IContentAssistant assistant= isv.getContentAssistant();
if (assistant instanceof ContentAssistant) {
JDIContentAssistPreference.changeConfiguration((ContentAssistant) assistant, event);
}
SourceViewerConfiguration configuration = getSourceViewerConfiguration();
if (configuration instanceof JavaSourceViewerConfiguration) {
JavaSourceViewerConfiguration jsv = (JavaSourceViewerConfiguration) configuration;
if (jsv.affectsTextPresentation(event)) {
jsv.handlePropertyChangeEvent(event);
isv.invalidateTextPresentation();
}
}
super.handlePreferenceStoreChanged(event);
}
}
protected IJavaThread getThread() {
return fThread;
}
/**
* Sets the thread to perform any evaluations in.
* Notifies the WaitThread waiting on getting an evaluation thread
* to perform an evaluation.
*/
protected synchronized void setThread(IJavaThread thread) {
fThread= thread;
notifyAll();
}
protected void launchVM() {
DebugPlugin.getDefault().addDebugEventFilter(this);
fLaunchedClassPath = getClassPath(getJavaProject());
fLaunchedWorkingDir = getWorkingDirectoryAttribute();
fLaunchedVMArgs = getVMArgsAttribute();
fLaunchedVM = getVMInstall();
Runnable r = new Runnable() {
@Override
public void run() {
ScrapbookLauncher.getDefault().launch(getFile());
}
};
BusyIndicator.showWhile(getShell().getDisplay(), r);
}
/**
* Return the <code>IFile</code> associated with the current
* editor input. Will return <code>null</code> if the current
* editor input is for an external file
*/
public IFile getFile() {
IEditorInput input= getEditorInput();
return input.getAdapter(IFile.class);
}
/* (non-Javadoc)
* @see org.eclipse.ui.texteditor.AbstractTextEditor#updateSelectionDependentActions()
*/
@Override
protected void updateSelectionDependentActions() {
super.updateSelectionDependentActions();
fireEvalStateChanged();
}
/**
* Terminates existing VM on a rename of the editor
*/
@Override
protected void setPartName(String title) {
cleanupOnRenameOrMove();
super.setPartName(title);
}
/**
* If the launch configuration has been copied, moved or
* renamed, shut down any running VM and clear the relevant cached information.
*/
protected void cleanupOnRenameOrMove() {
if(isVMLaunched()) {
shutDownVM();
} else {
fThread= null;
fEvaluationContext= null;
fLaunchedClassPath= null;
if (fEngine != null) {
fEngine.dispose();
fEngine= null;
}
}
fJavaProject= null;
}
/**
* Returns whether this editor has been opened on a resource that
* is in a Java project.
*/
protected boolean isInJavaProject() {
try {
return findJavaProject() != null;
} catch (CoreException ce) {
JDIDebugUIPlugin.log(ce);
}
return false;
}
/**
* Displays an error dialog indicating that evaluation
* cannot occur outside of a Java Project.
*/
protected void reportNotInJavaProjectError() {
String projectName= null;
IFile file= getFile();
if (file != null) {
IProject p= file.getProject();
projectName= p.getName();
}
String message= ""; //$NON-NLS-1$
if (projectName != null) {
message = projectName + SnippetMessages.getString("JavaSnippetEditor._is_not_a_Java_Project._n_1"); //$NON-NLS-1$
}
showError(message + SnippetMessages.getString("JavaSnippetEditor.Unable_to_perform_evaluation_outside_of_a_Java_Project_2")); //$NON-NLS-1$
}
/**
* Asks the user for the workspace path
* of a file resource and saves the document there.
* @see org.eclipse.ui.texteditor.AbstractTextEditor#performSaveAs(org.eclipse.core.runtime.IProgressMonitor)
*/
@Override
protected void performSaveAs(IProgressMonitor progressMonitor) {
Shell shell= getSite().getShell();
SaveAsDialog dialog= new SaveAsDialog(shell);
dialog.open();
IPath path= dialog.getResult();
if (path == null) {
if (progressMonitor != null) {
progressMonitor.setCanceled(true);
}
return;
}
IWorkspace workspace= ResourcesPlugin.getWorkspace();
IFile file= workspace.getRoot().getFile(path);
final IEditorInput newInput= new FileEditorInput(file);
WorkspaceModifyOperation op= new WorkspaceModifyOperation() {
@Override
public void execute(final IProgressMonitor monitor) throws CoreException {
IDocumentProvider dp= getDocumentProvider();
dp.saveDocument(monitor, newInput, dp.getDocument(getEditorInput()), true);
}
};
boolean success= false;
try {
getDocumentProvider().aboutToChange(newInput);
PlatformUI.getWorkbench().getProgressService().busyCursorWhile(op);
success= true;
} catch (InterruptedException x) {
} catch (InvocationTargetException x) {
JDIDebugUIPlugin.log(x);
String title= SnippetMessages.getString("JavaSnippetEditor.Problems_During_Save_As..._3"); //$NON-NLS-1$
String msg= SnippetMessages.getString("JavaSnippetEditor.Save_could_not_be_completed.__4") + x.getTargetException().getMessage(); //$NON-NLS-1$
MessageDialog.openError(shell, title, msg);
} finally {
getDocumentProvider().changed(newInput);
if (success) {
setInput(newInput);
}
}
if (progressMonitor != null) {
progressMonitor.setCanceled(!success);
}
}
/* (non-Javadoc)
* @see org.eclipse.ui.ISaveablePart#isSaveAsAllowed()
*/
@Override
public boolean isSaveAsAllowed() {
return true;
}
protected IClassFileEvaluationEngine getEvaluationEngine() {
if (fEngine == null) {
IPath outputLocation = getJavaProject().getProject().getWorkingLocation(JDIDebugUIPlugin.getUniqueIdentifier());
java.io.File f = new java.io.File(outputLocation.toOSString());
fEngine = EvaluationManager.newClassFileEvaluationEngine(getJavaProject(), (IJavaDebugTarget)getThread().getDebugTarget(), f);
}
if (getImports() != null) {
fEngine.setImports(getImports());
} else {
fEngine.setImports(new String[]{});
}
return fEngine;
}
/* (non-Javadoc)
* @see org.eclipse.ui.texteditor.AbstractTextEditor#createSourceViewer(org.eclipse.swt.widgets.Composite, org.eclipse.jface.text.source.IVerticalRuler, int)
*/
@Override
protected ISourceViewer createSourceViewer(Composite parent, IVerticalRuler ruler, int styles) {
fAnnotationAccess= getAnnotationAccess();
fOverviewRuler= createOverviewRuler(getSharedColors());
ISourceViewer viewer= new JDISourceViewer(parent, ruler, getOverviewRuler(), isOverviewRulerVisible(), styles | SWT.LEFT_TO_RIGHT);
// ensure decoration support has been created and configured.
getSourceViewerDecorationSupport(viewer);
return viewer;
}
/**
* Returns the working directory attribute for this scrapbook
*/
protected String getWorkingDirectoryAttribute() {
IFile file= getFile();
if (file != null) {
try {
return ScrapbookLauncher.getWorkingDirectoryAttribute(file);
} catch (CoreException e) {
JDIDebugUIPlugin.log(e);
}
}
return null;
}
/**
* Returns the working directory attribute for this scrapbook
*/
protected String getVMArgsAttribute() {
IFile file= getFile();
if (file != null) {
try {
return ScrapbookLauncher.getVMArgsAttribute(file);
} catch (CoreException e) {
JDIDebugUIPlugin.log(e);
}
}
return null;
}
/**
* Returns the vm install for this scrapbook
*/
protected IVMInstall getVMInstall() {
IFile file= getFile();
if (file != null) {
try {
return ScrapbookLauncher.getVMInstall(file);
} catch (CoreException e) {
JDIDebugUIPlugin.log(e);
}
}
return null;
}
/**
* Executes the given runnable in the Display thread
*/
protected void async(Runnable r) {
Control control= getVerticalRuler().getControl();
if (!control.isDisposed()) {
control.getDisplay().asyncExec(r);
}
}
protected void showAndSelect(final String text, final int offset) {
Runnable r = new Runnable() {
@Override
public void run() {
try {
getSourceViewer().getDocument().replace(offset, 0, text);
} catch (BadLocationException e) {
JDIDebugUIPlugin.log(e);
}
selectAndReveal(offset, text.length());
}
};
async(r);
}
@SuppressWarnings("unchecked")
@Override
public <T> T getAdapter(Class<T> required) {
if (required == IShowInTargetList.class) {
return (T) new IShowInTargetList() {
@Override
public String[] getShowInTargetIds() {
return new String[] { JavaUI.ID_PACKAGES, IPageLayout.ID_RES_NAV };
}
};
}
return super.getAdapter(required);
}
}