1 /*******************************************************************************
2 * Copyright (c) 2010, 2011 Association for Decentralized Information Management in
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
10 * VTT Technical Research Centre of Finland - initial API and implementation
11 *******************************************************************************/
12 package org.simantics.browsing.ui.model.browsecontexts;
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;
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;
73 * BrowseContext holds all contributions related to given set of browse contexts.
75 * @author Hannu Niemistö
77 public class BrowseContext {
79 public static final boolean DEBUG = false;
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<>();
93 private final String[] uris;
95 private BrowseContext(String[] uris) {
97 throw new NullPointerException("null URIs");
101 public String[] getURIs() {
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;
115 * Creates a new BrowseContext for the given Collection of {@link Resource}s.
118 * @param browseContextResources
119 * @return new BrowseContext
120 * @throws DatabaseException
121 * @throws InvalidContribution
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)) {
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);
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
150 //browseContext.visualize();
151 return browseContext;
154 public static Set<String> getBrowseContextClosure(RequestProcessor processor, final Set<String> browseContexts) throws DatabaseException {
155 return processor.syncRequest(new Read<Set<String>>() {
157 public Set<String> perform(ReadGraph graph) throws DatabaseException {
158 Collection<Resource> browseContextResources = new ArrayList<Resource>(browseContexts.size());
159 for (String browseContext : browseContexts) {
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.");
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));
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))
191 * Finds the possible children of the given {@link NodeContext} parameter.
195 * @return Collection of children or an empty collection in case node has no children
196 * @throws DatabaseException
198 public Collection<NodeContext> getChildren(ReadGraph graph, NodeContext parent) throws DatabaseException {
199 if(isFlattened(graph, parent))
200 return Collections.emptyList();
202 return getChildrenImpl(graph, parent);
205 private Collection<NodeContext> getChildrenImpl(ReadGraph graph, NodeContext parent) throws DatabaseException {
206 NodeType nodeType = getNodeType(graph, parent);
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);
219 contributions = contribs.values();
221 for(ChildContribution contribution : contributions) {
222 Collection<NodeContext> children = contribution.getChildren(graph, parent);
223 result.addAll(children);
225 System.err.println("contribution: " + contribution.getIdentifier());
226 for(NodeContext ctx : children)
227 System.err.println("-" + ctx);
231 // Sorting the result
232 if(!result.isEmpty()) {
233 for(SorterContribution contribution : sorterContributions.get(graph, nodeType)) {
234 Sorter sorter = contribution.getSorter(graph, parent);
236 sorter.sort(graph, this, result);
240 AlphanumericSorter.INSTANCE.sort(graph, this, result);
243 result = flatten(graph, result);
244 //result = augment(graph, result);
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)) {
254 flattened.addAll(getChildrenImpl(graph, node));
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;
266 abc = graph.syncRequest(new ResolveActionBrowseContext(context));
267 if(abc == null) abc = (ActionBrowseContext)context.getConstant(BuiltinKeys.ACTION_BROWSE_CONTEXT);
269 result.add(NodeContextBuilder.buildWithData(NodeType.KEY_SEQUENCE_EXT,
271 context.getConstant(BuiltinKeys.INPUT),
272 context.getConstant(NodeType.TYPE),
273 context.getConstant(BuiltinKeys.UI_CONTEXT),
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();
285 * Finds the possible parents of the given {@link NodeContext} parameter.
289 * @return Collection of parents or an empty Collection in case node has no parents.
290 * @throws DatabaseException
292 public Collection<NodeContext> getParents(ReadGraph graph, NodeContext child) throws DatabaseException {
293 NodeType nodeType = getNodeType(graph, child);
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));
303 public boolean hasChildren(ReadGraph graph, NodeContext parent) throws DatabaseException {
304 NodeType nodeType = getNodeType(graph, parent);
307 for(ChildContribution contribution : childContributions.get(graph, nodeType))
308 if(contribution.hasChildren(graph, parent))
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);
329 * Finds labels for the given {@link NodeContext} parameter.
333 * @return Map containing all the labels assigned by key indicating the column e.g. "single"
334 * @throws DatabaseException
336 public Map<String, String> getLabel(ReadGraph graph, NodeContext parent) throws DatabaseException {
337 NodeType nodeType = getNodeType(graph, parent);
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);
346 return Collections.singletonMap(ColumnKeys.SINGLE, "(no label rule)");
350 * Finds {@link ImageDescriptor}s for the given {@link NodeContext} parameter.
354 * @return Map containing all the {@ImageDescriptor}s or empty
355 * @throws DatabaseException
357 public Map<String, ImageDescriptor> getImage(ReadGraph graph, NodeContext parent) throws DatabaseException {
358 NodeType nodeType = getNodeType(graph, parent);
360 return Collections.emptyMap();
361 for(ImageContribution contribution : imageContributions.get(graph, nodeType)) {
362 Map<String, ImageDescriptor> image = contribution.getImage(graph, parent);
366 return Collections.emptyMap();
370 * Finds if the given {@link NodeContext} is checked or not.
375 * @throws DatabaseException
377 public CheckedState getCheckedState(ReadGraph graph, NodeContext parent) throws DatabaseException {
378 NodeType nodeType = getNodeType(graph, parent);
380 return CheckedState.NOT_CHECKED;
381 for(CheckedStateContribution contribution : checkedStateContributions.get(graph, nodeType)) {
382 CheckedState state = contribution.getCheckedState(graph, parent);
386 return CheckedState.NOT_CHECKED;
390 * Finds {@link LabelDecorator} for the given {@link NodeContext} parameter.
395 * @throws DatabaseException
397 public LabelDecorator getLabelDecorator(ReadGraph graph, NodeContext context) throws DatabaseException {
398 NodeType nodeType = getNodeType(graph, context);
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);
407 return CompositeLabelDecorator.create(decorators);
411 * Finds {@link ImageDecorator} for the given {@link NodeContext} parameter.
416 * @throws DatabaseException
418 public ImageDecorator getImageDecorator(ReadGraph graph, NodeContext context) throws DatabaseException {
419 NodeType nodeType = getNodeType(graph, context);
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);
428 return CompositeImageDecorator.create(decorators);
432 * Finds {@link Modifier} for the given {@link NodeContext} parameter.
438 * @throws DatabaseException
440 public Modifier getModifier(ReadGraph graph, NodeContext context,
441 String columnKey) throws DatabaseException {
442 NodeType nodeType = getNodeType(graph, context);
444 for(ModifierContribution contribution : modifierContributions.get(graph, nodeType)) {
445 Modifier modifier = contribution.getModifier(graph, context, columnKey);
446 if(modifier == NoModifierRule.NO_MODIFIER)
454 public TooltipContribution shouldCreateToolTip(ReadGraph graph, Event event, NodeContext context) throws DatabaseException {
455 NodeType nodeType = getNodeType(graph, context);
457 for(TooltipContribution contribution : tooltipContributions.get(graph, nodeType)) {
458 if (contribution.shouldCreateToolTip(graph, context))
464 public Object getTooltip(TooltipContribution contribution, Object event, Object parent, NodeContext context) throws DatabaseException {
465 Object tooltip = contribution.getTooltip(event, parent, context);
471 private Graph toGraph() {
472 Graph graph = new Graph();
473 new Node(graph, "Foo");
477 @SuppressWarnings("unused")
478 private void visualize() {
479 final Graph graph = toGraph();
484 final Display display = new Display();
485 final Shell shell = new Shell(display);
487 GraphvizComponent comp = new GraphvizComponent(shell, 0);
488 comp.setGraph(graph);
490 comp.setBounds(0, 0, 800, 600);
493 while (!shell.isDisposed()) {
494 if (!display.readAndDispatch()) display.sleep();
502 public int hashCode() {
503 return Arrays.hashCode(uris);
507 public boolean equals(Object obj) {
512 if (getClass() != obj.getClass())
514 BrowseContext other = (BrowseContext) obj;
515 return Arrays.equals(uris, other.uris);
519 public String toString() {
520 return getClass().getSimpleName() + Arrays.toString(uris);