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;
71 import org.slf4j.Logger;
72 import org.slf4j.LoggerFactory;
75 * BrowseContext holds all contributions related to given set of browse contexts.
77 * @author Hannu Niemistö
79 public class BrowseContext {
81 private static final Logger LOGGER = LoggerFactory.getLogger(BrowseContext.class);
82 public static final boolean DEBUG = false;
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<>();
96 private final String[] uris;
98 private BrowseContext(String[] uris) {
100 throw new NullPointerException("null URIs");
104 public String[] getURIs() {
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;
118 * Creates a new BrowseContext for the given Collection of {@link Resource}s.
121 * @param browseContextResources
122 * @return new BrowseContext
123 * @throws DatabaseException
124 * @throws InvalidContribution
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)) {
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);
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
153 //browseContext.visualize();
154 return browseContext;
157 public static Set<String> getBrowseContextClosure(RequestProcessor processor, final Set<String> browseContexts) throws DatabaseException {
158 return processor.syncRequest(new Read<Set<String>>() {
160 public Set<String> perform(ReadGraph graph) throws DatabaseException {
161 Collection<Resource> browseContextResources = new ArrayList<Resource>(browseContexts.size());
162 for (String browseContext : browseContexts) {
164 browseContextResources.add(graph.getResource(browseContext));
165 } catch (ResourceNotFoundException e) {
166 LOGGER.error("Didn't find " + browseContext + " while loading model browser.", e);
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));
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))
193 * Finds the possible children of the given {@link NodeContext} parameter.
197 * @return Collection of children or an empty collection in case node has no children
198 * @throws DatabaseException
200 public Collection<NodeContext> getChildren(ReadGraph graph, NodeContext parent) throws DatabaseException {
201 if(isFlattened(graph, parent))
202 return Collections.emptyList();
204 return getChildrenImpl(graph, parent);
207 private Collection<NodeContext> getChildrenImpl(ReadGraph graph, NodeContext parent) throws DatabaseException {
208 NodeType nodeType = getNodeType(graph, parent);
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);
221 contributions = contribs.values();
223 for(ChildContribution contribution : contributions) {
224 Collection<NodeContext> children = contribution.getChildren(graph, parent);
225 result.addAll(children);
227 LOGGER.info("contribution: " + contribution.getIdentifier());
228 for(NodeContext ctx : children)
229 LOGGER.info("-" + ctx);
233 // Sorting the result
234 if(!result.isEmpty()) {
235 for(SorterContribution contribution : sorterContributions.get(graph, nodeType)) {
236 Sorter sorter = contribution.getSorter(graph, parent);
238 sorter.sort(graph, this, result);
242 AlphanumericSorter.INSTANCE.sort(graph, this, result);
245 result = flatten(graph, result);
246 //result = augment(graph, result);
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)) {
256 flattened.addAll(getChildrenImpl(graph, node));
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;
268 abc = graph.syncRequest(new ResolveActionBrowseContext(context));
269 if(abc == null) abc = (ActionBrowseContext)context.getConstant(BuiltinKeys.ACTION_BROWSE_CONTEXT);
271 result.add(NodeContextBuilder.buildWithData(NodeType.KEY_SEQUENCE_EXT,
273 context.getConstant(BuiltinKeys.INPUT),
274 context.getConstant(NodeType.TYPE),
275 context.getConstant(BuiltinKeys.UI_CONTEXT),
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();
287 * Finds the possible parents of the given {@link NodeContext} parameter.
291 * @return Collection of parents or an empty Collection in case node has no parents.
292 * @throws DatabaseException
294 public Collection<NodeContext> getParents(ReadGraph graph, NodeContext child) throws DatabaseException {
295 NodeType nodeType = getNodeType(graph, child);
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));
305 public boolean hasChildren(ReadGraph graph, NodeContext parent) throws DatabaseException {
306 NodeType nodeType = getNodeType(graph, parent);
309 for(ChildContribution contribution : childContributions.get(graph, nodeType))
310 if(contribution.hasChildren(graph, parent))
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);
331 * Finds labels for the given {@link NodeContext} parameter.
335 * @return Map containing all the labels assigned by key indicating the column e.g. "single"
336 * @throws DatabaseException
338 public Map<String, String> getLabel(ReadGraph graph, NodeContext parent) throws DatabaseException {
339 NodeType nodeType = getNodeType(graph, parent);
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);
348 return Collections.singletonMap(ColumnKeys.SINGLE, "(no label rule)");
352 * Finds {@link ImageDescriptor}s for the given {@link NodeContext} parameter.
356 * @return Map containing all the {@ImageDescriptor}s or empty
357 * @throws DatabaseException
359 public Map<String, ImageDescriptor> getImage(ReadGraph graph, NodeContext parent) throws DatabaseException {
360 NodeType nodeType = getNodeType(graph, parent);
362 return Collections.emptyMap();
363 for(ImageContribution contribution : imageContributions.get(graph, nodeType)) {
364 Map<String, ImageDescriptor> image = contribution.getImage(graph, parent);
368 return Collections.emptyMap();
372 * Finds if the given {@link NodeContext} is checked or not.
377 * @throws DatabaseException
379 public CheckedState getCheckedState(ReadGraph graph, NodeContext parent) throws DatabaseException {
380 NodeType nodeType = getNodeType(graph, parent);
382 return CheckedState.NOT_CHECKED;
383 for(CheckedStateContribution contribution : checkedStateContributions.get(graph, nodeType)) {
384 CheckedState state = contribution.getCheckedState(graph, parent);
388 return CheckedState.NOT_CHECKED;
392 * Finds {@link LabelDecorator} for the given {@link NodeContext} parameter.
397 * @throws DatabaseException
399 public LabelDecorator getLabelDecorator(ReadGraph graph, NodeContext context) throws DatabaseException {
400 NodeType nodeType = getNodeType(graph, context);
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);
409 return CompositeLabelDecorator.create(decorators);
413 * Finds {@link ImageDecorator} for the given {@link NodeContext} parameter.
418 * @throws DatabaseException
420 public ImageDecorator getImageDecorator(ReadGraph graph, NodeContext context) throws DatabaseException {
421 NodeType nodeType = getNodeType(graph, context);
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);
430 return CompositeImageDecorator.create(decorators);
434 * Finds {@link Modifier} for the given {@link NodeContext} parameter.
440 * @throws DatabaseException
442 public Modifier getModifier(ReadGraph graph, NodeContext context,
443 String columnKey) throws DatabaseException {
444 NodeType nodeType = getNodeType(graph, context);
446 for(ModifierContribution contribution : modifierContributions.get(graph, nodeType)) {
447 Modifier modifier = contribution.getModifier(graph, context, columnKey);
448 if(modifier == NoModifierRule.NO_MODIFIER)
456 public TooltipContribution shouldCreateToolTip(ReadGraph graph, Event event, NodeContext context) throws DatabaseException {
457 NodeType nodeType = getNodeType(graph, context);
459 for(TooltipContribution contribution : tooltipContributions.get(graph, nodeType)) {
460 if (contribution.shouldCreateToolTip(graph, context))
466 public Object getTooltip(TooltipContribution contribution, Object event, Object parent, NodeContext context) throws DatabaseException {
467 Object tooltip = contribution.getTooltip(event, parent, context);
473 private Graph toGraph() {
474 Graph graph = new Graph();
475 new Node(graph, "Foo");
479 @SuppressWarnings("unused")
480 private void visualize() {
481 final Graph graph = toGraph();
486 final Display display = new Display();
487 final Shell shell = new Shell(display);
489 GraphvizComponent comp = new GraphvizComponent(shell, 0);
490 comp.setGraph(graph);
492 comp.setBounds(0, 0, 800, 600);
495 while (!shell.isDisposed()) {
496 if (!display.readAndDispatch()) display.sleep();
504 public int hashCode() {
505 return Arrays.hashCode(uris);
509 public boolean equals(Object obj) {
514 if (getClass() != obj.getClass())
516 BrowseContext other = (BrowseContext) obj;
517 return Arrays.equals(uris, other.uris);
521 public String toString() {
522 return getClass().getSimpleName() + Arrays.toString(uris);