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