--- /dev/null
+/*******************************************************************************\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.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.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
+\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
+ );\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 = resolveABC ? graph.syncRequest(new ResolveActionBrowseContext(context)) : null;\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
+ 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