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