com.redhat.ceylon.eclipse.code.outline.CeylonOutlinePage.java Source code

Java tutorial

Introduction

Here is the source code for com.redhat.ceylon.eclipse.code.outline.CeylonOutlinePage.java

Source

/*******************************************************************************
* Copyright (c) 2007 IBM Corporation.
* 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:
*    Robert Fuhrer (rfuhrer@watson.ibm.com) - initial API and implementation
*******************************************************************************/

package com.redhat.ceylon.eclipse.code.outline;

import static com.redhat.ceylon.eclipse.code.editor.Util.getCurrentEditor;
import static com.redhat.ceylon.eclipse.code.parse.CeylonSourcePositionLocator.getEndOffset;
import static com.redhat.ceylon.eclipse.code.parse.CeylonSourcePositionLocator.getStartOffset;
import static com.redhat.ceylon.eclipse.code.parse.TreeLifecycleListener.Stage.TYPE_ANALYSIS;
import static com.redhat.ceylon.eclipse.ui.CeylonPlugin.PLUGIN_ID;

import java.util.ArrayList;
import java.util.List;

import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jdt.internal.ui.JavaPlugin;
import org.eclipse.jdt.internal.ui.actions.CollapseAllAction;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IToolBarManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.viewers.IElementComparer;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.ITreeSelection;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.TreePath;
import org.eclipse.jface.viewers.TreeSelection;
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.custom.BusyIndicator;
import org.eclipse.swt.custom.CaretEvent;
import org.eclipse.swt.custom.CaretListener;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.ui.part.IPageSite;
import org.eclipse.ui.texteditor.ITextEditor;
import org.eclipse.ui.views.contentoutline.ContentOutlinePage;

import com.redhat.ceylon.compiler.typechecker.model.Declaration;
import com.redhat.ceylon.compiler.typechecker.tree.Node;
import com.redhat.ceylon.compiler.typechecker.tree.Tree;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.CompilationUnit;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.SyntheticVariable;
import com.redhat.ceylon.compiler.typechecker.tree.Visitor;
import com.redhat.ceylon.eclipse.code.parse.CeylonParseController;
import com.redhat.ceylon.eclipse.code.parse.TreeLifecycleListener;
import com.redhat.ceylon.eclipse.ui.CeylonPlugin;
import com.redhat.ceylon.eclipse.ui.CeylonResources;

public class CeylonOutlinePage extends ContentOutlinePage implements TreeLifecycleListener, CaretListener {

    private static final String OUTLINE_POPUP_MENU_ID = PLUGIN_ID + ".outline.popupMenu";

    private final ITreeContentProvider contentProvider;
    private final CeylonOutlineBuilder modelBuilder;
    private final CeylonLabelProvider labelProvider;
    private final CeylonParseController parseController;

    public CeylonOutlinePage(CeylonParseController parseController, CeylonOutlineBuilder modelBuilder) {

        this.parseController = parseController;
        this.modelBuilder = modelBuilder;
        labelProvider = new CeylonLabelProvider();

        contentProvider = new ITreeContentProvider() {
            public Object[] getChildren(Object element) {
                return ((CeylonOutlineNode) element).getChildren().toArray();
            }

            public Object getParent(Object element) {
                return ((CeylonOutlineNode) element).getParent();
            }

            public boolean hasChildren(Object element) {
                Object[] children = getChildren(element);
                return children != null && children.length > 0;
            }

            public Object[] getElements(Object inputElement) {
                return getChildren(inputElement);
            }

            @Override
            public void dispose() {
            }

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

    public Stage getStage() {
        return TYPE_ANALYSIS;
    }

    @Override
    public void update(CeylonParseController parseController, IProgressMonitor monitor) {
        update(parseController);
    }

    public void update(final CeylonParseController parseController) {
        TreeViewer treeViewer = getTreeViewer();
        if (treeViewer != null && !treeViewer.getTree().isDisposed()) {
            treeViewer.getTree().getDisplay().asyncExec(new Runnable() {
                public void run() {
                    TreeViewer treeViewer = getTreeViewer();
                    if (treeViewer != null && !treeViewer.getTree().isDisposed()) {
                        treeViewer.setInput(modelBuilder.buildTree(parseController.getRootNode()));
                    }
                }
            });
        }
    }

    private volatile boolean suspend = false;

    @Override
    public void selectionChanged(SelectionChangedEvent event) {
        super.selectionChanged(event);
        if (!suspend) {
            ITreeSelection sel = (ITreeSelection) event.getSelection();
            if (!sel.isEmpty()) {
                Node node = ((CeylonOutlineNode) sel.getFirstElement()).getTreeNode();
                int startOffset = getStartOffset(node);
                int endOffset = getEndOffset(node);
                int length = endOffset - startOffset + 1;
                ((ITextEditor) getCurrentEditor()).selectAndReveal(startOffset, length);
            }
        }
    }

    public void createControl(Composite parent) {
        super.createControl(parent);
        TreeViewer viewer = getTreeViewer();
        viewer.setContentProvider(contentProvider);
        if (labelProvider != null) {
            viewer.setLabelProvider(labelProvider);
        }
        viewer.addSelectionChangedListener(this);
        CeylonOutlineNode rootNode = modelBuilder.buildTree(parseController.getRootNode());
        viewer.setAutoExpandLevel(4);
        viewer.setInput(rootNode);
        viewer.setComparer(new IElementComparer() {
            @Override
            public int hashCode(Object element) {
                return element.hashCode();
            }

            @Override
            public boolean equals(Object a, Object b) {
                return a.equals(b);
            }
        });

        IPageSite site = getSite();
        IToolBarManager toolBarManager = site.getActionBars().getToolBarManager();
        toolBarManager.add(new ExpandAllAction());
        toolBarManager.add(new CollapseAllAction(viewer));
        toolBarManager.add(new LexicalSortingAction());
        toolBarManager.add(new HideNonSharedAction());

        MenuManager mm = new MenuManager();
        JavaPlugin.createStandardGroups(mm);
        /*mm.add(new GroupMarker("find"));
        mm.add(new Separator());
        mm.add(new GroupMarker("refactor"));
        mm.add(new GroupMarker(IWorkbenchActionConstants.MB_ADDITIONS));*/

        site.registerContextMenu(OUTLINE_POPUP_MENU_ID, mm, getTreeViewer());
        site.setSelectionProvider(getTreeViewer());

        viewer.getControl().setMenu(mm.createContextMenu(viewer.getControl()));
    }

    private class ExpandAllAction extends Action {

        public ExpandAllAction() {
            super("Expand All");
            setToolTipText("Expand All");

            ImageDescriptor desc = CeylonPlugin.getInstance().getImageRegistry()
                    .getDescriptor(CeylonResources.EXPAND_ALL);
            setHoverImageDescriptor(desc);
            setImageDescriptor(desc);
        }

        public void run() {
            TreeViewer outlineViewer = getTreeViewer();
            if (outlineViewer != null) {
                outlineViewer.expandAll();
            }
        }

    }

    class LexicalSortingAction extends Action {

        private ViewerComparator fElementComparator = new ViewerComparator() {
            @Override
            public int compare(Viewer viewer, Object e1, Object e2) {
                CeylonOutlineNode t1 = (CeylonOutlineNode) e1;
                CeylonOutlineNode t2 = (CeylonOutlineNode) e2;
                int cat1 = t1.getCategory();
                int cat2 = t2.getCategory();
                if (cat1 == cat2) {
                    return labelProvider.getText(t1).compareTo(labelProvider.getText(t2));
                }
                return cat1 - cat2;
            }
        };

        private ViewerComparator fPositionComparator = new ViewerComparator() {
            @Override
            public int compare(Viewer viewer, Object e1, Object e2) {
                return getStartOffset(e1) - getStartOffset(e2);
            }
        };

        public LexicalSortingAction() {
            super();
            setText("Sort");
            setToolTipText("Sort by name");
            setDescription("Sort entries lexically by name");

            ImageDescriptor desc = CeylonPlugin.getInstance().image("alphab_sort_co.gif");
            this.setHoverImageDescriptor(desc);
            this.setImageDescriptor(desc);

            boolean checked = CeylonPlugin.getInstance().getPreferenceStore()
                    .getBoolean("LexicalSortingAction.isChecked");
            valueChanged(checked, false);
        }

        public void run() {
            valueChanged(isChecked(), true);
        }

        private void valueChanged(final boolean on, boolean store) {
            final TreeViewer outlineViewer = getTreeViewer();
            setChecked(on);
            BusyIndicator.showWhile(outlineViewer.getControl().getDisplay(), new Runnable() {
                public void run() {
                    if (on)
                        outlineViewer.setComparator(fElementComparator);
                    else
                        outlineViewer.setComparator(fPositionComparator);
                }
            });

            if (store) {
                CeylonPlugin.getInstance().getPreferenceStore().setValue("LexicalSortingAction.isChecked", on);
            }
        }
    }

    private class HideNonSharedAction extends Action {

        private ViewerFilter filter = new ViewerFilter() {
            @Override
            public boolean select(Viewer viewer, Object parentElement, Object element) {
                Node node = ((CeylonOutlineNode) element).getTreeNode();
                if (node instanceof Tree.Declaration) {
                    Declaration declaration = ((Tree.Declaration) node).getDeclarationModel();
                    if (declaration != null) {
                        return declaration.isShared();
                    }
                }
                return true;
            }
        };

        public HideNonSharedAction() {
            super();
            setText("Hide non-shared");
            setToolTipText("Hide non-shared members");
            setDescription("Hide non-shared members");

            ImageDescriptor desc = CeylonPlugin.getInstance().image("public_co.gif");
            setHoverImageDescriptor(desc);
            setImageDescriptor(desc);

            boolean checked = CeylonPlugin.getInstance().getPreferenceStore()
                    .getBoolean("HideNonSharedAction.isChecked");
            valueChanged(checked, false);
        }

        public void run() {
            valueChanged(isChecked(), true);
        }

        private void valueChanged(final boolean on, boolean store) {
            final TreeViewer outlineViewer = getTreeViewer();
            setChecked(on);
            BusyIndicator.showWhile(outlineViewer.getControl().getDisplay(), new Runnable() {
                public void run() {
                    if (on)
                        outlineViewer.addFilter(filter);
                    else
                        outlineViewer.removeFilter(filter);
                }
            });

            if (store) {
                CeylonPlugin.getInstance().getPreferenceStore().setValue("HideNonSharedAction.isChecked", on);
            }
        }

    }

    @Override
    public void caretMoved(final CaretEvent event) {
        if (suspend)
            return;
        suspend = true;
        CompilationUnit rootNode = parseController.getRootNode();
        if (rootNode == null)
            return;
        OutlineNodeVisitor v = new OutlineNodeVisitor(event.caretOffset);
        rootNode.visit(v);
        if (!v.result.isEmpty()) {
            //List<CeylonOutlineNode> segments = new ArrayList<CeylonOutlineNode>();
            //segments.add(new CeylonOutlineNode(rootNode, CeylonOutlineNode.ROOT_CATEGORY));
            //segments.add(new CeylonOutlineNode(v.result));
            setSelection(new TreeSelection(new TreePath(v.result.toArray())));
        }
        suspend = false;
    }

    class OutlineNodeVisitor extends Visitor {
        private final int offset;
        private final boolean hideNonshared;

        OutlineNodeVisitor(int offset) {
            this.offset = offset;
            hideNonshared = CeylonPlugin.getInstance().getPreferenceStore()
                    .getBoolean("HideNonSharedAction.isChecked");
        }

        List<CeylonOutlineNode> result = new ArrayList<CeylonOutlineNode>();

        @Override
        public void visit(Tree.Declaration that) {
            if (!(that instanceof Tree.Parameter) && !(that instanceof Tree.TypeParameterDeclaration)
                    && !(that instanceof Tree.TypeConstraint) && !(that instanceof Tree.Variable
                            && ((Tree.Variable) that).getType() instanceof SyntheticVariable)) {
                if (inBounds(that)) {
                    Declaration dm = that.getDeclarationModel();
                    if (!hideNonshared || dm != null && dm.isShared()) {
                        result.add(new CeylonOutlineNode(that));
                        super.visit(that);
                    }
                } else {
                    super.visit(that);
                }
            } else {
                super.visit(that);
            }
        }

        @Override
        public void visit(Tree.PackageDescriptor that) {
            if (inBounds(that)) {
                result.add(new CeylonOutlineNode(that));
            }
            super.visit(that);
        }

        @Override
        public void visit(Tree.ModuleDescriptor that) {
            if (inBounds(that)) {
                result.add(new CeylonOutlineNode(that));
            }
            super.visit(that);
        }

        @Override
        public void visit(Tree.Import that) {
            if (inBounds(that)) {
                result.add(new CeylonOutlineNode(that));
            }
            super.visit(that);
        }

        @Override
        public void visit(Tree.ImportModule that) {
            if (inBounds(that)) {
                result.add(new CeylonOutlineNode(that));
            }
            super.visit(that);
        }

        private boolean inBounds(Node that) {
            Integer tokenStartIndex = that.getStartIndex();
            Integer tokenStopIndex = that.getStopIndex();
            return tokenStartIndex != null && tokenStopIndex != null && tokenStartIndex <= offset
                    && tokenStopIndex + 1 >= offset;
        }
    }

}