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.Shell;
\r
27 import org.simantics.browsing.ui.BuiltinKeys;
\r
28 import org.simantics.browsing.ui.CheckedState;
\r
29 import org.simantics.browsing.ui.NodeContext;
\r
30 import org.simantics.browsing.ui.common.ColumnKeys;
\r
31 import org.simantics.browsing.ui.common.NodeContextBuilder;
\r
32 import org.simantics.browsing.ui.content.CompositeImageDecorator;
\r
33 import org.simantics.browsing.ui.content.CompositeLabelDecorator;
\r
34 import org.simantics.browsing.ui.content.ImageDecorator;
\r
35 import org.simantics.browsing.ui.content.LabelDecorator;
\r
36 import org.simantics.browsing.ui.content.Labeler.Modifier;
\r
37 import org.simantics.browsing.ui.model.InvalidContribution;
\r
38 import org.simantics.browsing.ui.model.actions.ActionBrowseContext;
\r
39 import org.simantics.browsing.ui.model.check.CheckedStateContribution;
\r
40 import org.simantics.browsing.ui.model.children.ChildContribution;
\r
41 import org.simantics.browsing.ui.model.imagedecorators.ImageDecorationContribution;
\r
42 import org.simantics.browsing.ui.model.images.ImageContribution;
\r
43 import org.simantics.browsing.ui.model.labeldecorators.LabelDecorationContribution;
\r
44 import org.simantics.browsing.ui.model.labels.LabelContribution;
\r
45 import org.simantics.browsing.ui.model.modifiers.ModifierContribution;
\r
46 import org.simantics.browsing.ui.model.modifiers.NoModifierRule;
\r
47 import org.simantics.browsing.ui.model.nodetypes.EntityNodeType;
\r
48 import org.simantics.browsing.ui.model.nodetypes.NodeType;
\r
49 import org.simantics.browsing.ui.model.nodetypes.NodeTypeMultiMap;
\r
50 import org.simantics.browsing.ui.model.nodetypes.OrderedNodeTypeMultiMap;
\r
51 import org.simantics.browsing.ui.model.nodetypes.SpecialNodeType;
\r
52 import org.simantics.browsing.ui.model.sorters.AlphanumericSorter;
\r
53 import org.simantics.browsing.ui.model.sorters.Sorter;
\r
54 import org.simantics.browsing.ui.model.sorters.SorterContribution;
\r
55 import org.simantics.browsing.ui.model.visuals.FlatNodeContribution;
\r
56 import org.simantics.browsing.ui.model.visuals.VisualsContribution;
\r
57 import org.simantics.db.ReadGraph;
\r
58 import org.simantics.db.RequestProcessor;
\r
59 import org.simantics.db.Resource;
\r
60 import org.simantics.db.exception.DatabaseException;
\r
61 import org.simantics.db.exception.ResourceNotFoundException;
\r
62 import org.simantics.db.layer0.variable.Variable;
\r
63 import org.simantics.db.request.Read;
\r
64 import org.simantics.graphviz.Graph;
\r
65 import org.simantics.graphviz.Node;
\r
66 import org.simantics.graphviz.ui.GraphvizComponent;
\r
67 import org.simantics.scl.reflection.OntologyVersions;
\r
68 import org.simantics.viewpoint.ontology.ViewpointResource;
\r
71 * BrowseContext holds all contributions related to given set of browse contexts.
\r
73 * @author Hannu Niemistö
\r
75 public class BrowseContext {
\r
77 public static final boolean DEBUG = false;
\r
79 NodeTypeMultiMap<ChildContribution> childContributions = new NodeTypeMultiMap<ChildContribution>();
\r
80 NodeTypeMultiMap<ChildContribution> parentContributions = new NodeTypeMultiMap<ChildContribution>();
\r
81 OrderedNodeTypeMultiMap<LabelContribution> labelContributions = new OrderedNodeTypeMultiMap<LabelContribution>();
\r
82 OrderedNodeTypeMultiMap<ImageContribution> imageContributions = new OrderedNodeTypeMultiMap<ImageContribution>();
\r
83 OrderedNodeTypeMultiMap<CheckedStateContribution> checkedStateContributions = new OrderedNodeTypeMultiMap<CheckedStateContribution>();
\r
84 OrderedNodeTypeMultiMap<LabelDecorationContribution> labelDecorationContributions = new OrderedNodeTypeMultiMap<LabelDecorationContribution>();
\r
85 OrderedNodeTypeMultiMap<ImageDecorationContribution> imageDecorationContributions = new OrderedNodeTypeMultiMap<ImageDecorationContribution>();
\r
86 OrderedNodeTypeMultiMap<ModifierContribution> modifierContributions = new OrderedNodeTypeMultiMap<ModifierContribution>();
\r
87 OrderedNodeTypeMultiMap<SorterContribution> sorterContributions = new OrderedNodeTypeMultiMap<SorterContribution>();
\r
88 OrderedNodeTypeMultiMap<FlatNodeContribution> flatNodeContributions = new OrderedNodeTypeMultiMap<FlatNodeContribution>();
\r
90 private final String[] uris;
\r
92 private BrowseContext(String[] uris) {
\r
94 throw new NullPointerException("null URIs");
\r
98 public String[] getURIs() {
\r
102 public static BrowseContext get(ReadGraph graph,NodeContext context,BrowseContext defaultContext, boolean useNodeBrowseContexts) throws DatabaseException {
\r
103 if(!useNodeBrowseContexts) return defaultContext;
\r
104 BrowseContext mbc = graph.syncRequest(new ResolveBrowseContext(context));
\r
105 if(mbc != null) return mbc;
\r
106 BrowseContext parentContext = (BrowseContext)context.getConstant(BuiltinKeys.BROWSE_CONTEXT);
\r
107 if(parentContext != null) return parentContext;
\r
108 return defaultContext;
\r
112 * Creates a new BrowseContext for the given Collection of {@link Resource}s.
\r
115 * @param browseContextResources
\r
116 * @return new BrowseContext
\r
117 * @throws DatabaseException
\r
118 * @throws InvalidContribution
\r
120 public static BrowseContext create(ReadGraph g, Collection<Resource> browseContextResources) throws DatabaseException, InvalidContribution {
\r
121 ViewpointResource vr = ViewpointResource.getInstance(g);
\r
122 BrowseContext browseContext = new BrowseContext( BrowseContexts.toSortedURIs(g, browseContextResources) );
\r
123 for(Resource browseContextResource : findSubcontexts(g, browseContextResources)) {
\r
125 for(Resource childContributionResource :
\r
126 g.getObjects(browseContextResource, vr.BrowseContext_HasChildContribution)) {
\r
127 ChildContribution contribution = ChildContribution.create(g, childContributionResource);
\r
128 browseContext.childContributions.put(contribution.getParentNodeType(), contribution);
\r
129 browseContext.parentContributions.put(contribution.getChildNodeType(), contribution);
\r
132 for(Resource visualsContributionResource :
\r
133 g.getObjects(browseContextResource, vr.BrowseContext_HasVisualsContribution)) {
\r
134 VisualsContribution.load(g, visualsContributionResource,
\r
135 browseContext.labelContributions,
\r
136 browseContext.imageContributions,
\r
137 browseContext.checkedStateContributions,
\r
138 browseContext.labelDecorationContributions,
\r
139 browseContext.imageDecorationContributions,
\r
140 browseContext.modifierContributions,
\r
141 browseContext.sorterContributions,
\r
142 browseContext.flatNodeContributions
\r
146 //browseContext.visualize();
\r
147 return browseContext;
\r
150 public static Set<String> getBrowseContextClosure(RequestProcessor processor, final Set<String> browseContexts) throws DatabaseException {
\r
151 return processor.syncRequest(new Read<Set<String>>() {
\r
153 public Set<String> perform(ReadGraph graph) throws DatabaseException {
\r
154 Collection<Resource> browseContextResources = new ArrayList<Resource>(browseContexts.size());
\r
155 for (String browseContext : browseContexts) {
\r
157 browseContextResources.add(graph.getResource(browseContext));
\r
158 } catch (ResourceNotFoundException e) {
\r
159 // Expected result, if no modelled contributions exist.
\r
160 //System.err.println("Didn't find " + browseContext + " while loading model browser.");
\r
163 Collection<Resource> allBrowseContextResources = BrowseContext.findSubcontexts(graph, browseContextResources);
\r
164 Set<String> result = new HashSet<String>();
\r
165 for (Resource r : allBrowseContextResources)
\r
166 result.add(graph.getURI(r));
\r
172 public static Collection<Resource> findSubcontexts(ReadGraph g,
\r
173 Collection<Resource> browseContexts) throws DatabaseException {
\r
174 ViewpointResource vr = ViewpointResource.getInstance(g);
\r
175 HashSet<Resource> result = new HashSet<Resource>(browseContexts);
\r
176 ArrayList<Resource> stack = new ArrayList<Resource>(browseContexts);
\r
177 while(!stack.isEmpty()) {
\r
178 Resource cur = stack.remove(stack.size()-1);
\r
179 for(Resource sc : g.getObjects(cur, vr.BrowseContext_Includes))
\r
187 * Finds the possible children of the given {@link NodeContext} parameter.
\r
191 * @return Collection of children or an empty collection in case node has no children
\r
192 * @throws DatabaseException
\r
194 public Collection<NodeContext> getChildren(ReadGraph graph, NodeContext parent) throws DatabaseException {
\r
195 if(isFlattened(graph, parent))
\r
196 return Collections.emptyList();
\r
198 return getChildrenImpl(graph, parent);
\r
201 private Collection<NodeContext> getChildrenImpl(ReadGraph graph, NodeContext parent) throws DatabaseException {
\r
202 NodeType nodeType = getNodeType(graph, parent);
\r
203 if(nodeType == null)
\r
204 return Collections.emptyList();
\r
205 ArrayList<NodeContext> result = new ArrayList<NodeContext>();
\r
206 Collection<ChildContribution> contributions = childContributions.get(graph, nodeType);
\r
207 if(contributions.size() > 1) {
\r
208 Map<String,ChildContribution> contribs = new HashMap<>();
\r
209 for(ChildContribution contribution : contributions) {
\r
210 String identifier = contribution.getIdentifier();
\r
211 ChildContribution current = contribs.get(identifier);
\r
212 if(current != null && current.getPriority() > contribution.getPriority()) continue;
\r
213 contribs.put(identifier, contribution);
\r
215 contributions = contribs.values();
\r
217 for(ChildContribution contribution : contributions) {
\r
218 Collection<NodeContext> children = contribution.getChildren(graph, parent);
\r
219 result.addAll(children);
\r
221 System.err.println("contribution: " + contribution.getIdentifier());
\r
222 for(NodeContext ctx : children)
\r
223 System.err.println("-" + ctx);
\r
227 // Sorting the result
\r
228 if(!result.isEmpty()) {
\r
229 for(SorterContribution contribution : sorterContributions.get(graph, nodeType)) {
\r
230 Sorter sorter = contribution.getSorter(graph, parent);
\r
231 if(sorter != null) {
\r
232 sorter.sort(graph, this, result);
\r
236 AlphanumericSorter.INSTANCE.sort(graph, this, result);
\r
239 result = flatten(graph, result);
\r
240 //result = augment(graph, result);
\r
245 private ArrayList<NodeContext> flatten(ReadGraph graph, ArrayList<NodeContext> result) throws DatabaseException {
\r
246 ArrayList<NodeContext> flattened = new ArrayList<NodeContext>();
\r
247 for(NodeContext node : result)
\r
248 if(isFlattened(graph, node)) {
\r
249 flattened.add(node);
\r
250 flattened.addAll(getChildrenImpl(graph, node));
\r
253 flattened.add(node);
\r
257 public static ArrayList<NodeContext> augment(ReadGraph graph, BrowseContext bc, Collection<NodeContext> contexts, boolean resolveABC) throws DatabaseException {
\r
258 ArrayList<NodeContext> result = new ArrayList<NodeContext>();
\r
259 for(NodeContext context : contexts) {
\r
260 ActionBrowseContext abc = resolveABC ? graph.syncRequest(new ResolveActionBrowseContext(context)) : null;
\r
261 result.add(NodeContextBuilder.buildWithData(NodeType.KEY_SEQUENCE_EXT,
\r
263 context.getConstant(BuiltinKeys.INPUT),
\r
264 context.getConstant(NodeType.TYPE),
\r
265 context.getConstant(BuiltinKeys.UI_CONTEXT),
\r
271 private boolean isFlattened(ReadGraph graph, NodeContext node) throws DatabaseException {
\r
272 NodeType nodeType = getNodeType(graph, node);
\r
273 return nodeType != null && !flatNodeContributions.get(graph, nodeType).isEmpty();
\r
277 * Finds the possible parents of the given {@link NodeContext} parameter.
\r
281 * @return Collection of parents or an empty Collection in case node has no parents.
\r
282 * @throws DatabaseException
\r
284 public Collection<NodeContext> getParents(ReadGraph graph, NodeContext child) throws DatabaseException {
\r
285 NodeType nodeType = getNodeType(graph, child);
\r
286 if(nodeType == null)
\r
287 return Collections.emptyList();
\r
288 ArrayList<NodeContext> result = new ArrayList<NodeContext>();
\r
289 for(ChildContribution contribution : parentContributions.get(graph, nodeType)) {
\r
290 result.addAll(contribution.getParents(graph, child));
\r
295 public boolean hasChildren(ReadGraph graph, NodeContext parent) throws DatabaseException {
\r
296 NodeType nodeType = getNodeType(graph, parent);
\r
297 if(nodeType == null)
\r
299 for(ChildContribution contribution : childContributions.get(graph, nodeType))
\r
300 if(contribution.hasChildren(graph, parent))
\r
305 private static NodeType getNodeType(ReadGraph graph, NodeContext parent) throws DatabaseException {
\r
306 NodeType nodeType = parent.getConstant(NodeType.TYPE);
\r
307 if(nodeType == null) {
\r
308 // TODO remove this code when root of model browser is fixed
\r
309 Object input = parent.getConstant(BuiltinKeys.INPUT);
\r
310 if(input instanceof Resource) {
\r
311 nodeType = EntityNodeType.getNodeTypeFor(graph, (Resource)input);
\r
312 } else if (input instanceof Variable) {
\r
313 String uri = OntologyVersions.getInstance().currentVersion("http://www.simantics.org/Modeling-0.0/ModelingBrowseContext/Variable");
\r
314 return new SpecialNodeType(graph.getResource(uri), Variable.class);
\r
321 * Finds labels for the given {@link NodeContext} parameter.
\r
325 * @return Map containing all the labels assigned by key indicating the column e.g. "single"
\r
326 * @throws DatabaseException
\r
328 public Map<String, String> getLabel(ReadGraph graph, NodeContext parent) throws DatabaseException {
\r
329 NodeType nodeType = getNodeType(graph, parent);
\r
330 if(nodeType == null)
\r
331 return Collections.singletonMap(ColumnKeys.SINGLE, "ERROR (no node type)");
\r
332 List<LabelContribution> contributions = labelContributions.get(graph, nodeType);
\r
333 for(LabelContribution contribution : contributions) {
\r
334 Map<String, String> label = contribution.getLabel(graph, parent);
\r
338 return Collections.singletonMap(ColumnKeys.SINGLE, "(no label rule)");
\r
342 * Finds {@link ImageDescriptor}s for the given {@link NodeContext} parameter.
\r
346 * @return Map containing all the {@ImageDescriptor}s or empty
\r
347 * @throws DatabaseException
\r
349 public Map<String, ImageDescriptor> getImage(ReadGraph graph, NodeContext parent) throws DatabaseException {
\r
350 NodeType nodeType = getNodeType(graph, parent);
\r
351 if(nodeType == null)
\r
352 return Collections.emptyMap();
\r
353 for(ImageContribution contribution : imageContributions.get(graph, nodeType)) {
\r
354 Map<String, ImageDescriptor> image = contribution.getImage(graph, parent);
\r
358 return Collections.emptyMap();
\r
362 * Finds if the given {@link NodeContext} is checked or not.
\r
367 * @throws DatabaseException
\r
369 public CheckedState getCheckedState(ReadGraph graph, NodeContext parent) throws DatabaseException {
\r
370 NodeType nodeType = getNodeType(graph, parent);
\r
371 if(nodeType == null)
\r
372 return CheckedState.NOT_CHECKED;
\r
373 for(CheckedStateContribution contribution : checkedStateContributions.get(graph, nodeType)) {
\r
374 CheckedState state = contribution.getCheckedState(graph, parent);
\r
378 return CheckedState.NOT_CHECKED;
\r
382 * Finds {@link LabelDecorator} for the given {@link NodeContext} parameter.
\r
387 * @throws DatabaseException
\r
389 public LabelDecorator getLabelDecorator(ReadGraph graph, NodeContext context) throws DatabaseException {
\r
390 NodeType nodeType = getNodeType(graph, context);
\r
391 if(nodeType == null)
\r
392 return CompositeLabelDecorator.ID;
\r
393 ArrayList<LabelDecorator> decorators = new ArrayList<LabelDecorator>();
\r
394 for(LabelDecorationContribution contribution : labelDecorationContributions.get(graph, nodeType)) {
\r
395 LabelDecorator decorator = contribution.getLabelDecorator(graph, context);
\r
396 if(decorator != null)
\r
397 decorators.add(decorator);
\r
399 return CompositeLabelDecorator.create(decorators);
\r
403 * Finds {@link ImageDecorator} for the given {@link NodeContext} parameter.
\r
408 * @throws DatabaseException
\r
410 public ImageDecorator getImageDecorator(ReadGraph graph, NodeContext context) throws DatabaseException {
\r
411 NodeType nodeType = getNodeType(graph, context);
\r
412 if(nodeType == null)
\r
413 return CompositeImageDecorator.ID;
\r
414 ArrayList<ImageDecorator> decorators = new ArrayList<ImageDecorator>();
\r
415 for(ImageDecorationContribution contribution : imageDecorationContributions.get(graph, nodeType)) {
\r
416 ImageDecorator decorator = contribution.getImageDecorator(graph, context);
\r
417 if(decorator != null)
\r
418 decorators.add(decorator);
\r
420 return CompositeImageDecorator.create(decorators);
\r
424 * Finds {@link Modifier} for the given {@link NodeContext} parameter.
\r
430 * @throws DatabaseException
\r
432 public Modifier getModifier(ReadGraph graph, NodeContext context,
\r
433 String columnKey) throws DatabaseException {
\r
434 NodeType nodeType = getNodeType(graph, context);
\r
435 if(nodeType != null)
\r
436 for(ModifierContribution contribution : modifierContributions.get(graph, nodeType)) {
\r
437 Modifier modifier = contribution.getModifier(graph, context, columnKey);
\r
438 if(modifier == NoModifierRule.NO_MODIFIER)
\r
440 if(modifier != null)
\r
446 private Graph toGraph() {
\r
447 Graph graph = new Graph();
\r
448 new Node(graph, "Foo");
\r
452 @SuppressWarnings("unused")
\r
453 private void visualize() {
\r
454 final Graph graph = toGraph();
\r
458 public void run() {
\r
459 final Display display = new Display();
\r
460 final Shell shell = new Shell(display);
\r
462 GraphvizComponent comp = new GraphvizComponent(shell, 0);
\r
463 comp.setGraph(graph);
\r
465 comp.setBounds(0, 0, 800, 600);
\r
468 while (!shell.isDisposed()) {
\r
469 if (!display.readAndDispatch()) display.sleep();
\r
477 public int hashCode() {
\r
478 return Arrays.hashCode(uris);
\r
482 public boolean equals(Object obj) {
\r
487 if (getClass() != obj.getClass())
\r
489 BrowseContext other = (BrowseContext) obj;
\r
490 return Arrays.equals(uris, other.uris);
\r
494 public String toString() {
\r
495 return getClass().getSimpleName() + Arrays.toString(uris);
\r