]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.browsing.ui.model/src/org/simantics/browsing/ui/model/browsecontexts/BrowseContext.java
BrowseContext creation fails when including invalid contributions
[simantics/platform.git] / bundles / org.simantics.browsing.ui.model / src / org / simantics / browsing / ui / model / browsecontexts / BrowseContext.java
index 8d5a427c2502bf573e22a87fd682f3eccd69ec34..c4f8c26829081fa290a6df3cd35549817f6d3439 100644 (file)
-/*******************************************************************************\r
- * Copyright (c) 2010, 2011 Association for Decentralized Information Management in\r
- * Industry THTH ry.\r
- * All rights reserved. This program and the accompanying materials\r
- * are made available under the terms of the Eclipse Public License v1.0\r
- * which accompanies this distribution, and is available at\r
- * http://www.eclipse.org/legal/epl-v10.html\r
- *\r
- * Contributors:\r
- *     VTT Technical Research Centre of Finland - initial API and implementation\r
- *******************************************************************************/\r
-package org.simantics.browsing.ui.model.browsecontexts;\r
-\r
-import java.util.ArrayList;\r
-import java.util.Arrays;\r
-import java.util.Collection;\r
-import java.util.Collections;\r
-import java.util.HashMap;\r
-import java.util.HashSet;\r
-import java.util.List;\r
-import java.util.Map;\r
-import java.util.Set;\r
-\r
-import org.eclipse.jface.resource.ImageDescriptor;\r
-import org.eclipse.swt.widgets.Display;\r
-import org.eclipse.swt.widgets.Event;\r
-import org.eclipse.swt.widgets.Shell;\r
-import org.simantics.browsing.ui.BuiltinKeys;\r
-import org.simantics.browsing.ui.CheckedState;\r
-import org.simantics.browsing.ui.NodeContext;\r
-import org.simantics.browsing.ui.common.ColumnKeys;\r
-import org.simantics.browsing.ui.common.NodeContextBuilder;\r
-import org.simantics.browsing.ui.content.CompositeImageDecorator;\r
-import org.simantics.browsing.ui.content.CompositeLabelDecorator;\r
-import org.simantics.browsing.ui.content.ImageDecorator;\r
-import org.simantics.browsing.ui.content.LabelDecorator;\r
-import org.simantics.browsing.ui.content.Labeler.Modifier;\r
-import org.simantics.browsing.ui.model.InvalidContribution;\r
-import org.simantics.browsing.ui.model.actions.ActionBrowseContext;\r
-import org.simantics.browsing.ui.model.check.CheckedStateContribution;\r
-import org.simantics.browsing.ui.model.children.ChildContribution;\r
-import org.simantics.browsing.ui.model.imagedecorators.ImageDecorationContribution;\r
-import org.simantics.browsing.ui.model.images.ImageContribution;\r
-import org.simantics.browsing.ui.model.labeldecorators.LabelDecorationContribution;\r
-import org.simantics.browsing.ui.model.labels.LabelContribution;\r
-import org.simantics.browsing.ui.model.modifiers.ModifierContribution;\r
-import org.simantics.browsing.ui.model.modifiers.NoModifierRule;\r
-import org.simantics.browsing.ui.model.nodetypes.EntityNodeType;\r
-import org.simantics.browsing.ui.model.nodetypes.NodeType;\r
-import org.simantics.browsing.ui.model.nodetypes.NodeTypeMultiMap;\r
-import org.simantics.browsing.ui.model.nodetypes.OrderedNodeTypeMultiMap;\r
-import org.simantics.browsing.ui.model.nodetypes.SpecialNodeType;\r
-import org.simantics.browsing.ui.model.sorters.AlphanumericSorter;\r
-import org.simantics.browsing.ui.model.sorters.Sorter;\r
-import org.simantics.browsing.ui.model.sorters.SorterContribution;\r
-import org.simantics.browsing.ui.model.tooltips.TooltipContribution;\r
-import org.simantics.browsing.ui.model.visuals.FlatNodeContribution;\r
-import org.simantics.browsing.ui.model.visuals.VisualsContribution;\r
-import org.simantics.db.ReadGraph;\r
-import org.simantics.db.RequestProcessor;\r
-import org.simantics.db.Resource;\r
-import org.simantics.db.exception.DatabaseException;\r
-import org.simantics.db.exception.ResourceNotFoundException;\r
-import org.simantics.db.layer0.variable.Variable;\r
-import org.simantics.db.request.Read;\r
-import org.simantics.graphviz.Graph;\r
-import org.simantics.graphviz.Node;\r
-import org.simantics.graphviz.ui.GraphvizComponent;\r
-import org.simantics.scl.reflection.OntologyVersions;\r
-import org.simantics.viewpoint.ontology.ViewpointResource;\r
-\r
-/**\r
- * BrowseContext holds all contributions related to given set of browse contexts.\r
- * \r
- * @author Hannu Niemistö\r
- */\r
-public class BrowseContext {\r
-       \r
-       public static final boolean DEBUG = false;\r
-       \r
-    NodeTypeMultiMap<ChildContribution> childContributions = new NodeTypeMultiMap<ChildContribution>();\r
-    NodeTypeMultiMap<ChildContribution> parentContributions = new NodeTypeMultiMap<ChildContribution>();\r
-    OrderedNodeTypeMultiMap<LabelContribution> labelContributions = new OrderedNodeTypeMultiMap<LabelContribution>();\r
-    OrderedNodeTypeMultiMap<ImageContribution> imageContributions = new OrderedNodeTypeMultiMap<ImageContribution>();\r
-    OrderedNodeTypeMultiMap<CheckedStateContribution> checkedStateContributions = new OrderedNodeTypeMultiMap<CheckedStateContribution>();\r
-    OrderedNodeTypeMultiMap<LabelDecorationContribution> labelDecorationContributions = new OrderedNodeTypeMultiMap<LabelDecorationContribution>();\r
-    OrderedNodeTypeMultiMap<ImageDecorationContribution> imageDecorationContributions = new OrderedNodeTypeMultiMap<ImageDecorationContribution>();\r
-    OrderedNodeTypeMultiMap<ModifierContribution> modifierContributions = new OrderedNodeTypeMultiMap<ModifierContribution>();\r
-    OrderedNodeTypeMultiMap<SorterContribution> sorterContributions = new OrderedNodeTypeMultiMap<SorterContribution>();\r
-    OrderedNodeTypeMultiMap<FlatNodeContribution> flatNodeContributions = new OrderedNodeTypeMultiMap<FlatNodeContribution>();\r
-    OrderedNodeTypeMultiMap<TooltipContribution> tooltipContributions = new OrderedNodeTypeMultiMap<>();\r
-\r
-    private final String[] uris; \r
-\r
-    private BrowseContext(String[] uris) {\r
-        if (uris == null)\r
-            throw new NullPointerException("null URIs");\r
-        this.uris = uris;\r
-    }\r
-\r
-    public String[] getURIs() {\r
-        return uris;\r
-    }\r
-\r
-    public static BrowseContext get(ReadGraph graph,NodeContext context,BrowseContext defaultContext, boolean useNodeBrowseContexts) throws DatabaseException {\r
-        if(!useNodeBrowseContexts) return defaultContext;\r
-        BrowseContext mbc = graph.syncRequest(new ResolveBrowseContext(context));\r
-        if(mbc != null) return mbc;\r
-        BrowseContext parentContext = (BrowseContext)context.getConstant(BuiltinKeys.BROWSE_CONTEXT);\r
-        if(parentContext != null) return parentContext;\r
-        return defaultContext;\r
-    }\r
-\r
-    /**\r
-     * Creates a new BrowseContext for the given Collection of {@link Resource}s.\r
-     * \r
-     * @param g\r
-     * @param browseContextResources\r
-     * @return new BrowseContext\r
-     * @throws DatabaseException\r
-     * @throws InvalidContribution\r
-     */\r
-    public static BrowseContext create(ReadGraph g, Collection<Resource> browseContextResources) throws DatabaseException, InvalidContribution {\r
-        ViewpointResource vr = ViewpointResource.getInstance(g);\r
-        BrowseContext browseContext = new BrowseContext( BrowseContexts.toSortedURIs(g, browseContextResources) );\r
-        for(Resource browseContextResource : findSubcontexts(g, browseContextResources)) {\r
-            \r
-            for(Resource childContributionResource : \r
-                g.getObjects(browseContextResource, vr.BrowseContext_HasChildContribution)) {\r
-                ChildContribution contribution = ChildContribution.create(g, childContributionResource);\r
-                browseContext.childContributions.put(contribution.getParentNodeType(), contribution);\r
-                browseContext.parentContributions.put(contribution.getChildNodeType(), contribution);\r
-            }\r
-            \r
-            for(Resource visualsContributionResource : \r
-                g.getObjects(browseContextResource, vr.BrowseContext_HasVisualsContribution)) {\r
-                VisualsContribution.load(g, visualsContributionResource,\r
-                        browseContext.labelContributions,\r
-                        browseContext.imageContributions,\r
-                        browseContext.checkedStateContributions,\r
-                        browseContext.labelDecorationContributions,\r
-                        browseContext.imageDecorationContributions,\r
-                        browseContext.modifierContributions,\r
-                        browseContext.sorterContributions,\r
-                        browseContext.flatNodeContributions,\r
-                        browseContext.tooltipContributions\r
-                        );\r
-            }\r
-        }\r
-        //browseContext.visualize();\r
-        return browseContext;\r
-    }\r
-\r
-    public static Set<String> getBrowseContextClosure(RequestProcessor processor, final Set<String> browseContexts) throws DatabaseException {\r
-        return processor.syncRequest(new Read<Set<String>>() {\r
-            @Override\r
-            public Set<String> perform(ReadGraph graph) throws DatabaseException {\r
-                Collection<Resource> browseContextResources = new ArrayList<Resource>(browseContexts.size());\r
-                for (String browseContext : browseContexts) {\r
-                    try {\r
-                        browseContextResources.add(graph.getResource(browseContext));\r
-                    } catch (ResourceNotFoundException e) {\r
-                        // Expected result, if no modelled contributions exist.\r
-                        //System.err.println("Didn't find " + browseContext + " while loading model browser.");\r
-                    }\r
-                }\r
-                Collection<Resource> allBrowseContextResources = BrowseContext.findSubcontexts(graph, browseContextResources);\r
-                Set<String> result = new HashSet<String>();\r
-                for (Resource r : allBrowseContextResources)\r
-                    result.add(graph.getURI(r));\r
-                        return result;\r
-            }\r
-        });\r
-    }\r
-\r
-    public static Collection<Resource> findSubcontexts(ReadGraph g,\r
-            Collection<Resource> browseContexts) throws DatabaseException {\r
-        ViewpointResource vr = ViewpointResource.getInstance(g);\r
-        HashSet<Resource> result = new HashSet<Resource>(browseContexts);\r
-        ArrayList<Resource> stack = new ArrayList<Resource>(browseContexts);\r
-        while(!stack.isEmpty()) {\r
-            Resource cur = stack.remove(stack.size()-1);\r
-            for(Resource sc : g.getObjects(cur, vr.BrowseContext_Includes))\r
-                if(result.add(sc))\r
-                    stack.add(sc);\r
-        }\r
-        return result;\r
-    }\r
-    \r
-    /**\r
-     * Finds the possible children of the given {@link NodeContext} parameter.\r
-     * \r
-     * @param graph\r
-     * @param parent\r
-     * @return Collection of children or an empty collection in case node has no children\r
-     * @throws DatabaseException\r
-     */\r
-    public Collection<NodeContext> getChildren(ReadGraph graph, NodeContext parent) throws DatabaseException {\r
-        if(isFlattened(graph, parent))\r
-            return Collections.emptyList();\r
-        else\r
-            return getChildrenImpl(graph, parent);\r
-    }\r
-    \r
-    private Collection<NodeContext> getChildrenImpl(ReadGraph graph, NodeContext parent) throws DatabaseException {\r
-        NodeType nodeType = getNodeType(graph, parent);\r
-        if(nodeType == null)\r
-            return Collections.emptyList();\r
-        ArrayList<NodeContext> result = new ArrayList<NodeContext>();\r
-        Collection<ChildContribution> contributions = childContributions.get(graph, nodeType);\r
-        if(contributions.size() > 1) {\r
-            Map<String,ChildContribution> contribs = new HashMap<>(); \r
-            for(ChildContribution contribution : contributions) {\r
-               String identifier = contribution.getIdentifier();\r
-               ChildContribution current = contribs.get(identifier);\r
-               if(current != null && current.getPriority() > contribution.getPriority()) continue;\r
-               contribs.put(identifier, contribution);\r
-            }\r
-            contributions = contribs.values();\r
-        }\r
-        for(ChildContribution contribution : contributions) {\r
-               Collection<NodeContext> children = contribution.getChildren(graph, parent);\r
-            result.addAll(children);\r
-            if(DEBUG) {\r
-                   System.err.println("contribution: " + contribution.getIdentifier());\r
-                   for(NodeContext ctx : children)\r
-                       System.err.println("-" + ctx);\r
-            }\r
-        }\r
-        \r
-        // Sorting the result\r
-        if(!result.isEmpty()) {            \r
-            for(SorterContribution contribution : sorterContributions.get(graph, nodeType)) { \r
-                Sorter sorter = contribution.getSorter(graph, parent);\r
-                if(sorter != null) {\r
-                    sorter.sort(graph, this, result);\r
-                    return result;\r
-                }\r
-            }\r
-            AlphanumericSorter.INSTANCE.sort(graph, this, result);\r
-        }\r
-        \r
-        result = flatten(graph, result);\r
-        //result = augment(graph, result);\r
-        \r
-        return result;\r
-    }\r
-    \r
-    private ArrayList<NodeContext> flatten(ReadGraph graph, ArrayList<NodeContext> result) throws DatabaseException {\r
-        ArrayList<NodeContext> flattened = new ArrayList<NodeContext>();\r
-        for(NodeContext node : result)\r
-            if(isFlattened(graph, node)) {\r
-                flattened.add(node);\r
-                flattened.addAll(getChildrenImpl(graph, node));\r
-            }\r
-            else\r
-                flattened.add(node);\r
-        return flattened;\r
-    }\r
-    \r
-    public static ArrayList<NodeContext> augment(ReadGraph graph, BrowseContext bc, Collection<NodeContext> contexts, boolean resolveABC) throws DatabaseException {\r
-        ArrayList<NodeContext> result = new ArrayList<NodeContext>();\r
-        for(NodeContext context : contexts) {\r
-               ActionBrowseContext abc = null;\r
-               if(resolveABC) {\r
-                       abc = graph.syncRequest(new ResolveActionBrowseContext(context));\r
-                       if(abc == null) abc = (ActionBrowseContext)context.getConstant(BuiltinKeys.ACTION_BROWSE_CONTEXT);\r
-               }\r
-            result.add(NodeContextBuilder.buildWithData(NodeType.KEY_SEQUENCE_EXT,\r
-                    new Object[] {\r
-                    context.getConstant(BuiltinKeys.INPUT), \r
-                    context.getConstant(NodeType.TYPE),\r
-                    context.getConstant(BuiltinKeys.UI_CONTEXT),\r
-                    bc, abc}));\r
-        }\r
-        return result;\r
-    }\r
-    \r
-    private boolean isFlattened(ReadGraph graph, NodeContext node) throws DatabaseException {\r
-        NodeType nodeType = getNodeType(graph, node);\r
-        return nodeType != null && !flatNodeContributions.get(graph, nodeType).isEmpty();\r
-    }\r
-\r
-    /**\r
-     * Finds the possible parents of the given {@link NodeContext} parameter.\r
-     * \r
-     * @param graph\r
-     * @param child\r
-     * @return Collection of parents or an empty Collection in case node has no parents.\r
-     * @throws DatabaseException\r
-     */\r
-    public Collection<NodeContext> getParents(ReadGraph graph, NodeContext child) throws DatabaseException {\r
-        NodeType nodeType = getNodeType(graph, child);\r
-        if(nodeType == null)\r
-            return Collections.emptyList();\r
-        ArrayList<NodeContext> result = new ArrayList<NodeContext>();\r
-        for(ChildContribution contribution : parentContributions.get(graph, nodeType)) {\r
-            result.addAll(contribution.getParents(graph, child));\r
-        }\r
-        return result;\r
-    }\r
-    \r
-    public boolean hasChildren(ReadGraph graph, NodeContext parent) throws DatabaseException {\r
-        NodeType nodeType = getNodeType(graph, parent);\r
-        if(nodeType == null)\r
-            return false;        \r
-        for(ChildContribution contribution : childContributions.get(graph, nodeType))\r
-            if(contribution.hasChildren(graph, parent))\r
-                return true;\r
-        return false;\r
-    }\r
-    \r
-    private static NodeType getNodeType(ReadGraph graph, NodeContext parent) throws DatabaseException {\r
-        NodeType nodeType = parent.getConstant(NodeType.TYPE);\r
-        if(nodeType == null) {            \r
-            // TODO remove this code when root of model browser is fixed\r
-            Object input = parent.getConstant(BuiltinKeys.INPUT);\r
-            if(input instanceof Resource) {\r
-                nodeType = EntityNodeType.getNodeTypeFor(graph, (Resource)input);\r
-            } else if (input instanceof Variable) {\r
-               String uri = OntologyVersions.getInstance().currentVersion("http://www.simantics.org/Modeling-0.0/ModelingBrowseContext/Variable");\r
-               return new SpecialNodeType(graph.getResource(uri), Variable.class);\r
-            }\r
-        }\r
-        return nodeType;\r
-    }\r
-    \r
-    /**\r
-     * Finds labels for the given {@link NodeContext} parameter.\r
-     * \r
-     * @param graph\r
-     * @param parent\r
-     * @return Map containing all the labels assigned by key indicating the column e.g. "single"\r
-     * @throws DatabaseException\r
-     */\r
-    public Map<String, String> getLabel(ReadGraph graph, NodeContext parent) throws DatabaseException {\r
-        NodeType nodeType = getNodeType(graph, parent);\r
-        if(nodeType == null)\r
-            return Collections.singletonMap(ColumnKeys.SINGLE, "ERROR (no node type)");\r
-        List<LabelContribution> contributions = labelContributions.get(graph, nodeType); \r
-        for(LabelContribution contribution : contributions) { \r
-            Map<String, String> label = contribution.getLabel(graph, parent);\r
-            if(label != null)\r
-                return label;\r
-        }\r
-        return Collections.singletonMap(ColumnKeys.SINGLE, "(no label rule)");\r
-    }\r
-\r
-    /**\r
-     * Finds {@link ImageDescriptor}s for the given {@link NodeContext} parameter.\r
-     * \r
-     * @param graph\r
-     * @param parent\r
-     * @return Map containing all the {@ImageDescriptor}s or empty\r
-     * @throws DatabaseException\r
-     */\r
-    public Map<String, ImageDescriptor> getImage(ReadGraph graph, NodeContext parent) throws DatabaseException {\r
-        NodeType nodeType = getNodeType(graph, parent);\r
-        if(nodeType == null)\r
-            return Collections.emptyMap();\r
-        for(ImageContribution contribution : imageContributions.get(graph, nodeType)) { \r
-            Map<String, ImageDescriptor> image = contribution.getImage(graph, parent);\r
-            if(image != null)\r
-                return image;\r
-        }\r
-        return Collections.emptyMap();\r
-    }\r
-\r
-    /**\r
-     * Finds if the given {@link NodeContext} is checked or not.\r
-     * \r
-     * @param graph\r
-     * @param parent\r
-     * @return\r
-     * @throws DatabaseException\r
-     */\r
-    public CheckedState getCheckedState(ReadGraph graph, NodeContext parent) throws DatabaseException {\r
-        NodeType nodeType = getNodeType(graph, parent);\r
-        if(nodeType == null)\r
-            return CheckedState.NOT_CHECKED;\r
-        for(CheckedStateContribution contribution : checkedStateContributions.get(graph, nodeType)) { \r
-            CheckedState state = contribution.getCheckedState(graph, parent);\r
-            if(state != null)\r
-                return state;\r
-        }\r
-        return CheckedState.NOT_CHECKED;\r
-    }\r
-\r
-    /**\r
-     * Finds {@link LabelDecorator} for the given {@link NodeContext} parameter.\r
-     * \r
-     * @param graph\r
-     * @param context\r
-     * @return\r
-     * @throws DatabaseException\r
-     */\r
-    public LabelDecorator getLabelDecorator(ReadGraph graph, NodeContext context) throws DatabaseException {\r
-        NodeType nodeType = getNodeType(graph, context);\r
-        if(nodeType == null)\r
-            return CompositeLabelDecorator.ID;\r
-        ArrayList<LabelDecorator> decorators = new ArrayList<LabelDecorator>();\r
-        for(LabelDecorationContribution contribution : labelDecorationContributions.get(graph, nodeType)) {\r
-            LabelDecorator decorator = contribution.getLabelDecorator(graph, context);\r
-            if(decorator != null)\r
-                decorators.add(decorator);\r
-        }\r
-        return CompositeLabelDecorator.create(decorators);\r
-    }\r
-\r
-    /**\r
-     * Finds {@link ImageDecorator} for the given {@link NodeContext} parameter.\r
-     * \r
-     * @param graph\r
-     * @param context\r
-     * @return\r
-     * @throws DatabaseException\r
-     */\r
-    public ImageDecorator getImageDecorator(ReadGraph graph, NodeContext context) throws DatabaseException {\r
-        NodeType nodeType = getNodeType(graph, context);\r
-        if(nodeType == null)\r
-            return CompositeImageDecorator.ID;\r
-        ArrayList<ImageDecorator> decorators = new ArrayList<ImageDecorator>();\r
-        for(ImageDecorationContribution contribution : imageDecorationContributions.get(graph, nodeType)) {\r
-            ImageDecorator decorator = contribution.getImageDecorator(graph, context);\r
-            if(decorator != null)\r
-                decorators.add(decorator);\r
-        }\r
-        return CompositeImageDecorator.create(decorators);\r
-    }\r
-\r
-    /**\r
-     * Finds {@link Modifier} for the given {@link NodeContext} parameter.\r
-     * \r
-     * @param graph\r
-     * @param context\r
-     * @param columnKey\r
-     * @return\r
-     * @throws DatabaseException\r
-     */\r
-    public Modifier getModifier(ReadGraph graph, NodeContext context,\r
-            String columnKey) throws DatabaseException {\r
-        NodeType nodeType = getNodeType(graph, context);\r
-        if(nodeType != null)\r
-            for(ModifierContribution contribution : modifierContributions.get(graph, nodeType)) { \r
-                Modifier modifier = contribution.getModifier(graph, context, columnKey);\r
-                if(modifier == NoModifierRule.NO_MODIFIER)\r
-                    return null;\r
-                if(modifier != null)\r
-                    return modifier;\r
-            }\r
-        return null;\r
-    }\r
-    \r
-    public TooltipContribution shouldCreateToolTip(ReadGraph graph, Event event, NodeContext context) throws DatabaseException {\r
-        NodeType nodeType = getNodeType(graph, context);\r
-        if(nodeType != null)\r
-            for(TooltipContribution contribution : tooltipContributions.get(graph, nodeType)) { \r
-                if (contribution.shouldCreateToolTip(graph, context))\r
-                    return contribution;\r
-            }\r
-        return null;\r
-    }\r
-    \r
-    public Object getTooltip(TooltipContribution contribution, Object event, Object parent, NodeContext context) throws DatabaseException {\r
-        Object tooltip = contribution.getTooltip(event, parent, context);\r
-        if (tooltip != null)\r
-            return tooltip;\r
-        return null;\r
-    }\r
-    \r
-    private Graph toGraph() {\r
-        Graph graph = new Graph();\r
-        new Node(graph, "Foo");\r
-        return graph;\r
-    }\r
-\r
-    @SuppressWarnings("unused")\r
-    private void visualize() {\r
-        final Graph graph = toGraph();\r
-                \r
-        // Show it\r
-        new Thread() {\r
-            public void run() {\r
-                final Display display = new Display();\r
-                final Shell shell = new Shell(display);\r
-\r
-                GraphvizComponent comp = new GraphvizComponent(shell, 0);\r
-                comp.setGraph(graph);\r
-\r
-                comp.setBounds(0, 0, 800, 600);\r
-                shell.pack();\r
-                shell.open ();\r
-                while (!shell.isDisposed()) {\r
-                    if (!display.readAndDispatch()) display.sleep();\r
-                }\r
-                display.dispose();\r
-            }\r
-        }.start();      \r
-    }\r
-\r
-    @Override\r
-    public int hashCode() {\r
-        return Arrays.hashCode(uris);\r
-    }\r
-\r
-    @Override\r
-    public boolean equals(Object obj) {\r
-        if (this == obj)\r
-            return true;\r
-        if (obj == null)\r
-            return false;\r
-        if (getClass() != obj.getClass())\r
-            return false;\r
-        BrowseContext other = (BrowseContext) obj;\r
-        return Arrays.equals(uris, other.uris);\r
-    }\r
-\r
-    @Override\r
-    public String toString() {\r
-        return getClass().getSimpleName() + Arrays.toString(uris);\r
-    }\r
-\r
-}\r
+/*******************************************************************************
+ * Copyright (c) 2010, 2011 Association for Decentralized Information Management in
+ * Industry THTH ry.
+ * 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:
+ *     VTT Technical Research Centre of Finland - initial API and implementation
+ *******************************************************************************/
+package org.simantics.browsing.ui.model.browsecontexts;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+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.jface.resource.ImageDescriptor;
+import org.eclipse.swt.widgets.Event;
+import org.simantics.browsing.ui.BuiltinKeys;
+import org.simantics.browsing.ui.CheckedState;
+import org.simantics.browsing.ui.NodeContext;
+import org.simantics.browsing.ui.common.ColumnKeys;
+import org.simantics.browsing.ui.common.NodeContextBuilder;
+import org.simantics.browsing.ui.content.CompositeImageDecorator;
+import org.simantics.browsing.ui.content.CompositeLabelDecorator;
+import org.simantics.browsing.ui.content.ImageDecorator;
+import org.simantics.browsing.ui.content.LabelDecorator;
+import org.simantics.browsing.ui.content.Labeler.Modifier;
+import org.simantics.browsing.ui.model.InvalidContribution;
+import org.simantics.browsing.ui.model.actions.ActionBrowseContext;
+import org.simantics.browsing.ui.model.check.CheckedStateContribution;
+import org.simantics.browsing.ui.model.children.ChildContribution;
+import org.simantics.browsing.ui.model.imagedecorators.ImageDecorationContribution;
+import org.simantics.browsing.ui.model.images.ImageContribution;
+import org.simantics.browsing.ui.model.labeldecorators.LabelDecorationContribution;
+import org.simantics.browsing.ui.model.labels.LabelContribution;
+import org.simantics.browsing.ui.model.modifiers.ModifierContribution;
+import org.simantics.browsing.ui.model.modifiers.NoModifierRule;
+import org.simantics.browsing.ui.model.nodetypes.EntityNodeType;
+import org.simantics.browsing.ui.model.nodetypes.NodeType;
+import org.simantics.browsing.ui.model.nodetypes.NodeTypeMultiMap;
+import org.simantics.browsing.ui.model.nodetypes.OrderedNodeTypeMultiMap;
+import org.simantics.browsing.ui.model.nodetypes.SpecialNodeType;
+import org.simantics.browsing.ui.model.sorters.AlphanumericSorter;
+import org.simantics.browsing.ui.model.sorters.Sorter;
+import org.simantics.browsing.ui.model.sorters.SorterContribution;
+import org.simantics.browsing.ui.model.tooltips.TooltipContribution;
+import org.simantics.browsing.ui.model.visuals.FlatNodeContribution;
+import org.simantics.browsing.ui.model.visuals.VisualsContribution;
+import org.simantics.db.ReadGraph;
+import org.simantics.db.RequestProcessor;
+import org.simantics.db.Resource;
+import org.simantics.db.common.utils.NameUtils;
+import org.simantics.db.exception.DatabaseException;
+import org.simantics.db.exception.ResourceNotFoundException;
+import org.simantics.db.layer0.variable.Variable;
+import org.simantics.db.request.Read;
+import org.simantics.scl.reflection.OntologyVersions;
+import org.simantics.viewpoint.ontology.ViewpointResource;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * BrowseContext holds all contributions related to given set of browse contexts.
+ * 
+ * @author Hannu Niemistö
+ */
+public class BrowseContext {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(BrowseContext.class);
+       public static final boolean DEBUG = false;
+       
+    NodeTypeMultiMap<ChildContribution> childContributions = new NodeTypeMultiMap<ChildContribution>();
+    NodeTypeMultiMap<ChildContribution> parentContributions = new NodeTypeMultiMap<ChildContribution>();
+    OrderedNodeTypeMultiMap<LabelContribution> labelContributions = new OrderedNodeTypeMultiMap<LabelContribution>();
+    OrderedNodeTypeMultiMap<ImageContribution> imageContributions = new OrderedNodeTypeMultiMap<ImageContribution>();
+    OrderedNodeTypeMultiMap<CheckedStateContribution> checkedStateContributions = new OrderedNodeTypeMultiMap<CheckedStateContribution>();
+    OrderedNodeTypeMultiMap<LabelDecorationContribution> labelDecorationContributions = new OrderedNodeTypeMultiMap<LabelDecorationContribution>();
+    OrderedNodeTypeMultiMap<ImageDecorationContribution> imageDecorationContributions = new OrderedNodeTypeMultiMap<ImageDecorationContribution>();
+    OrderedNodeTypeMultiMap<ModifierContribution> modifierContributions = new OrderedNodeTypeMultiMap<ModifierContribution>();
+    OrderedNodeTypeMultiMap<SorterContribution> sorterContributions = new OrderedNodeTypeMultiMap<SorterContribution>();
+    OrderedNodeTypeMultiMap<FlatNodeContribution> flatNodeContributions = new OrderedNodeTypeMultiMap<FlatNodeContribution>();
+    OrderedNodeTypeMultiMap<TooltipContribution> tooltipContributions = new OrderedNodeTypeMultiMap<>();
+
+    private final String[] uris; 
+
+    private BrowseContext(String[] uris) {
+        if (uris == null)
+            throw new NullPointerException("null URIs");
+        this.uris = uris;
+    }
+
+    public String[] getURIs() {
+        return uris;
+    }
+
+    public static BrowseContext get(ReadGraph graph,NodeContext context,BrowseContext defaultContext, boolean useNodeBrowseContexts) throws DatabaseException {
+        if(!useNodeBrowseContexts) return defaultContext;
+        BrowseContext mbc = graph.syncRequest(new ResolveBrowseContext(context));
+        if(mbc != null) return mbc;
+        BrowseContext parentContext = (BrowseContext)context.getConstant(BuiltinKeys.BROWSE_CONTEXT);
+        if(parentContext != null) return parentContext;
+        return defaultContext;
+    }
+
+    /**
+     * Creates a new BrowseContext for the given Collection of {@link Resource}s.
+     * 
+     * @param g
+     * @param browseContextResources
+     * @return new BrowseContext
+     * @throws DatabaseException
+     * @throws InvalidContribution
+     */
+    public static BrowseContext create(ReadGraph g, Collection<Resource> browseContextResources) throws DatabaseException, InvalidContribution {
+        ViewpointResource vr = ViewpointResource.getInstance(g);
+        BrowseContext browseContext = new BrowseContext( BrowseContexts.toSortedURIs(g, browseContextResources) );
+        for(Resource browseContextResource : findSubcontexts(g, browseContextResources)) {
+            
+            for(Resource childContributionResource : 
+                g.getObjects(browseContextResource, vr.BrowseContext_HasChildContribution)) {
+                try {
+                    ChildContribution contribution = ChildContribution.create(g, childContributionResource);
+                    browseContext.childContributions.put(contribution.getParentNodeType(), contribution);
+                    browseContext.parentContributions.put(contribution.getChildNodeType(), contribution);
+                } catch (DatabaseException e) {
+                    LOGGER.error("Failed to load child contribution " + NameUtils.getSafeName(g, childContributionResource), e);
+                }
+            }
+            
+            for(Resource visualsContributionResource : 
+                g.getObjects(browseContextResource, vr.BrowseContext_HasVisualsContribution)) {
+                try {
+                    VisualsContribution.load(g, visualsContributionResource,
+                            browseContext.labelContributions,
+                            browseContext.imageContributions,
+                            browseContext.checkedStateContributions,
+                            browseContext.labelDecorationContributions,
+                            browseContext.imageDecorationContributions,
+                            browseContext.modifierContributions,
+                            browseContext.sorterContributions,
+                            browseContext.flatNodeContributions,
+                            browseContext.tooltipContributions
+                        );
+                } catch (DatabaseException e) {
+                    LOGGER.error("Failed to load visuals contribution " + NameUtils.getSafeName(g, visualsContributionResource), e);
+                }
+            }
+        }
+        //browseContext.visualize();
+        return browseContext;
+    }
+
+    public static Set<String> getBrowseContextClosure(RequestProcessor processor, final Set<String> browseContexts) throws DatabaseException {
+        return processor.syncRequest(new Read<Set<String>>() {
+            @Override
+            public Set<String> perform(ReadGraph graph) throws DatabaseException {
+                Collection<Resource> browseContextResources = new ArrayList<Resource>(browseContexts.size());
+                for (String browseContext : browseContexts) {
+                    try {
+                        browseContextResources.add(graph.getResource(browseContext));
+                    } catch (ResourceNotFoundException e) {
+                        LOGGER.error("Didn't find " + browseContext + " while loading model browser.", e);
+                    }
+                }
+                Collection<Resource> allBrowseContextResources = BrowseContext.findSubcontexts(graph, browseContextResources);
+                Set<String> result = new HashSet<String>();
+                for (Resource r : allBrowseContextResources)
+                    result.add(graph.getURI(r));
+                        return result;
+            }
+        });
+    }
+
+    public static Collection<Resource> findSubcontexts(ReadGraph g,
+            Collection<Resource> browseContexts) throws DatabaseException {
+        ViewpointResource vr = ViewpointResource.getInstance(g);
+        HashSet<Resource> result = new HashSet<Resource>(browseContexts);
+        ArrayList<Resource> stack = new ArrayList<Resource>(browseContexts);
+        while(!stack.isEmpty()) {
+            Resource cur = stack.remove(stack.size()-1);
+            for(Resource sc : g.getObjects(cur, vr.BrowseContext_Includes))
+                if(result.add(sc))
+                    stack.add(sc);
+        }
+        return result;
+    }
+    
+    /**
+     * Finds the possible children of the given {@link NodeContext} parameter.
+     * 
+     * @param graph
+     * @param parent
+     * @return Collection of children or an empty collection in case node has no children
+     * @throws DatabaseException
+     */
+    public Collection<NodeContext> getChildren(ReadGraph graph, NodeContext parent) throws DatabaseException {
+        if(isFlattened(graph, parent))
+            return Collections.emptyList();
+        else
+            return getChildrenImpl(graph, parent);
+    }
+    
+    private Collection<NodeContext> getChildrenImpl(ReadGraph graph, NodeContext parent) throws DatabaseException {
+        NodeType nodeType = getNodeType(graph, parent);
+        if(nodeType == null)
+            return Collections.emptyList();
+        ArrayList<NodeContext> result = new ArrayList<NodeContext>();
+        Collection<ChildContribution> contributions = childContributions.get(graph, nodeType);
+        if(contributions.size() > 1) {
+            Map<String,ChildContribution> contribs = new HashMap<>(); 
+            for(ChildContribution contribution : contributions) {
+               String identifier = contribution.getIdentifier();
+               ChildContribution current = contribs.get(identifier);
+               if(current != null && current.getPriority() > contribution.getPriority()) continue;
+               contribs.put(identifier, contribution);
+            }
+            contributions = contribs.values();
+        }
+        for(ChildContribution contribution : contributions) {
+               Collection<NodeContext> children = contribution.getChildren(graph, parent);
+            result.addAll(children);
+            if(DEBUG) {
+                   LOGGER.info("contribution: " + contribution.getIdentifier());
+                   for(NodeContext ctx : children)
+                       LOGGER.info("-" + ctx);
+            }
+        }
+        
+        // Sorting the result
+        if(!result.isEmpty()) {            
+            for(SorterContribution contribution : sorterContributions.get(graph, nodeType)) { 
+                Sorter sorter = contribution.getSorter(graph, parent);
+                if(sorter != null) {
+                    sorter.sort(graph, this, result);
+                    return result;
+                }
+            }
+            AlphanumericSorter.INSTANCE.sort(graph, this, result);
+        }
+        
+        result = flatten(graph, result);
+        //result = augment(graph, result);
+        
+        return result;
+    }
+    
+    private ArrayList<NodeContext> flatten(ReadGraph graph, ArrayList<NodeContext> result) throws DatabaseException {
+        ArrayList<NodeContext> flattened = new ArrayList<NodeContext>();
+        for(NodeContext node : result)
+            if(isFlattened(graph, node)) {
+                flattened.add(node);
+                flattened.addAll(getChildrenImpl(graph, node));
+            }
+            else
+                flattened.add(node);
+        return flattened;
+    }
+    
+    public static ArrayList<NodeContext> augment(ReadGraph graph, BrowseContext bc, Collection<NodeContext> contexts, boolean resolveABC) throws DatabaseException {
+        ArrayList<NodeContext> result = new ArrayList<NodeContext>();
+        for(NodeContext context : contexts) {
+               ActionBrowseContext abc = null;
+               if(resolveABC) {
+                       abc = graph.syncRequest(new ResolveActionBrowseContext(context));
+                       if(abc == null) abc = (ActionBrowseContext)context.getConstant(BuiltinKeys.ACTION_BROWSE_CONTEXT);
+               }
+            result.add(NodeContextBuilder.buildWithData(NodeType.KEY_SEQUENCE_EXT,
+                    new Object[] {
+                    context.getConstant(BuiltinKeys.INPUT), 
+                    context.getConstant(NodeType.TYPE),
+                    context.getConstant(BuiltinKeys.UI_CONTEXT),
+                    bc, abc}));
+        }
+        return result;
+    }
+    
+    private boolean isFlattened(ReadGraph graph, NodeContext node) throws DatabaseException {
+        NodeType nodeType = getNodeType(graph, node);
+        return nodeType != null && !flatNodeContributions.get(graph, nodeType).isEmpty();
+    }
+
+    /**
+     * Finds the possible parents of the given {@link NodeContext} parameter.
+     * 
+     * @param graph
+     * @param child
+     * @return Collection of parents or an empty Collection in case node has no parents.
+     * @throws DatabaseException
+     */
+    public Collection<NodeContext> getParents(ReadGraph graph, NodeContext child) throws DatabaseException {
+        NodeType nodeType = getNodeType(graph, child);
+        if(nodeType == null)
+            return Collections.emptyList();
+        ArrayList<NodeContext> result = new ArrayList<NodeContext>();
+        for(ChildContribution contribution : parentContributions.get(graph, nodeType)) {
+            result.addAll(contribution.getParents(graph, child));
+        }
+        return result;
+    }
+    
+    public boolean hasChildren(ReadGraph graph, NodeContext parent) throws DatabaseException {
+        NodeType nodeType = getNodeType(graph, parent);
+        if(nodeType == null)
+            return false;        
+        for(ChildContribution contribution : childContributions.get(graph, nodeType))
+            if(contribution.hasChildren(graph, parent))
+                return true;
+        return false;
+    }
+    
+    private static NodeType getNodeType(ReadGraph graph, NodeContext parent) throws DatabaseException {
+        NodeType nodeType = parent.getConstant(NodeType.TYPE);
+        if(nodeType == null) {            
+            // TODO remove this code when root of model browser is fixed
+            Object input = parent.getConstant(BuiltinKeys.INPUT);
+            if(input instanceof Resource) {
+                nodeType = EntityNodeType.getNodeTypeFor(graph, (Resource)input);
+            } else if (input instanceof Variable) {
+               String uri = OntologyVersions.getInstance().currentVersion("http://www.simantics.org/Modeling-0.0/ModelingBrowseContext/Variable");
+               return new SpecialNodeType(graph.getResource(uri), Variable.class);
+            }
+        }
+        return nodeType;
+    }
+    
+    /**
+     * Finds labels for the given {@link NodeContext} parameter.
+     * 
+     * @param graph
+     * @param parent
+     * @return Map containing all the labels assigned by key indicating the column e.g. "single"
+     * @throws DatabaseException
+     */
+    public Map<String, String> getLabel(ReadGraph graph, NodeContext parent) throws DatabaseException {
+        NodeType nodeType = getNodeType(graph, parent);
+        if(nodeType == null)
+            return Collections.singletonMap(ColumnKeys.SINGLE, "ERROR (no node type)");
+        List<LabelContribution> contributions = labelContributions.get(graph, nodeType); 
+        for(LabelContribution contribution : contributions) { 
+            Map<String, String> label = contribution.getLabel(graph, parent);
+            if(label != null)
+                return label;
+        }
+        return Collections.singletonMap(ColumnKeys.SINGLE, "(no label rule)");
+    }
+
+    /**
+     * Finds {@link ImageDescriptor}s for the given {@link NodeContext} parameter.
+     * 
+     * @param graph
+     * @param parent
+     * @return Map containing all the {@ImageDescriptor}s or empty
+     * @throws DatabaseException
+     */
+    public Map<String, ImageDescriptor> getImage(ReadGraph graph, NodeContext parent) throws DatabaseException {
+        NodeType nodeType = getNodeType(graph, parent);
+        if(nodeType == null)
+            return Collections.emptyMap();
+        for(ImageContribution contribution : imageContributions.get(graph, nodeType)) { 
+            Map<String, ImageDescriptor> image = contribution.getImage(graph, parent);
+            if(image != null)
+                return image;
+        }
+        return Collections.emptyMap();
+    }
+
+    /**
+     * Finds if the given {@link NodeContext} is checked or not.
+     * 
+     * @param graph
+     * @param parent
+     * @return
+     * @throws DatabaseException
+     */
+    public CheckedState getCheckedState(ReadGraph graph, NodeContext parent) throws DatabaseException {
+        NodeType nodeType = getNodeType(graph, parent);
+        if(nodeType == null)
+            return CheckedState.NOT_CHECKED;
+        for(CheckedStateContribution contribution : checkedStateContributions.get(graph, nodeType)) { 
+            CheckedState state = contribution.getCheckedState(graph, parent);
+            if(state != null)
+                return state;
+        }
+        return CheckedState.NOT_CHECKED;
+    }
+
+    /**
+     * Finds {@link LabelDecorator} for the given {@link NodeContext} parameter.
+     * 
+     * @param graph
+     * @param context
+     * @return
+     * @throws DatabaseException
+     */
+    public LabelDecorator getLabelDecorator(ReadGraph graph, NodeContext context) throws DatabaseException {
+        NodeType nodeType = getNodeType(graph, context);
+        if(nodeType == null)
+            return CompositeLabelDecorator.ID;
+        ArrayList<LabelDecorator> decorators = new ArrayList<LabelDecorator>();
+        for(LabelDecorationContribution contribution : labelDecorationContributions.get(graph, nodeType)) {
+            LabelDecorator decorator = contribution.getLabelDecorator(graph, context);
+            if(decorator != null)
+                decorators.add(decorator);
+        }
+        return CompositeLabelDecorator.create(decorators);
+    }
+
+    /**
+     * Finds {@link ImageDecorator} for the given {@link NodeContext} parameter.
+     * 
+     * @param graph
+     * @param context
+     * @return
+     * @throws DatabaseException
+     */
+    public ImageDecorator getImageDecorator(ReadGraph graph, NodeContext context) throws DatabaseException {
+        NodeType nodeType = getNodeType(graph, context);
+        if(nodeType == null)
+            return CompositeImageDecorator.ID;
+        ArrayList<ImageDecorator> decorators = new ArrayList<ImageDecorator>();
+        for(ImageDecorationContribution contribution : imageDecorationContributions.get(graph, nodeType)) {
+            ImageDecorator decorator = contribution.getImageDecorator(graph, context);
+            if(decorator != null)
+                decorators.add(decorator);
+        }
+        return CompositeImageDecorator.create(decorators);
+    }
+
+    /**
+     * Finds {@link Modifier} for the given {@link NodeContext} parameter.
+     * 
+     * @param graph
+     * @param context
+     * @param columnKey
+     * @return
+     * @throws DatabaseException
+     */
+    public Modifier getModifier(ReadGraph graph, NodeContext context,
+            String columnKey) throws DatabaseException {
+        NodeType nodeType = getNodeType(graph, context);
+        if(nodeType != null)
+            for(ModifierContribution contribution : modifierContributions.get(graph, nodeType)) { 
+                Modifier modifier = contribution.getModifier(graph, context, columnKey);
+                if(modifier == NoModifierRule.NO_MODIFIER)
+                    return null;
+                if(modifier != null)
+                    return modifier;
+            }
+        return null;
+    }
+    
+    public TooltipContribution shouldCreateToolTip(ReadGraph graph, Event event, NodeContext context) throws DatabaseException {
+        NodeType nodeType = getNodeType(graph, context);
+        if(nodeType != null)
+            for(TooltipContribution contribution : tooltipContributions.get(graph, nodeType)) { 
+                if (contribution.shouldCreateToolTip(graph, context))
+                    return contribution;
+            }
+        return null;
+    }
+    
+    public Object getTooltip(TooltipContribution contribution, Object event, Object parent, NodeContext context) throws DatabaseException {
+        Object tooltip = contribution.getTooltip(event, parent, context);
+        if (tooltip != null)
+            return tooltip;
+        return null;
+    }
+
+    @Override
+    public int hashCode() {
+        return Arrays.hashCode(uris);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj)
+            return true;
+        if (obj == null)
+            return false;
+        if (getClass() != obj.getClass())
+            return false;
+        BrowseContext other = (BrowseContext) obj;
+        return Arrays.equals(uris, other.uris);
+    }
+
+    @Override
+    public String toString() {
+        return getClass().getSimpleName() + Arrays.toString(uris);
+    }
+
+}