org.phpsrc.eclipse.pti.tools.phpdepend.ui.views.metricrunner.MetricViewer.java Source code

Java tutorial

Introduction

Here is the source code for org.phpsrc.eclipse.pti.tools.phpdepend.ui.views.metricrunner.MetricViewer.java

Source

/*******************************************************************************
 * Copyright (c) 2010 Sven Kiera
 * 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
 *******************************************************************************/

package org.phpsrc.eclipse.pti.tools.phpdepend.ui.views.metricrunner;

import java.util.AbstractList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;

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.action.Separator;
import org.eclipse.jface.viewers.AbstractTreeViewer;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.StructuredViewer;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerFilter;
import org.eclipse.php.internal.ui.SelectionProviderMediator;
import org.eclipse.swt.SWT;
import org.eclipse.swt.dnd.Clipboard;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.TableItem;
import org.eclipse.ui.IWorkbenchActionConstants;
import org.eclipse.ui.part.PageBook;
import org.phpsrc.eclipse.pti.tools.phpdepend.core.model.IMetricElement;
import org.phpsrc.eclipse.pti.tools.phpdepend.core.model.MetricClass;
import org.phpsrc.eclipse.pti.tools.phpdepend.core.model.MetricElement;
import org.phpsrc.eclipse.pti.tools.phpdepend.core.model.MetricFile;
import org.phpsrc.eclipse.pti.tools.phpdepend.core.model.MetricMethod;
import org.phpsrc.eclipse.pti.tools.phpdepend.core.model.MetricRunSession;
import org.phpsrc.eclipse.pti.tools.phpdepend.core.model.MetricSummary;

public class MetricViewer {
    private final class MetricSelectionListener implements ISelectionChangedListener {
        public void selectionChanged(SelectionChangedEvent event) {
            handleSelected();
        }
    }

    private final class MetricOpenListener extends SelectionAdapter {
        public void widgetDefaultSelected(SelectionEvent e) {
            handleDefaultSelected();
        }
    }

    private final class FailuresOnlyFilter extends ViewerFilter {
        public boolean select(Viewer viewer, Object parentElement, Object element) {
            return select(((IMetricElement) element));
        }

        public boolean select(IMetricElement metricElement) {
            return metricElement.hasWarnings() || metricElement.hasErrors();
        }
    }

    private static class ReverseList extends AbstractList {
        private final List fList;

        public ReverseList(List list) {
            fList = list;
        }

        public Object get(int index) {
            return fList.get(fList.size() - index - 1);
        }

        public int size() {
            return fList.size();
        }
    }

    private class ExpandAllAction extends Action {
        public ExpandAllAction() {
            setText(PHPDependMessages.ExpandAllAction_text);
            setToolTipText(PHPDependMessages.ExpandAllAction_tooltip);
        }

        public void run() {
            fTreeViewer.expandAll();
        }
    }

    private final FailuresOnlyFilter fFailuresOnlyFilter = new FailuresOnlyFilter();

    private final MetricRunnerViewPart fMetricRunnerPart;
    private final Clipboard fClipboard;

    private PageBook fViewerbook;
    private TreeViewer fTreeViewer;
    private MetricSessionTreeContentProvider fTreeContentProvider;
    private MetricSessionLabelProvider fTreeLabelProvider;
    private TableViewer fTableViewer;
    private MetricSessionTableContentProvider fTableContentProvider;
    private MetricSessionLabelProvider fTableLabelProvider;
    private SelectionProviderMediator fSelectionProvider;

    private int fLayoutMode;
    private boolean fTreeHasFilter;
    private boolean fTableHasFilter;

    private MetricRunSession fMetricRunSession;

    private boolean fTreeNeedsRefresh;
    private boolean fTableNeedsRefresh;
    private HashSet/* <MetricElement> */ fNeedUpdate;

    private LinkedList/* <TestSuiteElement> */ fAutoClose;
    private HashSet/* <TestSuite> */ fAutoExpand;

    public MetricViewer(Composite parent, Clipboard clipboard, MetricRunnerViewPart runner) {
        fMetricRunnerPart = runner;
        fClipboard = clipboard;

        fLayoutMode = MetricRunnerViewPart.LAYOUT_HIERARCHICAL;

        createTestViewers(parent);

        registerViewersRefresh();

        initContextMenu();
    }

    private void createTestViewers(Composite parent) {
        fViewerbook = new PageBook(parent, SWT.NULL);

        fTreeViewer = new TreeViewer(fViewerbook, SWT.V_SCROLL | SWT.SINGLE);
        fTreeViewer.setUseHashlookup(true);
        fTreeContentProvider = new MetricSessionTreeContentProvider();
        fTreeViewer.setContentProvider(fTreeContentProvider);
        fTreeLabelProvider = new MetricSessionLabelProvider(fMetricRunnerPart,
                MetricRunnerViewPart.LAYOUT_HIERARCHICAL);
        fTreeViewer.setLabelProvider(fTreeLabelProvider);

        fTableViewer = new TableViewer(fViewerbook, SWT.V_SCROLL | SWT.H_SCROLL | SWT.SINGLE);
        fTableViewer.setUseHashlookup(true);
        fTableContentProvider = new MetricSessionTableContentProvider();
        fTableViewer.setContentProvider(fTableContentProvider);
        fTableLabelProvider = new MetricSessionLabelProvider(fMetricRunnerPart, MetricRunnerViewPart.LAYOUT_FLAT);
        fTableViewer.setLabelProvider(fTableLabelProvider);

        fSelectionProvider = new SelectionProviderMediator(new StructuredViewer[] { fTreeViewer, fTableViewer },
                fTreeViewer);
        fSelectionProvider.addSelectionChangedListener(new MetricSelectionListener());
        MetricOpenListener testOpenListener = new MetricOpenListener();
        fTreeViewer.getTree().addSelectionListener(testOpenListener);
        fTableViewer.getTable().addSelectionListener(testOpenListener);

        fMetricRunnerPart.getSite().setSelectionProvider(fSelectionProvider);

        fViewerbook.showPage(fTreeViewer.getTree());
    }

    private void initContextMenu() {
        MenuManager menuMgr = new MenuManager("#PopupMenu"); //$NON-NLS-1$
        menuMgr.setRemoveAllWhenShown(true);
        menuMgr.addMenuListener(new IMenuListener() {
            public void menuAboutToShow(IMenuManager manager) {
                handleMenuAboutToShow(manager);
            }
        });
        fMetricRunnerPart.getSite().registerContextMenu(menuMgr, fSelectionProvider);
        Menu menu = menuMgr.createContextMenu(fViewerbook);
        fTreeViewer.getTree().setMenu(menu);
        fTableViewer.getTable().setMenu(menu);
    }

    void handleMenuAboutToShow(IMenuManager manager) {
        IStructuredSelection selection = (IStructuredSelection) fSelectionProvider.getSelection();
        if (!selection.isEmpty()) {
            IMetricElement element = (IMetricElement) selection.getFirstElement();

            if (element instanceof MetricMethod) {
                manager.add(new OpenFileAction(fMetricRunnerPart, (MetricMethod) element));
            } else if (element instanceof MetricClass) {
                manager.add(new OpenFileAction(fMetricRunnerPart, (MetricClass) element));
            } else if (element instanceof MetricFile) {
                manager.add(new OpenFileAction(fMetricRunnerPart, (MetricFile) element));
            }

            if (fLayoutMode == MetricRunnerViewPart.LAYOUT_HIERARCHICAL) {
                manager.add(new Separator());
                manager.add(new ExpandAllAction());
            }

        }
        manager.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS));
        manager.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS + "-end")); //$NON-NLS-1$
    }

    public Control getTestViewerControl() {
        return fViewerbook;
    }

    public synchronized void registerActiveSession(MetricRunSession testRunSession) {
        fMetricRunSession = testRunSession;
        registerViewersRefresh();
    }

    void handleDefaultSelected() {
        IStructuredSelection selection = (IStructuredSelection) fSelectionProvider.getSelection();
        if (selection.size() != 1)
            return;

        IMetricElement element = (IMetricElement) selection.getFirstElement();

        OpenFileAction action = null;
        if (element instanceof MetricMethod) {
            action = new OpenFileAction(fMetricRunnerPart, (MetricMethod) element);
        } else if (element instanceof MetricClass) {
            action = new OpenFileAction(fMetricRunnerPart, (MetricClass) element);
        } else if (element instanceof MetricFile) {
            action = new OpenFileAction(fMetricRunnerPart, (MetricFile) element);
        } else {
            throw new IllegalStateException(String.valueOf(element));
        }

        if (action != null && action.isEnabled())
            action.run();
    }

    private void handleSelected() {
        IStructuredSelection selection = (IStructuredSelection) fSelectionProvider.getSelection();
        IMetricElement metricElement = null;
        if (selection.size() == 1) {
            metricElement = (IMetricElement) selection.getFirstElement();
        }
        fMetricRunnerPart.handleElementSelected(metricElement);
    }

    public synchronized void setShowTime(boolean showTime) {
        try {
            fViewerbook.setRedraw(false);
            fTreeLabelProvider.setShowTime(showTime);
            fTableLabelProvider.setShowTime(showTime);
        } finally {
            fViewerbook.setRedraw(true);
        }
    }

    public synchronized void setShowFailuresOnly(boolean failuresOnly, int layoutMode) {
        /*
         * Management of fTreeViewer and fTableViewer
         * ****************************************** - invisible viewer is
         * updated on registerViewerUpdate unless its f*NeedsRefresh is true -
         * invisible viewer is not refreshed upfront - on layout change, new
         * viewer is refreshed if necessary - filter only applies to "current"
         * layout mode / viewer
         */
        try {
            fViewerbook.setRedraw(false);

            IStructuredSelection selection = null;
            boolean switchLayout = layoutMode != fLayoutMode;
            if (switchLayout) {
                selection = (IStructuredSelection) fSelectionProvider.getSelection();
                if (layoutMode == MetricRunnerViewPart.LAYOUT_HIERARCHICAL) {
                    if (fTreeNeedsRefresh) {
                        clearUpdateAndExpansion();
                    }
                } else {
                    if (fTableNeedsRefresh) {
                        clearUpdateAndExpansion();
                    }
                }
                fLayoutMode = layoutMode;
                fViewerbook.showPage(getActiveViewer().getControl());
            }

            // avoid realizing all TableItems, especially in flat mode!
            StructuredViewer viewer = getActiveViewer();
            if (failuresOnly) {
                if (!getActiveViewerHasFilter()) {
                    setActiveViewerNeedsRefresh(true);
                    setActiveViewerHasFilter(true);
                    viewer.setInput(null);
                    viewer.addFilter(fFailuresOnlyFilter);
                }

            } else {
                if (getActiveViewerHasFilter()) {
                    setActiveViewerNeedsRefresh(true);
                    setActiveViewerHasFilter(false);
                    viewer.setInput(null);
                    viewer.removeFilter(fFailuresOnlyFilter);
                }
            }
            processChangesInUI();

            if (selection != null) {
                // workaround for
                // https://bugs.eclipse.org/bugs/show_bug.cgi?id=125708
                // (ITreeSelection not adapted if TreePaths changed):
                StructuredSelection flatSelection = new StructuredSelection(selection.toList());
                fSelectionProvider.setSelection(flatSelection, true);
            }

        } finally {
            fViewerbook.setRedraw(true);
        }
    }

    private boolean getActiveViewerHasFilter() {
        if (fLayoutMode == MetricRunnerViewPart.LAYOUT_HIERARCHICAL)
            return fTreeHasFilter;
        else
            return fTableHasFilter;
    }

    private void setActiveViewerHasFilter(boolean filter) {
        if (fLayoutMode == MetricRunnerViewPart.LAYOUT_HIERARCHICAL)
            fTreeHasFilter = filter;
        else
            fTableHasFilter = filter;
    }

    private StructuredViewer getActiveViewer() {
        if (fLayoutMode == MetricRunnerViewPart.LAYOUT_HIERARCHICAL)
            return fTreeViewer;
        else
            return fTableViewer;
    }

    private boolean getActiveViewerNeedsRefresh() {
        if (fLayoutMode == MetricRunnerViewPart.LAYOUT_HIERARCHICAL)
            return fTreeNeedsRefresh;
        else
            return fTableNeedsRefresh;
    }

    private void setActiveViewerNeedsRefresh(boolean needsRefresh) {
        if (fLayoutMode == MetricRunnerViewPart.LAYOUT_HIERARCHICAL)
            fTreeNeedsRefresh = needsRefresh;
        else
            fTableNeedsRefresh = needsRefresh;
    }

    /**
     * To be called periodically by the TestRunnerViewPart (in the UI thread).
     */
    public void processChangesInUI() {
        MetricSummary testRoot;
        if (fMetricRunSession == null) {
            registerViewersRefresh();
            fTreeNeedsRefresh = false;
            fTableNeedsRefresh = false;
            fTreeViewer.setInput(null);
            fTableViewer.setInput(null);
            return;
        }

        testRoot = fMetricRunSession.getSummaryRoot();

        StructuredViewer viewer = getActiveViewer();
        if (getActiveViewerNeedsRefresh()) {
            clearUpdateAndExpansion();
            setActiveViewerNeedsRefresh(false);
            viewer.setInput(testRoot);

        } else {
            Object[] toUpdate;
            synchronized (this) {
                toUpdate = fNeedUpdate.toArray();
                fNeedUpdate.clear();
            }
            if (!fTreeNeedsRefresh && toUpdate.length > 0) {
                if (fTreeHasFilter)
                    for (int i = 0; i < toUpdate.length; i++)
                        updateElementInTree((MetricElement) toUpdate[i]);
                else {
                    HashSet toUpdateWithParents = new HashSet();
                    toUpdateWithParents.addAll(Arrays.asList(toUpdate));
                    for (int i = 0; i < toUpdate.length; i++) {
                        IMetricElement parent = ((MetricElement) toUpdate[i]).getParent();
                        while (parent != null) {
                            toUpdateWithParents.add(parent);
                            parent = parent.getParent();
                        }
                    }
                    fTreeViewer.update(toUpdateWithParents.toArray(), null);
                }
            }
            if (!fTableNeedsRefresh && toUpdate.length > 0) {
                if (fTableHasFilter)
                    for (int i = 0; i < toUpdate.length; i++)
                        updateElementInTable((MetricElement) toUpdate[i]);
                else
                    fTableViewer.update(toUpdate, null);
            }
        }
        autoScrollInUI();
    }

    private void updateElementInTree(final MetricElement MetricElement) {
        if (isShown(MetricElement)) {
            updateShownElementInTree(MetricElement);
        } else {
            IMetricElement current = MetricElement;
            do {
                if (fTreeViewer.testFindItem(current) != null)
                    fTreeViewer.remove(current);
                current = current.getParent();
            } while (!(current instanceof MetricSummary) && !isShown(current));

            while (current != null && !(current instanceof MetricSummary)) {
                fTreeViewer.update(current, null);
                current = current.getParent();
            }
        }
    }

    private void updateShownElementInTree(IMetricElement metricElement) {
        if (metricElement == null || metricElement instanceof MetricSummary) // paranoia
            // null
            // check
            return;

        IMetricElement parent = metricElement.getParent();
        updateShownElementInTree(parent); // make sure parent is shown and
        // up-to-date

        if (fTreeViewer.testFindItem(metricElement) == null) {
            fTreeViewer.add(parent, metricElement); // if not yet in tree: add
        } else {
            fTreeViewer.update(metricElement, null); // if in tree: update
        }
    }

    private void updateElementInTable(IMetricElement element) {
        if (isShown(element)) {
            if (fTableViewer.testFindItem(element) == null) {
                IMetricElement previous = getNextFailure(element, false);
                int insertionIndex = -1;
                if (previous != null) {
                    TableItem item = (TableItem) fTableViewer.testFindItem(previous);
                    if (item != null)
                        insertionIndex = fTableViewer.getTable().indexOf(item);
                }
                fTableViewer.insert(element, insertionIndex);
            } else {
                fTableViewer.update(element, null);
            }
        } else {
            fTableViewer.remove(element);
        }
    }

    private boolean isShown(IMetricElement current) {
        return fFailuresOnlyFilter.select(current);
    }

    private void autoScrollInUI() {
        // if (!fTestRunnerPart.isAutoScroll()) {
        if (true) {
            clearAutoExpand();
            fAutoClose.clear();
            return;
        }

        synchronized (this) {
            for (Iterator iter = fAutoExpand.iterator(); iter.hasNext();) {
                IMetricElement suite = (IMetricElement) iter.next();
                fTreeViewer.setExpandedState(suite, true);
            }
            clearAutoExpand();
        }

        IMetricElement current = null;

        IMetricElement parent = current == null ? null : (IMetricElement) fTreeContentProvider.getParent(current);
        if (fAutoClose.isEmpty() || !fAutoClose.getLast().equals(parent)) {
            // we're in a new branch, so let's close old OK branches:
            for (ListIterator iter = fAutoClose.listIterator(fAutoClose.size()); iter.hasPrevious();) {
                IMetricElement previousAutoOpened = (IMetricElement) iter.previous();
                if (previousAutoOpened.equals(parent))
                    break;

                if (previousAutoOpened.getStatus() == MetricElement.Status.OK) {
                    // auto-opened the element, and all children are OK -> auto
                    // close
                    iter.remove();
                    fTreeViewer.collapseToLevel(previousAutoOpened, AbstractTreeViewer.ALL_LEVELS);
                }
            }

            while (parent != null && !fMetricRunSession.getSummaryRoot().equals(parent)
                    && fTreeViewer.getExpandedState(parent) == false) {
                fAutoClose.add(parent); // add to auto-opened elements -> close
                // later if STATUS_OK
                parent = (IMetricElement) fTreeContentProvider.getParent(parent);
            }
        }
        if (current != null)
            fTreeViewer.reveal(current);
    }

    public void selectFirstFailure() {
        IMetricElement firstFailure = getNextChildFailure(fMetricRunSession.getSummaryRoot(), true);
        if (firstFailure != null)
            getActiveViewer().setSelection(new StructuredSelection(firstFailure), true);
    }

    public void selectFailure(boolean showNext) {
        IStructuredSelection selection = (IStructuredSelection) getActiveViewer().getSelection();
        IMetricElement selected = (MetricElement) selection.getFirstElement();
        IMetricElement next;

        if (selected == null)
            selected = fMetricRunSession.getSummaryRoot();

        next = getNextFailure(selected, showNext);
        if (next != null)
            getActiveViewer().setSelection(new StructuredSelection(next), true);
    }

    private IMetricElement getNextFailure(IMetricElement selected, boolean showNext) {
        IMetricElement nextChild = getNextChildFailure(selected, showNext);
        if (nextChild != null)
            return nextChild;

        return getNextFailureSibling(selected, showNext);
    }

    private IMetricElement getNextFailureSibling(IMetricElement current, boolean showNext) {
        IMetricElement parent = current.getParent();
        if (parent == null)
            return null;

        List<IMetricElement> siblings = Arrays.asList(parent.getChildren());
        if (!showNext)
            siblings = new ReverseList(siblings);

        int nextIndex = siblings.indexOf(current) + 1;
        for (int i = nextIndex; i < siblings.size(); i++) {
            IMetricElement sibling = siblings.get(i);
            if (sibling.hasErrors() || sibling.hasWarnings()) {
                if (sibling.getStatus().isErrorOrWarning()) {
                    return sibling;
                } else {
                    return getNextChildFailure(sibling, showNext);
                }
            }
        }

        return getNextFailureSibling(parent, showNext);
    }

    private IMetricElement getNextChildFailure(IMetricElement root, boolean showNext) {
        List<IMetricElement> children = Arrays.asList(root.getChildren());
        if (!showNext)
            children = new ReverseList(children);
        for (int i = 0; i < children.size(); i++) {
            MetricElement child = (MetricElement) children.get(i);
            if (child.hasErrors() || child.hasWarnings()) {
                if (child.getStatus().isErrorOrWarning()) {
                    return child;
                } else {
                    return getNextChildFailure(child, showNext);
                }
            }
        }

        return null;
    }

    public synchronized void registerViewersRefresh() {
        fTreeNeedsRefresh = true;
        fTableNeedsRefresh = true;
        clearUpdateAndExpansion();
    }

    private void clearUpdateAndExpansion() {
        fNeedUpdate = new LinkedHashSet();
        fAutoClose = new LinkedList();
        fAutoExpand = new HashSet();
    }

    /**
     * @param MetricElement
     *            the added test
     */
    public synchronized void registerTestAdded(MetricElement MetricElement) {
        // TODO: performance: would only need to refresh parent of added element
        fTreeNeedsRefresh = true;
        fTableNeedsRefresh = true;
    }

    public synchronized void registerViewerUpdate(final MetricElement MetricElement) {
        fNeedUpdate.add(MetricElement);
    }

    private synchronized void clearAutoExpand() {
        fAutoExpand.clear();
    }

    public synchronized void registerFailedForAutoScroll(MetricElement MetricElement) {
        Object parent = fTreeContentProvider.getParent(MetricElement);
        if (parent != null)
            fAutoExpand.add(parent);
    }

    public void expandFirstLevel() {
        fTreeViewer.expandToLevel(2);
    }

}