]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.browsing.ui.model/src/org/simantics/browsing/ui/model/browsecontexts/BrowseContext.java
50281f20dc4e1287ea502145175029c7b31666ec
[simantics/platform.git] / bundles / org.simantics.browsing.ui.model / src / org / simantics / browsing / ui / model / browsecontexts / BrowseContext.java
1 /*******************************************************************************\r
2  * Copyright (c) 2010, 2011 Association for Decentralized Information Management in\r
3  * Industry THTH ry.\r
4  * All rights reserved. This program and the accompanying materials\r
5  * are made available under the terms of the Eclipse Public License v1.0\r
6  * which accompanies this distribution, and is available at\r
7  * http://www.eclipse.org/legal/epl-v10.html\r
8  *\r
9  * Contributors:\r
10  *     VTT Technical Research Centre of Finland - initial API and implementation\r
11  *******************************************************************************/\r
12 package org.simantics.browsing.ui.model.browsecontexts;\r
13 \r
14 import java.util.ArrayList;\r
15 import java.util.Arrays;\r
16 import java.util.Collection;\r
17 import java.util.Collections;\r
18 import java.util.HashMap;\r
19 import java.util.HashSet;\r
20 import java.util.List;\r
21 import java.util.Map;\r
22 import java.util.Set;\r
23 \r
24 import org.eclipse.jface.resource.ImageDescriptor;\r
25 import org.eclipse.swt.widgets.Display;\r
26 import org.eclipse.swt.widgets.Shell;\r
27 import org.simantics.browsing.ui.BuiltinKeys;\r
28 import org.simantics.browsing.ui.CheckedState;\r
29 import org.simantics.browsing.ui.NodeContext;\r
30 import org.simantics.browsing.ui.common.ColumnKeys;\r
31 import org.simantics.browsing.ui.common.NodeContextBuilder;\r
32 import org.simantics.browsing.ui.content.CompositeImageDecorator;\r
33 import org.simantics.browsing.ui.content.CompositeLabelDecorator;\r
34 import org.simantics.browsing.ui.content.ImageDecorator;\r
35 import org.simantics.browsing.ui.content.LabelDecorator;\r
36 import org.simantics.browsing.ui.content.Labeler.Modifier;\r
37 import org.simantics.browsing.ui.model.InvalidContribution;\r
38 import org.simantics.browsing.ui.model.actions.ActionBrowseContext;\r
39 import org.simantics.browsing.ui.model.check.CheckedStateContribution;\r
40 import org.simantics.browsing.ui.model.children.ChildContribution;\r
41 import org.simantics.browsing.ui.model.imagedecorators.ImageDecorationContribution;\r
42 import org.simantics.browsing.ui.model.images.ImageContribution;\r
43 import org.simantics.browsing.ui.model.labeldecorators.LabelDecorationContribution;\r
44 import org.simantics.browsing.ui.model.labels.LabelContribution;\r
45 import org.simantics.browsing.ui.model.modifiers.ModifierContribution;\r
46 import org.simantics.browsing.ui.model.modifiers.NoModifierRule;\r
47 import org.simantics.browsing.ui.model.nodetypes.EntityNodeType;\r
48 import org.simantics.browsing.ui.model.nodetypes.NodeType;\r
49 import org.simantics.browsing.ui.model.nodetypes.NodeTypeMultiMap;\r
50 import org.simantics.browsing.ui.model.nodetypes.OrderedNodeTypeMultiMap;\r
51 import org.simantics.browsing.ui.model.nodetypes.SpecialNodeType;\r
52 import org.simantics.browsing.ui.model.sorters.AlphanumericSorter;\r
53 import org.simantics.browsing.ui.model.sorters.Sorter;\r
54 import org.simantics.browsing.ui.model.sorters.SorterContribution;\r
55 import org.simantics.browsing.ui.model.visuals.FlatNodeContribution;\r
56 import org.simantics.browsing.ui.model.visuals.VisualsContribution;\r
57 import org.simantics.db.ReadGraph;\r
58 import org.simantics.db.RequestProcessor;\r
59 import org.simantics.db.Resource;\r
60 import org.simantics.db.exception.DatabaseException;\r
61 import org.simantics.db.exception.ResourceNotFoundException;\r
62 import org.simantics.db.layer0.variable.Variable;\r
63 import org.simantics.db.request.Read;\r
64 import org.simantics.graphviz.Graph;\r
65 import org.simantics.graphviz.Node;\r
66 import org.simantics.graphviz.ui.GraphvizComponent;\r
67 import org.simantics.scl.reflection.OntologyVersions;\r
68 import org.simantics.viewpoint.ontology.ViewpointResource;\r
69 \r
70 /**\r
71  * BrowseContext holds all contributions related to given set of browse contexts.\r
72  * \r
73  * @author Hannu Niemistö\r
74  */\r
75 public class BrowseContext {\r
76         \r
77         public static final boolean DEBUG = false;\r
78         \r
79     NodeTypeMultiMap<ChildContribution> childContributions = new NodeTypeMultiMap<ChildContribution>();\r
80     NodeTypeMultiMap<ChildContribution> parentContributions = new NodeTypeMultiMap<ChildContribution>();\r
81     OrderedNodeTypeMultiMap<LabelContribution> labelContributions = new OrderedNodeTypeMultiMap<LabelContribution>();\r
82     OrderedNodeTypeMultiMap<ImageContribution> imageContributions = new OrderedNodeTypeMultiMap<ImageContribution>();\r
83     OrderedNodeTypeMultiMap<CheckedStateContribution> checkedStateContributions = new OrderedNodeTypeMultiMap<CheckedStateContribution>();\r
84     OrderedNodeTypeMultiMap<LabelDecorationContribution> labelDecorationContributions = new OrderedNodeTypeMultiMap<LabelDecorationContribution>();\r
85     OrderedNodeTypeMultiMap<ImageDecorationContribution> imageDecorationContributions = new OrderedNodeTypeMultiMap<ImageDecorationContribution>();\r
86     OrderedNodeTypeMultiMap<ModifierContribution> modifierContributions = new OrderedNodeTypeMultiMap<ModifierContribution>();\r
87     OrderedNodeTypeMultiMap<SorterContribution> sorterContributions = new OrderedNodeTypeMultiMap<SorterContribution>();\r
88     OrderedNodeTypeMultiMap<FlatNodeContribution> flatNodeContributions = new OrderedNodeTypeMultiMap<FlatNodeContribution>();\r
89 \r
90     private final String[] uris; \r
91 \r
92     private BrowseContext(String[] uris) {\r
93         if (uris == null)\r
94             throw new NullPointerException("null URIs");\r
95         this.uris = uris;\r
96     }\r
97 \r
98     public String[] getURIs() {\r
99         return uris;\r
100     }\r
101 \r
102     public static BrowseContext get(ReadGraph graph,NodeContext context,BrowseContext defaultContext, boolean useNodeBrowseContexts) throws DatabaseException {\r
103         if(!useNodeBrowseContexts) return defaultContext;\r
104         BrowseContext mbc = graph.syncRequest(new ResolveBrowseContext(context));\r
105         if(mbc != null) return mbc;\r
106         BrowseContext parentContext = (BrowseContext)context.getConstant(BuiltinKeys.BROWSE_CONTEXT);\r
107         if(parentContext != null) return parentContext;\r
108         return defaultContext;\r
109     }\r
110 \r
111     /**\r
112      * Creates a new BrowseContext for the given Collection of {@link Resource}s.\r
113      * \r
114      * @param g\r
115      * @param browseContextResources\r
116      * @return new BrowseContext\r
117      * @throws DatabaseException\r
118      * @throws InvalidContribution\r
119      */\r
120     public static BrowseContext create(ReadGraph g, Collection<Resource> browseContextResources) throws DatabaseException, InvalidContribution {\r
121         ViewpointResource vr = ViewpointResource.getInstance(g);\r
122         BrowseContext browseContext = new BrowseContext( BrowseContexts.toSortedURIs(g, browseContextResources) );\r
123         for(Resource browseContextResource : findSubcontexts(g, browseContextResources)) {\r
124             \r
125             for(Resource childContributionResource : \r
126                 g.getObjects(browseContextResource, vr.BrowseContext_HasChildContribution)) {\r
127                 ChildContribution contribution = ChildContribution.create(g, childContributionResource);\r
128                 browseContext.childContributions.put(contribution.getParentNodeType(), contribution);\r
129                 browseContext.parentContributions.put(contribution.getChildNodeType(), contribution);\r
130             }\r
131             \r
132             for(Resource visualsContributionResource : \r
133                 g.getObjects(browseContextResource, vr.BrowseContext_HasVisualsContribution)) {\r
134                 VisualsContribution.load(g, visualsContributionResource,\r
135                         browseContext.labelContributions,\r
136                         browseContext.imageContributions,\r
137                         browseContext.checkedStateContributions,\r
138                         browseContext.labelDecorationContributions,\r
139                         browseContext.imageDecorationContributions,\r
140                         browseContext.modifierContributions,\r
141                         browseContext.sorterContributions,\r
142                         browseContext.flatNodeContributions\r
143                         );\r
144             }\r
145         }\r
146         //browseContext.visualize();\r
147         return browseContext;\r
148     }\r
149 \r
150     public static Set<String> getBrowseContextClosure(RequestProcessor processor, final Set<String> browseContexts) throws DatabaseException {\r
151         return processor.syncRequest(new Read<Set<String>>() {\r
152             @Override\r
153             public Set<String> perform(ReadGraph graph) throws DatabaseException {\r
154                 Collection<Resource> browseContextResources = new ArrayList<Resource>(browseContexts.size());\r
155                 for (String browseContext : browseContexts) {\r
156                     try {\r
157                         browseContextResources.add(graph.getResource(browseContext));\r
158                     } catch (ResourceNotFoundException e) {\r
159                         // Expected result, if no modelled contributions exist.\r
160                         //System.err.println("Didn't find " + browseContext + " while loading model browser.");\r
161                     }\r
162                 }\r
163                 Collection<Resource> allBrowseContextResources = BrowseContext.findSubcontexts(graph, browseContextResources);\r
164                 Set<String> result = new HashSet<String>();\r
165                 for (Resource r : allBrowseContextResources)\r
166                     result.add(graph.getURI(r));\r
167                         return result;\r
168             }\r
169         });\r
170     }\r
171 \r
172     public static Collection<Resource> findSubcontexts(ReadGraph g,\r
173             Collection<Resource> browseContexts) throws DatabaseException {\r
174         ViewpointResource vr = ViewpointResource.getInstance(g);\r
175         HashSet<Resource> result = new HashSet<Resource>(browseContexts);\r
176         ArrayList<Resource> stack = new ArrayList<Resource>(browseContexts);\r
177         while(!stack.isEmpty()) {\r
178             Resource cur = stack.remove(stack.size()-1);\r
179             for(Resource sc : g.getObjects(cur, vr.BrowseContext_Includes))\r
180                 if(result.add(sc))\r
181                     stack.add(sc);\r
182         }\r
183         return result;\r
184     }\r
185     \r
186     /**\r
187      * Finds the possible children of the given {@link NodeContext} parameter.\r
188      * \r
189      * @param graph\r
190      * @param parent\r
191      * @return Collection of children or an empty collection in case node has no children\r
192      * @throws DatabaseException\r
193      */\r
194     public Collection<NodeContext> getChildren(ReadGraph graph, NodeContext parent) throws DatabaseException {\r
195         if(isFlattened(graph, parent))\r
196             return Collections.emptyList();\r
197         else\r
198             return getChildrenImpl(graph, parent);\r
199     }\r
200     \r
201     private Collection<NodeContext> getChildrenImpl(ReadGraph graph, NodeContext parent) throws DatabaseException {\r
202         NodeType nodeType = getNodeType(graph, parent);\r
203         if(nodeType == null)\r
204             return Collections.emptyList();\r
205         ArrayList<NodeContext> result = new ArrayList<NodeContext>();\r
206         Collection<ChildContribution> contributions = childContributions.get(graph, nodeType);\r
207         if(contributions.size() > 1) {\r
208             Map<String,ChildContribution> contribs = new HashMap<>(); \r
209             for(ChildContribution contribution : contributions) {\r
210                 String identifier = contribution.getIdentifier();\r
211                 ChildContribution current = contribs.get(identifier);\r
212                 if(current != null && current.getPriority() > contribution.getPriority()) continue;\r
213                 contribs.put(identifier, contribution);\r
214             }\r
215             contributions = contribs.values();\r
216         }\r
217         for(ChildContribution contribution : contributions) {\r
218                 Collection<NodeContext> children = contribution.getChildren(graph, parent);\r
219             result.addAll(children);\r
220             if(DEBUG) {\r
221                     System.err.println("contribution: " + contribution.getIdentifier());\r
222                     for(NodeContext ctx : children)\r
223                         System.err.println("-" + ctx);\r
224             }\r
225         }\r
226         \r
227         // Sorting the result\r
228         if(!result.isEmpty()) {            \r
229             for(SorterContribution contribution : sorterContributions.get(graph, nodeType)) { \r
230                 Sorter sorter = contribution.getSorter(graph, parent);\r
231                 if(sorter != null) {\r
232                     sorter.sort(graph, this, result);\r
233                     return result;\r
234                 }\r
235             }\r
236             AlphanumericSorter.INSTANCE.sort(graph, this, result);\r
237         }\r
238         \r
239         result = flatten(graph, result);\r
240         //result = augment(graph, result);\r
241         \r
242         return result;\r
243     }\r
244     \r
245     private ArrayList<NodeContext> flatten(ReadGraph graph, ArrayList<NodeContext> result) throws DatabaseException {\r
246         ArrayList<NodeContext> flattened = new ArrayList<NodeContext>();\r
247         for(NodeContext node : result)\r
248             if(isFlattened(graph, node)) {\r
249                 flattened.add(node);\r
250                 flattened.addAll(getChildrenImpl(graph, node));\r
251             }\r
252             else\r
253                 flattened.add(node);\r
254         return flattened;\r
255     }\r
256     \r
257     public static ArrayList<NodeContext> augment(ReadGraph graph, BrowseContext bc, Collection<NodeContext> contexts, boolean resolveABC) throws DatabaseException {\r
258         ArrayList<NodeContext> result = new ArrayList<NodeContext>();\r
259         for(NodeContext context : contexts) {\r
260             ActionBrowseContext abc = resolveABC ? graph.syncRequest(new ResolveActionBrowseContext(context)) : null;\r
261             result.add(NodeContextBuilder.buildWithData(NodeType.KEY_SEQUENCE_EXT,\r
262                     new Object[] {\r
263                     context.getConstant(BuiltinKeys.INPUT), \r
264                     context.getConstant(NodeType.TYPE),\r
265                     context.getConstant(BuiltinKeys.UI_CONTEXT),\r
266                     bc, abc}));\r
267         }\r
268         return result;\r
269     }\r
270     \r
271     private boolean isFlattened(ReadGraph graph, NodeContext node) throws DatabaseException {\r
272         NodeType nodeType = getNodeType(graph, node);\r
273         return nodeType != null && !flatNodeContributions.get(graph, nodeType).isEmpty();\r
274     }\r
275 \r
276     /**\r
277      * Finds the possible parents of the given {@link NodeContext} parameter.\r
278      * \r
279      * @param graph\r
280      * @param child\r
281      * @return Collection of parents or an empty Collection in case node has no parents.\r
282      * @throws DatabaseException\r
283      */\r
284     public Collection<NodeContext> getParents(ReadGraph graph, NodeContext child) throws DatabaseException {\r
285         NodeType nodeType = getNodeType(graph, child);\r
286         if(nodeType == null)\r
287             return Collections.emptyList();\r
288         ArrayList<NodeContext> result = new ArrayList<NodeContext>();\r
289         for(ChildContribution contribution : parentContributions.get(graph, nodeType)) {\r
290             result.addAll(contribution.getParents(graph, child));\r
291         }\r
292         return result;\r
293     }\r
294     \r
295     public boolean hasChildren(ReadGraph graph, NodeContext parent) throws DatabaseException {\r
296         NodeType nodeType = getNodeType(graph, parent);\r
297         if(nodeType == null)\r
298             return false;        \r
299         for(ChildContribution contribution : childContributions.get(graph, nodeType))\r
300             if(contribution.hasChildren(graph, parent))\r
301                 return true;\r
302         return false;\r
303     }\r
304     \r
305     private static NodeType getNodeType(ReadGraph graph, NodeContext parent) throws DatabaseException {\r
306         NodeType nodeType = parent.getConstant(NodeType.TYPE);\r
307         if(nodeType == null) {            \r
308             // TODO remove this code when root of model browser is fixed\r
309             Object input = parent.getConstant(BuiltinKeys.INPUT);\r
310             if(input instanceof Resource) {\r
311                 nodeType = EntityNodeType.getNodeTypeFor(graph, (Resource)input);\r
312             } else if (input instanceof Variable) {\r
313                 String uri = OntologyVersions.getInstance().currentVersion("http://www.simantics.org/Modeling-0.0/ModelingBrowseContext/Variable");\r
314                 return new SpecialNodeType(graph.getResource(uri), Variable.class);\r
315             }\r
316         }\r
317         return nodeType;\r
318     }\r
319     \r
320     /**\r
321      * Finds labels for the given {@link NodeContext} parameter.\r
322      * \r
323      * @param graph\r
324      * @param parent\r
325      * @return Map containing all the labels assigned by key indicating the column e.g. "single"\r
326      * @throws DatabaseException\r
327      */\r
328     public Map<String, String> getLabel(ReadGraph graph, NodeContext parent) throws DatabaseException {\r
329         NodeType nodeType = getNodeType(graph, parent);\r
330         if(nodeType == null)\r
331             return Collections.singletonMap(ColumnKeys.SINGLE, "ERROR (no node type)");\r
332         List<LabelContribution> contributions = labelContributions.get(graph, nodeType); \r
333         for(LabelContribution contribution : contributions) { \r
334             Map<String, String> label = contribution.getLabel(graph, parent);\r
335             if(label != null)\r
336                 return label;\r
337         }\r
338         return Collections.singletonMap(ColumnKeys.SINGLE, "(no label rule)");\r
339     }\r
340 \r
341     /**\r
342      * Finds {@link ImageDescriptor}s for the given {@link NodeContext} parameter.\r
343      * \r
344      * @param graph\r
345      * @param parent\r
346      * @return Map containing all the {@ImageDescriptor}s or empty\r
347      * @throws DatabaseException\r
348      */\r
349     public Map<String, ImageDescriptor> getImage(ReadGraph graph, NodeContext parent) throws DatabaseException {\r
350         NodeType nodeType = getNodeType(graph, parent);\r
351         if(nodeType == null)\r
352             return Collections.emptyMap();\r
353         for(ImageContribution contribution : imageContributions.get(graph, nodeType)) { \r
354             Map<String, ImageDescriptor> image = contribution.getImage(graph, parent);\r
355             if(image != null)\r
356                 return image;\r
357         }\r
358         return Collections.emptyMap();\r
359     }\r
360 \r
361     /**\r
362      * Finds if the given {@link NodeContext} is checked or not.\r
363      * \r
364      * @param graph\r
365      * @param parent\r
366      * @return\r
367      * @throws DatabaseException\r
368      */\r
369     public CheckedState getCheckedState(ReadGraph graph, NodeContext parent) throws DatabaseException {\r
370         NodeType nodeType = getNodeType(graph, parent);\r
371         if(nodeType == null)\r
372             return CheckedState.NOT_CHECKED;\r
373         for(CheckedStateContribution contribution : checkedStateContributions.get(graph, nodeType)) { \r
374             CheckedState state = contribution.getCheckedState(graph, parent);\r
375             if(state != null)\r
376                 return state;\r
377         }\r
378         return CheckedState.NOT_CHECKED;\r
379     }\r
380 \r
381     /**\r
382      * Finds {@link LabelDecorator} for the given {@link NodeContext} parameter.\r
383      * \r
384      * @param graph\r
385      * @param context\r
386      * @return\r
387      * @throws DatabaseException\r
388      */\r
389     public LabelDecorator getLabelDecorator(ReadGraph graph, NodeContext context) throws DatabaseException {\r
390         NodeType nodeType = getNodeType(graph, context);\r
391         if(nodeType == null)\r
392             return CompositeLabelDecorator.ID;\r
393         ArrayList<LabelDecorator> decorators = new ArrayList<LabelDecorator>();\r
394         for(LabelDecorationContribution contribution : labelDecorationContributions.get(graph, nodeType)) {\r
395             LabelDecorator decorator = contribution.getLabelDecorator(graph, context);\r
396             if(decorator != null)\r
397                 decorators.add(decorator);\r
398         }\r
399         return CompositeLabelDecorator.create(decorators);\r
400     }\r
401 \r
402     /**\r
403      * Finds {@link ImageDecorator} for the given {@link NodeContext} parameter.\r
404      * \r
405      * @param graph\r
406      * @param context\r
407      * @return\r
408      * @throws DatabaseException\r
409      */\r
410     public ImageDecorator getImageDecorator(ReadGraph graph, NodeContext context) throws DatabaseException {\r
411         NodeType nodeType = getNodeType(graph, context);\r
412         if(nodeType == null)\r
413             return CompositeImageDecorator.ID;\r
414         ArrayList<ImageDecorator> decorators = new ArrayList<ImageDecorator>();\r
415         for(ImageDecorationContribution contribution : imageDecorationContributions.get(graph, nodeType)) {\r
416             ImageDecorator decorator = contribution.getImageDecorator(graph, context);\r
417             if(decorator != null)\r
418                 decorators.add(decorator);\r
419         }\r
420         return CompositeImageDecorator.create(decorators);\r
421     }\r
422 \r
423     /**\r
424      * Finds {@link Modifier} for the given {@link NodeContext} parameter.\r
425      * \r
426      * @param graph\r
427      * @param context\r
428      * @param columnKey\r
429      * @return\r
430      * @throws DatabaseException\r
431      */\r
432     public Modifier getModifier(ReadGraph graph, NodeContext context,\r
433             String columnKey) throws DatabaseException {\r
434         NodeType nodeType = getNodeType(graph, context);\r
435         if(nodeType != null)\r
436             for(ModifierContribution contribution : modifierContributions.get(graph, nodeType)) { \r
437                 Modifier modifier = contribution.getModifier(graph, context, columnKey);\r
438                 if(modifier == NoModifierRule.NO_MODIFIER)\r
439                     return null;\r
440                 if(modifier != null)\r
441                     return modifier;\r
442             }\r
443         return null;\r
444     }\r
445     \r
446     private Graph toGraph() {\r
447         Graph graph = new Graph();\r
448         new Node(graph, "Foo");\r
449         return graph;\r
450     }\r
451 \r
452     @SuppressWarnings("unused")\r
453     private void visualize() {\r
454         final Graph graph = toGraph();\r
455                 \r
456         // Show it\r
457         new Thread() {\r
458             public void run() {\r
459                 final Display display = new Display();\r
460                 final Shell shell = new Shell(display);\r
461 \r
462                 GraphvizComponent comp = new GraphvizComponent(shell, 0);\r
463                 comp.setGraph(graph);\r
464 \r
465                 comp.setBounds(0, 0, 800, 600);\r
466                 shell.pack();\r
467                 shell.open ();\r
468                 while (!shell.isDisposed()) {\r
469                     if (!display.readAndDispatch()) display.sleep();\r
470                 }\r
471                 display.dispose();\r
472             }\r
473         }.start();      \r
474     }\r
475 \r
476     @Override\r
477     public int hashCode() {\r
478         return Arrays.hashCode(uris);\r
479     }\r
480 \r
481     @Override\r
482     public boolean equals(Object obj) {\r
483         if (this == obj)\r
484             return true;\r
485         if (obj == null)\r
486             return false;\r
487         if (getClass() != obj.getClass())\r
488             return false;\r
489         BrowseContext other = (BrowseContext) obj;\r
490         return Arrays.equals(uris, other.uris);\r
491     }\r
492 \r
493     @Override\r
494     public String toString() {\r
495         return getClass().getSimpleName() + Arrays.toString(uris);\r
496     }\r
497 \r
498 }\r