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.Event;
26 import org.simantics.browsing.ui.BuiltinKeys;
27 import org.simantics.browsing.ui.CheckedState;
28 import org.simantics.browsing.ui.NodeContext;
29 import org.simantics.browsing.ui.common.ColumnKeys;
30 import org.simantics.browsing.ui.common.NodeContextBuilder;
31 import org.simantics.browsing.ui.content.CompositeImageDecorator;
32 import org.simantics.browsing.ui.content.CompositeLabelDecorator;
33 import org.simantics.browsing.ui.content.ImageDecorator;
34 import org.simantics.browsing.ui.content.LabelDecorator;
35 import org.simantics.browsing.ui.content.Labeler.Modifier;
36 import org.simantics.browsing.ui.model.InvalidContribution;
37 import org.simantics.browsing.ui.model.actions.ActionBrowseContext;
38 import org.simantics.browsing.ui.model.check.CheckedStateContribution;
39 import org.simantics.browsing.ui.model.children.ChildContribution;
40 import org.simantics.browsing.ui.model.imagedecorators.ImageDecorationContribution;
41 import org.simantics.browsing.ui.model.images.ImageContribution;
42 import org.simantics.browsing.ui.model.labeldecorators.LabelDecorationContribution;
43 import org.simantics.browsing.ui.model.labels.LabelContribution;
44 import org.simantics.browsing.ui.model.modifiers.ModifierContribution;
45 import org.simantics.browsing.ui.model.modifiers.NoModifierRule;
46 import org.simantics.browsing.ui.model.nodetypes.EntityNodeType;
47 import org.simantics.browsing.ui.model.nodetypes.NodeType;
48 import org.simantics.browsing.ui.model.nodetypes.NodeTypeMultiMap;
49 import org.simantics.browsing.ui.model.nodetypes.OrderedNodeTypeMultiMap;
50 import org.simantics.browsing.ui.model.nodetypes.SpecialNodeType;
51 import org.simantics.browsing.ui.model.sorters.AlphanumericSorter;
52 import org.simantics.browsing.ui.model.sorters.Sorter;
53 import org.simantics.browsing.ui.model.sorters.SorterContribution;
54 import org.simantics.browsing.ui.model.tooltips.TooltipContribution;
55 import org.simantics.browsing.ui.model.visuals.FlatNodeContribution;
56 import org.simantics.browsing.ui.model.visuals.VisualsContribution;
57 import org.simantics.db.ReadGraph;
58 import org.simantics.db.RequestProcessor;
59 import org.simantics.db.Resource;
60 import org.simantics.db.common.procedure.adapter.TransientCacheAsyncListener;
61 import org.simantics.db.common.request.ResourceRead;
62 import org.simantics.db.common.request.UnaryRead;
63 import org.simantics.db.common.utils.NameUtils;
64 import org.simantics.db.exception.DatabaseException;
65 import org.simantics.db.exception.ResourceNotFoundException;
66 import org.simantics.db.layer0.variable.Variable;
67 import org.simantics.scl.reflection.OntologyVersions;
68 import org.simantics.viewpoint.ontology.ViewpointResource;
69 import org.slf4j.Logger;
70 import org.slf4j.LoggerFactory;
73 * BrowseContext holds all contributions related to given set of browse contexts.
75 * @author Hannu Niemistö
77 public class BrowseContext {
79 private static final Logger LOGGER = LoggerFactory.getLogger(BrowseContext.class);
80 public static final boolean DEBUG = false;
82 NodeTypeMultiMap<ChildContribution> childContributions = new NodeTypeMultiMap<>();
83 NodeTypeMultiMap<ChildContribution> parentContributions = new NodeTypeMultiMap<>();
84 OrderedNodeTypeMultiMap<LabelContribution> labelContributions = new OrderedNodeTypeMultiMap<>();
85 OrderedNodeTypeMultiMap<ImageContribution> imageContributions = new OrderedNodeTypeMultiMap<>();
86 OrderedNodeTypeMultiMap<CheckedStateContribution> checkedStateContributions = new OrderedNodeTypeMultiMap<>();
87 OrderedNodeTypeMultiMap<LabelDecorationContribution> labelDecorationContributions = new OrderedNodeTypeMultiMap<>();
88 OrderedNodeTypeMultiMap<ImageDecorationContribution> imageDecorationContributions = new OrderedNodeTypeMultiMap<>();
89 OrderedNodeTypeMultiMap<ModifierContribution> modifierContributions = new OrderedNodeTypeMultiMap<>();
90 OrderedNodeTypeMultiMap<SorterContribution> sorterContributions = new OrderedNodeTypeMultiMap<>();
91 OrderedNodeTypeMultiMap<FlatNodeContribution> flatNodeContributions = new OrderedNodeTypeMultiMap<>();
92 OrderedNodeTypeMultiMap<TooltipContribution> tooltipContributions = new OrderedNodeTypeMultiMap<>();
94 private final String[] uris;
96 private BrowseContext(String... uris) {
98 throw new NullPointerException("null URIs");
102 public String[] getURIs() {
106 public static BrowseContext get(ReadGraph graph,NodeContext context,BrowseContext defaultContext, boolean useNodeBrowseContexts) throws DatabaseException {
107 if(!useNodeBrowseContexts) return defaultContext;
108 BrowseContext mbc = graph.syncRequest(new ResolveBrowseContext(context));
109 if(mbc != null) return mbc;
110 BrowseContext parentContext = (BrowseContext)context.getConstant(BuiltinKeys.BROWSE_CONTEXT);
111 if(parentContext != null) return parentContext;
112 return defaultContext;
115 private static BrowseContext loadCachedVisuals(ReadGraph g, Resource visualsContributionResource) throws DatabaseException, InvalidContribution {
117 return g.syncRequest(new ResourceRead<BrowseContext>(visualsContributionResource) {
119 public BrowseContext perform(ReadGraph graph) throws DatabaseException {
121 BrowseContext bc = new BrowseContext();
122 VisualsContribution.load(g, visualsContributionResource,
123 bc.labelContributions,
124 bc.imageContributions,
125 bc.checkedStateContributions,
126 bc.labelDecorationContributions,
127 bc.imageDecorationContributions,
128 bc.modifierContributions,
129 bc.sorterContributions,
130 bc.flatNodeContributions,
131 bc.tooltipContributions
134 } catch (InvalidContribution e) {
135 throw new DatabaseException(e);
138 }, TransientCacheAsyncListener.instance());
139 } catch (DatabaseException e) {
140 Throwable c = e.getCause();
141 if (c instanceof InvalidContribution)
142 throw (InvalidContribution) c;
148 * Creates a new BrowseContext for the given Collection of {@link Resource}s.
151 * @param browseContextResources
152 * @return new BrowseContext
153 * @throws DatabaseException
154 * @throws InvalidContribution
156 public static BrowseContext create(ReadGraph g, Collection<Resource> browseContextResources) throws DatabaseException, InvalidContribution {
157 ViewpointResource vr = ViewpointResource.getInstance(g);
158 BrowseContext browseContext = new BrowseContext( BrowseContexts.toSortedURIs(g, browseContextResources) );
159 for(Resource browseContextResource : findSubcontexts(g, browseContextResources)) {
161 for(Resource childContributionResource :
162 g.getObjects(browseContextResource, vr.BrowseContext_HasChildContribution)) {
164 ChildContribution contribution = ChildContribution.createCached(g, childContributionResource);
165 browseContext.childContributions.put(contribution.getParentNodeType(), contribution);
166 browseContext.parentContributions.put(contribution.getChildNodeType(), contribution);
167 } catch (DatabaseException e) {
168 LOGGER.error("Failed to load child contribution " + NameUtils.getSafeName(g, childContributionResource), e);
172 for(Resource visualsContributionResource :
173 g.getObjects(browseContextResource, vr.BrowseContext_HasVisualsContribution)) {
175 BrowseContext visuals = loadCachedVisuals(g, visualsContributionResource);
176 visuals.labelContributions.appendTo(browseContext.labelContributions);
177 visuals.imageContributions.appendTo(browseContext.imageContributions);
178 visuals.checkedStateContributions.appendTo(browseContext.checkedStateContributions);
179 visuals.labelDecorationContributions.appendTo(browseContext.labelDecorationContributions);
180 visuals.imageDecorationContributions.appendTo(browseContext.imageDecorationContributions);
181 visuals.modifierContributions.appendTo(browseContext.modifierContributions);
182 visuals.sorterContributions.appendTo(browseContext.sorterContributions);
183 visuals.flatNodeContributions.appendTo(browseContext.flatNodeContributions);
184 visuals.tooltipContributions.appendTo(browseContext.tooltipContributions);
185 } catch (DatabaseException e) {
186 LOGGER.error("Failed to load visuals contribution " + NameUtils.getSafeName(g, visualsContributionResource), e);
190 //browseContext.visualize();
191 return browseContext;
194 public static Set<String> getBrowseContextClosure(RequestProcessor processor, final Set<String> browseContexts) throws DatabaseException {
195 return processor.syncRequest(new UnaryRead<Set<String>, Set<String>>(browseContexts) {
197 public Set<String> perform(ReadGraph graph) throws DatabaseException {
198 Collection<Resource> browseContextResources = new ArrayList<>(parameter.size());
199 for (String browseContext : parameter) {
201 browseContextResources.add(graph.getResource(browseContext));
202 } catch (ResourceNotFoundException e) {
203 LOGGER.error("Didn't find " + browseContext + " while loading model browser.", e);
206 Collection<Resource> allBrowseContextResources = BrowseContext.findSubcontexts(graph, browseContextResources);
207 Set<String> result = new HashSet<>();
208 for (Resource r : allBrowseContextResources)
209 result.add(graph.getURI(r));
212 }, TransientCacheAsyncListener.instance());
215 public static Collection<Resource> findSubcontexts(ReadGraph g, Collection<Resource> browseContexts)
216 throws DatabaseException {
217 return g.syncRequest(new UnaryRead<Collection<Resource>, Collection<Resource>>(browseContexts) {
219 public Collection<Resource> perform(ReadGraph graph) throws DatabaseException {
220 return findSubcontexts0(graph, parameter);
222 }, TransientCacheAsyncListener.instance());
225 private static Collection<Resource> findSubcontexts0(ReadGraph g,
226 Collection<Resource> browseContexts) throws DatabaseException {
227 ViewpointResource vr = ViewpointResource.getInstance(g);
228 HashSet<Resource> result = new HashSet<>(browseContexts);
229 ArrayList<Resource> stack = new ArrayList<>(browseContexts);
230 while(!stack.isEmpty()) {
231 Resource cur = stack.remove(stack.size()-1);
232 for(Resource sc : g.getObjects(cur, vr.BrowseContext_Includes))
240 * Finds the possible children of the given {@link NodeContext} parameter.
244 * @return Collection of children or an empty collection in case node has no children
245 * @throws DatabaseException
247 public Collection<NodeContext> getChildren(ReadGraph graph, NodeContext parent) throws DatabaseException {
248 if(isFlattened(graph, parent))
249 return Collections.emptyList();
251 return getChildrenImpl(graph, parent);
254 private Collection<NodeContext> getChildrenImpl(ReadGraph graph, NodeContext parent) throws DatabaseException {
255 NodeType nodeType = getNodeType(graph, parent);
257 return Collections.emptyList();
258 ArrayList<NodeContext> result = new ArrayList<NodeContext>();
259 Collection<ChildContribution> contributions = childContributions.get(graph, nodeType);
260 if(contributions.size() > 1) {
261 Map<String,ChildContribution> contribs = new HashMap<>();
262 for(ChildContribution contribution : contributions) {
263 String identifier = contribution.getIdentifier();
264 ChildContribution current = contribs.get(identifier);
265 if(current != null && current.getPriority() > contribution.getPriority()) continue;
266 contribs.put(identifier, contribution);
268 contributions = contribs.values();
270 for(ChildContribution contribution : contributions) {
271 Collection<NodeContext> children = contribution.getChildren(graph, parent);
272 result.addAll(children);
274 LOGGER.info("contribution: " + contribution.getIdentifier());
275 for(NodeContext ctx : children)
276 LOGGER.info("-" + ctx);
280 // Sorting the result
281 if(!result.isEmpty()) {
282 for(SorterContribution contribution : sorterContributions.get(graph, nodeType)) {
283 Sorter sorter = contribution.getSorter(graph, parent);
285 sorter.sort(graph, this, result);
289 AlphanumericSorter.INSTANCE.sort(graph, this, result);
292 result = flatten(graph, result);
293 //result = augment(graph, result);
298 private ArrayList<NodeContext> flatten(ReadGraph graph, ArrayList<NodeContext> result) throws DatabaseException {
299 ArrayList<NodeContext> flattened = new ArrayList<NodeContext>();
300 for(NodeContext node : result)
301 if(isFlattened(graph, node)) {
303 flattened.addAll(getChildrenImpl(graph, node));
310 public static ArrayList<NodeContext> augment(ReadGraph graph, BrowseContext bc, Collection<NodeContext> contexts, boolean resolveABC) throws DatabaseException {
311 ArrayList<NodeContext> result = new ArrayList<NodeContext>();
312 for(NodeContext context : contexts) {
313 ActionBrowseContext abc = null;
315 abc = graph.syncRequest(new ResolveActionBrowseContext(context));
316 if(abc == null) abc = (ActionBrowseContext)context.getConstant(BuiltinKeys.ACTION_BROWSE_CONTEXT);
318 result.add(NodeContextBuilder.buildWithData(NodeType.KEY_SEQUENCE_EXT,
320 context.getConstant(BuiltinKeys.INPUT),
321 context.getConstant(NodeType.TYPE),
322 context.getConstant(BuiltinKeys.UI_CONTEXT),
328 private boolean isFlattened(ReadGraph graph, NodeContext node) throws DatabaseException {
329 NodeType nodeType = getNodeType(graph, node);
330 return nodeType != null && !flatNodeContributions.get(graph, nodeType).isEmpty();
334 * Finds the possible parents of the given {@link NodeContext} parameter.
338 * @return Collection of parents or an empty Collection in case node has no parents.
339 * @throws DatabaseException
341 public Collection<NodeContext> getParents(ReadGraph graph, NodeContext child) throws DatabaseException {
342 NodeType nodeType = getNodeType(graph, child);
344 return Collections.emptyList();
345 ArrayList<NodeContext> result = new ArrayList<NodeContext>();
346 for(ChildContribution contribution : parentContributions.get(graph, nodeType)) {
347 result.addAll(contribution.getParents(graph, child));
352 public boolean hasChildren(ReadGraph graph, NodeContext parent) throws DatabaseException {
353 NodeType nodeType = getNodeType(graph, parent);
356 for(ChildContribution contribution : childContributions.get(graph, nodeType))
357 if(contribution.hasChildren(graph, parent))
362 private static NodeType getNodeType(ReadGraph graph, NodeContext parent) throws DatabaseException {
363 NodeType nodeType = parent.getConstant(NodeType.TYPE);
364 if(nodeType == null) {
365 // TODO remove this code when root of model browser is fixed
366 Object input = parent.getConstant(BuiltinKeys.INPUT);
367 if(input instanceof Resource) {
368 nodeType = EntityNodeType.getNodeTypeFor(graph, (Resource)input);
369 } else if (input instanceof Variable) {
370 String uri = OntologyVersions.getInstance().currentVersion("http://www.simantics.org/Modeling-0.0/ModelingBrowseContext/Variable");
371 return new SpecialNodeType(graph.getResource(uri), Variable.class);
378 * Finds labels for the given {@link NodeContext} parameter.
382 * @return Map containing all the labels assigned by key indicating the column e.g. "single"
383 * @throws DatabaseException
385 public Map<String, String> getLabel(ReadGraph graph, NodeContext parent) throws DatabaseException {
386 NodeType nodeType = getNodeType(graph, parent);
388 return Collections.singletonMap(ColumnKeys.SINGLE, "ERROR (no node type)");
389 List<LabelContribution> contributions = labelContributions.get(graph, nodeType);
390 for(LabelContribution contribution : contributions) {
391 Map<String, String> label = contribution.getLabel(graph, parent);
395 return Collections.singletonMap(ColumnKeys.SINGLE, "(no label rule)");
399 * Finds {@link ImageDescriptor}s for the given {@link NodeContext} parameter.
403 * @return Map containing all the {@ImageDescriptor}s or empty
404 * @throws DatabaseException
406 public Map<String, ImageDescriptor> getImage(ReadGraph graph, NodeContext parent) throws DatabaseException {
407 NodeType nodeType = getNodeType(graph, parent);
409 return Collections.emptyMap();
410 for(ImageContribution contribution : imageContributions.get(graph, nodeType)) {
411 Map<String, ImageDescriptor> image = contribution.getImage(graph, parent);
415 return Collections.emptyMap();
419 * Finds if the given {@link NodeContext} is checked or not.
424 * @throws DatabaseException
426 public CheckedState getCheckedState(ReadGraph graph, NodeContext parent) throws DatabaseException {
427 NodeType nodeType = getNodeType(graph, parent);
429 return CheckedState.NOT_CHECKED;
430 for(CheckedStateContribution contribution : checkedStateContributions.get(graph, nodeType)) {
431 CheckedState state = contribution.getCheckedState(graph, parent);
435 return CheckedState.NOT_CHECKED;
439 * Finds {@link LabelDecorator} for the given {@link NodeContext} parameter.
444 * @throws DatabaseException
446 public LabelDecorator getLabelDecorator(ReadGraph graph, NodeContext context) throws DatabaseException {
447 NodeType nodeType = getNodeType(graph, context);
449 return CompositeLabelDecorator.ID;
450 ArrayList<LabelDecorator> decorators = new ArrayList<LabelDecorator>();
451 for(LabelDecorationContribution contribution : labelDecorationContributions.get(graph, nodeType)) {
452 LabelDecorator decorator = contribution.getLabelDecorator(graph, context);
453 if(decorator != null)
454 decorators.add(decorator);
456 return CompositeLabelDecorator.create(decorators);
460 * Finds {@link ImageDecorator} for the given {@link NodeContext} parameter.
465 * @throws DatabaseException
467 public ImageDecorator getImageDecorator(ReadGraph graph, NodeContext context) throws DatabaseException {
468 NodeType nodeType = getNodeType(graph, context);
470 return CompositeImageDecorator.ID;
471 ArrayList<ImageDecorator> decorators = new ArrayList<ImageDecorator>();
472 for(ImageDecorationContribution contribution : imageDecorationContributions.get(graph, nodeType)) {
473 ImageDecorator decorator = contribution.getImageDecorator(graph, context);
474 if(decorator != null)
475 decorators.add(decorator);
477 return CompositeImageDecorator.create(decorators);
481 * Finds {@link Modifier} for the given {@link NodeContext} parameter.
487 * @throws DatabaseException
489 public Modifier getModifier(ReadGraph graph, NodeContext context,
490 String columnKey) throws DatabaseException {
491 NodeType nodeType = getNodeType(graph, context);
493 for(ModifierContribution contribution : modifierContributions.get(graph, nodeType)) {
494 Modifier modifier = contribution.getModifier(graph, context, columnKey);
495 if(modifier == NoModifierRule.NO_MODIFIER)
503 public TooltipContribution shouldCreateToolTip(ReadGraph graph, Event event, NodeContext context) throws DatabaseException {
504 NodeType nodeType = getNodeType(graph, context);
506 for(TooltipContribution contribution : tooltipContributions.get(graph, nodeType)) {
507 if (contribution.shouldCreateToolTip(graph, context))
513 public Object getTooltip(TooltipContribution contribution, Object event, Object parent, NodeContext context) throws DatabaseException {
514 Object tooltip = contribution.getTooltip(event, parent, context);
521 public int hashCode() {
522 return Arrays.hashCode(uris);
526 public boolean equals(Object obj) {
531 if (getClass() != obj.getClass())
533 BrowseContext other = (BrowseContext) obj;
534 return Arrays.equals(uris, other.uris);
538 public String toString() {
539 return getClass().getSimpleName() + Arrays.toString(uris);