com.google.dart.tools.ui.internal.text.editor.DartOutlinePage_NEW.java Source code

Java tutorial

Introduction

Here is the source code for com.google.dart.tools.ui.internal.text.editor.DartOutlinePage_NEW.java

Source

/*
 * Copyright (c) 2014, 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.ui.internal.text.editor;

import com.google.common.base.Objects;
import com.google.dart.tools.ui.DartPluginImages;
import com.google.dart.tools.ui.DartToolsPlugin;
import com.google.dart.tools.ui.actions.InstrumentedAction;
import com.google.dart.tools.ui.instrumentation.UIInstrumentationBuilder;
import com.google.dart.tools.ui.internal.text.DartHelpContextIds;
import com.google.dart.tools.ui.internal.util.SWTUtil;
import com.google.dart.tools.ui.internal.viewsupport.ColoredViewersManager;

import org.dartlang.analysis.server.protocol.Element;
import org.dartlang.analysis.server.protocol.Outline;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.ListenerList;
import org.eclipse.jface.action.IAction;
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.preference.IPreferenceStore;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.jface.viewers.DelegatingStyledCellLabelProvider;
import org.eclipse.jface.viewers.DelegatingStyledCellLabelProvider.IStyledLabelProvider;
import org.eclipse.jface.viewers.DoubleClickEvent;
import org.eclipse.jface.viewers.IDoubleClickListener;
import org.eclipse.jface.viewers.IElementComparer;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.StyledString;
import org.eclipse.jface.viewers.TreePath;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerComparator;
import org.eclipse.jface.viewers.ViewerFilter;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.BusyIndicator;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.ui.IActionBars;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.actions.ActionContext;
import org.eclipse.ui.actions.ActionGroup;
import org.eclipse.ui.part.IPageSite;
import org.eclipse.ui.part.Page;
import org.eclipse.ui.views.contentoutline.IContentOutlinePage;

import java.util.List;

/**
 * {@link IContentOutlinePage} for {@link DartEditor}.
 */
public class DartOutlinePage_NEW extends Page implements IContentOutlinePage {

    /**
     * {@link ViewerComparator} for {@link Outline} names.
     */
    public static class NameComparator extends ViewerComparator {
        private static final int NOT_ELEMENT = 2;
        private static final int PRIVATE_ELEMENT = 1;
        private static final int PUBLIC_ELEMENT = 0;

        @Override
        public int category(Object e) {
            if (!(e instanceof Outline)) {
                return NOT_ELEMENT;
            }
            Outline outline = (Outline) e;
            if (outline.getElement().isPrivate()) {
                return PRIVATE_ELEMENT;
            }
            return PUBLIC_ELEMENT;
        }

        @Override
        public int compare(Viewer viewer, Object e1, Object e2) {
            // compare categories
            int cat1 = category(e1);
            int cat2 = category(e2);
            if (cat1 != cat2) {
                return cat1 - cat2;
            }
            // check types
            if (!(e1 instanceof Outline)) {
                return 0;
            }
            if (!(e2 instanceof Outline)) {
                return 0;
            }
            // compare names
            String name1 = ((Outline) e1).getElement().getName();
            String name2 = ((Outline) e2).getElement().getName();
            if (name1 == null || name2 == null) {
                return 0;
            }
            return name1.compareTo(name2);
        }
    }

    public static class OutlineContentProvider implements ITreeContentProvider {
        @Override
        public void dispose() {
        }

        @Override
        public Object[] getChildren(Object parentElement) {
            List<Outline> outlineList = ((Outline) parentElement).getChildren();
            return outlineList.toArray(new Outline[outlineList.size()]);
        }

        @Override
        public Object[] getElements(Object inputElement) {
            List<Outline> outlineList = ((Outline) inputElement).getChildren();
            return outlineList.toArray(new Outline[outlineList.size()]);
        }

        @Override
        public Object getParent(Object element) {
            return ((Outline) element).getParent();
        }

        @Override
        public boolean hasChildren(Object element) {
            return getChildren(element).length != 0;
        }

        @Override
        public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
        }
    }

    public static class OutlineLabelProvider extends LabelProvider implements IStyledLabelProvider {
        private final ElementLabelProvider_NEW elementLabelProvider = new ElementLabelProvider_NEW();

        @Override
        public Image getImage(Object obj) {
            Outline outline = (Outline) obj;
            Element element = outline.getElement();
            return elementLabelProvider.getImage(element);
        }

        @Override
        public StyledString getStyledText(Object obj) {
            Outline outline = (Outline) obj;
            Element element = outline.getElement();
            return elementLabelProvider.getStyledText(element);
        }

        @Override
        public String getText(Object obj) {
            Outline outline = (Outline) obj;
            Element element = outline.getElement();
            return elementLabelProvider.getText(element);
        }
    }

    /**
     * {@link ViewerComparator} for {@link Outline} positions.
     */
    public static class PositionComparator extends ViewerComparator {
        @Override
        public int compare(Viewer viewer, Object e1, Object e2) {
            if (!(e1 instanceof Outline)) {
                return 0;
            }
            if (!(e2 instanceof Outline)) {
                return 0;
            }
            int offset1 = ((Outline) e1).getElement().getLocation().getOffset();
            int offset2 = ((Outline) e2).getElement().getLocation().getOffset();
            return offset1 - offset2;
        }
    }

    private class CollapseAllAction extends InstrumentedAction {
        CollapseAllAction() {
            super("Collapse All"); //$NON-NLS-1$
            setDescription("Collapse All"); //$NON-NLS-1$
            setToolTipText("Collapse All"); //$NON-NLS-1$
            DartPluginImages.setLocalImageDescriptors(this, "collapseall.gif"); //$NON-NLS-1$
            PlatformUI.getWorkbench().getHelpSystem().setHelp(this, DartHelpContextIds.COLLAPSE_ALL_ACTION);
        }

        @Override
        protected void doRun(Event event, UIInstrumentationBuilder instrumentation) {
            viewer.collapseAll();
        }
    }

    private class DartOutlineViewer extends TreeViewer {
        public DartOutlineViewer(Tree tree) {
            super(tree);
            setUseHashlookup(true);
            tree.setBackgroundMode(SWT.INHERIT_FORCE);
        }

        private void updateColors() {
            SWTUtil.setColors(getTree(), preferences);
        }
    }

    private class ExpandAllAction extends InstrumentedAction {
        ExpandAllAction() {
            super("Expand All"); //$NON-NLS-1$
            setDescription("Expand All"); //$NON-NLS-1$
            setToolTipText("Expand All"); //$NON-NLS-1$
            DartPluginImages.setLocalImageDescriptors(this, "expandall.gif"); //$NON-NLS-1$
            PlatformUI.getWorkbench().getHelpSystem().setHelp(this, DartHelpContextIds.EXPAND_ALL_ACTION);
        }

        @Override
        protected void doRun(Event event, UIInstrumentationBuilder instrumentation) {
            viewer.expandAll();
        }
    }

    private class HideNonPublicAction extends InstrumentedAction {
        HideNonPublicAction() {
            super("Hide non-public members", IAction.AS_CHECK_BOX); //$NON-NLS-1$
            setDescription("Hide non-public members"); //$NON-NLS-1$
            setToolTipText("Hide non-public members"); //$NON-NLS-1$
            DartPluginImages.setLocalImageDescriptors(this, "public_co.gif"); //$NON-NLS-1$
            PlatformUI.getWorkbench().getHelpSystem().setHelp(this, DartHelpContextIds.HIDE_NON_PUBLIC_ACTION);
        }

        @Override
        protected void doRun(Event event, UIInstrumentationBuilder instrumentation) {
            if (isChecked()) {
                viewer.addFilter(PUBLIC_FILTER);
            } else {
                viewer.removeFilter(PUBLIC_FILTER);
            }
        }
    }

    private class LexicalSortingAction extends InstrumentedAction {
        public LexicalSortingAction() {
            PlatformUI.getWorkbench().getHelpSystem().setHelp(this,
                    DartHelpContextIds.LEXICAL_SORTING_OUTLINE_ACTION);
            setText(DartEditorMessages.JavaOutlinePage_Sort_label);
            DartPluginImages.setLocalImageDescriptors(this, "alphab_sort_co.gif"); //$NON-NLS-1$
            setToolTipText(DartEditorMessages.JavaOutlinePage_Sort_tooltip);
            setDescription(DartEditorMessages.JavaOutlinePage_Sort_description);
            // restore selection
            boolean checked = DartToolsPlugin.getDefault().getPreferenceStore()
                    .getBoolean("LexicalSortingAction.isChecked"); //$NON-NLS-1$
            valueChanged(checked, false);
        }

        @Override
        protected void doRun(Event event, UIInstrumentationBuilder instrumentation) {
            valueChanged(isChecked(), true);
        }

        private void valueChanged(final boolean on, boolean store) {
            setChecked(on);
            // set comparator
            BusyIndicator.showWhile(viewer.getControl().getDisplay(), new Runnable() {
                @Override
                public void run() {
                    if (on) {
                        viewer.setComparator(NAME_COMPARATOR);
                    } else {
                        viewer.setComparator(POSITION_COMPARATOR);
                    }
                }
            });
            // may be remember selection
            if (store) {
                DartToolsPlugin.getDefault().getPreferenceStore().setValue("LexicalSortingAction.isChecked", on); //$NON-NLS-1$
            }
        }
    }

    public static final IElementComparer OUTLINE_COMPARER = new IElementComparer() {
        @Override
        public boolean equals(Object a, Object b) {
            if (a instanceof TreePath) {
                a = ((TreePath) a).getLastSegment();
            }
            if (b instanceof TreePath) {
                b = ((TreePath) b).getLastSegment();
            }
            if (a == b) {
                return true;
            }
            if (!(a instanceof Outline) || !(b instanceof Outline)) {
                return false;
            }
            Outline outlineA = (Outline) a;
            Outline outlineB = (Outline) b;
            if (!equals(outlineA.getParent(), outlineB.getParent())) {
                return false;
            }
            String nameA = outlineA.getElement().getName();
            String nameB = outlineB.getElement().getName();
            return nameA.equals(nameB);
        }

        @Override
        public int hashCode(Object element) {
            return element.hashCode();
        }
    };

    private static Outline getOutlineAtOffset(Outline outline, int offset) {
        if (outline == null) {
            return null;
        }
        if (!outline.containsInclusive(offset)) {
            return null;
        }
        for (Outline child : outline.getChildren()) {
            Outline result = getOutlineAtOffset(child, offset);
            if (result != null) {
                return result;
            }
        }
        return outline;
    }

    /**
     * Checks if two given {@link Outline} objects are equal, and copies location information from "b"
     * to "a".
     */
    private static boolean isEqualOutlineTree(Outline a, Outline b) {
        if (a == null || b == null) {
            return false;
        }
        // compare elements
        Element elementA = a.getElement();
        Element elementB = b.getElement();
        if (!ElementLabelProvider_NEW.areEqual(elementA, elementB)) {
            return false;
        }
        // compare children
        List<Outline> childrenA = a.getChildren();
        List<Outline> childrenB = b.getChildren();
        if (childrenA.size() != childrenB.size()) {
            return false;
        }
        for (int i = 0; i < childrenA.size(); i++) {
            Outline childA = childrenA.get(i);
            Outline childB = childrenB.get(i);
            if (!isEqualOutlineTree(childA, childB)) {
                return false;
            }
        }
        // OK, update "a"
        a.setOffset(b.getOffset());
        a.setLength(b.getLength());
        a.setElement(b.getElement());
        return true;
    }

    private final ListenerList selectionChangedListeners = new ListenerList(ListenerList.IDENTITY);
    private final String contextMenuID;
    private DartEditor editor;
    private Outline input;
    private int offset;
    private DartOutlineViewer viewer;
    private boolean ignoreSelectionChangedEvent = false;
    private Menu contextMenu;
    private CompositeActionGroup actionGroups;

    private IPreferenceStore preferences;

    private IPropertyChangeListener propertyChangeListener = new IPropertyChangeListener() {
        @Override
        public void propertyChange(PropertyChangeEvent event) {
            doPropertyChange(event);
        }
    };
    private static final ViewerComparator NAME_COMPARATOR = new NameComparator();
    private static final ViewerComparator POSITION_COMPARATOR = new PositionComparator();

    private static final ViewerFilter PUBLIC_FILTER = new ViewerFilter() {
        @Override
        public boolean select(Viewer viewer, Object parentElement, Object o) {
            if (o instanceof Outline) {
                throw new IllegalStateException("com.google.dart.server.Outline not yet supported.");
                //        return !((Outline) o).getElement().isPrivate();
            }
            return false;
        }
    };

    public DartOutlinePage_NEW(String contextMenuID, DartEditor editor) {
        Assert.isNotNull(editor);
        this.contextMenuID = contextMenuID;
        this.editor = editor;
    }

    @Override
    public void addSelectionChangedListener(ISelectionChangedListener listener) {
        if (viewer != null) {
            viewer.addSelectionChangedListener(listener);
        } else {
            selectionChangedListeners.add(listener);
        }
    }

    @Override
    public void createControl(Composite parent) {
        preferences = DartToolsPlugin.getDefault().getCombinedPreferenceStore();
        Tree tree = new Tree(parent, SWT.MULTI | SWT.FULL_SELECTION);
        // create "viewer"
        viewer = new DartOutlineViewer(tree);
        ColoredViewersManager.install(viewer);
        viewer.setComparer(OUTLINE_COMPARER);
        viewer.setContentProvider(new OutlineContentProvider());
        viewer.setLabelProvider(new DelegatingStyledCellLabelProvider(new OutlineLabelProvider()));
        SWTUtil.bindJFaceResourcesFontToControl(tree);
        internalSetInput();
        // install listeners added before UI creation
        {
            Object[] listeners = selectionChangedListeners.getListeners();
            for (Object listener : listeners) {
                selectionChangedListeners.remove(listener);
                viewer.addSelectionChangedListener((ISelectionChangedListener) listener);
            }
        }
        // prepare context menu
        MenuManager manager = new MenuManager(contextMenuID, contextMenuID);
        manager.setRemoveAllWhenShown(true);
        manager.addMenuListener(new IMenuListener() {
            @Override
            public void menuAboutToShow(IMenuManager m) {
                contextMenuAboutToShow(m);
            }
        });
        contextMenu = manager.createContextMenu(tree);
        tree.setMenu(contextMenu);
        // register "viewer" as selection provide for this view
        IPageSite site = getSite();
        site.setSelectionProvider(viewer);
        actionGroups = new CompositeActionGroup(new ActionGroup[] {});
        // configure actions
        {
            IActionBars actionBars = site.getActionBars();
            {
                IToolBarManager toolBarManager = actionBars.getToolBarManager();
                toolBarManager.add(new HideNonPublicAction());
                toolBarManager.add(new LexicalSortingAction());
                toolBarManager.add(new ExpandAllAction());
                toolBarManager.add(new CollapseAllAction());
            }
            actionGroups.fillActionBars(actionBars);
        }
        // handle TreeViewer actions
        viewer.addSelectionChangedListener(new ISelectionChangedListener() {
            @Override
            public void selectionChanged(SelectionChangedEvent event) {
                if (ignoreSelectionChangedEvent) {
                    return;
                }
                editor.doSelectionChanged(event.getSelection());
            }
        });
        viewer.addDoubleClickListener(new IDoubleClickListener() {
            @Override
            public void doubleClick(DoubleClickEvent event) {
                toggleExpansion(event.getSelection());
            }
        });
        // update colors
        preferences.addPropertyChangeListener(propertyChangeListener);
        viewer.updateColors();
    }

    @Override
    public void dispose() {
        // clear "editor"
        if (editor == null) {
            return;
        }
        editor.outlinePageClosed();
        editor = null;
        // remove property listeners
        if (propertyChangeListener != null) {
            preferences.removePropertyChangeListener(propertyChangeListener);
            propertyChangeListener = null;
        }
        // dispose "contextMenu"
        if (contextMenu != null && !contextMenu.isDisposed()) {
            contextMenu.dispose();
            contextMenu = null;
        }
        // dispose actions
        if (actionGroups != null) {
            actionGroups.dispose();
            actionGroups = null;
        }
        // we are not selection provider anymore
        getSite().setSelectionProvider(null);
        // done
        viewer = null;
        input = null;
        super.dispose();
    }

    @Override
    public Control getControl() {
        if (viewer != null) {
            return viewer.getControl();
        }
        return null;
    }

    @Override
    public ISelection getSelection() {
        if (viewer == null) {
            return StructuredSelection.EMPTY;
        }
        return viewer.getSelection();
    }

    @Override
    public void removeSelectionChangedListener(ISelectionChangedListener listener) {
        if (viewer != null) {
            viewer.removeSelectionChangedListener(listener);
        } else {
            selectionChangedListeners.remove(listener);
        }
    }

    public void select(int offset) {
        Outline newOutline = getOutlineAtOffset(input, offset);
        if (newOutline == null || viewer == null) {
            return;
        }
        // check if the same Outline is selected
        IStructuredSelection selection = (IStructuredSelection) viewer.getSelection();
        Outline oldOutline = (Outline) selection.getFirstElement();
        if (isEqualOutlineTree(oldOutline, newOutline)) {
            return;
        }
        // set new selection
        setSelection(new StructuredSelection(newOutline));
        viewer.reveal(newOutline);
    }

    @Override
    public void setFocus() {
        if (viewer != null) {
            viewer.getControl().setFocus();
        }
    }

    public void setInput(Outline input, int offset) {
        if (isEqualOutlineTree(this.input, input)) {
            return;
        }
        this.input = input;
        this.offset = offset;
        internalSetInput();
    }

    @Override
    public void setSelection(ISelection newSelection) {
        if (!Objects.equal(viewer.getSelection(), newSelection)) {
            ignoreSelectionChangedEvent = true;
            try {
                viewer.setSelection(newSelection, true);
            } finally {
                ignoreSelectionChangedEvent = false;
            }
        }
    }

    protected void toggleExpansion(ISelection selection) {
        if (selection instanceof IStructuredSelection) {
            Object sel = ((IStructuredSelection) selection).getFirstElement();
            boolean expanded = viewer.getExpandedState(sel);
            if (expanded) {
                viewer.collapseToLevel(sel, 1);
            } else {
                viewer.expandToLevel(sel, 1);
            }
        }
    }

    private void contextMenuAboutToShow(IMenuManager menu) {
        DartToolsPlugin.createStandardGroups(menu);
        IStructuredSelection selection = (IStructuredSelection) getSelection();
        actionGroups.setContext(new ActionContext(selection));
        actionGroups.fillContextMenu(menu);
    }

    private void doPropertyChange(PropertyChangeEvent event) {
        SWTUtil.runUI(new Runnable() {
            @Override
            public void run() {
                if (viewer != null) {
                    viewer.updateColors();
                    viewer.refresh(false);
                }
            }
        });
    }

    private void internalSetInput() {
        if (viewer == null) {
            return;
        }
        if (input == null) {
            return;
        }
        Object[] expandedElements = viewer.getExpandedElements();
        ignoreSelectionChangedEvent = true;
        try {
            viewer.setInput(input);
        } finally {
            ignoreSelectionChangedEvent = false;
        }
        viewer.setExpandedElements(expandedElements);
        select(offset);
    }
}