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 = null;
\r
266 abc = graph.syncRequest(new ResolveActionBrowseContext(context));
\r
267 if(abc == null) abc = (ActionBrowseContext)context.getConstant(BuiltinKeys.ACTION_BROWSE_CONTEXT);
\r
269 result.add(NodeContextBuilder.buildWithData(NodeType.KEY_SEQUENCE_EXT,
\r
271 context.getConstant(BuiltinKeys.INPUT),
\r
272 context.getConstant(NodeType.TYPE),
\r
273 context.getConstant(BuiltinKeys.UI_CONTEXT),
\r
279 private boolean isFlattened(ReadGraph graph, NodeContext node) throws DatabaseException {
\r
280 NodeType nodeType = getNodeType(graph, node);
\r
281 return nodeType != null && !flatNodeContributions.get(graph, nodeType).isEmpty();
\r
285 * Finds the possible parents of the given {@link NodeContext} parameter.
\r
289 * @return Collection of parents or an empty Collection in case node has no parents.
\r
290 * @throws DatabaseException
\r
292 public Collection<NodeContext> getParents(ReadGraph graph, NodeContext child) throws DatabaseException {
\r
293 NodeType nodeType = getNodeType(graph, child);
\r
294 if(nodeType == null)
\r
295 return Collections.emptyList();
\r
296 ArrayList<NodeContext> result = new ArrayList<NodeContext>();
\r
297 for(ChildContribution contribution : parentContributions.get(graph, nodeType)) {
\r
298 result.addAll(contribution.getParents(graph, child));
\r
303 public boolean hasChildren(ReadGraph graph, NodeContext parent) throws DatabaseException {
\r
304 NodeType nodeType = getNodeType(graph, parent);
\r
305 if(nodeType == null)
\r
307 for(ChildContribution contribution : childContributions.get(graph, nodeType))
\r
308 if(contribution.hasChildren(graph, parent))
\r
313 private static NodeType getNodeType(ReadGraph graph, NodeContext parent) throws DatabaseException {
\r
314 NodeType nodeType = parent.getConstant(NodeType.TYPE);
\r
315 if(nodeType == null) {
\r
316 // TODO remove this code when root of model browser is fixed
\r
317 Object input = parent.getConstant(BuiltinKeys.INPUT);
\r
318 if(input instanceof Resource) {
\r
319 nodeType = EntityNodeType.getNodeTypeFor(graph, (Resource)input);
\r
320 } else if (input instanceof Variable) {
\r
321 String uri = OntologyVersions.getInstance().currentVersion("http://www.simantics.org/Modeling-0.0/ModelingBrowseContext/Variable");
\r
322 return new SpecialNodeType(graph.getResource(uri), Variable.class);
\r
329 * Finds labels for the given {@link NodeContext} parameter.
\r
333 * @return Map containing all the labels assigned by key indicating the column e.g. "single"
\r
334 * @throws DatabaseException
\r
336 public Map<String, String> getLabel(ReadGraph graph, NodeContext parent) throws DatabaseException {
\r
337 NodeType nodeType = getNodeType(graph, parent);
\r
338 if(nodeType == null)
\r
339 return Collections.singletonMap(ColumnKeys.SINGLE, "ERROR (no node type)");
\r
340 List<LabelContribution> contributions = labelContributions.get(graph, nodeType);
\r
341 for(LabelContribution contribution : contributions) {
\r
342 Map<String, String> label = contribution.getLabel(graph, parent);
\r
346 return Collections.singletonMap(ColumnKeys.SINGLE, "(no label rule)");
\r
350 * Finds {@link ImageDescriptor}s for the given {@link NodeContext} parameter.
\r
354 * @return Map containing all the {@ImageDescriptor}s or empty
\r
355 * @throws DatabaseException
\r
357 public Map<String, ImageDescriptor> getImage(ReadGraph graph, NodeContext parent) throws DatabaseException {
\r
358 NodeType nodeType = getNodeType(graph, parent);
\r
359 if(nodeType == null)
\r
360 return Collections.emptyMap();
\r
361 for(ImageContribution contribution : imageContributions.get(graph, nodeType)) {
\r
362 Map<String, ImageDescriptor> image = contribution.getImage(graph, parent);
\r
366 return Collections.emptyMap();
\r
370 * Finds if the given {@link NodeContext} is checked or not.
\r
375 * @throws DatabaseException
\r
377 public CheckedState getCheckedState(ReadGraph graph, NodeContext parent) throws DatabaseException {
\r
378 NodeType nodeType = getNodeType(graph, parent);
\r
379 if(nodeType == null)
\r
380 return CheckedState.NOT_CHECKED;
\r
381 for(CheckedStateContribution contribution : checkedStateContributions.get(graph, nodeType)) {
\r
382 CheckedState state = contribution.getCheckedState(graph, parent);
\r
386 return CheckedState.NOT_CHECKED;
\r
390 * Finds {@link LabelDecorator} for the given {@link NodeContext} parameter.
\r
395 * @throws DatabaseException
\r
397 public LabelDecorator getLabelDecorator(ReadGraph graph, NodeContext context) throws DatabaseException {
\r
398 NodeType nodeType = getNodeType(graph, context);
\r
399 if(nodeType == null)
\r
400 return CompositeLabelDecorator.ID;
\r
401 ArrayList<LabelDecorator> decorators = new ArrayList<LabelDecorator>();
\r
402 for(LabelDecorationContribution contribution : labelDecorationContributions.get(graph, nodeType)) {
\r
403 LabelDecorator decorator = contribution.getLabelDecorator(graph, context);
\r
404 if(decorator != null)
\r
405 decorators.add(decorator);
\r
407 return CompositeLabelDecorator.create(decorators);
\r
411 * Finds {@link ImageDecorator} for the given {@link NodeContext} parameter.
\r
416 * @throws DatabaseException
\r
418 public ImageDecorator getImageDecorator(ReadGraph graph, NodeContext context) throws DatabaseException {
\r
419 NodeType nodeType = getNodeType(graph, context);
\r
420 if(nodeType == null)
\r
421 return CompositeImageDecorator.ID;
\r
422 ArrayList<ImageDecorator> decorators = new ArrayList<ImageDecorator>();
\r
423 for(ImageDecorationContribution contribution : imageDecorationContributions.get(graph, nodeType)) {
\r
424 ImageDecorator decorator = contribution.getImageDecorator(graph, context);
\r
425 if(decorator != null)
\r
426 decorators.add(decorator);
\r
428 return CompositeImageDecorator.create(decorators);
\r
432 * Finds {@link Modifier} for the given {@link NodeContext} parameter.
\r
438 * @throws DatabaseException
\r
440 public Modifier getModifier(ReadGraph graph, NodeContext context,
\r
441 String columnKey) throws DatabaseException {
\r
442 NodeType nodeType = getNodeType(graph, context);
\r
443 if(nodeType != null)
\r
444 for(ModifierContribution contribution : modifierContributions.get(graph, nodeType)) {
\r
445 Modifier modifier = contribution.getModifier(graph, context, columnKey);
\r
446 if(modifier == NoModifierRule.NO_MODIFIER)
\r
448 if(modifier != null)
\r
454 public TooltipContribution shouldCreateToolTip(ReadGraph graph, Event event, NodeContext context) throws DatabaseException {
\r
455 NodeType nodeType = getNodeType(graph, context);
\r
456 if(nodeType != null)
\r
457 for(TooltipContribution contribution : tooltipContributions.get(graph, nodeType)) {
\r
458 if (contribution.shouldCreateToolTip(graph, context))
\r
459 return contribution;
\r
464 public Object getTooltip(TooltipContribution contribution, Object event, Object parent, NodeContext context) throws DatabaseException {
\r
465 Object tooltip = contribution.getTooltip(event, parent, context);
\r
466 if (tooltip != null)
\r
471 private Graph toGraph() {
\r
472 Graph graph = new Graph();
\r
473 new Node(graph, "Foo");
\r
477 @SuppressWarnings("unused")
\r
478 private void visualize() {
\r
479 final Graph graph = toGraph();
\r
483 public void run() {
\r
484 final Display display = new Display();
\r
485 final Shell shell = new Shell(display);
\r
487 GraphvizComponent comp = new GraphvizComponent(shell, 0);
\r
488 comp.setGraph(graph);
\r
490 comp.setBounds(0, 0, 800, 600);
\r
493 while (!shell.isDisposed()) {
\r
494 if (!display.readAndDispatch()) display.sleep();
\r
502 public int hashCode() {
\r
503 return Arrays.hashCode(uris);
\r
507 public boolean equals(Object obj) {
\r
512 if (getClass() != obj.getClass())
\r
514 BrowseContext other = (BrowseContext) obj;
\r
515 return Arrays.equals(uris, other.uris);
\r
519 public String toString() {
\r
520 return getClass().getSimpleName() + Arrays.toString(uris);
\r