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