com.google.dart.tools.debug.ui.internal.objectinspector.ObjectInspectorView.java Source code

Java tutorial

Introduction

Here is the source code for com.google.dart.tools.debug.ui.internal.objectinspector.ObjectInspectorView.java

Source

/*
 * Copyright (c) 2013, the Dart project authors.
 * 
 * Licensed under the Eclipse Public License v1.0 (the "License"); you may not use this file except
 * in compliance with the License. You may obtain a copy of the License at
 * 
 * http://www.eclipse.org/legal/epl-v10.html
 * 
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and limitations under
 * the License.
 */

package com.google.dart.tools.debug.ui.internal.objectinspector;

import com.google.dart.tools.core.DartCore;
import com.google.dart.tools.debug.core.util.IDartDebugValue;
import com.google.dart.tools.debug.ui.internal.DartDebugUIPlugin;
import com.google.dart.tools.debug.ui.internal.objectinspector.ExpressionEvaluateJob.ExpressionListener;
import com.google.dart.tools.debug.ui.internal.presentation.DartDebugModelPresentation;
import com.google.dart.tools.ui.DartToolsPlugin;
import com.google.dart.tools.ui.DartUI;
import com.google.dart.tools.ui.actions.InstrumentedAction;
import com.google.dart.tools.ui.instrumentation.UIInstrumentationBuilder;
import com.google.dart.tools.ui.internal.text.editor.DartDocumentSetupParticipant;
import com.google.dart.tools.ui.internal.text.functions.PreferencesAdapter;
import com.google.dart.tools.ui.internal.util.SelectionUtil;
import com.google.dart.tools.ui.text.DartPartitions;
import com.google.dart.tools.ui.text.DartSourceViewerConfiguration;
import com.google.dart.tools.ui.text.DartTextTools;

import org.eclipse.core.commands.operations.IUndoContext;
import org.eclipse.core.filebuffers.IDocumentSetupParticipant;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.debug.core.DebugEvent;
import org.eclipse.debug.core.DebugException;
import org.eclipse.debug.core.DebugPlugin;
import org.eclipse.debug.core.IDebugEventSetListener;
import org.eclipse.debug.core.model.IDebugTarget;
import org.eclipse.debug.core.model.IValue;
import org.eclipse.debug.core.model.IVariable;
import org.eclipse.debug.core.model.IWatchExpressionResult;
import org.eclipse.debug.ui.IValueDetailListener;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IMenuListener;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.Document;
import org.eclipse.jface.text.DocumentEvent;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IDocumentListener;
import org.eclipse.jface.text.ITextOperationTarget;
import org.eclipse.jface.text.ITextSelection;
import org.eclipse.jface.text.IUndoManagerExtension;
import org.eclipse.jface.text.TextViewerUndoManager;
import org.eclipse.jface.text.source.SourceViewer;
import org.eclipse.jface.text.source.SourceViewerConfiguration;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.TreeViewerColumn;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.SashForm;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.ui.IActionBars;
import org.eclipse.ui.IMemento;
import org.eclipse.ui.IViewSite;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.actions.ActionFactory;
import org.eclipse.ui.actions.ActionFactory.IWorkbenchAction;
import org.eclipse.ui.editors.text.EditorsUI;
import org.eclipse.ui.operations.RedoActionHandler;
import org.eclipse.ui.operations.UndoActionHandler;
import org.eclipse.ui.part.ViewPart;
import org.eclipse.ui.progress.IWorkbenchSiteProgressService;
import org.eclipse.ui.texteditor.ChainedPreferenceStore;
import org.eclipse.ui.texteditor.IUpdate;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

// TODO: multiple inspected objects

// TODO: code completion

// TODO: forward, back, clear toolbar icons

// TODO: have a keystroke sequence for evaluation

// TODO: insert the eval'd text w/ right justification

// TODO: insert the eval'd text using italics

// TODO: be able to right-click on an eval area and inspect the associated object

// TODO: insert after the expression being evaluated

// TODO: try and reduce the async apis

// TODO: right click, inspect it action on the value area

// TODO: ctrl-enter to eval and print the selection?

// TODO: refactor this class to reduce its complexity

// TODO: the view needs to clear itself when the connection shuts down

// TODO: we really need to populate the tree in a lazy manner

/**
 * The Dart object inspector view.
 */
public class ObjectInspectorView extends ViewPart implements IDebugEventSetListener {
    class TextViewerAction extends Action implements IUpdate {
        private int actionId;

        TextViewerAction(int actionId) {
            this.actionId = actionId;
        }

        @Override
        public boolean isEnabled() {
            return sourceViewer.canDoOperation(actionId);
        }

        @Override
        public void run() {
            sourceViewer.doOperation(actionId);

            updateActions();
        }

        @Override
        public void update() {
            if (super.isEnabled() != isEnabled()) {
                setEnabled(isEnabled());
            }
        }
    }

    private class DoItAction extends InstrumentedAction implements IUpdate {
        public DoItAction() {
            super("Do It");

            setId(getText());
        }

        @Override
        public void update() {
            setEnabled(sourceViewer.canDoOperation(ITextOperationTarget.COPY));
        }

        @Override
        protected void doRun(Event event, UIInstrumentationBuilder instrumentation) {

            try {
                instrumentation.data("Value", getValue().getValueString());
            } catch (Exception e) {
            }
            instrumentation.data("Expression", getExpressionText());

            Job job = new ExpressionEvaluateJob(getValue(), getExpressionText(), new ExpressionListener() {
                @Override
                public void watchEvaluationFinished(IWatchExpressionResult result, String stringValue) {
                    if (result.hasErrors()) {
                        displayError(result);
                    }
                }
            });

            job.schedule();

        }
    }

    private class InspectItAction extends InstrumentedAction implements IUpdate {
        public InspectItAction() {
            super("Inspect It...", DartDebugUIPlugin.getImageDescriptor("obj16/watchlist_view.gif"));

            setId(getText());
        }

        @Override
        public void update() {
            setEnabled(sourceViewer.canDoOperation(ITextOperationTarget.COPY));
        }

        @Override
        protected void doRun(Event event, UIInstrumentationBuilder instrumentation) {

            try {
                instrumentation.data("Value", getValue().getValueString());
            } catch (Exception e) {
            }
            instrumentation.data("Expression", getExpressionText());

            Job job = new ExpressionEvaluateJob(getValue(), getExpressionText(), new ExpressionListener() {
                @Override
                public void watchEvaluationFinished(IWatchExpressionResult result, String stringValue) {
                    if (result.hasErrors()) {
                        displayError(result);
                    } else {
                        inspectAsync(result.getValue());
                    }
                }
            });

            job.schedule();

        }
    }

    private class PrintItAction extends InstrumentedAction implements IUpdate {
        public PrintItAction() {
            super("Print It");

            setId(getText());
        }

        @Override
        public void update() {
            setEnabled(sourceViewer.canDoOperation(ITextOperationTarget.COPY));
        }

        @Override
        protected void doRun(Event event, UIInstrumentationBuilder instrumentation) {

            try {
                instrumentation.data("Value", getValue().getValueString());
            } catch (Exception e) {
            }
            instrumentation.data("Expression", getExpressionText());

            Job job = new ExpressionEvaluateJob(getValue(), getExpressionText(), new ExpressionListener() {
                @Override
                public void watchEvaluationFinished(IWatchExpressionResult result, String stringValue) {
                    if (result.hasErrors()) {
                        displayError(result);
                    } else {
                        appendToTextArea(stringValue);
                    }
                }
            });

            job.schedule();

        }
    }

    private static DartDebugModelPresentation presentation = new DartDebugModelPresentation();

    public static void inspect(IValue value) {
        IWorkbenchPage page = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();

        try {
            ObjectInspectorView view = (ObjectInspectorView) page.showView(DartUI.ID_INSPECTOR_VIEW);

            view.inspectValue(value);
        } catch (PartInitException e) {
            DartDebugUIPlugin.logError(e);
        }
    }

    protected static void inspectAsync(final IValue value) {
        Display.getDefault().asyncExec(new Runnable() {
            @Override
            public void run() {
                inspect(value);
            }
        });
    }

    private TreeViewer treeViewer;

    private UndoActionHandler undoAction;

    private RedoActionHandler redoAction;

    private IUndoContext undoContext;

    private SourceViewer sourceViewer;

    private Map<String, IUpdate> textActions = new HashMap<String, IUpdate>();

    private DoItAction doItAction;

    private PrintItAction printItAction;
    private InspectItAction inspectItAction;
    static Object EXPRESSION_EVAL_JOB_FAMILY = new Object();

    public ObjectInspectorView() {
        DebugPlugin.getDefault().addDebugEventListener(this);
    }

    @Override
    public void createPartControl(Composite parent) {
        SashForm sash = new SashForm(parent, SWT.VERTICAL);

        treeViewer = new TreeViewer(sash, SWT.SINGLE | SWT.V_SCROLL | SWT.FULL_SELECTION);
        treeViewer.setLabelProvider(new NameLabelProvider());
        treeViewer.setContentProvider(new ObjectInspectorContentProvider());
        treeViewer.addSelectionChangedListener(new ISelectionChangedListener() {
            @Override
            public void selectionChanged(SelectionChangedEvent event) {
                ISelection sel = event.getSelection();
                Object object = SelectionUtil.getSingleElement(sel);

                if (object instanceof IVariable) {
                    IVariable variable = (IVariable) object;

                    try {
                        presentation.computeDetail(variable.getValue(), new IValueDetailListener() {
                            @Override
                            public void detailComputed(IValue value, String result) {
                                updateStatusLine(result);
                            }
                        });
                    } catch (DebugException e) {
                        DartDebugUIPlugin.logError(e);
                    }
                } else {
                    clearStatusLine();
                }
            }
        });

        TreeViewerColumn nameColumn = new TreeViewerColumn(treeViewer, SWT.LEFT);
        nameColumn.setLabelProvider(new NameLabelProvider());
        nameColumn.getColumn().setText("Name");
        nameColumn.getColumn().setWidth(120);
        nameColumn.getColumn().setResizable(true);

        TreeViewerColumn valueColumn = new TreeViewerColumn(treeViewer, SWT.LEFT);
        valueColumn.setLabelProvider(new ValueLabelProvider());
        valueColumn.getColumn().setText("Value");
        valueColumn.getColumn().setWidth(140);
        valueColumn.getColumn().setResizable(true);

        Tree tree = treeViewer.getTree();
        tree.setHeaderVisible(true);

        sourceViewer = new SourceViewer(sash, null, SWT.V_SCROLL | SWT.WRAP);
        sourceViewer.configure(getSourceViewerConfiguration());
        sourceViewer.setDocument(createDocument());
        sourceViewer.setUndoManager(new TextViewerUndoManager(100));
        sourceViewer.getTextWidget().setFont(JFaceResources.getFont(JFaceResources.TEXT_FONT));
        sourceViewer.getTextWidget().setTabs(2);
        sourceViewer.getDocument().addDocumentListener(new IDocumentListener() {
            @Override
            public void documentAboutToBeChanged(DocumentEvent event) {

            }

            @Override
            public void documentChanged(DocumentEvent event) {
                updateActions();
            }
        });
        sourceViewer.addSelectionChangedListener(new ISelectionChangedListener() {
            @Override
            public void selectionChanged(SelectionChangedEvent event) {
                updateActions();
            }
        });

        sourceViewer.activatePlugins();

        createActions();
        createGlobalActionHandlers();
        hookContextMenu();
        updateActions();

        //    PresentationReconciler presentationReconciler = new PresentationReconciler();
        //    presentationReconciler.install(textViewer);

        sash.setWeights(new int[] { 60, 40 });
    }

    @Override
    public void dispose() {
        DebugPlugin.getDefault().removeDebugEventListener(this);

        super.dispose();
    }

    @Override
    public void handleDebugEvents(DebugEvent[] events) {
        for (DebugEvent event : events) {
            if (event.getKind() == DebugEvent.TERMINATE && event.getSource() instanceof IDebugTarget) {
                handleDebugTargetTerminated((IDebugTarget) event.getSource());
            }
        }
    }

    @Override
    public void init(IViewSite site, IMemento memento) throws PartInitException {
        super.init(site, memento);

        IWorkbenchSiteProgressService progressService = (IWorkbenchSiteProgressService) getViewSite()
                .getAdapter(IWorkbenchSiteProgressService.class);

        if (progressService != null) {
            initProgressService(progressService);
        }
    }

    @Override
    public void setFocus() {
        treeViewer.getControl().setFocus();
    }

    protected void addTextAction(ActionFactory actionFactory, int textOperation) {
        IWorkbenchWindow window = getViewSite().getWorkbenchWindow();
        IWorkbenchAction globalAction = actionFactory.create(window);

        // Create our text action.
        TextViewerAction textAction = new TextViewerAction(textOperation);

        textActions.put(actionFactory.getId(), textAction);

        // Copy its properties from the global action.
        textAction.setText(globalAction.getText());
        textAction.setToolTipText(globalAction.getToolTipText());
        textAction.setDescription(globalAction.getDescription());
        textAction.setImageDescriptor(globalAction.getImageDescriptor());
        textAction.setDisabledImageDescriptor(globalAction.getDisabledImageDescriptor());
        textAction.setAccelerator(globalAction.getAccelerator());

        // Make sure it's up to date.
        textAction.update();

        // Register our text action with the global action handler.
        IActionBars actionBars = getViewSite().getActionBars();

        actionBars.setGlobalActionHandler(actionFactory.getId(), textAction);
    }

    protected void clearStatusLine() {
        setStatusLine(null);
    }

    protected void clearViewer() {
        Display display = Display.getDefault();

        if (display != null) {
            display.asyncExec(new Runnable() {
                @Override
                public void run() {
                    setContentDescription(" ");
                    treeViewer.setInput(null);
                }
            });
        }
    }

    protected void fillContextMenu(IMenuManager manager) {
        manager.add(doItAction);
        manager.add(printItAction);
        manager.add(inspectItAction);
    }

    protected String getExpressionText() {
        // TODO: if there's a selection, return that; else return the entire text?

        return ((ITextSelection) sourceViewer.getSelection()).getText();
    }

    protected void initProgressService(IWorkbenchSiteProgressService progressService) {
        progressService.showBusyForFamily(EXPRESSION_EVAL_JOB_FAMILY);
    }

    protected void inspectValue(IValue value) {
        presentation.computeDetail(value, new IValueDetailListener() {
            @Override
            public void detailComputed(final IValue value, final String result) {
                Display.getDefault().asyncExec(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            setContentDescription(
                                    "UNDER CONSTRUCTION! " + value.getReferenceTypeName() + ": " + result);
                        } catch (DebugException e) {

                        }
                    }
                });
            }
        });

        treeViewer.setInput(value);
    }

    protected void setStatusLine(String message) {
        getViewSite().getActionBars().getStatusLineManager().setMessage(message);
    }

    private void appendToTextArea(final String result) {
        // TODO: display the results in italic and right-aligned

        Display.getDefault().asyncExec(new Runnable() {
            @Override
            public void run() {
                IDocument document = sourceViewer.getDocument();

                String insert = result;

                try {
                    String current = document.get();

                    if (!current.endsWith("\n")) {
                        insert = "\n" + insert;
                    }

                    if (!insert.endsWith("\n")) {
                        insert += "\n";
                    }

                    document.replace(document.getLength(), 0, insert);
                } catch (BadLocationException e) {
                    DartDebugUIPlugin.logError(e);
                }
            }
        });
    }

    private void createActions() {
        doItAction = new DoItAction();
        printItAction = new PrintItAction();
        inspectItAction = new InspectItAction();

        textActions.put(doItAction.getId(), doItAction);
        textActions.put(printItAction.getId(), printItAction);
        textActions.put(inspectItAction.getId(), inspectItAction);
    }

    private IDocument createDocument() {
        IDocument document = new Document();
        IDocumentSetupParticipant setupParticipant = new DartDocumentSetupParticipant();

        setupParticipant.setup(document);

        return document;
    }

    private void createGlobalActionHandlers() {
        undoContext = ((IUndoManagerExtension) sourceViewer.getUndoManager()).getUndoContext();

        // set up action handlers that operate on the current context
        undoAction = new UndoActionHandler(getSite(), undoContext);
        redoAction = new RedoActionHandler(getSite(), undoContext);

        IActionBars actionBars = getViewSite().getActionBars();

        actionBars.setGlobalActionHandler(ActionFactory.UNDO.getId(), undoAction);
        actionBars.setGlobalActionHandler(ActionFactory.REDO.getId(), redoAction);

        // Install the standard text actions.
        addTextAction(ActionFactory.CUT, ITextOperationTarget.CUT);
        addTextAction(ActionFactory.COPY, ITextOperationTarget.COPY);
        addTextAction(ActionFactory.PASTE, ITextOperationTarget.PASTE);
        addTextAction(ActionFactory.DELETE, ITextOperationTarget.DELETE);
        addTextAction(ActionFactory.SELECT_ALL, ITextOperationTarget.SELECT_ALL);
    }

    private IPreferenceStore createPreferenceStore() {
        List<IPreferenceStore> stores = new ArrayList<IPreferenceStore>();

        stores.add(DartToolsPlugin.getDefault().getPreferenceStore());
        stores.add(new PreferencesAdapter(DartCore.getPlugin().getPluginPreferences()));
        stores.add(EditorsUI.getPreferenceStore());

        return new ChainedPreferenceStore(stores.toArray(new IPreferenceStore[stores.size()]));
    }

    private void displayError(final IWatchExpressionResult result) {
        // TODO(devoncarew): what's the best way to display these errors?
        Display.getDefault().asyncExec(new Runnable() {
            @Override
            public void run() {
                getViewSite().getActionBars().getStatusLineManager().setErrorMessage(result.getErrorMessages()[0]);
            }
        });
    }

    private SourceViewerConfiguration getSourceViewerConfiguration() {
        DartTextTools textTools = DartToolsPlugin.getDefault().getDartTextTools();

        return new DartSourceViewerConfiguration(textTools.getColorManager(), createPreferenceStore(), null,
                DartPartitions.DART_PARTITIONING);
    }

    private IDartDebugValue getValue() {
        Object object = treeViewer.getInput();

        if (object instanceof IDartDebugValue) {
            return (IDartDebugValue) object;
        } else {
            return null;
        }
    }

    private void handleDebugTargetTerminated(IDebugTarget target) {
        Object object = treeViewer.getInput();

        if (object instanceof IValue) {
            IValue value = (IValue) object;

            if (value.getDebugTarget() == target) {
                clearViewer();
            }
        }
    }

    private void hookContextMenu() {
        MenuManager menuMgr = new MenuManager("#PopupMenu");

        menuMgr.setRemoveAllWhenShown(true);
        menuMgr.addMenuListener(new IMenuListener() {
            @Override
            public void menuAboutToShow(IMenuManager manager) {
                ObjectInspectorView.this.fillContextMenu(manager);
            }
        });

        Menu menu = menuMgr.createContextMenu(sourceViewer.getControl());
        sourceViewer.getControl().setMenu(menu);
        getSite().registerContextMenu(menuMgr, sourceViewer);
    }

    private void updateActions() {
        for (IUpdate action : textActions.values()) {
            action.update();
        }
    }

    private void updateStatusLine(final String message) {
        Display display = Display.getDefault();

        if (display != null) {
            display.asyncExec(new Runnable() {
                @Override
                public void run() {
                    setStatusLine(message);
                }
            });
        }
    }

}