| /******************************************************************************* |
| * Copyright (c) 2010, 2021 Obeo. |
| * 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: |
| * Obeo - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.acceleo.ui.interpreter.view; |
| |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.concurrent.Callable; |
| import java.util.concurrent.CancellationException; |
| import java.util.concurrent.ExecutionException; |
| import java.util.concurrent.ExecutorService; |
| import java.util.concurrent.Executors; |
| import java.util.concurrent.Future; |
| |
| import org.eclipse.acceleo.ui.interpreter.InterpreterPlugin; |
| import org.eclipse.acceleo.ui.interpreter.internal.IInterpreterConstants; |
| import org.eclipse.acceleo.ui.interpreter.internal.InterpreterImages; |
| import org.eclipse.acceleo.ui.interpreter.internal.InterpreterMessages; |
| import org.eclipse.acceleo.ui.interpreter.internal.SWTUtil; |
| import org.eclipse.acceleo.ui.interpreter.internal.compatibility.view.FormMessageManagerFactory; |
| import org.eclipse.acceleo.ui.interpreter.internal.compatibility.view.IFormMessageManager; |
| import org.eclipse.acceleo.ui.interpreter.internal.language.DefaultLanguageInterpreter; |
| import org.eclipse.acceleo.ui.interpreter.internal.language.LanguageInterpreterDescriptor; |
| import org.eclipse.acceleo.ui.interpreter.internal.language.LanguageInterpreterRegistry; |
| import org.eclipse.acceleo.ui.interpreter.internal.optional.InterpreterDependencyChecks; |
| import org.eclipse.acceleo.ui.interpreter.internal.optional.debug.DebugViewHelper; |
| import org.eclipse.acceleo.ui.interpreter.internal.view.GeneratedTextDialog; |
| import org.eclipse.acceleo.ui.interpreter.internal.view.InterpreterFileStorage; |
| import org.eclipse.acceleo.ui.interpreter.internal.view.ResultDragListener; |
| import org.eclipse.acceleo.ui.interpreter.internal.view.StorageEditorInput; |
| import org.eclipse.acceleo.ui.interpreter.internal.view.VariableContentProvider; |
| import org.eclipse.acceleo.ui.interpreter.internal.view.VariableDropListener; |
| import org.eclipse.acceleo.ui.interpreter.internal.view.VariableLabelProvider; |
| import org.eclipse.acceleo.ui.interpreter.internal.view.actions.ClearExpressionViewerAction; |
| import org.eclipse.acceleo.ui.interpreter.internal.view.actions.ClearResultViewerAction; |
| import org.eclipse.acceleo.ui.interpreter.internal.view.actions.ClearVariableViewerAction; |
| import org.eclipse.acceleo.ui.interpreter.internal.view.actions.DeleteVariableOrValueAction; |
| import org.eclipse.acceleo.ui.interpreter.internal.view.actions.EvaluateAction; |
| import org.eclipse.acceleo.ui.interpreter.internal.view.actions.LexicalSortAction; |
| import org.eclipse.acceleo.ui.interpreter.internal.view.actions.LinkWithEditorContextAction; |
| import org.eclipse.acceleo.ui.interpreter.internal.view.actions.NewVariableAction; |
| import org.eclipse.acceleo.ui.interpreter.internal.view.actions.NewVariableWizardAction; |
| import org.eclipse.acceleo.ui.interpreter.internal.view.actions.RenameVariableAction; |
| import org.eclipse.acceleo.ui.interpreter.internal.view.actions.ToggleRealTimeAction; |
| import org.eclipse.acceleo.ui.interpreter.internal.view.actions.ToggleStepByStepVisibilityAction; |
| import org.eclipse.acceleo.ui.interpreter.internal.view.actions.ToggleVariableVisibilityAction; |
| import org.eclipse.acceleo.ui.interpreter.language.AbstractLanguageInterpreter; |
| import org.eclipse.acceleo.ui.interpreter.language.CompilationResult; |
| import org.eclipse.acceleo.ui.interpreter.language.EvaluationContext; |
| import org.eclipse.acceleo.ui.interpreter.language.EvaluationResult; |
| import org.eclipse.acceleo.ui.interpreter.language.IInterpreterSourceViewer; |
| import org.eclipse.acceleo.ui.interpreter.language.InterpreterContext; |
| import org.eclipse.acceleo.ui.interpreter.language.SplitExpression; |
| import org.eclipse.acceleo.ui.interpreter.language.SubExpression; |
| import org.eclipse.acceleo.ui.interpreter.view.providers.ResultContentProvider; |
| import org.eclipse.acceleo.ui.interpreter.view.providers.ResultLabelProvider; |
| import org.eclipse.acceleo.ui.interpreter.view.providers.StepByStepContentProvider; |
| import org.eclipse.acceleo.ui.interpreter.view.providers.StepLabelProvider; |
| import org.eclipse.core.commands.IHandler; |
| import org.eclipse.core.resources.IStorage; |
| import org.eclipse.core.runtime.IAdaptable; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.core.runtime.MultiStatus; |
| import org.eclipse.core.runtime.Platform; |
| import org.eclipse.core.runtime.Status; |
| import org.eclipse.emf.common.notify.AdapterFactory; |
| import org.eclipse.emf.common.notify.Notifier; |
| import org.eclipse.emf.ecore.EObject; |
| import org.eclipse.emf.edit.provider.ComposedAdapterFactory; |
| import org.eclipse.emf.edit.provider.ReflectiveItemProviderAdapterFactory; |
| import org.eclipse.emf.edit.ui.dnd.LocalTransfer; |
| import org.eclipse.jface.action.Action; |
| import org.eclipse.jface.action.IAction; |
| import org.eclipse.jface.action.IContributionItem; |
| import org.eclipse.jface.action.IMenuListener; |
| import org.eclipse.jface.action.IMenuManager; |
| import org.eclipse.jface.action.IToolBarManager; |
| import org.eclipse.jface.action.MenuManager; |
| import org.eclipse.jface.action.Separator; |
| import org.eclipse.jface.action.ToolBarManager; |
| import org.eclipse.jface.commands.ActionHandler; |
| import org.eclipse.jface.dialogs.IMessageProvider; |
| import org.eclipse.jface.resource.ImageDescriptor; |
| import org.eclipse.jface.text.Document; |
| import org.eclipse.jface.text.ITextListener; |
| import org.eclipse.jface.text.ITextOperationTarget; |
| import org.eclipse.jface.text.ITextSelection; |
| import org.eclipse.jface.text.TextEvent; |
| import org.eclipse.jface.text.TextSelection; |
| import org.eclipse.jface.text.source.SourceViewer; |
| import org.eclipse.jface.util.LocalSelectionTransfer; |
| import org.eclipse.jface.viewers.ColumnViewerToolTipSupport; |
| import org.eclipse.jface.viewers.DoubleClickEvent; |
| import org.eclipse.jface.viewers.IDoubleClickListener; |
| import org.eclipse.jface.viewers.ISelection; |
| import org.eclipse.jface.viewers.ISelectionChangedListener; |
| import org.eclipse.jface.viewers.IStructuredSelection; |
| import org.eclipse.jface.viewers.ITreeSelection; |
| import org.eclipse.jface.viewers.SelectionChangedEvent; |
| import org.eclipse.jface.viewers.StructuredViewer; |
| import org.eclipse.jface.viewers.TreePath; |
| import org.eclipse.jface.viewers.TreeViewer; |
| import org.eclipse.jface.viewers.Viewer; |
| import org.eclipse.swt.SWT; |
| import org.eclipse.swt.custom.SashForm; |
| import org.eclipse.swt.dnd.DND; |
| import org.eclipse.swt.dnd.Transfer; |
| import org.eclipse.swt.events.DisposeEvent; |
| import org.eclipse.swt.events.DisposeListener; |
| import org.eclipse.swt.events.KeyAdapter; |
| import org.eclipse.swt.events.KeyEvent; |
| import org.eclipse.swt.graphics.Cursor; |
| import org.eclipse.swt.graphics.Image; |
| import org.eclipse.swt.layout.FillLayout; |
| import org.eclipse.swt.layout.GridData; |
| import org.eclipse.swt.layout.GridLayout; |
| import org.eclipse.swt.widgets.Composite; |
| import org.eclipse.swt.widgets.Control; |
| import org.eclipse.swt.widgets.Display; |
| import org.eclipse.swt.widgets.Menu; |
| import org.eclipse.swt.widgets.ToolBar; |
| import org.eclipse.swt.widgets.Tree; |
| import org.eclipse.swt.widgets.TreeItem; |
| import org.eclipse.ui.IEditorDescriptor; |
| import org.eclipse.ui.IEditorInput; |
| import org.eclipse.ui.IEditorPart; |
| import org.eclipse.ui.IEditorReference; |
| import org.eclipse.ui.IMemento; |
| import org.eclipse.ui.IPartListener; |
| import org.eclipse.ui.IPartListener2; |
| import org.eclipse.ui.ISelectionListener; |
| import org.eclipse.ui.IViewSite; |
| import org.eclipse.ui.IWorkbench; |
| import org.eclipse.ui.IWorkbenchActionConstants; |
| import org.eclipse.ui.IWorkbenchPage; |
| import org.eclipse.ui.IWorkbenchPart; |
| import org.eclipse.ui.IWorkbenchPartReference; |
| import org.eclipse.ui.IWorkbenchWindow; |
| import org.eclipse.ui.PartInitException; |
| import org.eclipse.ui.PlatformUI; |
| import org.eclipse.ui.contexts.IContextActivation; |
| import org.eclipse.ui.contexts.IContextService; |
| import org.eclipse.ui.editors.text.EditorsUI; |
| import org.eclipse.ui.editors.text.TextSourceViewerConfiguration; |
| import org.eclipse.ui.forms.widgets.ExpandableComposite; |
| import org.eclipse.ui.forms.widgets.Form; |
| import org.eclipse.ui.forms.widgets.FormToolkit; |
| import org.eclipse.ui.forms.widgets.Section; |
| import org.eclipse.ui.handlers.IHandlerActivation; |
| import org.eclipse.ui.handlers.IHandlerService; |
| import org.eclipse.ui.part.ViewPart; |
| import org.eclipse.ui.texteditor.ITextEditorActionDefinitionIds; |
| |
| /** |
| * The Actual "Interpreter" view that will be displayed in the Eclipse workbench. |
| * |
| * @author <a href="mailto:laurent.goubet@obeo.fr">Laurent Goubet</a> |
| */ |
| public class InterpreterView extends ViewPart { |
| /** Prefix of the messages corresponding to compilation problems. */ |
| protected static final String COMPILATION_MESSAGE_PREFIX = "compilation.message"; //$NON-NLS-1$ |
| |
| /** Prefix of the messages corresponding to evaluation problems. */ |
| protected static final String EVALUATION_MESSAGE_PREFIX = "evaluation.message"; //$NON-NLS-1$ |
| |
| /** |
| * This will be used as the key for the "information" message that the interpreter will display for |
| * compilation "OK" status. There will only be one. |
| */ |
| private static final String COMPILATION_INFO_MESSAGE_KEY = "interpreter.compilation.info.message"; //$NON-NLS-1$ |
| |
| /** |
| * This will be used as the key for the "information" message that the interpreter will display for |
| * evaluation "OK" status. There will only be one. |
| */ |
| private static final String EVALUATION_INFO_MESSAGE_KEY = "interpreter.evaluation.info.message"; //$NON-NLS-1$ |
| |
| /** ID of the interpreter view context. Must be kept in sync with the plugin.xml declaration. */ |
| private static final String INTERPRETER_VIEW_CONTEXT_ID = "org.eclipse.acceleo.ui.interpreter.interpreterview"; //$NON-NLS-1$ |
| |
| /** ID of the toolbar's group in which we'll add language specific actions. */ |
| private static final String LANGUAGE_SPECIFIC_ACTION_GROUP = "LanguageSpecificActions"; //$NON-NLS-1$ |
| |
| /** Key for the currently selected language as stored in this view's memento. */ |
| private static final String MEMENTO_CURRENT_LANGUAGE_KEY = "org.eclipse.acceleo.ui.interpreter.memento.current.language"; //$NON-NLS-1$ |
| |
| /** Key for the expression as stored in this view's memento. */ |
| private static final String MEMENTO_EXPRESSION_KEY = "org.eclipse.acceleo.ui.interpreter.memento.expression"; //$NON-NLS-1$ |
| |
| /** Key for the real-time compilation state as stored in this view's memento. */ |
| private static final String MEMENTO_REAL_TIME_KEY = "org.eclipse.acceleo.ui.interpreter.memento.realtime"; //$NON-NLS-1$ |
| |
| /** Key for the hidden state of the variable viewer as stored in this view's memento. */ |
| private static final String MEMENTO_VARIABLES_VISIBLE_KEY = "org.eclipse.acceleo.ui.interpreter.memento.variables.hide"; //$NON-NLS-1$ |
| |
| /** Key for the hidden state of the Sub-Expressions viewer as stored in this view's memento. */ |
| private static final String MEMENTO_SUB_EXPRESSIONS_VISIBLE_KEY = "org.eclipse.acceleo.ui.interpreter.memento.subexpressions.hide"; //$NON-NLS-1$ |
| |
| /** Key for the sorted state of the result viewer as stored in this view's memento. */ |
| private static final String MEMENTO_RESULT_SORTED_KEY = "org.eclipse.acceleo.ui.interpreter.memento.result.sorted"; //$NON-NLS-1$ |
| |
| /** Key for the sorted state of the Sub-Expressions viewer as stored in this view's memento. */ |
| private static final String MEMENTO_SUB_EXPRESSIONS_SORTED_KEY = "org.eclipse.acceleo.ui.interpreter.memento.subexpressions.sorted"; //$NON-NLS-1$ |
| |
| /** We'll use this as the id of our viewers' menus. */ |
| private static final String MENU_ID = "#PopupMenu"; //$NON-NLS-1$ |
| |
| /** |
| * Id for command "Redo" in category "Edit". This should be directly referenced from |
| * org.eclipse.ui.IWorkbenchCommandConstants.EDIT_REDO ... though that would break our Eclipse 3.4 |
| * compatibility. |
| */ |
| private static final String WORKBENCH_CONSTANT_EDIT_REDO = "org.eclipse.ui.edit.redo"; //$NON-NLS-1$ |
| |
| /** |
| * Id for command "Undo" in category "Edit". This should be directly referenced from |
| * org.eclipse.ui.IWorkbenchCommandConstants.EDIT_UNDO ... though that would break our Eclipse 3.4 |
| * compatibility. |
| */ |
| private static final String WORKBENCH_CONSTANT_EDIT_UNDO = "org.eclipse.ui.edit.undo"; //$NON-NLS-1$ |
| |
| /** This will be added to the result view for OclVoid instances. */ |
| private static final String NULL_RESULT_OBJECT = "null"; //$NON-NLS-1$ |
| |
| /** |
| * If we have a compilation result, this will contain it (note that some language are not compiled, thus |
| * an evaluation task can legally be created while this is <code>null</code>. |
| */ |
| protected CompilationResult compilationResult; |
| |
| /** Thread which purpose is to compile the expression and update the context with the result. */ |
| protected CompilationThread compilationThread; |
| |
| /** Keeps a reference to the "link with editor" action. */ |
| protected LinkWithEditorContextAction linkWithEditorContextAction; |
| |
| /** This executor service will be used in order to launch the evaluation tasks of this interpreter. */ |
| /* package */ExecutorService evaluationPool = Executors.newSingleThreadExecutor(); |
| |
| /** This executor service will be used in order to launch the splitting tasks of this interpreter. */ |
| /* package */ExecutorService splittingPool = Executors.newSingleThreadExecutor(); |
| |
| /** This will hold the real-time evaluation thread. */ |
| /* package */RealTimeThread realTimeThread; |
| |
| /** Listener reacting to activations of this view. */ |
| private IPartListener activationListener; |
| |
| /** Content assist activation token. This will be needed to deactivate our handler. */ |
| private IHandlerActivation activationTokenContentAssist; |
| |
| /** Redo activation token. This will be needed to deactivate our handler. */ |
| private IHandlerActivation activationTokenRedo; |
| |
| /** Undo action activation token. This will be needed to deactivate our handler. */ |
| private IHandlerActivation activationTokenUndo; |
| |
| /** This executor service will be used in order to launch the compilation tasks of this interpreter. */ |
| private ExecutorService compilationPool = Executors.newSingleThreadExecutor(); |
| |
| /** Context activation token. This will be needed to deactivate it. */ |
| private IContextActivation contextActivationToken; |
| |
| /** Currently selected language descriptor. */ |
| private LanguageInterpreterDescriptor currentLanguage; |
| |
| /** Currently selected language interpreter. */ |
| private AbstractLanguageInterpreter currentLanguageInterpreter; |
| |
| /** This will be used to listen to editor focus changes in the workbench. */ |
| private IPartListener2 editorPartListener = new InterpreterEditorPartListener(); |
| |
| /** This will be used to listen to EObject selection within the workbench. */ |
| private ISelectionListener eobjectSelectionListener; |
| |
| /** Thread which purpose is to evaluate the expression and update the view with the result. */ |
| private EvaluationThread evaluationThread; |
| |
| /** Thread which purpose is to split the expression and update the view with the sub-expressions tree. */ |
| private ExpressionSplittingThread expressionSplittingThread; |
| |
| /** |
| * Keeps a reference to the "expression" section of the interpreter form. This will be used to re-create |
| * the result viewer when changing language. |
| */ |
| private Section expressionSection; |
| |
| /** |
| * This source viewer will be used in order to display the "expression" area in which the user can type |
| * the expression that is to be evaluated. |
| */ |
| private SourceViewer expressionViewer; |
| |
| /** We'll create this {@link SashForm} as the main body of the interpreter form. */ |
| private SashForm formBody; |
| |
| /** We'll create this {@link SashForm} as the sub-expression and result body of the interpreter form. */ |
| private SashForm bottomLeftColumn; |
| |
| /** The composite that will display the sub-expressions. */ |
| private Composite subExpressionComposite; |
| |
| /** |
| * Keeps a reference to the toolkit used to create our form. This will be used when switching languages. |
| */ |
| private FormToolkit formToolkit; |
| |
| /** Form that will contain the interpreter itself (left part of the view). */ |
| private Form interpreterForm; |
| |
| /** Kept as an instance member, this will allow us to set unique identifiers to the status messages. */ |
| private int messageCount; |
| |
| /** |
| * We'll use this indirection layer for the form's message manager in order to bypass the API breakage for |
| * Ganymede. |
| */ |
| private IFormMessageManager messageManager; |
| |
| /** Memento from which to restore this view's state. */ |
| private IMemento partMemento; |
| |
| /** This indicates whether we should be compiling the expression in real-time. */ |
| private boolean realTime; |
| |
| /** |
| * Keeps a reference to the "result" section of the interpreter form. This will be used to re-create the |
| * result viewer when changing language. |
| */ |
| private Section resultSection; |
| |
| /** Viewer in which we'll display the result of the evaluations. */ |
| private Viewer resultViewer; |
| |
| /** Viewer in which we'll display sub-expressions if the current language has a splitter. */ |
| private TreeViewer subExpressionViewer; |
| |
| /** |
| * This will hold the current selection of EObjects (in the workspace). |
| * |
| * @deprecated use {@link #selectedNotifiers} instead. |
| */ |
| @Deprecated |
| private List<EObject> selectedEObjects; |
| |
| /** This will hold the current selection of ENotifiers (in the workspace). */ |
| private List<Notifier> selectedNotifiers; |
| |
| /** The "right column" composite of the interpreter form, displaying the variables when not hidden. */ |
| private Composite variableColumn; |
| |
| /** Viewer in which we'll display the accessible variables. */ |
| private TreeViewer variableViewer; |
| |
| /** Indicates whether the variable viewer is visible. */ |
| private boolean variableVisible; |
| |
| /** Indicates whether the step-by-step viewer is visible. */ |
| private boolean subExpressionsVisible; |
| |
| /** |
| * Creates a tool bar for the given section. |
| * |
| * @param section |
| * The section for which we need a tool bar. |
| * @return The created tool bar. |
| */ |
| protected static final ToolBarManager createSectionToolBar(Section section) { |
| final ToolBarManager toolBarManager = new ToolBarManager(SWT.FLAT | SWT.HORIZONTAL); |
| final ToolBar toolBar = toolBarManager.createControl(section); |
| |
| final Cursor handCursor = new Cursor(Display.getCurrent(), SWT.CURSOR_HAND); |
| toolBar.setCursor(handCursor); |
| // Cursor needs to be explicitly disposed |
| toolBar.addDisposeListener(new DisposeListener() { |
| public void widgetDisposed(DisposeEvent e) { |
| if (!handCursor.isDisposed()) { |
| handCursor.dispose(); |
| } |
| } |
| }); |
| |
| section.setTextClient(toolBar); |
| toolBar.setData(toolBarManager); |
| toolBar.addDisposeListener(new DisposeListener() { |
| public void widgetDisposed(DisposeEvent e) { |
| toolBar.setData(null); |
| } |
| }); |
| |
| return toolBarManager; |
| } |
| |
| /** |
| * Returns the toolbar of the given section if any. |
| * |
| * @param section |
| * The section of which we need the toolbar. |
| * @return The toolbar of the given section if any. |
| */ |
| protected static final ToolBarManager getSectionToolBar(Section section) { |
| Control textClient = section.getTextClient(); |
| if (textClient instanceof ToolBar) { |
| ToolBar toolBar = (ToolBar)textClient; |
| Object data = toolBar.getData(); |
| if (data instanceof ToolBarManager) { |
| return (ToolBarManager)data; |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Opens the "add variable" wizard in order to create a new variable with the given value. |
| * |
| * @param variableValue |
| * The variable value |
| */ |
| public void addVariables(Object variableValue) { |
| NewVariableAction action = new NewVariableAction(variableViewer, variableValue); |
| action.run(); |
| } |
| |
| /** |
| * Asks for the compilation of the current expression. |
| * <p> |
| * This will take a snapshot of the current interpreter context and launch a new thread for the |
| * compilation. This thread can and will be cancelled whenever a new compilation is required. |
| * </p> |
| */ |
| private void compileExpression() { |
| final InterpreterContext context = getInterpreterContext(); |
| final Callable<CompilationResult> compilationTask = getCurrentLanguageInterpreter() |
| .getCompilationTask(context); |
| |
| clearCompilationMessages(); |
| |
| if (compilationTask != null) { |
| Future<CompilationResult> compilationFuture = compilationPool.submit(compilationTask); |
| compilationThread = new CompilationThread(compilationFuture); |
| compilationThread.start(); |
| } |
| } |
| |
| /** |
| * This evaluates the selected sub-step. |
| * |
| * @param expression |
| * The expression to evaluate. |
| */ |
| protected void evaluateSubExpression(final Object expression) { |
| if (this.expressionViewer == null || this.expressionViewer.getTextWidget() == null |
| || this.expressionViewer.getTextWidget().isDisposed()) { |
| return; |
| } |
| |
| // Cancel previous compilation task |
| if (compilationThread != null && !compilationThread.isInterrupted()) { |
| compilationThread.interrupt(); |
| } |
| // Cancel running evaluation task |
| if (evaluationThread != null && !evaluationThread.isInterrupted()) { |
| evaluationThread.interrupt(); |
| } |
| |
| clearCompilationMessages(); |
| Future<CompilationResult> compilationFuture = compilationPool |
| .submit(new Callable<CompilationResult>() { |
| public CompilationResult call() throws Exception { |
| return new CompilationResult(expression); |
| } |
| }); |
| compilationThread = new CompilationThread(compilationFuture); |
| compilationThread.start(); |
| |
| InterpreterContext interpreterContext = getInterpreterContext(); |
| if (interpreterContext != null) { |
| evaluationThread = new EvaluationThread(interpreterContext); |
| evaluationThread.start(); |
| } |
| } |
| |
| /** |
| * {@inheritDoc} |
| * |
| * @see org.eclipse.ui.part.WorkbenchPart#createPartControl(org.eclipse.swt.widgets.Composite) |
| */ |
| @Override |
| public void createPartControl(Composite parent) { |
| Composite rootContainer = new SashForm(parent, SWT.HORIZONTAL); |
| GridLayout layout = new GridLayout(); |
| layout.marginHeight = 0; |
| layout.marginWidth = 0; |
| layout.verticalSpacing = 0; |
| layout.horizontalSpacing = 0; |
| rootContainer.setLayout(layout); |
| |
| formToolkit = new FormToolkit(rootContainer.getDisplay()); |
| |
| createInterpreterForm(formToolkit, rootContainer); |
| |
| // The view's state has been restored on-the-fly. We can now discard the memento. |
| partMemento = null; |
| } |
| |
| /** |
| * {@inheritDoc} |
| * |
| * @see org.eclipse.ui.part.WorkbenchPart#dispose() |
| */ |
| @Override |
| public void dispose() { |
| if (this.compilationThread != null && !this.compilationThread.isInterrupted()) { |
| this.compilationThread.interrupt(); |
| } |
| if (this.evaluationThread != null && !this.evaluationThread.isInterrupted()) { |
| this.evaluationThread.interrupt(); |
| } |
| |
| if (contextActivationToken != null) { |
| IContextService contextService = (IContextService)getSite().getService(IContextService.class); |
| contextService.deactivateContext(contextActivationToken); |
| } |
| |
| IHandlerService handlerService = (IHandlerService)getSite().getService(IHandlerService.class); |
| if (activationTokenContentAssist != null) { |
| handlerService.deactivateHandler(activationTokenContentAssist); |
| } |
| if (activationTokenRedo != null) { |
| handlerService.deactivateHandler(activationTokenRedo); |
| } |
| if (activationTokenUndo != null) { |
| handlerService.deactivateHandler(activationTokenUndo); |
| } |
| |
| IWorkbenchWindow workbenchWindow = PlatformUI.getWorkbench().getActiveWorkbenchWindow(); |
| if (workbenchWindow != null && workbenchWindow.getActivePage() != null |
| && eobjectSelectionListener != null) { |
| workbenchWindow.getActivePage().removeSelectionListener(eobjectSelectionListener); |
| } |
| clearSelection(); |
| |
| IWorkbenchPage currentPage = getSite().getPage(); |
| if (currentPage != null) { |
| currentPage.removePartListener(editorPartListener); |
| } |
| |
| getSite().getPage().removePartListener(activationListener); |
| |
| super.dispose(); |
| } |
| |
| /** |
| * Compiles the current expression and launches its evaluation. |
| */ |
| public void compileAndEvaluate() { |
| boolean cancelRequest = false; |
| if (this.expressionViewer == null || this.expressionViewer.getTextWidget() == null |
| || this.expressionViewer.getTextWidget().isDisposed()) { |
| cancelRequest = true; |
| } else { |
| final String text = this.expressionViewer.getTextWidget().getText(); |
| if (text == null || text.length() == 0) { |
| cancelRequest = true; |
| } |
| } |
| if (cancelRequest) { |
| return; |
| } |
| |
| // Cancel previously running threads |
| if (compilationThread != null && !compilationThread.isInterrupted()) { |
| compilationThread.interrupt(); |
| } |
| if (evaluationThread != null && !evaluationThread.isInterrupted()) { |
| evaluationThread.interrupt(); |
| } |
| if (expressionSplittingThread != null && !expressionSplittingThread.isInterrupted()) { |
| expressionSplittingThread.interrupt(); |
| } |
| |
| compileExpression(); |
| evaluate(); |
| splitExpression(); |
| } |
| |
| /** |
| * Evaluates the currently entered expression with the current context. |
| */ |
| private void evaluate() { |
| clearEvaluationMessages(); |
| |
| InterpreterContext interpreterContext = getInterpreterContext(); |
| if (interpreterContext != null) { |
| evaluationThread = new EvaluationThread(interpreterContext); |
| evaluationThread.start(); |
| } |
| } |
| |
| /** |
| * This split the expressions into sub-steps. |
| */ |
| private void splitExpression() { |
| // populates the sub-expressions tree viewer |
| InterpreterContext interpreterContext = getInterpreterContext(); |
| if (interpreterContext != null) { |
| expressionSplittingThread = new ExpressionSplittingThread(interpreterContext); |
| expressionSplittingThread.start(); |
| } |
| } |
| |
| /** |
| * This sets sub expressions in the expected viewer. |
| * |
| * @param splitExpression |
| * The split expression. |
| */ |
| protected final void setSubExpressions(SplitExpression splitExpression) { |
| TreeViewer viewer = subExpressionViewer; |
| if (splitExpression != null) { |
| viewer.setInput(Collections.singleton(splitExpression)); |
| } else { |
| viewer.setInput(null); |
| } |
| } |
| |
| /** |
| * Returns the currently selected language. |
| * |
| * @return The currently selected language. |
| */ |
| public final AbstractLanguageInterpreter getCurrentLanguageInterpreter() { |
| if (currentLanguageInterpreter == null) { |
| if (getCurrentLanguageDescriptor() != null) { |
| currentLanguageInterpreter = getCurrentLanguageDescriptor().createLanguageInterpreter(); |
| } else { |
| currentLanguageInterpreter = new DefaultLanguageInterpreter(); |
| } |
| } |
| return currentLanguageInterpreter; |
| } |
| |
| /** |
| * Returns the current interpreter context. This will mainly be used by the compilation tasks of the |
| * language interpreters. |
| * |
| * @return The current interpreter context. |
| */ |
| @SuppressWarnings("unchecked") |
| public InterpreterContext getInterpreterContext() { |
| if (expressionViewer == null || expressionViewer.getTextWidget() == null) { |
| return null; |
| } |
| |
| String fullExpression = expressionViewer.getTextWidget().getText(); |
| |
| List<Notifier> targetNotifiers = selectedNotifiers; |
| if (targetNotifiers == null) { |
| targetNotifiers = Collections.emptyList(); |
| } |
| |
| final List<Variable> variables; |
| Object variableViewerInput = variableViewer.getInput(); |
| if (variableViewerInput instanceof List<?>) { |
| variables = new ArrayList<Variable>((List<Variable>)variableViewerInput); |
| } else { |
| variables = Collections.emptyList(); |
| } |
| |
| ISelection selection = expressionViewer.getSelection(); |
| if (selection == null |
| || (selection instanceof ITextSelection && ((ITextSelection)selection).getLength() == 0)) { |
| selection = new TextSelection(expressionViewer.getDocument(), 0, fullExpression.length()); |
| } |
| |
| // Is the "debug" view currently active and showing an Acceleo thread? |
| if (InterpreterDependencyChecks.isDebugAccessible()) { |
| List<Variable> debugVariables = DebugViewHelper.getCurrentDebugThreadVariables(); |
| for (Variable var : debugVariables) { |
| boolean duplicate = false; |
| for (int i = 0; i < variables.size() && !duplicate; i++) { |
| duplicate = variables.get(i).getName().equals(var.getName()); |
| } |
| if (!duplicate) { |
| variables.addAll(debugVariables); |
| } |
| } |
| } |
| |
| return new InterpreterContext(fullExpression, selection, targetNotifiers, variables); |
| } |
| |
| /** |
| * {@inheritDoc} |
| * |
| * @see org.eclipse.ui.part.ViewPart#init(org.eclipse.ui.IViewSite, org.eclipse.ui.IMemento) |
| */ |
| @Override |
| public void init(IViewSite site, IMemento memento) throws PartInitException { |
| super.init(site, memento); |
| this.partMemento = memento; |
| |
| IContextService contextService = (IContextService)site.getService(IContextService.class); |
| contextActivationToken = contextService.activateContext(INTERPRETER_VIEW_CONTEXT_ID); |
| |
| eobjectSelectionListener = new NotifierSelectionListener(); |
| activationListener = new ActivationListener(this); |
| site.getPage().addPartListener(activationListener); |
| site.getPage().addSelectionListener(eobjectSelectionListener); |
| |
| if (site.getPart() != null && site.getPage().getSelection() != null) { |
| eobjectSelectionListener.selectionChanged(site.getPart(), site.getPage().getSelection()); |
| } |
| } |
| |
| /** |
| * Indicates if the variables are visible in the view. |
| * |
| * @return <code>true</code> if the variables are visible, <code>false</code> otherwise. |
| */ |
| public boolean isVariableVisible() { |
| return variableVisible; |
| } |
| |
| /** Link the current language interpreter with the current editor. */ |
| public void linkWithEditorContext() { |
| IWorkbenchPage page = getSite().getPage(); |
| if (!linkWithEditorContextAction.isEnabled() || page == null || page.getActiveEditor() == null) { |
| return; |
| } |
| |
| IEditorPart activeEditor = page.getActiveEditor(); |
| if (linkWithEditorContextAction.isChecked()) { |
| getCurrentLanguageInterpreter().linkWithEditor(activeEditor); |
| linkWithEditorContextAction.changeTooltip(activeEditor); |
| } else { |
| getCurrentLanguageInterpreter().linkWithEditor(null); |
| linkWithEditorContextAction.changeTooltip(null); |
| /* |
| * Re-compute enablement : we may have left the toggle "enabled" while there is no active |
| * "linkable with" editors |
| */ |
| if (activeEditor == null) { |
| linkWithEditorContextAction.setEnabled(false); |
| } else { |
| linkWithEditorContextAction |
| .setEnabled(getCurrentLanguageInterpreter().canLinkWithEditor(activeEditor)); |
| } |
| } |
| } |
| |
| /** |
| * {@inheritDoc} |
| * |
| * @see org.eclipse.ui.part.ViewPart#saveState(org.eclipse.ui.IMemento) |
| */ |
| @Override |
| public void saveState(IMemento memento) { |
| if (partMemento != null) { |
| // Part had not been restored, keep old state |
| memento.putMemento(partMemento); |
| } else { |
| if (getCurrentLanguageDescriptor() != null) { |
| memento.putString(MEMENTO_CURRENT_LANGUAGE_KEY, |
| getCurrentLanguageDescriptor().getClassName()); |
| } |
| memento.putString(MEMENTO_EXPRESSION_KEY, expressionViewer.getTextWidget().getText()); |
| memento.putBoolean(MEMENTO_REAL_TIME_KEY, Boolean.valueOf(realTime)); |
| memento.putBoolean(MEMENTO_VARIABLES_VISIBLE_KEY, |
| Boolean.valueOf(variableViewer.getControl().isVisible())); |
| memento.putBoolean(MEMENTO_SUB_EXPRESSIONS_VISIBLE_KEY, |
| Boolean.valueOf(subExpressionViewer.getControl().isVisible())); |
| if (resultViewer instanceof StructuredViewer) { |
| memento.putBoolean(MEMENTO_RESULT_SORTED_KEY, |
| Boolean.valueOf(((StructuredViewer)resultViewer).getComparator() != null)); |
| } |
| memento.putBoolean(MEMENTO_SUB_EXPRESSIONS_SORTED_KEY, |
| Boolean.valueOf(subExpressionViewer.getComparator() != null)); |
| } |
| } |
| |
| /** |
| * {@inheritDoc} |
| * |
| * @see org.eclipse.ui.part.WorkbenchPart#setFocus() |
| */ |
| @Override |
| public void setFocus() { |
| if (expressionViewer != null) { |
| expressionViewer.getControl().setFocus(); |
| } |
| } |
| |
| /** Enables (or disables) the real-time evaluation of expressions. */ |
| public synchronized void toggleRealTime() { |
| realTime = !realTime; |
| if (realTime) { |
| realTimeThread = new RealTimeThread(); |
| |
| // Launch a compilation right from the get-go |
| compileAndEvaluate(); |
| |
| realTimeThread.start(); |
| } else { |
| if (realTimeThread != null) { |
| realTimeThread.interrupt(); |
| realTimeThread = null; |
| } |
| } |
| } |
| |
| /** |
| * Shows or hides the variables viewer. |
| */ |
| public void toggleVariableVisibility() { |
| if (variableColumn != null && !variableColumn.isDisposed()) { |
| variableVisible = !variableVisible; |
| variableColumn.setVisible(variableVisible); |
| final int[] newWeights; |
| if (variableVisible) { |
| newWeights = new int[] {3, 1, }; |
| } else { |
| newWeights = new int[] {1, 0, }; |
| } |
| formBody.setWeights(newWeights); |
| getForm().layout(); |
| } |
| } |
| |
| /** |
| * Shows or hides the step-by-step viewer. |
| */ |
| public void toggleStepByStepVisibility() { |
| if (subExpressionComposite != null && !subExpressionComposite.isDisposed()) { |
| subExpressionsVisible = !subExpressionsVisible; |
| subExpressionComposite.setVisible(subExpressionsVisible); |
| final int[] newWeights; |
| if (subExpressionsVisible) { |
| newWeights = new int[] {1, 1, }; |
| } else { |
| newWeights = new int[] {0, 1, }; |
| } |
| bottomLeftColumn.setWeights(newWeights); |
| getForm().layout(); |
| } |
| } |
| |
| /** |
| * Adds a new message to the form. |
| * |
| * @param messageKey |
| * Key of the message. Will be used to find and remove it later on. |
| * @param message |
| * Text of the message that is to be added to the form. |
| * @param messageType |
| * Type of the message as defined in {@link IMessageProvider}. |
| */ |
| protected final void addMessage(String messageKey, String message, int messageType) { |
| Control targetControl = null; |
| if (messageType != IMessageProvider.NONE && messageKey.startsWith(COMPILATION_MESSAGE_PREFIX)) { |
| targetControl = expressionSection; |
| } else if (messageType != IMessageProvider.NONE && messageKey.startsWith(EVALUATION_MESSAGE_PREFIX)) { |
| targetControl = resultSection; |
| } |
| |
| if (!getForm().isDisposed()) { |
| if (targetControl != null) { |
| getMessageManager().addMessage(messageKey, message, messageType, targetControl); |
| } else { |
| getMessageManager().addMessage(messageKey, message, messageType); |
| } |
| } |
| } |
| |
| /** |
| * This will be used by the {@link CompilationThread}s in order to parse the compilation or evaluation |
| * results' status and add the necessary problem message to the form. |
| * |
| * @param status |
| * Status which messages is to be added to the form. |
| * @param keyPrefix |
| * Prefix of the message key. |
| */ |
| protected final void addStatusMessages(IStatus status, String keyPrefix) { |
| if (status instanceof MultiStatus) { |
| for (IStatus child : status.getChildren()) { |
| addStatusMessages(child, keyPrefix); |
| } |
| } else { |
| String messageKey; |
| if (status.getSeverity() == IStatus.OK) { |
| if (keyPrefix.equals(COMPILATION_MESSAGE_PREFIX)) { |
| messageKey = COMPILATION_INFO_MESSAGE_KEY; |
| } else { |
| messageKey = EVALUATION_INFO_MESSAGE_KEY; |
| } |
| } else { |
| messageKey = keyPrefix + "." + messageCount++; //$NON-NLS-1$ |
| } |
| |
| addMessage(messageKey, status.getMessage(), convertStatusToMessageSeverity(status.getSeverity())); |
| } |
| } |
| |
| /** |
| * Creates a new list for the selection if needed, and adds the given object to it. |
| * |
| * @param object |
| * The element that is to be added to the current selection. |
| * @deprecated use {@link #addToSelection(Notifier)} instead. |
| */ |
| @Deprecated |
| protected void addToSelection(EObject object) { |
| if (selectedNotifiers == null) { |
| // Assumes the "usual" selection is always 1 element long |
| selectedNotifiers = new ArrayList<Notifier>(1); |
| selectedEObjects = new ArrayList<EObject>(1); |
| } |
| selectedNotifiers.add(object); |
| selectedEObjects.add(object); |
| } |
| |
| /** |
| * Creates a new list for the selection if needed, and adds the given notifier into it. |
| * |
| * @param notifier |
| * The element that is to be added to the current selection. |
| */ |
| protected void addToSelection(Notifier notifier) { |
| if (selectedNotifiers == null) { |
| // Assumes the "usual" selection is always 1 element long |
| selectedNotifiers = new ArrayList<Notifier>(1); |
| selectedEObjects = new ArrayList<EObject>(1); |
| } |
| selectedNotifiers.add(notifier); |
| if (notifier instanceof EObject) { |
| selectedEObjects.add((EObject)notifier); |
| } |
| } |
| |
| /** |
| * Clears all compilation messages out of the interpreter view. |
| */ |
| protected void clearCompilationMessages() { |
| /* |
| * add a dummy message to ensure there is always one while we clear the rest (we'll need to reset the |
| * color without having _all_ messages removed, lest the color stays at its previous state : bug |
| * 357906 in FormHeading.MessageRegion#showMessage(). |
| */ |
| final String dummyMessageKey = "none"; //$NON-NLS-1$ |
| getMessageManager().addMessage(dummyMessageKey, "", IMessageProvider.ERROR); //$NON-NLS-1$ |
| // Remove all actual messages |
| getMessageManager().removeMessage(COMPILATION_MESSAGE_PREFIX); |
| getMessageManager().removeMessages(expressionSection); |
| // Now, reset the color by modifying our (existing) dummy message to a lesser severity |
| getMessageManager().addMessage(dummyMessageKey, "", IMessageProvider.NONE); //$NON-NLS-1$ |
| // Finally, remove our dummy |
| getMessageManager().removeMessage(dummyMessageKey); |
| } |
| |
| /** |
| * Clears all evaluation messages out of the interpreter view. |
| */ |
| protected void clearEvaluationMessages() { |
| /* |
| * add a dummy message to ensure there is always one while we clear the rest (we'll need to reset the |
| * color without having _all_ messages removed, lest the color stays at its previous state : bug |
| * 357906 in FormHeading.MessageRegion#showMessage(). |
| */ |
| final String dummyMessageKey = "none"; //$NON-NLS-1$ |
| getMessageManager().addMessage(dummyMessageKey, "", IMessageProvider.ERROR); //$NON-NLS-1$ |
| // Remove all actual messages |
| getMessageManager().removeMessage(EVALUATION_INFO_MESSAGE_KEY); |
| getMessageManager().removeMessages(resultSection); |
| // Now, reset the color by modifying our (existing) dummy message to a lesser severity |
| getMessageManager().addMessage(dummyMessageKey, "", IMessageProvider.NONE); //$NON-NLS-1$ |
| // Finally, remove our dummy |
| getMessageManager().removeMessage(dummyMessageKey); |
| } |
| |
| /** |
| * If we currently have EObjects selected, this will clear the whole list. |
| */ |
| protected void clearSelection() { |
| if (selectedNotifiers != null) { |
| selectedNotifiers.clear(); |
| selectedNotifiers = null; |
| selectedEObjects.clear(); |
| selectedEObjects = null; |
| } |
| } |
| |
| /** |
| * Creates the adapter factory that will be used for our label and content providers. |
| * |
| * @return The adapter factory that will be used for our label and content providers. |
| */ |
| protected AdapterFactory createAdapterFactory() { |
| ComposedAdapterFactory adapterFactory = new ComposedAdapterFactory( |
| ComposedAdapterFactory.Descriptor.Registry.INSTANCE); |
| adapterFactory.addAdapterFactory(new ReflectiveItemProviderAdapterFactory()); |
| return adapterFactory; |
| } |
| |
| /** |
| * Creates the expression viewer menu listener. This listener is in charge of filling the menu's actions. |
| * |
| * @param viewer |
| * The expression viewer. |
| * @return The newly created listener. |
| */ |
| protected IMenuListener createExpressionMenuListener(SourceViewer viewer) { |
| return new ExpressionMenuListener(viewer); |
| } |
| |
| /** |
| * This will be called to create the "Expression" section (top part of the left column) of the |
| * "Interpreter" form. |
| * |
| * @param toolkit |
| * Toolkit that can be used to create form parts. |
| * @param leftColumn |
| * Left column of the "Interpreter" form. |
| */ |
| protected void createExpressionSection(FormToolkit toolkit, Composite leftColumn) { |
| expressionSection = toolkit.createSection(leftColumn, ExpandableComposite.TITLE_BAR); |
| expressionSection.setText(InterpreterMessages.getString("interpreter.view.expression.section.name")); //$NON-NLS-1$ |
| |
| Composite expressionSectionBody = toolkit.createComposite(expressionSection); |
| GridLayout expressionSectionLayout = new GridLayout(); |
| expressionSectionBody.setLayout(expressionSectionLayout); |
| |
| expressionViewer = createExpressionViewer(expressionSectionBody); |
| GridData gridData = new GridData(GridData.FILL_BOTH); |
| expressionViewer.getControl().setLayoutData(gridData); |
| |
| createSectionToolBar(expressionSection); |
| populateExpressionSectionToolbar(expressionSection); |
| |
| expressionSection.setClient(expressionSectionBody); |
| |
| expressionSectionBody.layout(); |
| expressionSection.layout(); |
| } |
| |
| /** |
| * Creates the source viewer for the currently selected language. |
| * |
| * @param parent |
| * Parent Composite of the result viewer. |
| * @return The source viewer for the currently selected language. |
| */ |
| protected SourceViewer createExpressionViewer(Composite parent) { |
| SourceViewer viewer = getCurrentLanguageInterpreter().createSourceViewer(parent); |
| if (viewer == null) { |
| viewer = SWTUtil.createScrollableSourceViewer(parent, SWT.H_SCROLL | SWT.V_SCROLL | SWT.BORDER); |
| viewer.configure(new TextSourceViewerConfiguration()); |
| viewer.setDocument(new Document()); |
| } |
| getCurrentLanguageInterpreter().configureSourceViewer(viewer); |
| |
| if (viewer instanceof IInterpreterSourceViewer) { |
| setUpContentAssist((IInterpreterSourceViewer)viewer); |
| } |
| setUpRealTimeCompilation(viewer); |
| setUpDefaultTextAction(viewer); |
| |
| createExpressionMenu(viewer); |
| |
| if (partMemento != null) { |
| String expression = partMemento.getString(MEMENTO_EXPRESSION_KEY); |
| if (expression != null) { |
| viewer.getDocument().set(expression); |
| } |
| } |
| return viewer; |
| } |
| |
| /** |
| * This will be called in order to create the "Interpreter" form. |
| * |
| * @param toolkit |
| * Toolkit that can be used to create the form. |
| * @param parent |
| * Parent composite of the form. |
| */ |
| protected void createInterpreterForm(FormToolkit toolkit, Composite parent) { |
| interpreterForm = toolkit.createForm(parent); |
| messageManager = FormMessageManagerFactory.createFormMessageManager(interpreterForm); |
| getMessageManager().setDecorationPosition(SWT.LEFT | SWT.TOP); |
| toolkit.decorateFormHeading(getForm()); |
| |
| populateLanguageMenu(getForm().getMenuManager()); |
| |
| /* |
| * This will be called at initialization only. When initializing the "languages" menu, we do select |
| * the accurate descriptor, but we do not set the language text and icon. There is a chance that no |
| * languages have been provided. Use a default text and image for these. |
| */ |
| String languageName = ""; //$NON-NLS-1$ |
| ImageDescriptor titleImageDescriptor = null; |
| if (getCurrentLanguageDescriptor() != null) { |
| languageName = getCurrentLanguageDescriptor().getLabel(); |
| titleImageDescriptor = getCurrentLanguageDescriptor().getIcon(); |
| } |
| getForm().setText(InterpreterMessages.getString("interpreter.view.title", //$NON-NLS-1$ |
| languageName)); |
| final Image titleImage; |
| if (titleImageDescriptor != null) { |
| titleImage = titleImageDescriptor.createImage(); |
| } else { |
| titleImage = InterpreterImages |
| .getImageDescriptor(IInterpreterConstants.INTERPRETER_VIEW_DEFAULT_ICON).createImage(); |
| } |
| setTitleImage(titleImage); |
| getForm().setImage(titleImage); |
| |
| Composite mainBody = getForm().getBody(); |
| mainBody.setLayout(new GridLayout()); |
| formBody = new SashForm(mainBody, SWT.HORIZONTAL | SWT.SMOOTH); |
| toolkit.adapt(formBody); |
| formBody.setLayoutData(new GridData(GridData.FILL_BOTH)); |
| |
| SashForm leftColumn = new SashForm(formBody, SWT.VERTICAL | SWT.SMOOTH); |
| toolkit.adapt(leftColumn); |
| |
| createExpressionSection(toolkit, leftColumn); |
| |
| bottomLeftColumn = new SashForm(leftColumn, SWT.HORIZONTAL | SWT.SMOOTH); |
| toolkit.adapt(bottomLeftColumn); |
| |
| subExpressionComposite = toolkit.createComposite(bottomLeftColumn); |
| subExpressionComposite.setLayout(new FillLayout()); |
| subExpressionComposite.setVisible(false); |
| Composite resultComposite = toolkit.createComposite(bottomLeftColumn); |
| resultComposite.setLayout(new FillLayout()); |
| |
| createSubExpressionsSection(toolkit, subExpressionComposite); |
| createResultSection(toolkit, resultComposite); |
| |
| bottomLeftColumn.setWeights(new int[] {1, 1, }); |
| |
| leftColumn.setWeights(new int[] {2, 3, }); |
| |
| variableColumn = toolkit.createComposite(formBody); |
| variableColumn.setLayout(new FillLayout()); |
| /* |
| * Variables are invisible by default. The toolbar initialization will restore their state, making |
| * them visible if they were previously. |
| */ |
| variableColumn.setVisible(false); |
| |
| createVariableSection(toolkit, variableColumn); |
| |
| formBody.setWeights(new int[] {3, 1, }); |
| |
| createToolBar(getForm()); |
| } |
| |
| /** |
| * This allows us to avoid a visual glitch on windows where the expression section has no borders and a |
| * "black square" in place of its vertical scroll bar. This glitch happened when closing and reopening the |
| * view without restarting the runtime. |
| */ |
| protected void refreshExpressionSection() { |
| if (!expressionSection.isDisposed()) { |
| expressionSection.layout(); |
| } |
| } |
| |
| /** |
| * Creates the result viewer menu listener. This listener is in charge of filling the menu's actions. |
| * |
| * @param viewer |
| * The result viewer. |
| * @return The newly created listener. |
| */ |
| protected IMenuListener createResultMenuListener(Viewer viewer) { |
| return new ResultMenuListener(viewer); |
| } |
| |
| /** |
| * This will be called to create the "Evaluation Result" section (bottom part of the left column) of the |
| * "Interpreter" form. |
| * |
| * @param toolkit |
| * Toolkit that can be used to create form parts. |
| * @param leftColumn |
| * Left column of the "Interpreter" form. |
| */ |
| protected void createResultSection(FormToolkit toolkit, Composite leftColumn) { |
| resultSection = toolkit.createSection(leftColumn, ExpandableComposite.TITLE_BAR); |
| resultSection.setText(InterpreterMessages.getString("interpreter.view.result.section.name")); //$NON-NLS-1$ |
| |
| Composite resultSectionBody = toolkit.createComposite(resultSection); |
| resultSectionBody.setLayout(new GridLayout()); |
| |
| resultViewer = createResultViewer(resultSectionBody); |
| GridData gridData = new GridData(GridData.FILL_BOTH); |
| resultViewer.getControl().setLayoutData(gridData); |
| |
| createSectionToolBar(resultSection); |
| populateResultSectionToolbar(resultSection); |
| |
| toolkit.paintBordersFor(resultSectionBody); |
| resultSection.setClient(resultSectionBody); |
| } |
| |
| /** |
| * Creates the result viewer for the currently selected language. |
| * |
| * @param parent |
| * Parent Composite of the result viewer. |
| * @return The result viewer for the currently selected language. |
| */ |
| protected Viewer createResultViewer(Composite parent) { |
| Viewer viewer = getCurrentLanguageInterpreter().createResultViewer(parent); |
| if (viewer == null) { |
| viewer = new TreeViewer(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL | SWT.BORDER); |
| ColumnViewerToolTipSupport.enableFor((TreeViewer)viewer); |
| |
| AdapterFactory adapterFactory = createAdapterFactory(); |
| TreeViewer treeViewer = (TreeViewer)viewer; |
| treeViewer.setContentProvider(new ResultContentProvider(adapterFactory)); |
| treeViewer.setLabelProvider(new ResultLabelProvider(adapterFactory)); |
| } |
| |
| if (viewer instanceof TreeViewer) { |
| setUpResultDragSupport((TreeViewer)viewer); |
| |
| ((TreeViewer)viewer).addDoubleClickListener(new ResultDoubleClickListener()); |
| } |
| |
| createResultMenu(viewer); |
| |
| return viewer; |
| } |
| |
| /** |
| * Creates the toolbar of our interpreter form. |
| * |
| * @param form |
| * The interpreter form. |
| */ |
| protected void createToolBar(Form form) { |
| IAction realTimeAction = new ToggleRealTimeAction(this); |
| if (partMemento != null) { |
| Boolean isRealTime = partMemento.getBoolean(MEMENTO_REAL_TIME_KEY); |
| if (isRealTime != null && isRealTime.booleanValue()) { |
| toggleRealTime(); |
| realTimeAction.setChecked(realTime); |
| } |
| } else { |
| // Real-time is active by default |
| toggleRealTime(); |
| realTimeAction.setChecked(realTime); |
| } |
| |
| linkWithEditorContextAction = new LinkWithEditorContextAction(this); |
| final IWorkbenchPage currentPage = getSite().getPage(); |
| if (currentPage != null) { |
| editorPartListener = new InterpreterEditorPartListener(); |
| getSite().getPage().addPartListener(editorPartListener); |
| |
| IEditorPart currentEditor = currentPage.getActiveEditor(); |
| if (currentEditor == null) { |
| linkWithEditorContextAction.setEnabled(false); |
| } else { |
| linkWithEditorContextAction |
| .setEnabled(getCurrentLanguageInterpreter().canLinkWithEditor(currentEditor)); |
| } |
| } else { |
| linkWithEditorContextAction.setEnabled(false); |
| } |
| |
| IAction variableVisibilityAction = new ToggleVariableVisibilityAction(this); |
| if (partMemento != null) { |
| Boolean isVariableVisible = partMemento.getBoolean(MEMENTO_VARIABLES_VISIBLE_KEY); |
| if (isVariableVisible != null && isVariableVisible.booleanValue()) { |
| toggleVariableVisibility(); |
| variableVisibilityAction.setChecked(variableVisible); |
| } |
| } |
| |
| IAction subExpressionsVisibilityAction = new ToggleStepByStepVisibilityAction(this); |
| if (partMemento != null) { |
| Boolean isSubExpressionsVisible = partMemento.getBoolean(MEMENTO_SUB_EXPRESSIONS_VISIBLE_KEY); |
| if (isSubExpressionsVisible != null && isSubExpressionsVisible.booleanValue()) { |
| toggleStepByStepVisibility(); |
| subExpressionsVisibilityAction.setChecked(subExpressionsVisible); |
| } |
| } |
| |
| IToolBarManager toolBarManager = form.getToolBarManager(); |
| toolBarManager.add(linkWithEditorContextAction); |
| toolBarManager.add(realTimeAction); |
| toolBarManager.add(variableVisibilityAction); |
| toolBarManager.add(subExpressionsVisibilityAction); |
| |
| toolBarManager.add(new Separator(LANGUAGE_SPECIFIC_ACTION_GROUP)); |
| |
| getCurrentLanguageInterpreter().addToolBarActions(this, toolBarManager); |
| |
| toolBarManager.update(true); |
| } |
| |
| /** |
| * Creates the variable viewer menu listener. This listener is in charge of filling the menu's actions. |
| * |
| * @param viewer |
| * The variable viewer. |
| * @return The newly created listener. |
| */ |
| protected IMenuListener createVariableMenuListener(TreeViewer viewer) { |
| return new VariableMenuListener(viewer); |
| } |
| |
| /** |
| * This will be called in order to create the "Variables" section (right column) of the "Interpreter" |
| * form. |
| * |
| * @param toolkit |
| * Toolkit that can be used to create form parts. |
| * @param rightColumn |
| * Right column of the "Interpreter" form. |
| */ |
| protected void createVariableSection(FormToolkit toolkit, Composite rightColumn) { |
| Section variableSection = toolkit.createSection(rightColumn, ExpandableComposite.TITLE_BAR); |
| variableSection.setText(InterpreterMessages.getString("interpreter.view.variable.section.name")); //$NON-NLS-1$ |
| |
| Composite variableSectionBody = toolkit.createComposite(variableSection); |
| variableSectionBody.setLayout(new FillLayout()); |
| |
| variableViewer = createVariableViewer(toolkit, variableSectionBody); |
| |
| ToolBarManager toolBarManager = createSectionToolBar(variableSection); |
| toolBarManager.add(new ClearVariableViewerAction(variableViewer)); |
| toolBarManager.update(true); |
| |
| toolkit.paintBordersFor(variableSectionBody); |
| variableSection.setClient(variableSectionBody); |
| } |
| |
| /** |
| * This will be called in order to create the TreeViewer used to display variables. |
| * |
| * @param toolkit |
| * Toolkit that can be used to create form parts. |
| * @param sectionBody |
| * Parent composite of the TreeViewer. |
| * @return The newly created viewer. |
| */ |
| protected TreeViewer createVariableViewer(FormToolkit toolkit, Composite sectionBody) { |
| Tree variableTree = toolkit.createTree(sectionBody, SWT.V_SCROLL | SWT.H_SCROLL | SWT.MULTI); |
| |
| TreeViewer viewer = new TreeViewer(variableTree); |
| ColumnViewerToolTipSupport.enableFor(viewer); |
| AdapterFactory adapterFactory = createAdapterFactory(); |
| viewer.setContentProvider(new VariableContentProvider(adapterFactory)); |
| viewer.setLabelProvider(new VariableLabelProvider(adapterFactory)); |
| |
| setUpVariableDropSupport(viewer); |
| createVariableMenu(viewer); |
| setUpVariableActions(viewer); |
| |
| viewer.setInput(new ArrayList<Variable>()); |
| |
| return viewer; |
| } |
| |
| /** |
| * This will be called in order to create the "Sub-Expressions" section (bottom left column) of the form. |
| * |
| * @param toolkit |
| * Toolkit that can be used to create form parts. |
| * @param parentComposite |
| * The sub-expression composite. |
| */ |
| protected void createSubExpressionsSection(FormToolkit toolkit, Composite parentComposite) { |
| Section subExpressionsSection = toolkit.createSection(parentComposite, ExpandableComposite.TITLE_BAR); |
| subExpressionsSection |
| .setText(InterpreterMessages.getString("interpreter.view.subexpression.section.name")); //$NON-NLS-1$ |
| |
| Composite subExpressionsSectionBody = toolkit.createComposite(subExpressionsSection); |
| subExpressionsSectionBody.setLayout(new GridLayout()); |
| |
| subExpressionViewer = createSubExpressionsViewer(toolkit, subExpressionsSectionBody); |
| GridData gridData = new GridData(GridData.FILL_BOTH); |
| subExpressionViewer.getControl().setLayoutData(gridData); |
| |
| createSectionToolBar(subExpressionsSection); |
| populateSubExpressionSectionToolbar(subExpressionsSection); |
| |
| toolkit.paintBordersFor(subExpressionsSectionBody); |
| subExpressionsSection.setClient(subExpressionsSectionBody); |
| } |
| |
| /** |
| * This will be called in order to create the TreeViewer used to display Sub-Expressions. |
| * |
| * @param toolkit |
| * Toolkit that can be used to create form parts. |
| * @param sectionBody |
| * Parent composite of the TreeViewer. |
| * @return The newly created viewer. |
| */ |
| protected TreeViewer createSubExpressionsViewer(FormToolkit toolkit, Composite sectionBody) { |
| Tree subExpressionsTree = toolkit.createTree(sectionBody, SWT.V_SCROLL | SWT.H_SCROLL | SWT.MULTI); |
| |
| TreeViewer viewer = new TreeViewer(subExpressionsTree); |
| ColumnViewerToolTipSupport.enableFor(viewer); |
| AdapterFactory adapterFactory = createAdapterFactory(); |
| viewer.setContentProvider(new StepByStepContentProvider(adapterFactory)); |
| viewer.setLabelProvider(new StepLabelProvider()); |
| viewer.addSelectionChangedListener(new SubExpressionListener()); |
| viewer.addDoubleClickListener(new SubExpressionDoubleClickListener()); |
| |
| return viewer; |
| } |
| |
| /** |
| * Returns the interpreter form. |
| * |
| * @return The interpreter form. |
| */ |
| protected Form getForm() { |
| return interpreterForm; |
| } |
| |
| /** |
| * Returns the indirection layer for the form's message manager. |
| * |
| * @return The indirection layer for the form's message manager. |
| */ |
| protected IFormMessageManager getMessageManager() { |
| return messageManager; |
| } |
| |
| /** |
| * Returns the current source viewer. |
| * <p> |
| * Note that the source viewer can change and be disposed over time; don't keep references to it. |
| * </p> |
| * |
| * @return The current source viewer. |
| */ |
| protected SourceViewer getSourceViewer() { |
| return expressionViewer; |
| } |
| |
| /** |
| * Populates the {@link #expressionSection}'s toolbar. |
| * |
| * @param section |
| * the expresssion section. |
| */ |
| protected void populateExpressionSectionToolbar(Section section) { |
| ToolBarManager toolBarManager = getSectionToolBar(section); |
| if (toolBarManager != null) { |
| toolBarManager.removeAll(); |
| toolBarManager.add(new EvaluateAction(this)); |
| toolBarManager.add(new ClearExpressionViewerAction(getSourceViewer())); |
| toolBarManager.update(true); |
| } |
| } |
| |
| /** |
| * Adds the "language selection" radio buttons to the interpreter's menu. |
| * |
| * @param menuManager |
| * The interpreter form's menu manager. |
| */ |
| protected final void populateLanguageMenu(IMenuManager menuManager) { |
| for (LanguageInterpreterDescriptor descriptor : LanguageInterpreterRegistry |
| .getRegisteredInterpreters()) { |
| IAction action = new ChangeLanguageAction(descriptor); |
| menuManager.add(action); |
| if (getCurrentLanguageDescriptor() == null) { |
| if (partMemento == null || partMemento.getString(MEMENTO_CURRENT_LANGUAGE_KEY) == null) { |
| currentLanguage = descriptor; |
| action.setChecked(true); |
| } else if (partMemento.getString(MEMENTO_CURRENT_LANGUAGE_KEY) |
| .equals(descriptor.getClassName())) { |
| currentLanguage = descriptor; |
| action.setChecked(true); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Populates the {@link #resultSection}'s toolbar. |
| * |
| * @param section |
| * the result section. |
| */ |
| protected void populateResultSectionToolbar(Section section) { |
| ToolBarManager toolBarManager = getSectionToolBar(section); |
| if (toolBarManager == null) { |
| return; |
| } |
| toolBarManager.removeAll(); |
| if (resultViewer instanceof StructuredViewer) { |
| final Action sortAction = new LexicalSortAction((StructuredViewer)resultViewer); |
| toolBarManager.add(sortAction); |
| if (partMemento != null && partMemento.getBoolean(MEMENTO_RESULT_SORTED_KEY) != null) { |
| boolean sortEnabled = partMemento.getBoolean(MEMENTO_RESULT_SORTED_KEY).booleanValue(); |
| sortAction.setChecked(sortEnabled); |
| if (sortEnabled) { |
| sortAction.run(); |
| } |
| } |
| } |
| toolBarManager.add(new ClearResultViewerAction(resultViewer)); |
| toolBarManager.update(true); |
| } |
| |
| /** |
| * Populates the sub expression section toolbar. |
| * |
| * @param section |
| * The section for which the toolbar should be populated |
| */ |
| protected void populateSubExpressionSectionToolbar(Section section) { |
| ToolBarManager toolBarManager = getSectionToolBar(section); |
| if (toolBarManager != null) { |
| toolBarManager.removeAll(); |
| final Action sortAction = new LexicalSortAction(subExpressionViewer); |
| toolBarManager.add(sortAction); |
| if (partMemento != null && partMemento.getBoolean(MEMENTO_SUB_EXPRESSIONS_SORTED_KEY) != null) { |
| boolean sortEnabled = partMemento.getBoolean(MEMENTO_SUB_EXPRESSIONS_SORTED_KEY) |
| .booleanValue(); |
| sortAction.setChecked(sortEnabled); |
| if (sortEnabled) { |
| sortAction.run(); |
| } |
| } |
| toolBarManager.add(new ClearResultViewerAction(subExpressionViewer)); |
| toolBarManager.update(true); |
| } |
| } |
| |
| /** |
| * Switch this interpreter to the given language. This will also re-title and re-create the viewers of |
| * this view. |
| * |
| * @param selectedLanguage |
| * The language to which this interpreter should be switched. |
| */ |
| protected void selectLanguage(LanguageInterpreterDescriptor selectedLanguage) { |
| if (currentLanguage == selectedLanguage) { |
| return; |
| } |
| |
| if (compilationThread != null && !compilationThread.isInterrupted()) { |
| compilationThread.interrupt(); |
| } |
| if (evaluationThread != null && !evaluationThread.isInterrupted()) { |
| evaluationThread.interrupt(); |
| } |
| if (expressionSplittingThread != null && !expressionSplittingThread.isInterrupted()) { |
| expressionSplittingThread.interrupt(); |
| } |
| |
| getMessageManager().removeAllMessages(); |
| |
| // Dispose of the language specific actions |
| IToolBarManager toolBarManager = getForm().getToolBarManager(); |
| IContributionItem[] items = toolBarManager.getItems(); |
| boolean dispose = false; |
| for (int i = 0; i < items.length; i++) { |
| IContributionItem item = items[i]; |
| if (dispose) { |
| toolBarManager.remove(item); |
| item.dispose(); |
| } else if (item instanceof Separator |
| && LANGUAGE_SPECIFIC_ACTION_GROUP.equals(((Separator)item).getGroupName())) { |
| dispose = true; |
| } |
| } |
| |
| getCurrentLanguageInterpreter().dispose(); |
| currentLanguageInterpreter = null; |
| |
| LanguageInterpreterDescriptor previousLanguage = getCurrentLanguageDescriptor(); |
| currentLanguage = selectedLanguage; |
| |
| // Change interpreter title |
| getForm().setText(InterpreterMessages.getString("interpreter.view.title", //$NON-NLS-1$ |
| getCurrentLanguageDescriptor().getLabel())); |
| |
| // Change view's icon |
| Image previousImage = null; |
| if (previousLanguage != null && previousLanguage.getIcon() != null |
| || getCurrentLanguageDescriptor().getIcon() != null) { |
| previousImage = getTitleImage(); |
| } |
| if (getCurrentLanguageDescriptor().getIcon() != null) { |
| setTitleImage(getCurrentLanguageDescriptor().getIcon().createImage()); |
| } else if (previousLanguage != null && previousLanguage.getIcon() != null) { |
| setTitleImage(InterpreterImages |
| .getImageDescriptor(IInterpreterConstants.INTERPRETER_VIEW_DEFAULT_ICON).createImage()); |
| } |
| if (previousImage != null) { |
| previousImage.dispose(); |
| } |
| getForm().setImage(getTitleImage()); |
| |
| if (expressionSection != null) { |
| Composite expressionSectionBody = (Composite)expressionSection.getClient(); |
| expressionViewer.getControl().dispose(); |
| |
| expressionViewer = createExpressionViewer(expressionSectionBody); |
| GridData gridData = new GridData(GridData.FILL_BOTH); |
| expressionViewer.getControl().setLayoutData(gridData); |
| |
| expressionSectionBody.layout(); |
| expressionSection.layout(); |
| } |
| |
| if (resultSection != null) { |
| Composite resultSectionBody = (Composite)resultSection.getClient(); |
| resultViewer.getControl().dispose(); |
| |
| resultViewer = createResultViewer(resultSectionBody); |
| GridData gridData = new GridData(GridData.FILL_BOTH); |
| resultViewer.getControl().setLayoutData(gridData); |
| |
| resultSectionBody.layout(); |
| resultSection.layout(); |
| } |
| |
| if (subExpressionViewer != null) { |
| subExpressionViewer.setInput(null); |
| } |
| |
| // re-fill the sections' toolbars |
| populateExpressionSectionToolbar(expressionSection); |
| populateResultSectionToolbar(resultSection); |
| // Change the state of the link with editor action |
| final IWorkbenchPage currentPage = getSite().getPage(); |
| if (currentPage != null) { |
| IEditorPart currentEditor = currentPage.getActiveEditor(); |
| if (currentEditor == null) { |
| linkWithEditorContextAction.setEnabled(false); |
| } else { |
| linkWithEditorContextAction |
| .setEnabled(getCurrentLanguageInterpreter().canLinkWithEditor(currentEditor)); |
| } |
| } else { |
| linkWithEditorContextAction.setEnabled(false); |
| } |
| // re-add language specific actions to the toolbar |
| getCurrentLanguageInterpreter().addToolBarActions(this, toolBarManager); |
| toolBarManager.update(true); |
| } |
| |
| /** |
| * Sets the result of the compilation to the given instance. |
| * |
| * @param compilationResult |
| * The current expression's compilation result. |
| */ |
| protected final void setCompilationResult(CompilationResult compilationResult) { |
| this.compilationResult = compilationResult; |
| } |
| |
| /** |
| * Update the result viewer. |
| * |
| * @param result |
| * The current expressions's evaluation result. |
| */ |
| protected final void setEvaluationResult(EvaluationResult result) { |
| List<Object> input = new ArrayList<Object>(); |
| Object evaluationResult = result.getEvaluationResult(); |
| if (evaluationResult instanceof Collection<?>) { |
| for (Object child : (Collection<?>)evaluationResult) { |
| if (child != null) { |
| input.add(child); |
| } else { |
| input.add(NULL_RESULT_OBJECT); |
| } |
| } |
| } else if (evaluationResult != null) { |
| input.add(evaluationResult); |
| } |
| |
| if (!resultViewer.getControl().isDisposed()) { |
| resultViewer.setInput(input); |
| } |
| } |
| |
| /** |
| * Sets up the content assist action for the given source viewer. |
| * |
| * @param viewer |
| * The viewer we need content assist on. |
| */ |
| protected final void setUpContentAssist(final IInterpreterSourceViewer viewer) { |
| IHandlerService service = (IHandlerService)getSite().getService(IHandlerService.class); |
| if (activationTokenContentAssist != null) { |
| service.deactivateHandler(activationTokenContentAssist); |
| } |
| |
| IAction contentAssistAction = new Action() { |
| @Override |
| public void run() { |
| viewer.showContentAssist(getInterpreterContext()); |
| } |
| }; |
| IHandler contentAssistHandler = new ActionHandler(contentAssistAction); |
| |
| activationTokenContentAssist = service.activateHandler( |
| ITextEditorActionDefinitionIds.CONTENT_ASSIST_PROPOSALS, contentAssistHandler); |
| } |
| |
| /** |
| * Sets up the real-time compilation action on the given viewer. The real-time thread specifically needs |
| * to be told whenever the expression is dirty as well as when the timer should be reset (in order not to |
| * compile when the user is entering the expression). |
| * |
| * @param viewer |
| * The viewer for which to set up the real-time compilation thread. |
| */ |
| protected void setUpRealTimeCompilation(final SourceViewer viewer) { |
| // XText tends to throw us into an infinite loop. |
| // Double-check that text actually changed. |
| viewer.addTextListener(new ITextListener() { |
| private String lastText; |
| |
| public void textChanged(TextEvent event) { |
| if (realTimeThread != null) { |
| boolean ignore = false; |
| String currentText = viewer.getDocument().get(); |
| if (lastText != null) { |
| ignore = lastText.equals(currentText); |
| } |
| if (!ignore) { |
| lastText = currentText; |
| realTimeThread.reset(); |
| realTimeThread.setDirty(); |
| } |
| } |
| } |
| }); |
| } |
| |
| /** |
| * Sets up the drag and drop support for the result viewer. |
| * |
| * @param viewer |
| * The result viewer. |
| */ |
| protected void setUpResultDragSupport(TreeViewer viewer) { |
| int operations = DND.DROP_COPY | DND.DROP_LINK | DND.DROP_MOVE; |
| Transfer[] transfers = new Transfer[] {LocalSelectionTransfer.getTransfer(), }; |
| |
| viewer.addDragSupport(operations, transfers, new ResultDragListener(viewer)); |
| } |
| |
| /** |
| * This will be called in order to set up the actions available on the given variable viewer. |
| * |
| * @param viewer |
| * The viewer on which to activate actions. |
| */ |
| protected void setUpVariableActions(final TreeViewer viewer) { |
| Tree tree = viewer.getTree(); |
| final Action renameAction = new RenameVariableAction(viewer); |
| final Action deleteAction = new DeleteVariableOrValueAction(viewer); |
| |
| tree.addKeyListener(new KeyAdapter() { |
| @Override |
| public void keyPressed(KeyEvent event) { |
| if (event.keyCode == SWT.DEL && event.stateMask == 0) { |
| if (deleteAction.isEnabled()) { |
| deleteAction.run(); |
| } |
| } else if (event.keyCode == SWT.F2 && event.stateMask == 0) { |
| if (renameAction.isEnabled()) { |
| renameAction.run(); |
| } |
| } |
| } |
| }); |
| } |
| |
| /** |
| * Sets up the drag and drop support for the variable viewer. |
| * |
| * @param viewer |
| * The variable viewer. |
| */ |
| protected void setUpVariableDropSupport(TreeViewer viewer) { |
| int operations = DND.DROP_DEFAULT | DND.DROP_COPY | DND.DROP_LINK | DND.DROP_MOVE; |
| Transfer[] transfers = new Transfer[] {LocalTransfer.getInstance(), |
| LocalSelectionTransfer.getTransfer(), }; |
| |
| viewer.addDropSupport(operations, transfers, new VariableDropListener(viewer)); |
| } |
| |
| /** |
| * Converts an IStatus severity to a IMessageProviderSeverity. |
| * |
| * @param statusSeverity |
| * The status severity to be converted. |
| * @return The corresponding IMessageProvider severity. |
| */ |
| private int convertStatusToMessageSeverity(int statusSeverity) { |
| int severity = IMessageProvider.NONE; |
| switch (statusSeverity) { |
| case IStatus.INFO: |
| severity = IMessageProvider.INFORMATION; |
| break; |
| case IStatus.WARNING: |
| severity = IMessageProvider.WARNING; |
| break; |
| case IStatus.ERROR: |
| severity = IMessageProvider.ERROR; |
| break; |
| default: |
| severity = IMessageProvider.NONE; |
| } |
| return severity; |
| } |
| |
| /** |
| * Creates the right-click menu that will be displayed for the expression viewer. |
| * |
| * @param viewer |
| * The expression viewer. |
| */ |
| private void createExpressionMenu(SourceViewer viewer) { |
| Menu menu = viewer.getTextWidget().getMenu(); |
| boolean createMenu = true; |
| if (menu != null) { |
| MenuManager manager = (MenuManager)menu |
| .getData("org.eclipse.jface.action.MenuManager.managerKey"); //$NON-NLS-1$ |
| if (manager != null) { |
| manager.addMenuListener(createExpressionMenuListener(viewer)); |
| createMenu = false; |
| } |
| } |
| |
| if (createMenu) { |
| MenuManager menuManager = new MenuManager(MENU_ID); |
| menuManager.setRemoveAllWhenShown(true); |
| menuManager.addMenuListener(createExpressionMenuListener(viewer)); |
| menu = menuManager.createContextMenu(viewer.getTextWidget()); |
| viewer.getTextWidget().setMenu(menu); |
| getSite().registerContextMenu(menuManager, viewer); |
| } |
| } |
| |
| /** |
| * Creates the right-click menu that will be displayed for the result viewer. |
| * |
| * @param viewer |
| * The result viewer. |
| */ |
| private void createResultMenu(Viewer viewer) { |
| MenuManager menuManager = new MenuManager(MENU_ID); |
| menuManager.setRemoveAllWhenShown(true); |
| menuManager.addMenuListener(createResultMenuListener(viewer)); |
| Menu menu = menuManager.createContextMenu(viewer.getControl()); |
| viewer.getControl().setMenu(menu); |
| getSite().registerContextMenu(menuManager, viewer); |
| } |
| |
| /** |
| * Creates the right-click menu that will be displayed for the variable viewer. |
| * |
| * @param viewer |
| * The variable viewer. |
| */ |
| private void createVariableMenu(TreeViewer viewer) { |
| MenuManager menuManager = new MenuManager(MENU_ID); |
| menuManager.setRemoveAllWhenShown(true); |
| menuManager.addMenuListener(createVariableMenuListener(viewer)); |
| Menu menu = menuManager.createContextMenu(viewer.getControl()); |
| viewer.getControl().setMenu(menu); |
| getSite().registerContextMenu(menuManager, viewer); |
| } |
| |
| /** |
| * Returns the currently selected language's descriptor. |
| * |
| * @return The currently selected language's descriptor. |
| */ |
| private LanguageInterpreterDescriptor getCurrentLanguageDescriptor() { |
| return currentLanguage; |
| } |
| |
| /** |
| * Sets up the default text actions (undo, redo) on the given source viewer. |
| * |
| * @param viewer |
| * The viewer on which to activate default text actions. |
| */ |
| private void setUpDefaultTextAction(final SourceViewer viewer) { |
| IHandlerService service = (IHandlerService)getSite().getService(IHandlerService.class); |
| if (activationTokenRedo != null) { |
| service.deactivateHandler(activationTokenRedo); |
| } |
| if (activationTokenUndo != null) { |
| service.deactivateHandler(activationTokenUndo); |
| } |
| |
| IAction redoAction = new Action() { |
| @Override |
| public void run() { |
| viewer.doOperation(ITextOperationTarget.REDO); |
| } |
| }; |
| IHandler redoHandler = new ActionHandler(redoAction); |
| |
| IAction undoAction = new Action() { |
| @Override |
| public void run() { |
| viewer.doOperation(ITextOperationTarget.UNDO); |
| } |
| }; |
| IHandler undoHandler = new ActionHandler(undoAction); |
| |
| activationTokenRedo = service.activateHandler(WORKBENCH_CONSTANT_EDIT_REDO, redoHandler); |
| activationTokenUndo = service.activateHandler(WORKBENCH_CONSTANT_EDIT_UNDO, undoHandler); |
| } |
| |
| /** |
| * This class will be used in order to populate the right-click menu of the Expression viewer. |
| * |
| * @author <a href="mailto:laurent.goubet@obeo.fr">Laurent Goubet</a> |
| */ |
| protected class ExpressionMenuListener implements IMenuListener { |
| /** The viewer on which this menu listener operates. */ |
| private final SourceViewer sourceViewer; |
| |
| /** |
| * Creates this menu listener given the viewer on which it operates. |
| * |
| * @param sourceViewer |
| * The viewer on which this menu listener operates. |
| */ |
| public ExpressionMenuListener(SourceViewer sourceViewer) { |
| this.sourceViewer = sourceViewer; |
| } |
| |
| /** |
| * {@inheritDoc} |
| * |
| * @see org.eclipse.jface.action.IMenuListener#menuAboutToShow(org.eclipse.jface.action.IMenuManager) |
| */ |
| public void menuAboutToShow(IMenuManager manager) { |
| manager.add(new Separator("org.eclipse.ui.interpreter.view.expression.menu")); //$NON-NLS-1$ |
| manager.add(new EvaluateAction(InterpreterView.this)); |
| manager.add(new ClearExpressionViewerAction(sourceViewer)); |
| manager.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS)); |
| } |
| } |
| |
| /** |
| * This implementation of a part listener will allow us to determine at all times whether the "work in |
| * editor context" action should be enabled. |
| * |
| * @author <a href="mailto:laurent.goubet@obeo.fr">Laurent Goubet</a> |
| */ |
| protected class InterpreterEditorPartListener implements IPartListener2 { |
| /** |
| * {@inheritDoc} |
| * |
| * @see org.eclipse.ui.IPartListener2#partActivated(org.eclipse.ui.IWorkbenchPartReference) |
| */ |
| public void partActivated(IWorkbenchPartReference partRef) { |
| // If the toggle is checked, defer enablement computing till we uncheck it |
| if (!linkWithEditorContextAction.isChecked() && partRef instanceof IEditorReference) { |
| linkWithEditorContextAction.setEnabled(getCurrentLanguageInterpreter() |
| .canLinkWithEditor(((IEditorReference)partRef).getEditor(false))); |
| } |
| } |
| |
| /** |
| * {@inheritDoc} |
| * |
| * @see org.eclipse.ui.IPartListener2#partBroughtToTop(org.eclipse.ui.IWorkbenchPartReference) |
| */ |
| public void partBroughtToTop(IWorkbenchPartReference partRef) { |
| // No need to react to this event |
| } |
| |
| /** |
| * {@inheritDoc} |
| * |
| * @see org.eclipse.ui.IPartListener2#partClosed(org.eclipse.ui.IWorkbenchPartReference) |
| */ |
| public void partClosed(IWorkbenchPartReference partRef) { |
| // If the toggle is checked, defer enablement computing till we uncheck it |
| if (!linkWithEditorContextAction.isChecked() && partRef instanceof IEditorReference |
| && getSite().getPage() != null) { |
| final IEditorPart editorPart = getSite().getPage().getActiveEditor(); |
| if (editorPart == null) { |
| linkWithEditorContextAction.setEnabled(false); |
| } else { |
| linkWithEditorContextAction |
| .setEnabled(getCurrentLanguageInterpreter().canLinkWithEditor(editorPart)); |
| } |
| } |
| } |
| |
| /** |
| * {@inheritDoc} |
| * |
| * @see org.eclipse.ui.IPartListener2#partDeactivated(org.eclipse.ui.IWorkbenchPartReference) |
| */ |
| public void partDeactivated(IWorkbenchPartReference partRef) { |
| // No need to react to this event |
| } |
| |
| /** |
| * {@inheritDoc} |
| * |
| * @see org.eclipse.ui.IPartListener2#partHidden(org.eclipse.ui.IWorkbenchPartReference) |
| */ |
| public void partHidden(IWorkbenchPartReference partRef) { |
| // No need to react to this event |
| } |
| |
| /** |
| * {@inheritDoc} |
| * |
| * @see org.eclipse.ui.IPartListener2#partInputChanged(org.eclipse.ui.IWorkbenchPartReference) |
| */ |
| public void partInputChanged(IWorkbenchPartReference partRef) { |
| // No need to react to this event |
| } |
| |
| /** |
| * {@inheritDoc} |
| * |
| * @see org.eclipse.ui.IPartListener2#partOpened(org.eclipse.ui.IWorkbenchPartReference) |
| */ |
| public void partOpened(IWorkbenchPartReference partRef) { |
| // No need to react to this event |
| } |
| |
| /** |
| * {@inheritDoc} |
| * |
| * @see org.eclipse.ui.IPartListener2#partVisible(org.eclipse.ui.IWorkbenchPartReference) |
| */ |
| public void partVisible(IWorkbenchPartReference partRef) { |
| // No need to react to this event |
| } |
| } |
| |
| /** |
| * This will allow us to react to double click events in the result view in order to display the long |
| * Strings and generated files in a more suitable way. |
| * |
| * @author <a href="mailto:laurent.goubet@obeo.fr">Laurent Goubet</a> |
| */ |
| protected static class ResultDoubleClickListener implements IDoubleClickListener { |
| /** |
| * {@inheritDoc} |
| * |
| * @see org.eclipse.jface.viewers.IDoubleClickListener#doubleClick(org.eclipse.jface.viewers.DoubleClickEvent) |
| */ |
| public void doubleClick(DoubleClickEvent event) { |
| ISelection selection = event.getSelection(); |
| if (selection.isEmpty() || !(selection instanceof IStructuredSelection)) { |
| return; |
| } |
| final Object target = ((IStructuredSelection)selection).getFirstElement(); |
| if (target instanceof InterpreterFile) { |
| showGeneratedFile((InterpreterFile)target); |
| } else if (target instanceof String |
| && (((String)target).indexOf('\n') >= 0 || ((String)target).indexOf('\r') >= 0)) { |
| GeneratedTextDialog dialog = new GeneratedTextDialog(Display.getCurrent().getActiveShell(), |
| "Evaluation Result", (String)target); //$NON-NLS-1$ |
| dialog.open(); |
| } else if (event.getViewer() instanceof TreeViewer |
| && ((TreeViewer)event.getViewer()).isExpandable(target)) { |
| final TreeViewer viewer = (TreeViewer)event.getViewer(); |
| if (selection instanceof ITreeSelection) { |
| TreePath[] paths = ((ITreeSelection)selection).getPathsFor(target); |
| for (int i = 0; i < paths.length; i++) { |
| viewer.setExpandedState(paths[i], !viewer.getExpandedState(paths[i])); |
| } |
| } else { |
| viewer.setExpandedState(target, !viewer.getExpandedState(target)); |
| } |
| } |
| } |
| |
| /** |
| * Displays the given generated file in its own read-only editor. |
| * |
| * @param file |
| * The file we are to display. |
| */ |
| private void showGeneratedFile(InterpreterFile file) { |
| final IWorkbench workbench = PlatformUI.getWorkbench(); |
| if (workbench.getActiveWorkbenchWindow() == null |
| || workbench.getActiveWorkbenchWindow().getActivePage() == null) { |
| return; |
| } |
| final IWorkbenchPage page = workbench.getActiveWorkbenchWindow().getActivePage(); |
| final IStorage storage = new InterpreterFileStorage(file); |
| final IEditorDescriptor editor = workbench.getEditorRegistry() |
| .getDefaultEditor(file.getFileName()); |
| final IEditorInput input = new StorageEditorInput(storage); |
| |
| try { |
| final String editorID; |
| if (editor == null) { |
| editorID = EditorsUI.DEFAULT_TEXT_EDITOR_ID; |
| } else { |
| editorID = editor.getId(); |
| } |
| page.openEditor(input, editorID); |
| } catch (PartInitException e) { |
| // swallow : we just won't open editors |
| } |
| } |
| } |
| |
| /** |
| * This will allow us to react to double click events in the sub-expressions view in order to expand when |
| * needed. |
| * |
| * @author <a href="mailto:laurent.goubet@obeo.fr">Laurent Goubet</a> |
| */ |
| protected static class SubExpressionDoubleClickListener implements IDoubleClickListener { |
| /** |
| * {@inheritDoc} |
| * |
| * @see org.eclipse.jface.viewers.IDoubleClickListener#doubleClick(org.eclipse.jface.viewers.DoubleClickEvent) |
| */ |
| public void doubleClick(DoubleClickEvent event) { |
| ISelection selection = event.getSelection(); |
| if (selection.isEmpty() || !(selection instanceof IStructuredSelection)) { |
| return; |
| } |
| final Object target = ((IStructuredSelection)selection).getFirstElement(); |
| if (event.getViewer() instanceof TreeViewer |
| && ((TreeViewer)event.getViewer()).isExpandable(target)) { |
| final TreeViewer viewer = (TreeViewer)event.getViewer(); |
| if (selection instanceof ITreeSelection) { |
| TreePath[] paths = ((ITreeSelection)selection).getPathsFor(target); |
| for (int i = 0; i < paths.length; i++) { |
| viewer.setExpandedState(paths[i], !viewer.getExpandedState(paths[i])); |
| } |
| } else { |
| viewer.setExpandedState(target, !viewer.getExpandedState(target)); |
| } |
| } |
| } |
| } |
| |
| /** |
| * This class will be used in order to populate the right-click menu of the result viewer. |
| * |
| * @author <a href="mailto:laurent.goubet@obeo.fr">Laurent Goubet</a> |
| */ |
| protected class ResultMenuListener implements IMenuListener { |
| /** The viewer on which this menu listener operates. */ |
| private Viewer resultViewer; |
| |
| /** |
| * Creates this menu listener given the viewer on which it operates. |
| * |
| * @param resultViewer |
| * The viewer on which this menu listener operates. |
| */ |
| public ResultMenuListener(Viewer resultViewer) { |
| this.resultViewer = resultViewer; |
| } |
| |
| /** |
| * {@inheritDoc} |
| * |
| * @see org.eclipse.jface.action.IMenuListener#menuAboutToShow(org.eclipse.jface.action.IMenuManager) |
| */ |
| public void menuAboutToShow(IMenuManager manager) { |
| manager.add(new ClearResultViewerAction(resultViewer)); |
| manager.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS)); |
| } |
| } |
| |
| /** |
| * This class will be used in order to populate the right-click menu of the Variable viewer. |
| * |
| * @author <a href="mailto:laurent.goubet@obeo.fr">Laurent Goubet</a> |
| */ |
| protected class VariableMenuListener implements IMenuListener { |
| /** The viewer on which this menu listener operates. */ |
| private TreeViewer variableViewer; |
| |
| /** |
| * Creates this menu listener given the viewer on which it operates. |
| * |
| * @param variableViewer |
| * The viewer on which this menu listener operates. |
| */ |
| public VariableMenuListener(TreeViewer variableViewer) { |
| this.variableViewer = variableViewer; |
| } |
| |
| /** |
| * {@inheritDoc} |
| * |
| * @see org.eclipse.jface.action.IMenuListener#menuAboutToShow(org.eclipse.jface.action.IMenuManager) |
| */ |
| public void menuAboutToShow(IMenuManager manager) { |
| final Variable variable = getCurrentVariable(); |
| manager.add(new NewVariableWizardAction(variableViewer, variable)); |
| manager.add(new ClearVariableViewerAction(variableViewer)); |
| manager.add(new Separator()); |
| manager.add(new DeleteVariableOrValueAction(variableViewer)); |
| manager.add(new RenameVariableAction(variableViewer)); |
| manager.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS)); |
| } |
| |
| /** |
| * Returns the first of the currently selected Variable(s). |
| * |
| * @return The first of the currently selected Variable(s) |
| */ |
| private Variable getCurrentVariable() { |
| if (variableViewer == null || variableViewer.getTree() == null |
| || variableViewer.getTree().isDisposed()) { |
| return null; |
| } |
| Tree tree = variableViewer.getTree(); |
| |
| TreeItem[] selectedItems = tree.getSelection(); |
| Variable selectedVariable = null; |
| if (selectedItems != null && selectedItems.length > 0) { |
| for (int i = 0; i < selectedItems.length && selectedVariable == null; i++) { |
| TreeItem item = selectedItems[i]; |
| if (item.getData() instanceof Variable) { |
| selectedVariable = (Variable)item.getData(); |
| } |
| } |
| for (int i = 0; i < selectedItems.length && selectedVariable == null; i++) { |
| TreeItem item = selectedItems[i].getParentItem(); |
| while (item != null && selectedVariable == null) { |
| if (item.getData() instanceof Variable) { |
| selectedVariable = (Variable)item.getData(); |
| } |
| item = item.getParentItem(); |
| } |
| } |
| } |
| return selectedVariable; |
| } |
| } |
| |
| /** |
| * This class will be used for all of our "language changing" actions. |
| * |
| * @author <a href="mailto:laurent.goubet@obeo.fr">Laurent Goubet</a> |
| */ |
| private class ChangeLanguageAction extends Action { |
| /** Language to which this action will change the interpreter when executed. */ |
| private final LanguageInterpreterDescriptor language; |
| |
| /** |
| * Instantiates our action given the language to which it will change. |
| * |
| * @param language |
| * The language to which this action will change the interpreter. |
| */ |
| ChangeLanguageAction(LanguageInterpreterDescriptor language) { |
| super(language.getLabel(), IAction.AS_RADIO_BUTTON); |
| this.language = language; |
| } |
| |
| /** |
| * {@inheritDoc} |
| * |
| * @see org.eclipse.jface.action.Action#run() |
| */ |
| @Override |
| public void run() { |
| selectLanguage(language); |
| } |
| } |
| |
| /** |
| * This implementation of a Thread will be used to wrap a compilation task as returned by the |
| * LanguageInterpreter, then asynchronously update the form with all error messages (if any) that were |
| * raised by this compilation task. Afterwards, this Thread will update the interpreter context with the |
| * compilation result. |
| * |
| * @author <a href="mailto:laurent.goubet@obeo.fr">Laurent Goubet</a> |
| */ |
| private class CompilationThread extends Thread { |
| /** |
| * We will set this flag on {@link #interrupt()} in order to determine whether the thread was |
| * cancelled (which could happen <b>after</b> the thread has completed, which would make the |
| * "interrupted" flag quiet. |
| */ |
| private boolean cancelled; |
| |
| /** The compilation thread which result we are to wait for. */ |
| private Future<CompilationResult> compilationTask; |
| |
| /** |
| * Instantiates a compilation thread given the compilation task of which we are to check the result. |
| * |
| * @param compilationTask |
| * Thread which result we are to wait for. |
| */ |
| CompilationThread(Future<CompilationResult> compilationTask) { |
| super("InterpreterCompilationThread"); //$NON-NLS-1$ |
| this.compilationTask = compilationTask; |
| } |
| |
| /** |
| * {@inheritDoc} |
| * |
| * @see java.lang.Thread#interrupt() |
| */ |
| @Override |
| public void interrupt() { |
| cancelled = true; |
| compilationTask.cancel(true); |
| super.interrupt(); |
| } |
| |
| /** |
| * {@inheritDoc} |
| * |
| * @see java.lang.Thread#run() |
| */ |
| @Override |
| public void run() { |
| try { |
| final CompilationResult result = compilationTask.get(); |
| checkCancelled(); |
| |
| if (result != null && result.getStatus() != null) { |
| Display.getDefault().asyncExec(new Runnable() { |
| /** |
| * {@inheritDoc} |
| * |
| * @see java.lang.Runnable#run() |
| */ |
| public void run() { |
| clearCompilationMessages(); |
| checkCancelled(); |
| addStatusMessages(result.getStatus(), COMPILATION_MESSAGE_PREFIX); |
| } |
| }); |
| } |
| |
| // Whether there were problems or not, update the context with this result. |
| setCompilationResult(result); |
| } catch (InterruptedException e) { |
| // Thread is expected to be cancelled if another is started |
| } catch (CancellationException e) { |
| // Thread is expected to be cancelled if another is started |
| } catch (ExecutionException e) { |
| checkCancelled(); |
| String message = e.getMessage(); |
| if (e.getCause() != null) { |
| message = e.getCause().getMessage(); |
| } |
| final IStatus status = new Status(IStatus.ERROR, InterpreterPlugin.PLUGIN_ID, message); |
| final CompilationResult result = new CompilationResult(status); |
| |
| Display.getDefault().asyncExec(new Runnable() { |
| /** |
| * {@inheritDoc} |
| * |
| * @see java.lang.Runnable#run() |
| */ |
| public void run() { |
| clearCompilationMessages(); |
| checkCancelled(); |
| addStatusMessages(status, COMPILATION_MESSAGE_PREFIX); |
| } |
| }); |
| |
| setCompilationResult(result); |
| } |
| } |
| |
| /** |
| * Throws a new {@link CancellationException} if the current thread has been cancelled. |
| */ |
| protected void checkCancelled() { |
| if (cancelled) { |
| throw new CancellationException(); |
| } |
| } |
| } |
| |
| /** |
| * This will be installed on the workbench page on which this view is displayed in order to listen to |
| * selection events corresponding to Notifiers. |
| * |
| * @author <a href="mailto:laurent.goubet@obeo.fr">Laurent Goubet</a> |
| */ |
| private class NotifierSelectionListener implements ISelectionListener { |
| /** |
| * Increases visibility of the default constructor. |
| */ |
| NotifierSelectionListener() { |
| // Increases visibility |
| } |
| |
| /** |
| * {@inheritDoc} |
| * |
| * @see org.eclipse.ui.ISelectionListener#selectionChanged(org.eclipse.ui.IWorkbenchPart, |
| * org.eclipse.jface.viewers.ISelection) |
| */ |
| @SuppressWarnings("unchecked") |
| public void selectionChanged(IWorkbenchPart part, ISelection selection) { |
| if (!selection.isEmpty() && selection instanceof IStructuredSelection) { |
| boolean cleared = false; |
| final Iterator<Object> selectionIterator = ((IStructuredSelection)selection).iterator(); |
| while (selectionIterator.hasNext()) { |
| final Object next = selectionIterator.next(); |
| final Notifier nextNotifier; |
| if (next instanceof Notifier) { |
| nextNotifier = (Notifier)next; |
| } else { |
| final Notifier adaptedNotifier = adaptAs(next, Notifier.class); |
| if (adaptedNotifier != null) { |
| nextNotifier = adaptedNotifier; |
| } else { |
| nextNotifier = adaptAs(next, EObject.class); |
| } |
| } |
| if (nextNotifier != null) { |
| // At least one of the selected objects is a Notifier, clear current selection |
| if (!cleared) { |
| clearSelection(); |
| cleared = true; |
| } |
| addToSelection(nextNotifier); |
| } |
| } |
| // If the selection changed somehow, relaunch the real-time evaluation |
| if (cleared && realTimeThread != null) { |
| realTimeThread.setDirty(); |
| } |
| } |
| } |
| |
| /** |
| * Adapts the given object to the given class. |
| * |
| * @param object |
| * The object to adapt |
| * @param clazz |
| * The class to which the object should be adapted |
| * @param <T> |
| * The type of the class to obtain |
| * @return The adapted object |
| */ |
| @SuppressWarnings("unchecked") |
| private <T> T adaptAs(Object object, Class<T> clazz) { |
| if (object instanceof IAdaptable) { |
| return (T)((IAdaptable)object).getAdapter(clazz); |
| } |
| return (T)Platform.getAdapterManager().getAdapter(object, clazz); |
| } |
| } |
| |
| /** |
| * This implementation of a Thread will be used to wrap an evaluation task as returned by the |
| * LanguageInterpreter, then asynchronously update the form with all error messages (if any) that were |
| * raised by this compilation task. Afterwards, this Thread will update the result view of the interpreter |
| * form. |
| * |
| * @author <a href="mailto:laurent.goubet@obeo.fr">Laurent Goubet</a> |
| */ |
| private class EvaluationThread extends Thread { |
| /** |
| * We will set this flag on {@link #interrupt()} in order to determine whether the thread was |
| * cancelled (which could happen <b>after</b> the thread has completed, which would make the |
| * "interrupted" flag quiet. |
| */ |
| private boolean cancelled; |
| |
| /** The evaluation thread which result we are to wait for. */ |
| private Future<EvaluationResult> evaluationTask; |
| |
| /** Context of the interpreter as it was when this Thread has been created. */ |
| private final InterpreterContext interpreterContext; |
| |
| /** |
| * Instantiates our Thread given the initial interpreter context. |
| * |
| * @param interpreterContext |
| * The initial interpreter context. |
| */ |
| EvaluationThread(InterpreterContext interpreterContext) { |
| super("InterpreterEvaluationThread"); //$NON-NLS-1$ |
| this.interpreterContext = interpreterContext; |
| } |
| |
| /** |
| * {@inheritDoc} |
| * |
| * @see java.lang.Thread#interrupt() |
| */ |
| @Override |
| public void interrupt() { |
| cancelled = true; |
| if (evaluationTask != null) { |
| evaluationTask.cancel(true); |
| } |
| super.interrupt(); |
| } |
| |
| /** |
| * {@inheritDoc} |
| * |
| * @see java.lang.Thread#run() |
| */ |
| @Override |
| public void run() { |
| try { |
| checkCancelled(); |
| Display.getDefault().syncExec(new Runnable() { |
| public void run() { |
| getForm().setBusy(true); |
| } |
| }); |
| // Cannot do anything before the current compilation thread stops. |
| if (compilationThread != null) { |
| compilationThread.join(); |
| } |
| checkCancelled(); |
| |
| Callable<EvaluationResult> evaluationCallable = getCurrentLanguageInterpreter() |
| .getEvaluationTask(new EvaluationContext(interpreterContext, compilationResult)); |
| evaluationTask = evaluationPool.submit(evaluationCallable); |
| |
| final EvaluationResult result = evaluationTask.get(); |
| checkCancelled(); |
| |
| if (result != null) { |
| Display.getDefault().asyncExec(new Runnable() { |
| /** |
| * {@inheritDoc} |
| * |
| * @see java.lang.Runnable#run() |
| */ |
| public void run() { |
| clearEvaluationMessages(); |
| if (result.getStatus() != null) { |
| addStatusMessages(result.getStatus(), EVALUATION_MESSAGE_PREFIX); |
| } |
| // whether there were problems or not, try and update the result viewer. |
| setEvaluationResult(result); |
| } |
| }); |
| } |
| } catch (InterruptedException e) { |
| // Thread is expected to be cancelled if another is started |
| } catch (CancellationException e) { |
| // Thread is expected to be cancelled if another is started |
| } catch (ExecutionException e) { |
| String message = e.getMessage(); |
| if (e.getCause() != null) { |
| message = e.getCause().getMessage(); |
| } |
| message = e.getClass().getName() + ' ' + message; |
| final IStatus status = new Status(IStatus.ERROR, InterpreterPlugin.PLUGIN_ID, message); |
| final EvaluationResult result = new EvaluationResult(status); |
| |
| Display.getDefault().asyncExec(new Runnable() { |
| /** |
| * {@inheritDoc} |
| * |
| * @see java.lang.Runnable#run() |
| */ |
| public void run() { |
| clearEvaluationMessages(); |
| addStatusMessages(status, EVALUATION_MESSAGE_PREFIX); |
| setEvaluationResult(result); |
| } |
| }); |
| } finally { |
| Display.getDefault().syncExec(new Runnable() { |
| public void run() { |
| getForm().setBusy(false); |
| } |
| }); |
| } |
| } |
| |
| /** |
| * Throws a new {@link CancellationException} if the current thread has been cancelled. |
| */ |
| protected void checkCancelled() { |
| if (cancelled) { |
| throw new CancellationException(); |
| } |
| } |
| } |
| |
| /** |
| * This implementation of a Thread will be used to wrap a splitting task as returned by the |
| * LanguageInterpreter, then asynchronously update the form with all error messages (if any) that were |
| * raised by this compilation task. Afterwards, this Thread will update the sub-expressions view of the |
| * interpreter form. |
| * |
| * @author <a href="mailto:marwa.rostren@obeo.fr">Marwa Rostren</a> |
| */ |
| private class ExpressionSplittingThread extends Thread { |
| /** |
| * We will set this flag on {@link #interrupt()} in order to determine whether the thread was |
| * cancelled (which could happen <b>after</b> the thread has completed, which would make the |
| * "interrupted" flag quiet. |
| */ |
| private boolean cancelled; |
| |
| /** The splitting thread which result we are to wait for. */ |
| private Future<SplitExpression> splittingTask; |
| |
| /** Context of the interpreter as it was when this Thread has been created. */ |
| private final InterpreterContext interpreterContext; |
| |
| /** |
| * Instantiates our Thread given the initial interpreter context. |
| * |
| * @param interpreterContext |
| * The initial interpreter context. |
| */ |
| ExpressionSplittingThread(InterpreterContext interpreterContext) { |
| super("InterpreterExpressionSplittingThread"); //$NON-NLS-1$ |
| this.interpreterContext = interpreterContext; |
| } |
| |
| /** |
| * {@inheritDoc} |
| * |
| * @see java.lang.Thread#interrupt() |
| */ |
| @Override |
| public void interrupt() { |
| cancelled = true; |
| if (splittingTask != null) { |
| splittingTask.cancel(true); |
| } |
| super.interrupt(); |
| } |
| |
| /** |
| * {@inheritDoc} |
| * |
| * @see java.lang.Thread#run() |
| */ |
| @Override |
| public void run() { |
| try { |
| checkCancelled(); |
| // Cannot do anything before the current compilation thread stops. |
| if (compilationThread != null) { |
| compilationThread.join(); |
| } |
| checkCancelled(); |
| |
| Callable<SplitExpression> splitExpressionCallable = getCurrentLanguageInterpreter() |
| .getExpressionSplittingTask( |
| new EvaluationContext(interpreterContext, compilationResult)); |
| |
| if (splitExpressionCallable == null) { |
| return; |
| } |
| |
| splittingTask = splittingPool.submit(splitExpressionCallable); |
| |
| final SplitExpression splitExpression = splittingTask.get(); |
| checkCancelled(); |
| |
| Display.getDefault().asyncExec(new Runnable() { |
| /** |
| * {@inheritDoc} |
| * |
| * @see java.lang.Runnable#run() |
| */ |
| public void run() { |
| setSubExpressions(splitExpression); |
| } |
| }); |
| } catch (InterruptedException e) { |
| // Thread is expected to be cancelled if another is started |
| } catch (CancellationException e) { |
| // Thread is expected to be cancelled if another is started |
| } catch (ExecutionException e) { |
| Display.getDefault().asyncExec(new Runnable() { |
| /** |
| * {@inheritDoc} |
| * |
| * @see java.lang.Runnable#run() |
| */ |
| public void run() { |
| setSubExpressions(null); |
| } |
| }); |
| } |
| } |
| |
| /** |
| * Throws a new {@link CancellationException} if the current thread has been cancelled. |
| */ |
| protected void checkCancelled() { |
| if (cancelled) { |
| throw new CancellationException(); |
| } |
| } |
| } |
| |
| /** |
| * This daemon thread will be launched whenever the "real-time" toggle is activated, and will only be |
| * stopped when the view is disposed or the "real-time" toggle is disabled. |
| * <p> |
| * This Thread will be constantly reset on modifications of the expression viewer, and will only really |
| * start its work if the expression is left untouched for a given count of seconds. |
| * </p> |
| * |
| * @author <a href="mailto:laurent.goubet@obeo.fr">Laurent Goubet</a> |
| */ |
| private class RealTimeThread extends Thread { |
| /** Time to wait before launching the evaluation (0.1 second by default). */ |
| private static final int DELAY = 100; |
| |
| /** This will be set to <code>true</code> whenever we need to recompile the expression. */ |
| private boolean dirty; |
| |
| /** The lock we'll acquire for this thread's work. */ |
| private final Object lock = new Object(); |
| |
| /** This will be set to <code>true</code> whenever we should reset this thread's timer. */ |
| private boolean reset; |
| |
| /** |
| * Instantiates the real-time evaluation thread. |
| */ |
| RealTimeThread() { |
| super("InterpreterRealTimeThread"); //$NON-NLS-1$ |
| setPriority(Thread.MIN_PRIORITY); |
| setDaemon(true); |
| } |
| |
| /** |
| * Resets this thread's timer. |
| */ |
| public void reset() { |
| synchronized(this) { |
| reset = true; |
| } |
| } |
| |
| /** |
| * {@inheritDoc} |
| * |
| * @see java.lang.Thread#run() |
| */ |
| @Override |
| public void run() { |
| while (!Thread.interrupted()) { |
| synchronized(lock) { |
| try { |
| lock.wait(DELAY); |
| } catch (InterruptedException e) { |
| // This is expected |
| } |
| } |
| |
| synchronized(this) { |
| if (reset) { |
| reset = false; |
| // If a reset has been asked for, stop this iteration |
| continue; |
| } |
| if (dirty) { |
| dirty = false; |
| } else { |
| // The expression does not need to be recompiled |
| continue; |
| } |
| } |
| |
| Display.getDefault().asyncExec(new Runnable() { |
| /** |
| * {@inheritDoc} |
| * |
| * @see java.lang.Runnable#run() |
| */ |
| public void run() { |
| compileAndEvaluate(); |
| } |
| }); |
| } |
| } |
| |
| /** |
| * Sets the "dirty" state of this thread to indicate the expression needs to be recompiled. |
| */ |
| public void setDirty() { |
| synchronized(this) { |
| dirty = true; |
| } |
| } |
| } |
| |
| /** |
| * This listener will react to activation events on this view in order to avoid some visual glitches |
| * appearing when closing then reopening the view. |
| * |
| * @author <a href="mailto:laurent.goubet@obeo.fr">Laurent Goubet</a> |
| */ |
| private static class ActivationListener implements IPartListener { |
| /** This interpreter view. */ |
| private final InterpreterView view; |
| |
| /** |
| * Constructs this listener. |
| * |
| * @param view |
| * The view which activation we are interested in. |
| */ |
| ActivationListener(InterpreterView view) { |
| this.view = view; |
| } |
| |
| /** |
| * {@inheritDoc} |
| * |
| * @see IPartListener#partActivated(IWorkbenchPart) |
| */ |
| public void partActivated(IWorkbenchPart part) { |
| if (part == view) { |
| view.refreshExpressionSection(); |
| } |
| } |
| |
| /** |
| * {@inheritDoc} |
| * |
| * @see IPartListener#partDeactivated(IWorkbenchPart) |
| */ |
| public void partDeactivated(IWorkbenchPart part) { |
| // Empty implementation |
| } |
| |
| /** |
| * {@inheritDoc} |
| * |
| * @see IPartListener#partClosed(IWorkbenchPart) |
| */ |
| public void partClosed(IWorkbenchPart part) { |
| // Empty implementation |
| } |
| |
| /** |
| * {@inheritDoc} |
| * |
| * @see IPartListener#partBroughtToTop(IWorkbenchPart) |
| */ |
| public void partBroughtToTop(IWorkbenchPart part) { |
| // Empty implementation |
| } |
| |
| /** |
| * {@inheritDoc} |
| * |
| * @see IPartListener#partOpened(IWorkbenchPart) |
| */ |
| public void partOpened(IWorkbenchPart part) { |
| // Empty implementation |
| } |
| } |
| |
| /** |
| * This will be added to the sub-expression viewer in order to launch evaluation of parts of the |
| * expression entered by the user as split through the expression splitting thread. |
| * |
| * @author <a href="mailto:marwa.rostren@obeo.fr">Marwa Rostren</a> |
| */ |
| protected class SubExpressionListener implements ISelectionChangedListener { |
| /** |
| * {@inheritDoc} |
| * |
| * @see org.eclipse.jface.viewers.ISelectionChangedListener#selectionChanged(org.eclipse.jface.viewers.SelectionChangedEvent) |
| */ |
| public void selectionChanged(SelectionChangedEvent event) { |
| if (event.getSelection() instanceof IStructuredSelection) { |
| Object selection = ((IStructuredSelection)event.getSelection()).getFirstElement(); |
| if (selection instanceof SubExpression) { |
| evaluateSubExpression(((SubExpression)selection).getExpression()); |
| } else if (selection instanceof SplitExpression) { |
| evaluateSubExpression(((SplitExpression)selection).getFullExpression()); |
| } |
| } |
| } |
| } |
| } |