1 /*******************************************************************************
\r
2 * Copyright (c) 2010, 2011 Association for Decentralized Information Management in
\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
10 * VTT Technical Research Centre of Finland - initial API and implementation
\r
11 *******************************************************************************/
\r
12 package org.simantics.browsing.ui.model.browsecontexts;
\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
24 import org.eclipse.jface.resource.ImageDescriptor;
\r
25 import org.eclipse.swt.widgets.Display;
\r
26 import org.eclipse.swt.widgets.Event;
\r
27 import org.eclipse.swt.widgets.Shell;
\r
28 import org.simantics.browsing.ui.BuiltinKeys;
\r
29 import org.simantics.browsing.ui.CheckedState;
\r
30 import org.simantics.browsing.ui.NodeContext;
\r
31 import org.simantics.browsing.ui.common.ColumnKeys;
\r
32 import org.simantics.browsing.ui.common.NodeContextBuilder;
\r
33 import org.simantics.browsing.ui.content.CompositeImageDecorator;
\r
34 import org.simantics.browsing.ui.content.CompositeLabelDecorator;
\r
35 import org.simantics.browsing.ui.content.ImageDecorator;
\r
36 import org.simantics.browsing.ui.content.LabelDecorator;
\r
37 import org.simantics.browsing.ui.content.Labeler.Modifier;
\r
38 import org.simantics.browsing.ui.model.InvalidContribution;
\r
39 import org.simantics.browsing.ui.model.actions.ActionBrowseContext;
\r
40 import org.simantics.browsing.ui.model.check.CheckedStateContribution;
\r
41 import org.simantics.browsing.ui.model.children.ChildContribution;
\r
42 import org.simantics.browsing.ui.model.imagedecorators.ImageDecorationContribution;
\r
43 import org.simantics.browsing.ui.model.images.ImageContribution;
\r
44 import org.simantics.browsing.ui.model.labeldecorators.LabelDecorationContribution;
\r
45 import org.simantics.browsing.ui.model.labels.LabelContribution;
\r
46 import org.simantics.browsing.ui.model.modifiers.ModifierContribution;
\r
47 import org.simantics.browsing.ui.model.modifiers.NoModifierRule;
\r
48 import org.simantics.browsing.ui.model.nodetypes.EntityNodeType;
\r
49 import org.simantics.browsing.ui.model.nodetypes.NodeType;
\r
50 import org.simantics.browsing.ui.model.nodetypes.NodeTypeMultiMap;
\r
51 import org.simantics.browsing.ui.model.nodetypes.OrderedNodeTypeMultiMap;
\r
52 import org.simantics.browsing.ui.model.nodetypes.SpecialNodeType;
\r
53 import org.simantics.browsing.ui.model.sorters.AlphanumericSorter;
\r
54 import org.simantics.browsing.ui.model.sorters.Sorter;
\r
55 import org.simantics.browsing.ui.model.sorters.SorterContribution;
\r
56 import org.simantics.browsing.ui.model.tooltips.TooltipContribution;
\r
57 import org.simantics.browsing.ui.model.visuals.FlatNodeContribution;
\r
58 import org.simantics.browsing.ui.model.visuals.VisualsContribution;
\r
59 import org.simantics.db.ReadGraph;
\r
60 import org.simantics.db.RequestProcessor;
\r
61 import org.simantics.db.Resource;
\r
62 import org.simantics.db.exception.DatabaseException;
\r
63 import org.simantics.db.exception.ResourceNotFoundException;
\r
64 import org.simantics.db.layer0.variable.Variable;
\r
65 import org.simantics.db.request.Read;
\r
66 import org.simantics.graphviz.Graph;
\r
67 import org.simantics.graphviz.Node;
\r
68 import org.simantics.graphviz.ui.GraphvizComponent;
\r
69 import org.simantics.scl.reflection.OntologyVersions;
\r
70 import org.simantics.viewpoint.ontology.ViewpointResource;
\r
73 * BrowseContext holds all contributions related to given set of browse contexts.
\r
75 * @author Hannu Niemistö
\r
77 public class BrowseContext {
\r
79 public static final boolean DEBUG = false;
\r
81 NodeTypeMultiMap<ChildContribution> childContributions = new NodeTypeMultiMap<ChildContribution>();
\r
82 NodeTypeMultiMap<ChildContribution> parentContributions = new NodeTypeMultiMap<ChildContribution>();
\r
83 OrderedNodeTypeMultiMap<LabelContribution> labelContributions = new OrderedNodeTypeMultiMap<LabelContribution>();
\r
84 OrderedNodeTypeMultiMap<ImageContribution> imageContributions = new OrderedNodeTypeMultiMap<ImageContribution>();
\r
85 OrderedNodeTypeMultiMap<CheckedStateContribution> checkedStateContributions = new OrderedNodeTypeMultiMap<CheckedStateContribution>();
\r
86 OrderedNodeTypeMultiMap<LabelDecorationContribution> labelDecorationContributions = new OrderedNodeTypeMultiMap<LabelDecorationContribution>();
\r
87 OrderedNodeTypeMultiMap<ImageDecorationContribution> imageDecorationContributions = new OrderedNodeTypeMultiMap<ImageDecorationContribution>();
\r
88 OrderedNodeTypeMultiMap<ModifierContribution> modifierContributions = new OrderedNodeTypeMultiMap<ModifierContribution>();
\r
89 OrderedNodeTypeMultiMap<SorterContribution> sorterContributions = new OrderedNodeTypeMultiMap<SorterContribution>();
\r
90 OrderedNodeTypeMultiMap<FlatNodeContribution> flatNodeContributions = new OrderedNodeTypeMultiMap<FlatNodeContribution>();
\r
91 OrderedNodeTypeMultiMap<TooltipContribution> tooltipContributions = new OrderedNodeTypeMultiMap<>();
\r
93 private final String[] uris;
\r
95 private BrowseContext(String[] uris) {
\r
97 throw new NullPointerException("null URIs");
\r
101 public String[] getURIs() {
\r
105 public static BrowseContext get(ReadGraph graph,NodeContext context,BrowseContext defaultContext, boolean useNodeBrowseContexts) throws DatabaseException {
\r
106 if(!useNodeBrowseContexts) return defaultContext;
\r
107 BrowseContext mbc = graph.syncRequest(new ResolveBrowseContext(context));
\r
108 if(mbc != null) return mbc;
\r
109 BrowseContext parentContext = (BrowseContext)context.getConstant(BuiltinKeys.BROWSE_CONTEXT);
\r
110 if(parentContext != null) return parentContext;
\r
111 return defaultContext;
\r
115 * Creates a new BrowseContext for the given Collection of {@link Resource}s.
\r
118 * @param browseContextResources
\r
119 * @return new BrowseContext
\r
120 * @throws DatabaseException
\r
121 * @throws InvalidContribution
\r
123 public static BrowseContext create(ReadGraph g, Collection<Resource> browseContextResources) throws DatabaseException, InvalidContribution {
\r
124 ViewpointResource vr = ViewpointResource.getInstance(g);
\r
125 BrowseContext browseContext = new BrowseContext( BrowseContexts.toSortedURIs(g, browseContextResources) );
\r
126 for(Resource browseContextResource : findSubcontexts(g, browseContextResources)) {
\r
128 for(Resource childContributionResource :
\r
129 g.getObjects(browseContextResource, vr.BrowseContext_HasChildContribution)) {
\r
130 ChildContribution contribution = ChildContribution.create(g, childContributionResource);
\r
131 browseContext.childContributions.put(contribution.getParentNodeType(), contribution);
\r
132 browseContext.parentContributions.put(contribution.getChildNodeType(), contribution);
\r
135 for(Resource visualsContributionResource :
\r
136 g.getObjects(browseContextResource, vr.BrowseContext_HasVisualsContribution)) {
\r
137 VisualsContribution.load(g, visualsContributionResource,
\r
138 browseContext.labelContributions,
\r
139 browseContext.imageContributions,
\r
140 browseContext.checkedStateContributions,
\r
141 browseContext.labelDecorationContributions,
\r
142 browseContext.imageDecorationContributions,
\r
143 browseContext.modifierContributions,
\r
144 browseContext.sorterContributions,
\r
145 browseContext.flatNodeContributions,
\r
146 browseContext.tooltipContributions
\r
150 //browseContext.visualize();
\r
151 return browseContext;
\r
154 public static Set<String> getBrowseContextClosure(RequestProcessor processor, final Set<String> browseContexts) throws DatabaseException {
\r
155 return processor.syncRequest(new Read<Set<String>>() {
\r
157 public Set<String> perform(ReadGraph graph) throws DatabaseException {
\r
158 Collection<Resource> browseContextResources = new ArrayList<Resource>(browseContexts.size());
\r
159 for (String browseContext : browseContexts) {
\r
161 browseContextResources.add(graph.getResource(browseContext));
\r
162 } catch (ResourceNotFoundException e) {
\r
163 // Expected result, if no modelled contributions exist.
\r
164 //System.err.println("Didn't find " + browseContext + " while loading model browser.");
\r
167 Collection<Resource> allBrowseContextResources = BrowseContext.findSubcontexts(graph, browseContextResources);
\r
168 Set<String> result = new HashSet<String>();
\r
169 for (Resource r : allBrowseContextResources)
\r
170 result.add(graph.getURI(r));
\r
176 public static Collection<Resource> findSubcontexts(ReadGraph g,
\r
177 Collection<Resource> browseContexts) throws DatabaseException {
\r
178 ViewpointResource vr = ViewpointResource.getInstance(g);
\r
179 HashSet<Resource> result = new HashSet<Resource>(browseContexts);
\r
180 ArrayList<Resource> stack = new ArrayList<Resource>(browseContexts);
\r
181 while(!stack.isEmpty()) {
\r
182 Resource cur = stack.remove(stack.size()-1);
\r
183 for(Resource sc : g.getObjects(cur, vr.BrowseContext_Includes))
\r
191 * Finds the possible children of the given {@link NodeContext} parameter.
\r
195 * @return Collection of children or an empty collection in case node has no children
\r
196 * @throws DatabaseException
\r
198 public Collection<NodeContext> getChildren(ReadGraph graph, NodeContext parent) throws DatabaseException {
\r
199 if(isFlattened(graph, parent))
\r
200 return Collections.emptyList();
\r
202 return getChildrenImpl(graph, parent);
\r
205 private Collection<NodeContext> getChildrenImpl(ReadGraph graph, NodeContext parent) throws DatabaseException {
\r
206 NodeType nodeType = getNodeType(graph, parent);
\r
207 if(nodeType == null)
\r
208 return Collections.emptyList();
\r
209 ArrayList<NodeContext> result = new ArrayList<NodeContext>();
\r
210 Collection<ChildContribution> contributions = childContributions.get(graph, nodeType);
\r
211 if(contributions.size() > 1) {
\r
212 Map<String,ChildContribution> contribs = new HashMap<>();
\r
213 for(ChildContribution contribution : contributions) {
\r
214 String identifier = contribution.getIdentifier();
\r
215 ChildContribution current = contribs.get(identifier);
\r
216 if(current != null && current.getPriority() > contribution.getPriority()) continue;
\r
217 contribs.put(identifier, contribution);
\r
219 contributions = contribs.values();
\r
221 for(ChildContribution contribution : contributions) {
\r
222 Collection<NodeContext> children = contribution.getChildren(graph, parent);
\r
223 result.addAll(children);
\r
225 System.err.println("contribution: " + contribution.getIdentifier());
\r
226 for(NodeContext ctx : children)
\r
227 System.err.println("-" + ctx);
\r
231 // Sorting the result
\r
232 if(!result.isEmpty()) {
\r
233 for(SorterContribution contribution : sorterContributions.get(graph, nodeType)) {
\r
234 Sorter sorter = contribution.getSorter(graph, parent);
\r
235 if(sorter != null) {
\r
236 sorter.sort(graph, this, result);
\r
240 AlphanumericSorter.INSTANCE.sort(graph, this, result);
\r
243 result = flatten(graph, result);
\r
244 //result = augment(graph, result);
\r
249 private ArrayList<NodeContext> flatten(ReadGraph graph, ArrayList<NodeContext> result) throws DatabaseException {
\r
250 ArrayList<NodeContext> flattened = new ArrayList<NodeContext>();
\r
251 for(NodeContext node : result)
\r
252 if(isFlattened(graph, node)) {
\r
253 flattened.add(node);
\r
254 flattened.addAll(getChildrenImpl(graph, node));
\r
257 flattened.add(node);
\r
261 public static ArrayList<NodeContext> augment(ReadGraph graph, BrowseContext bc, Collection<NodeContext> contexts, boolean resolveABC) throws DatabaseException {
\r
262 ArrayList<NodeContext> result = new ArrayList<NodeContext>();
\r
263 for(NodeContext context : contexts) {
\r
264 ActionBrowseContext abc = resolveABC ? graph.syncRequest(new ResolveActionBrowseContext(context)) : null;
\r
265 result.add(NodeContextBuilder.buildWithData(NodeType.KEY_SEQUENCE_EXT,
\r
267 context.getConstant(BuiltinKeys.INPUT),
\r
268 context.getConstant(NodeType.TYPE),
\r
269 context.getConstant(BuiltinKeys.UI_CONTEXT),
\r
275 private boolean isFlattened(ReadGraph graph, NodeContext node) throws DatabaseException {
\r
276 NodeType nodeType = getNodeType(graph, node);
\r
277 return nodeType != null && !flatNodeContributions.get(graph, nodeType).isEmpty();
\r
281 * Finds the possible parents of the given {@link NodeContext} parameter.
\r
285 * @return Collection of parents or an empty Collection in case node has no parents.
\r
286 * @throws DatabaseException
\r
288 public Collection<NodeContext> getParents(ReadGraph graph, NodeContext child) throws DatabaseException {
\r
289 NodeType nodeType = getNodeType(graph, child);
\r
290 if(nodeType == null)
\r
291 return Collections.emptyList();
\r
292 ArrayList<NodeContext> result = new ArrayList<NodeContext>();
\r
293 for(ChildContribution contribution : parentContributions.get(graph, nodeType)) {
\r
294 result.addAll(contribution.getParents(graph, child));
\r
299 public boolean hasChildren(ReadGraph graph, NodeContext parent) throws DatabaseException {
\r
300 NodeType nodeType = getNodeType(graph, parent);
\r
301 if(nodeType == null)
\r
303 for(ChildContribution contribution : childContributions.get(graph, nodeType))
\r
304 if(contribution.hasChildren(graph, parent))
\r
309 private static NodeType getNodeType(ReadGraph graph, NodeContext parent) throws DatabaseException {
\r
310 NodeType nodeType = parent.getConstant(NodeType.TYPE);
\r
311 if(nodeType == null) {
\r
312 // TODO remove this code when root of model browser is fixed
\r
313 Object input = parent.getConstant(BuiltinKeys.INPUT);
\r
314 if(input instanceof Resource) {
\r
315 nodeType = EntityNodeType.getNodeTypeFor(graph, (Resource)input);
\r
316 } else if (input instanceof Variable) {
\r
317 String uri = OntologyVersions.getInstance().currentVersion("http://www.simantics.org/Modeling-0.0/ModelingBrowseContext/Variable");
\r
318 return new SpecialNodeType(graph.getResource(uri), Variable.class);
\r
325 * Finds labels for the given {@link NodeContext} parameter.
\r
329 * @return Map containing all the labels assigned by key indicating the column e.g. "single"
\r
330 * @throws DatabaseException
\r
332 public Map<String, String> getLabel(ReadGraph graph, NodeContext parent) throws DatabaseException {
\r
333 NodeType nodeType = getNodeType(graph, parent);
\r
334 if(nodeType == null)
\r
335 return Collections.singletonMap(ColumnKeys.SINGLE, "ERROR (no node type)");
\r
336 List<LabelContribution> contributions = labelContributions.get(graph, nodeType);
\r
337 for(LabelContribution contribution : contributions) {
\r
338 Map<String, String> label = contribution.getLabel(graph, parent);
\r
342 return Collections.singletonMap(ColumnKeys.SINGLE, "(no label rule)");
\r
346 * Finds {@link ImageDescriptor}s for the given {@link NodeContext} parameter.
\r
350 * @return Map containing all the {@ImageDescriptor}s or empty
\r
351 * @throws DatabaseException
\r
353 public Map<String, ImageDescriptor> getImage(ReadGraph graph, NodeContext parent) throws DatabaseException {
\r
354 NodeType nodeType = getNodeType(graph, parent);
\r
355 if(nodeType == null)
\r
356 return Collections.emptyMap();
\r
357 for(ImageContribution contribution : imageContributions.get(graph, nodeType)) {
\r
358 Map<String, ImageDescriptor> image = contribution.getImage(graph, parent);
\r
362 return Collections.emptyMap();
\r
366 * Finds if the given {@link NodeContext} is checked or not.
\r
371 * @throws DatabaseException
\r
373 public CheckedState getCheckedState(ReadGraph graph, NodeContext parent) throws DatabaseException {
\r
374 NodeType nodeType = getNodeType(graph, parent);
\r
375 if(nodeType == null)
\r
376 return CheckedState.NOT_CHECKED;
\r
377 for(CheckedStateContribution contribution : checkedStateContributions.get(graph, nodeType)) {
\r
378 CheckedState state = contribution.getCheckedState(graph, parent);
\r
382 return CheckedState.NOT_CHECKED;
\r
386 * Finds {@link LabelDecorator} for the given {@link NodeContext} parameter.
\r
391 * @throws DatabaseException
\r
393 public LabelDecorator getLabelDecorator(ReadGraph graph, NodeContext context) throws DatabaseException {
\r
394 NodeType nodeType = getNodeType(graph, context);
\r
395 if(nodeType == null)
\r
396 return CompositeLabelDecorator.ID;
\r
397 ArrayList<LabelDecorator> decorators = new ArrayList<LabelDecorator>();
\r
398 for(LabelDecorationContribution contribution : labelDecorationContributions.get(graph, nodeType)) {
\r
399 LabelDecorator decorator = contribution.getLabelDecorator(graph, context);
\r
400 if(decorator != null)
\r
401 decorators.add(decorator);
\r
403 return CompositeLabelDecorator.create(decorators);
\r
407 * Finds {@link ImageDecorator} for the given {@link NodeContext} parameter.
\r
412 * @throws DatabaseException
\r
414 public ImageDecorator getImageDecorator(ReadGraph graph, NodeContext context) throws DatabaseException {
\r
415 NodeType nodeType = getNodeType(graph, context);
\r
416 if(nodeType == null)
\r
417 return CompositeImageDecorator.ID;
\r
418 ArrayList<ImageDecorator> decorators = new ArrayList<ImageDecorator>();
\r
419 for(ImageDecorationContribution contribution : imageDecorationContributions.get(graph, nodeType)) {
\r
420 ImageDecorator decorator = contribution.getImageDecorator(graph, context);
\r
421 if(decorator != null)
\r
422 decorators.add(decorator);
\r
424 return CompositeImageDecorator.create(decorators);
\r
428 * Finds {@link Modifier} for the given {@link NodeContext} parameter.
\r
434 * @throws DatabaseException
\r
436 public Modifier getModifier(ReadGraph graph, NodeContext context,
\r
437 String columnKey) throws DatabaseException {
\r
438 NodeType nodeType = getNodeType(graph, context);
\r
439 if(nodeType != null)
\r
440 for(ModifierContribution contribution : modifierContributions.get(graph, nodeType)) {
\r
441 Modifier modifier = contribution.getModifier(graph, context, columnKey);
\r
442 if(modifier == NoModifierRule.NO_MODIFIER)
\r
444 if(modifier != null)
\r
450 public TooltipContribution shouldCreateToolTip(ReadGraph graph, Event event, NodeContext context) throws DatabaseException {
\r
451 NodeType nodeType = getNodeType(graph, context);
\r
452 if(nodeType != null)
\r
453 for(TooltipContribution contribution : tooltipContributions.get(graph, nodeType)) {
\r
454 if (contribution.shouldCreateToolTip(graph, context))
\r
455 return contribution;
\r
460 public Object getTooltip(TooltipContribution contribution, Object event, Object parent, NodeContext context) throws DatabaseException {
\r
461 Object tooltip = contribution.getTooltip(event, parent, context);
\r
462 if (tooltip != null)
\r
467 private Graph toGraph() {
\r
468 Graph graph = new Graph();
\r
469 new Node(graph, "Foo");
\r
473 @SuppressWarnings("unused")
\r
474 private void visualize() {
\r
475 final Graph graph = toGraph();
\r
479 public void run() {
\r
480 final Display display = new Display();
\r
481 final Shell shell = new Shell(display);
\r
483 GraphvizComponent comp = new GraphvizComponent(shell, 0);
\r
484 comp.setGraph(graph);
\r
486 comp.setBounds(0, 0, 800, 600);
\r
489 while (!shell.isDisposed()) {
\r
490 if (!display.readAndDispatch()) display.sleep();
\r
498 public int hashCode() {
\r
499 return Arrays.hashCode(uris);
\r
503 public boolean equals(Object obj) {
\r
508 if (getClass() != obj.getClass())
\r
510 BrowseContext other = (BrowseContext) obj;
\r
511 return Arrays.equals(uris, other.uris);
\r
515 public String toString() {
\r
516 return getClass().getSimpleName() + Arrays.toString(uris);
\r