org.eclipse.incquery.tooling.ui.queryexplorer.QueryExplorer.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.incquery.tooling.ui.queryexplorer.QueryExplorer.java

Source

/*******************************************************************************
 * Copyright (c) 2010-2012, Zoltan Ujhelyi, Tamas Szabo, Istvan Rath and Daniel Varro
 * 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:
 *   Zoltan Ujhelyi, Tamas Szabo - initial API and implementation
 *******************************************************************************/

package org.eclipse.incquery.tooling.ui.queryexplorer;

import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.eclipse.core.databinding.observable.value.IObservableValue;
import org.eclipse.core.databinding.observable.value.IValueChangeListener;
import org.eclipse.core.databinding.observable.value.ValueChangeEvent;
import org.eclipse.incquery.runtime.api.IQuerySpecification;
import org.eclipse.incquery.runtime.extensibility.QueryBackendRegistry;
import org.eclipse.incquery.runtime.matchers.backend.QueryEvaluationHint;
import org.eclipse.incquery.tooling.ui.IncQueryGUIPlugin;
import org.eclipse.incquery.tooling.ui.queryexplorer.content.detail.DetailsViewerUtil;
import org.eclipse.incquery.tooling.ui.queryexplorer.content.flyout.FlyoutControlComposite;
import org.eclipse.incquery.tooling.ui.queryexplorer.content.flyout.FlyoutPreferences;
import org.eclipse.incquery.tooling.ui.queryexplorer.content.flyout.IFlyoutPreferences;
import org.eclipse.incquery.tooling.ui.queryexplorer.content.matcher.PatternMatchContent;
import org.eclipse.incquery.tooling.ui.queryexplorer.content.matcher.PatternMatcherContent;
import org.eclipse.incquery.tooling.ui.queryexplorer.content.matcher.PatternMatcherRootContentKey;
import org.eclipse.incquery.tooling.ui.queryexplorer.content.matcher.QueryExplorerLabelProvider;
import org.eclipse.incquery.tooling.ui.queryexplorer.content.matcher.QueryExplorerObservableFactory;
import org.eclipse.incquery.tooling.ui.queryexplorer.content.matcher.QueryExplorerTreeStructureAdvisor;
import org.eclipse.incquery.tooling.ui.queryexplorer.content.matcher.RootContent;
import org.eclipse.incquery.tooling.ui.queryexplorer.content.patternsviewer.PatternComponent;
import org.eclipse.incquery.tooling.ui.queryexplorer.content.patternsviewer.PatternsViewerFlatContentProvider;
import org.eclipse.incquery.tooling.ui.queryexplorer.content.patternsviewer.PatternsViewerFlatLabelProvider;
import org.eclipse.incquery.tooling.ui.queryexplorer.content.patternsviewer.PatternsViewerHierarchicalContentProvider;
import org.eclipse.incquery.tooling.ui.queryexplorer.content.patternsviewer.PatternsViewerHierarchicalLabelProvider;
import org.eclipse.incquery.tooling.ui.queryexplorer.content.patternsviewer.PatternsViewerInput;
import org.eclipse.incquery.tooling.ui.queryexplorer.preference.PreferenceConstants;
import org.eclipse.incquery.tooling.ui.queryexplorer.util.CheckStateListener;
import org.eclipse.incquery.tooling.ui.queryexplorer.util.CheckStateProvider;
import org.eclipse.incquery.tooling.ui.queryexplorer.util.DoubleClickListener;
import org.eclipse.incquery.tooling.ui.queryexplorer.util.QueryExplorerPatternRegistry;
import org.eclipse.jface.action.GroupMarker;
import org.eclipse.jface.action.IMenuListener;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.databinding.viewers.ObservableListTreeContentProvider;
import org.eclipse.jface.databinding.viewers.ViewersObservables;
import org.eclipse.jface.viewers.CheckboxTreeViewer;
import org.eclipse.jface.viewers.ColumnViewerToolTipSupport;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.TreePath;
import org.eclipse.jface.viewers.TreeSelection;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.Table;
import org.eclipse.ui.IMemento;
import org.eclipse.ui.IViewSite;
import org.eclipse.ui.IWorkbenchActionConstants;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.part.ViewPart;

import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import com.google.inject.Inject;
import com.google.inject.Injector;

/**
 * The Query Explorer can be used to observe the contents of the pattern matchers on given EMF models. The patterns and
 * models can be loaded into the explorer and it provides a tree viewer to browse the contents. Additional functionality
 * involves the possibility to filter match sets, navigate to source model elements, select which patterns are active,
 * etc.
 * 
 * @author Tamas Szabo (itemis AG)
 * 
 */
public class QueryExplorer extends ViewPart {

    private static final String PACKAGE_PRESENTATION_STATE = "packagePresentationState";
    private static final String PATTERNS_VIEWER_FLYOUT_STATE = "patternsViewerFlyoutState";
    private static final String DETAILS_VIEW_FLYOUT_STATE = "detailsViewFlyoutState";

    public static final String QUERY_EXPLORER_ANNOTATION = "QueryExplorer";
    public static final String QUERY_EXPLORER_CHECKED_PARAMETER = "checked";

    public static final String ID = "org.eclipse.incquery.tooling.ui.queryexplorer.QueryExplorer";

    private final Map<PatternMatcherRootContentKey, IModelConnector> modelConnectorMap;
    private final Map<IModelConnector, PatternMatcherRootContentKey> modelConnectorMapReversed;

    private TableViewer detailsTableViewer;
    private CheckboxTreeViewer patternsTreeViewer;
    private TreeViewer matcherTreeViewer;

    private final RootContent treeViewerRootContent;

    public static PatternsViewerInput patternsViewerInput;

    private FlyoutControlComposite patternsViewerFlyout;
    private FlyoutControlComposite detailsViewerFlyout;

    private IFlyoutPreferences detailsViewerFlyoutPreferences;
    private IFlyoutPreferences patternsViewerFlyoutPreferences;

    private final PatternsViewerFlatContentProvider flatCP;
    private final PatternsViewerFlatLabelProvider flatLP;
    private final PatternsViewerHierarchicalContentProvider hierarchicalCP;
    private final PatternsViewerHierarchicalLabelProvider hierarchicalLP;

    @Inject
    private Injector injector;

    @Inject
    private DetailsViewerUtil tableViewerUtil;

    private String mementoPackagePresentation = "flat";

    private QueryEvaluationHint hints;

    public QueryExplorer() {
        modelConnectorMap = new HashMap<PatternMatcherRootContentKey, IModelConnector>();
        modelConnectorMapReversed = new HashMap<IModelConnector, PatternMatcherRootContentKey>();
        patternsViewerInput = new PatternsViewerInput();
        treeViewerRootContent = new RootContent();
        flatCP = new PatternsViewerFlatContentProvider();
        flatLP = new PatternsViewerFlatLabelProvider(patternsViewerInput);
        hierarchicalCP = new PatternsViewerHierarchicalContentProvider();
        hierarchicalLP = new PatternsViewerHierarchicalLabelProvider(patternsViewerInput);
        hints = new QueryEvaluationHint(QueryBackendRegistry.getInstance().getDefaultBackendClass(),
                new HashMap<String, Object>());
    }

    /**
     * @return the {@link QueryEvaluationHint} instance used when creating matchers
     */
    public QueryEvaluationHint getHints() {
        return hints;
    }

    /**
     * @param hints the hints to set
     * @throws NullPointerException if the given hint instance is null
     */
    public void setHints(QueryEvaluationHint hints) {
        Preconditions.checkNotNull(hints);
        this.hints = hints;
    }

    public RootContent getRootContent() {
        return treeViewerRootContent;
    }

    public void load(PatternMatcherRootContentKey key, IModelConnector modelConnector) {
        if (!this.modelConnectorMap.containsKey(key)) {
            this.modelConnectorMap.put(key, modelConnector);
            this.modelConnectorMapReversed.put(modelConnector, key);
            treeViewerRootContent.addPatternMatcherRoot(key, getHints());
        }
    }

    private void unload(PatternMatcherRootContentKey key, IModelConnector modelConnector) {
        this.modelConnectorMapReversed.remove(modelConnector);
        this.modelConnectorMap.remove(key);
        treeViewerRootContent.removePatternMatcherRoot(key);
        modelConnector.unloadModel();
    }

    public IModelConnector getModelConnector(PatternMatcherRootContentKey key) {
        return this.modelConnectorMap.get(key);
    }

    public Collection<PatternMatcherRootContentKey> getPatternMatcherRootContentKeys() {
        Set<PatternMatcherRootContentKey> keys = new HashSet<PatternMatcherRootContentKey>();
        keys.addAll(this.modelConnectorMap.keySet());
        return Collections.unmodifiableSet(keys);
    }

    public void unload(IModelConnector modelConnector) {
        if (this.modelConnectorMapReversed.containsKey(modelConnector)) {
            this.unload(modelConnectorMapReversed.get(modelConnector), modelConnector);
        }
    }

    public void unload(PatternMatcherRootContentKey key) {
        if (this.modelConnectorMap.containsKey(key)) {
            this.unload(key, modelConnectorMap.get(key));
        }
    }

    public static QueryExplorer getInstance() {
        // In Juno activeWorkbenchWindow will be null when Eclipse is closing
        IWorkbenchWindow activeWorkbenchWindow = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
        return getInstance(activeWorkbenchWindow);
    }

    /**
     * @since 1.0
     */
    public static QueryExplorer getInstance(IWorkbenchWindow activeWorkbenchWindow) {
        IWorkbenchPart instance = null;
        if (activeWorkbenchWindow != null && activeWorkbenchWindow.getActivePage() != null) {
            instance = activeWorkbenchWindow.getActivePage().getActivePart();
            if (!(instance instanceof QueryExplorer)) {
                instance = activeWorkbenchWindow.getActivePage().findView(ID);
            }
        }
        return (QueryExplorer) instance;
    }

    public TreeViewer getMatcherTreeViewer() {
        return matcherTreeViewer;
    }

    public PatternsViewerFlatContentProvider getFlatContentProvider() {
        return flatCP;
    }

    public PatternsViewerFlatLabelProvider getFlatLabelProvider() {
        return flatLP;
    }

    public PatternsViewerHierarchicalContentProvider getHierarchicalContentProvider() {
        return hierarchicalCP;
    }

    public PatternsViewerHierarchicalLabelProvider getHierarchicalLabelProvider() {
        return hierarchicalLP;
    }

    @Override
    public void init(IViewSite site, IMemento memento) throws PartInitException {
        super.init(site, memento);
        int detailsState = IFlyoutPreferences.STATE_OPEN;
        int patternsState = IFlyoutPreferences.STATE_COLLAPSED;
        if (memento != null) {
            if (memento.getInteger(DETAILS_VIEW_FLYOUT_STATE) != null) {
                detailsState = memento.getInteger(DETAILS_VIEW_FLYOUT_STATE);
            }
            if (memento.getInteger(PATTERNS_VIEWER_FLYOUT_STATE) != null) {
                patternsState = memento.getInteger(DETAILS_VIEW_FLYOUT_STATE);
            }
            if (memento.getString(PACKAGE_PRESENTATION_STATE) != null) {
                mementoPackagePresentation = memento.getString(PACKAGE_PRESENTATION_STATE);
            }
        }
        detailsViewerFlyoutPreferences = new FlyoutPreferences(IFlyoutPreferences.DOCK_EAST, detailsState, 300);
        patternsViewerFlyoutPreferences = new FlyoutPreferences(IFlyoutPreferences.DOCK_WEST, patternsState, 100);

        IncQueryGUIPlugin.getDefault().getPreferenceStore().setDefault(PreferenceConstants.WILDCARD_MODE, true);
    }

    public void clearTableViewer() {
        if (detailsTableViewer.getContentProvider() != null) {
            detailsTableViewer.setInput(null);
        }
    }

    @Override
    public void createPartControl(Composite parent) {
        detailsViewerFlyout = new FlyoutControlComposite(parent, SWT.NONE, detailsViewerFlyoutPreferences);
        detailsViewerFlyout.setTitleText("Details / Filters");
        detailsViewerFlyout.setValidDockLocations(IFlyoutPreferences.DOCK_EAST);

        patternsViewerFlyout = new FlyoutControlComposite(detailsViewerFlyout.getClientParent(), SWT.NONE,
                patternsViewerFlyoutPreferences);
        patternsViewerFlyout.setTitleText("Pattern registry");
        patternsViewerFlyout.setValidDockLocations(IFlyoutPreferences.DOCK_WEST);

        matcherTreeViewer = new TreeViewer(patternsViewerFlyout.getClientParent());
        detailsTableViewer = new TableViewer(detailsViewerFlyout.getFlyoutParent(), SWT.FULL_SELECTION);

        // matcherTreeViewer configuration
        matcherTreeViewer.setContentProvider(new ObservableListTreeContentProvider(
                new QueryExplorerObservableFactory(), new QueryExplorerTreeStructureAdvisor()));
        matcherTreeViewer.setLabelProvider(new QueryExplorerLabelProvider());
        matcherTreeViewer.setInput(treeViewerRootContent);
        treeViewerRootContent.setViewer(matcherTreeViewer);
        ColumnViewerToolTipSupport.enableFor(matcherTreeViewer);
        matcherTreeViewer.setComparator(null);

        IObservableValue selection = ViewersObservables.observeSingleSelection(matcherTreeViewer);
        selection.addValueChangeListener(new RootContentSelectionChangeListener());

        DoubleClickListener listener = new DoubleClickListener();
        injector.injectMembers(listener);
        matcherTreeViewer.addDoubleClickListener(listener);

        // patternsViewer configuration
        patternsTreeViewer = new CheckboxTreeViewer(patternsViewerFlyout.getFlyoutParent(),
                SWT.CHECK | SWT.BORDER | SWT.MULTI);
        patternsTreeViewer.setCheckStateProvider(new CheckStateProvider());
        patternsTreeViewer.addCheckStateListener(new CheckStateListener(this));
        setPackagePresentation(mementoPackagePresentation, false);
        patternsTreeViewer.setInput(patternsViewerInput);

        // Create menu manager.
        MenuManager matcherTreeViewerMenuManager = new MenuManager();
        matcherTreeViewerMenuManager.setRemoveAllWhenShown(true);
        matcherTreeViewerMenuManager.addMenuListener(new IMenuListener() {
            @Override
            public void menuAboutToShow(IMenuManager mgr) {
                fillContextMenu(mgr);
            }
        });
        // Create menu for tree viewer
        Menu matcherTreeViewerMenu = matcherTreeViewerMenuManager.createContextMenu(matcherTreeViewer.getControl());
        matcherTreeViewer.getControl().setMenu(matcherTreeViewerMenu);
        getSite().registerContextMenu("org.eclipse.incquery.tooling.ui.queryexplorer.QueryExplorer.treeViewerMenu",
                matcherTreeViewerMenuManager, matcherTreeViewer);

        MenuManager patternsViewerMenuManager = new MenuManager();
        patternsViewerMenuManager.setRemoveAllWhenShown(true);
        patternsViewerMenuManager.addMenuListener(new IMenuListener() {
            @Override
            public void menuAboutToShow(IMenuManager mgr) {
                fillContextMenu(mgr);
            }
        });
        // Create menu for patterns viewer
        Menu patternsViewerMenu = patternsViewerMenuManager.createContextMenu(patternsTreeViewer.getControl());
        patternsTreeViewer.getControl().setMenu(patternsViewerMenu);
        getSite().registerContextMenu(
                "org.eclipse.incquery.tooling.ui.queryexplorer.QueryExplorer.patternsViewerMenu",
                patternsViewerMenuManager, patternsTreeViewer);

        // tableView configuration
        Table table = detailsTableViewer.getTable();
        table.setHeaderVisible(true);
        table.setLinesVisible(true);

        GridData gridData = new GridData();
        gridData.verticalAlignment = GridData.FILL;
        gridData.horizontalSpan = 2;
        gridData.grabExcessHorizontalSpace = true;
        gridData.grabExcessVerticalSpace = true;
        gridData.horizontalAlignment = GridData.FILL;
        detailsTableViewer.getControl().setLayoutData(gridData);

        // Focus listening and selection providing
        getSite().setSelectionProvider(matcherTreeViewer);

        // removed listener due to new attach feature in https://bugs.eclipse.org/bugs/show_bug.cgi?id=429858
        // initFileListener();
        initPatternsViewerWithGeneratedPatterns();
    }

    private void fillContextMenu(IMenuManager mgr) {
        mgr.add(new GroupMarker(IWorkbenchActionConstants.MB_ADDITIONS));
    }

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

    private class RootContentSelectionChangeListener implements IValueChangeListener {

        @Override
        public void handleValueChange(ValueChangeEvent event) {
            Object value = event.getObservableValue().getValue();

            clearTableViewer();

            if (value instanceof PatternMatcherContent) {
                PatternMatcherContent observableMatcher = (PatternMatcherContent) value;
                if (observableMatcher.getMatcher() != null) {
                    tableViewerUtil.prepareFor(observableMatcher, detailsTableViewer);
                    String patternFqn = observableMatcher.getMatcher().getSpecification().getFullyQualifiedName();
                    IQuerySpecification<?> pattern = QueryExplorerPatternRegistry.getInstance()
                            .getPatternByFqn(patternFqn);
                    List<PatternComponent> components = null;
                    if (QueryExplorerPatternRegistry.getInstance().isGenerated(pattern)) {
                        components = patternsViewerInput.getGeneratedPatternsRoot().find(patternFqn);
                        components.add(0, patternsViewerInput.getGeneratedPatternsRoot());
                    } else {
                        components = patternsViewerInput.getGenericPatternsRoot().find(patternFqn);
                        components.add(0, patternsViewerInput.getGenericPatternsRoot());
                    }

                    if (components != null) {
                        patternsTreeViewer.setSelection(new TreeSelection(new TreePath(components.toArray())));
                    }
                } else {
                    clearTableViewer();
                }
            } else if (value instanceof PatternMatchContent) {
                PatternMatchContent match = (PatternMatchContent) value;
                tableViewerUtil.prepareFor(match, detailsTableViewer);
            }
        }
    }

    private void initPatternsViewerWithGeneratedPatterns() {
        for (IQuerySpecification<?> pattern : QueryExplorerPatternRegistry.getGeneratedQuerySpecifications()) {
            String patternFqn = pattern.getFullyQualifiedName();
            QueryExplorerPatternRegistry.getInstance().addGeneratedPattern(pattern);

            // check for QE annotation https://bugs.eclipse.org/bugs/show_bug.cgi?id=412700
            Optional<Boolean> checkedValue = QueryExplorerPatternRegistry.getQueryExplorerCheckedValue(pattern);
            Boolean computedCheckedValue = checkedValue.or(false);
            // add to active patterns only if explicitly 
            if (computedCheckedValue) {
                QueryExplorerPatternRegistry.getInstance().addActivePattern(pattern);
            }

            PatternComponent component = patternsViewerInput.getGeneratedPatternsRoot().addComponent(patternFqn);
            component.setCheckedState(computedCheckedValue);
        }

        patternsTreeViewer.refresh();
        patternsViewerInput.getGeneratedPatternsRoot().updateHasChildren();
        patternsViewerInput.getGenericPatternsRoot().setCheckedState(false);
    }

    public PatternsViewerInput getPatternsViewerRoot() {
        return patternsViewerInput;
    }

    public CheckboxTreeViewer getPatternsViewer() {
        return patternsTreeViewer;
    }

    public FlyoutControlComposite getPatternsViewerFlyout() {
        return patternsViewerFlyout;
    }

    @Override
    public void saveState(IMemento memento) {
        super.saveState(memento);
        memento.putInteger(DETAILS_VIEW_FLYOUT_STATE, detailsViewerFlyout.getPreferences().getState());
        memento.putInteger(PATTERNS_VIEWER_FLYOUT_STATE, patternsViewerFlyout.getPreferences().getState());
        memento.putString(PACKAGE_PRESENTATION_STATE,
                (patternsTreeViewer.getContentProvider() == flatCP) ? "flat" : "hierarchical");
    }

    public void setPackagePresentation(String command, boolean update) {

        if (command.contains("flat")) {
            patternsTreeViewer.setContentProvider(flatCP);
            patternsTreeViewer.setLabelProvider(flatLP);
        } else {
            patternsTreeViewer.setContentProvider(hierarchicalCP);
            patternsTreeViewer.setLabelProvider(hierarchicalLP);
        }

        if (update) {
            patternsViewerInput.getGeneratedPatternsRoot().updateHasChildren();
            patternsViewerInput.getGenericPatternsRoot().updateHasChildren();
        }
    }
}