/******************************************************************************* * Copyright (c) 2000, 2015 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 * 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 { IThread[] threads= fVM.getThreads(); for (int i = 0; i < threads.length; i++) { IThread iThread = threads[i]; if (iThread.isSuspended()) { iThread.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()) { List<ISnippetStateChangedListener> v = new ArrayList<>(fSnippetStateListeners); for (int i= 0; i < v.size(); i++) { ISnippetStateChangedListener l= v.get(i); l.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) { StringBuffer resultString= new StringBuffer(); 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 StringBuffer errorString = new StringBuffer(); for (int i = 0; i < errors.length; i++) { errorString.append(errors[i] + 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 (int i = 0; i < events.length; i++) { DebugEvent e = events[i]; 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 IStackFrame[] frames = jt.getStackFrames(); for (int j = 0; j < frames.length; j++) { IJavaStackFrame frame = (IJavaStackFrame)frames[j]; 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); } }