1 package org.simantics.interop.diagram;
3 import java.util.ArrayList;
4 import java.util.Collection;
5 import java.util.HashMap;
6 import java.util.HashSet;
10 import java.util.Stack;
12 import org.simantics.databoard.Bindings;
13 import org.simantics.db.ReadGraph;
14 import org.simantics.db.RequestProcessor;
15 import org.simantics.db.Resource;
16 import org.simantics.db.WriteGraph;
17 import org.simantics.db.common.procedure.adapter.TransientCacheListener;
18 import org.simantics.db.common.request.ResourceRead;
19 import org.simantics.db.common.utils.OrderedSetUtils;
20 import org.simantics.db.exception.DatabaseException;
21 import org.simantics.db.exception.ManyObjectsForFunctionalRelationException;
22 import org.simantics.db.exception.NoSingleResultException;
23 import org.simantics.db.exception.ServiceException;
24 import org.simantics.db.layer0.adapter.Template;
25 import org.simantics.diagram.stubs.DiagramResource;
26 import org.simantics.diagram.synchronization.graph.DiagramGraphUtil;
27 import org.simantics.g2d.page.DiagramDesc;
28 import org.simantics.layer0.Layer0;
29 import org.simantics.modeling.ModelingResources;
30 import org.simantics.operation.Layer0X;
31 import org.simantics.simulation.ontology.SimulationResource;
32 import org.simantics.structural.stubs.StructuralResource2;
33 import org.simantics.utils.datastructures.Arrays;
34 import org.simantics.utils.page.PageDesc;
38 * @author Marko Luukkainen
40 public abstract class Diagram<T extends Symbol> {
42 public static boolean DO_NO_MIX_SECTION_NAMES = false;
43 public static boolean USE_MERGE_CONNECTS = true;
45 private Resource composite;
46 private Resource diagram;
48 private Map<Resource,Integer> indexMap = new HashMap<Resource, Integer>();
50 private Map<Resource,T> elementToSymbolMap = new HashMap<Resource, T>();
52 private Map<Resource,Diagram<T>> compositeToDiagramMap = new HashMap<Resource, Diagram<T>>();
54 protected Diagram(ReadGraph g, Resource composite, Resource diagram) throws DatabaseException {
55 this.composite = composite;
56 this.diagram = diagram;
59 protected abstract Diagram<T> construct(ReadGraph g, Resource composite, Resource diagram) throws DatabaseException;
60 protected abstract T constructSymbol(ReadGraph g, Resource element, Resource component) throws DatabaseException;
62 public Resource getComposite() {
66 public Resource getDiagram() {
71 public void clearCaches() {
72 for (Diagram<T> d : compositeToDiagramMap.values()) {
73 for (T s : d.elementToSymbolMap.values())
75 d.elementToSymbolMap.clear();
77 compositeToDiagramMap.clear();
81 public String generateName(ReadGraph g, Resource type, String name) throws DatabaseException {
82 Layer0 l0 = Layer0.getInstance(g);
83 Layer0X l0x = Layer0X.getInstance(g);
84 String prefix = g.getPossibleRelatedValue(type, l0x.HasGeneratedNamePrefix);
85 if (prefix == null || prefix.length() == 0)
86 prefix = g.getRelatedValue(type, l0.HasName);
88 Set<String> reserved = null;
90 reserved = getAllModuleNamesForModel(g, composite);
92 String realname = name + "_" + prefix;
94 String toTry = realname + Integer.toString(index);
95 if (!reserved.contains(toTry))
99 realname += Integer.toString(index);
102 Integer index = indexMap.get(type);
106 if (reserved == null)
107 reserved = getAllModuleNamesForModel(g, composite);
110 String toTry = prefix + Integer.toString(index);
111 if (!reserved.contains(toTry))
117 indexMap.put(type, index);
118 return prefix + index;
121 private Set<String> getAllModuleNamesForModel(ReadGraph graph, final Resource res) throws DatabaseException {
123 Resource model = DiagramUtils.getModel(graph, res);
124 Resource configuration = DiagramUtils.getConfiguration(graph, model);
125 Set<String> names = new HashSet<String>();
126 Collection<Resource> composites = getAllComposites(graph, configuration);
127 for (Resource composite : composites) {
128 names.addAll(getAllModuleNamesForComposite(graph, composite));
133 protected abstract Resource getGraphicalCompositeType(ReadGraph graph) throws DatabaseException;
134 protected abstract Resource getFolderType(ReadGraph graph) throws DatabaseException;
136 @SuppressWarnings("unchecked")
137 private Collection<Resource> getAllComposites(RequestProcessor processor, Resource configuration) throws DatabaseException {
138 return (Set<Resource>)processor.syncRequest(new ResourceRead<Object>(configuration) {
140 public Object perform(ReadGraph graph) throws DatabaseException {
141 Layer0 l0 = Layer0.getInstance(graph);
142 Set<Resource> composites = new HashSet<Resource>();
143 Stack<Resource> resources = new Stack<Resource>();
144 Resource graphicalCompositeType = getGraphicalCompositeType(graph);
145 Resource folderType = getFolderType(graph);
146 resources.add(resource);
147 while (!resources.isEmpty()) {
148 Resource r = resources.pop();
149 if (graph.isInstanceOf(r, graphicalCompositeType)) {
151 } else if (folderType != null && graph.isInstanceOf(r, folderType)){
152 resources.addAll(graph.getObjects(r, l0.ConsistsOf));
157 }, TransientCacheListener.instance());
160 @SuppressWarnings("unchecked")
161 private Set<String> getAllModuleNamesForComposite(RequestProcessor processor, Resource composite) throws DatabaseException {
162 return (Set<String>)processor.syncRequest(new ResourceRead<Object>(composite) {
164 public Object perform(ReadGraph graph) throws DatabaseException {
165 Layer0 l0 = Layer0.getInstance(graph);
166 Set<String> names = new HashSet<String>();
167 Collection<Resource> components = graph.getObjects(resource, l0.ConsistsOf);
168 for (Resource component : components) {
169 String n = graph.getPossibleRelatedValue(component, l0.HasName, Bindings.STRING);
175 }, TransientCacheListener.instance());
179 public Diagram<T> createDiagram(WriteGraph g,String name, Resource compositeType, Resource parent) throws DatabaseException {
180 return createDiagram(g, name, null, null,compositeType, parent);
186 * Creates a new Diagram
188 * @param name name of the diagram
189 * @param compositeType composite type of the diagram
190 * @param parent resource where diagram is added
192 * @throws DatabaseException
194 @SuppressWarnings("deprecation")
195 public Diagram<T> createDiagram(WriteGraph g,String name, DiagramDesc desc, Template tpl, Resource compositeType, Resource parent) throws DatabaseException {
196 Layer0 l0 = Layer0.getInstance(g);
197 Layer0X l0x = Layer0X.getInstance(g);
198 ModelingResources m = ModelingResources.getInstance(g);
201 Resource composite = g.newResource();
202 g.claim(composite, l0.InstanceOf, compositeType);
205 Resource diagramType = getDiagramFromComposite(g, compositeType);
207 Resource diagram = OrderedSetUtils.create(g, diagramType);
209 g.claim(composite, m.CompositeToDiagram, diagram);
211 // link to parent and set name
212 g.claimLiteral(composite, l0.HasName, name);
213 g.claim(composite, l0.PartOf, parent);
216 Resource mapping = g.newResource();
217 g.claim(mapping, l0.InstanceOf, m.DiagramToCompositeMapping);
218 g.claim(diagram, l0x.HasTrigger, mapping);
221 Map<String,Object> params = new HashMap<String, Object>();
222 params.put("diagram", diagram);
223 tpl.apply(g, params);
227 // Make diagram part of a dummy container library attached to the parent
228 // composite if it's not already part of something.
229 // This gives the diagram a proper URI without connecting it directly to the composite with ConsistsOf.
230 // This would cause problems because a diagram is a structural composite/component also.
231 g.claimLiteral(diagram, l0.HasName, name, Bindings.STRING);
232 Resource container = g.newResource();
233 g.claim(container, l0.InstanceOf, null, l0.Library);
234 g.addLiteral(container, l0.HasName, l0.NameOf, l0.String, "__CONTAINER__", Bindings.STRING);
235 g.claim(container, l0.ConsistsOf, diagram);
236 g.claim(composite, l0.ConsistsOf, container);
239 DiagramGraphUtil.setDiagramDesc(g, diagram, desc);
241 Diagram<T> diag = construct(g, composite, diagram);
242 diag.compositeToDiagramMap = compositeToDiagramMap;
243 compositeToDiagramMap.put(composite, diag);
247 public Diagram<T> fromExisting(ReadGraph g, Resource diagram) throws DatabaseException {
248 ModelingResources m = ModelingResources.getInstance(g);
249 StructuralResource2 s = StructuralResource2.getInstance(g);
250 Resource composite = null;
251 if (g.isInstanceOf(diagram, s.Composite)) {
253 diagram = g.getPossibleObject(composite, m.CompositeToDiagram);
257 composite = g.getPossibleObject(diagram, m.DiagramToComposite);
258 if (composite == null)
262 Diagram<T> diag = compositeToDiagramMap.get(composite);
266 diag = construct(g,composite, diagram);
267 diag.compositeToDiagramMap = compositeToDiagramMap;
268 compositeToDiagramMap.put(composite, diag);
277 * Returns diagram type from composite type
279 * @param compositeType
280 * @return diagram type
281 * @throws NoSingleResultException
282 * @throws ManyObjectsForFunctionalRelationException
283 * @throws ServiceException
285 private static Resource getDiagramFromComposite(ReadGraph g, Resource compositeType) throws DatabaseException {
286 ModelingResources m = ModelingResources.getInstance(g);
287 Collection<Resource> diagramTemplates = g.getAssertedObjects(compositeType, m.HasModelingTemplate);
288 for (Resource diagramTemplate : diagramTemplates) {
289 Resource diagramType = g.getPossibleObject(diagramTemplate, m.HasDiagramType);
290 if (diagramType != null)
294 throw new RuntimeException("Cannot find diagramType for composite " + compositeType);
301 * Adds new symbol to a diagram
305 * @throws DatabaseException
307 public T addSymbol(WriteGraph g, Resource symbolType) throws DatabaseException {
308 return addSymbol(g, symbolType, 0, 0);
312 * Adds new symbol to a diagram
317 * @throws DatabaseException
319 public T addSymbol(WriteGraph g, Resource symbolType, String name) throws DatabaseException {
320 return addSymbol(g, symbolType, name, 0, 0);
324 * Adds new symbol to a diagram
326 * @param diagramConf Diagram configuration
327 * @param symbolType type of the new symbol
330 * @return configuration of the new Symbol
331 * @throws DatabaseException
333 public T addSymbol(WriteGraph g, Resource symbolType, double x, double y) throws DatabaseException {
334 return addSymbol(g, symbolType,null,x,y);
338 * Adds new symbol to a diagram
345 * @throws DatabaseException
347 public T addSymbol(WriteGraph g, Resource elementType, String name, double x, double y) throws DatabaseException {
348 if (elementType == null)
349 throw new NullPointerException("Element type is null");
350 Layer0 l0 = Layer0.getInstance(g);
352 DiagramResource d = DiagramResource.getInstance(g);
353 ModelingResources m = ModelingResources.getInstance(g);
355 Resource componentType = null;
356 if (g.isInheritedFrom(elementType, d.DefinedElement))
357 componentType = Symbol.getComponentTypeFromSymbolType(g, elementType);
359 componentType = elementType;
360 elementType = Symbol.getSymbolTypeFromComponentType(g,componentType);
364 Resource element = g.newResource();
365 g.claim(element, l0.InstanceOf, elementType);
367 Resource module = g.newResource();
368 g.claim(module, l0.InstanceOf, componentType);
370 g.claim(module, m.ComponentToElement, element);
371 g.claim(module, m.Mapped, module);
373 String id = generateName(g, componentType, name);
374 g.claimLiteral(module, l0.HasName, id);
375 g.claimLiteral(element, l0.HasName, id);
377 OrderedSetUtils.add(g, this.diagram, element);
378 g.claim(this.diagram, l0.ConsistsOf, element);
379 g.claim(this.composite, l0.ConsistsOf, module);
381 T s = constructSymbol(g, element, module);
382 s.setSymbolTranslation(g, x, y);
384 elementToSymbolMap.put(element, s);
389 * Returns a Symbol for given resource.
391 * @param element Element or Component of the Symbol.
393 * @throws ManyObjectsForFunctionalRelationException
394 * @throws ServiceException
395 * @throws NoSingleResultException
397 public T getSymbol(ReadGraph g, Resource element) throws DatabaseException {
398 ModelingResources mr = ModelingResources.getInstance(g);
399 DiagramResource dr = DiagramResource.getInstance(g);
400 if (g.isInstanceOf(element, dr.Element))
401 return getSymbol(g, element, g.getPossibleObject(element, mr.ElementToComponent));
403 return getSymbol(g, g.getSingleObject(element, mr.ComponentToElement), element);
407 * Returns a Symbol for given resource.
409 * @param element Element Resource of the Symbol.
410 * @param component Component Resource of the symbol.
413 public T getSymbol(ReadGraph g, Resource element, Resource component) throws DatabaseException {
414 T s = elementToSymbolMap.get(element);
416 // TODO : check that symbol actually belongs to this diagram
417 s = constructSymbol(g, element, component);
418 elementToSymbolMap.put(element, s);
420 if (s.component == null)
421 s.component = component;
422 else if (!s.component.equals(component)) {
424 return constructSymbol(g, element, component);
431 * Adds new element to a diagram
433 * @param symbolType type of the element.
437 * @throws DatabaseException
439 public T addElement(WriteGraph g, Resource symbolType, double x, double y) throws DatabaseException {
440 Layer0 l0 = Layer0.getInstance(g);
442 Resource element = g.newResource();
443 g.claim(element, l0.InstanceOf, symbolType);
445 DiagramUtils.addElement(g, this, element);
447 T s = constructSymbol(g,element,null);
449 s.setSymbolTranslation(g, x, y);
451 elementToSymbolMap.put(element, s);
455 public T getSingleSymbol(ReadGraph g, String symbolName) throws DatabaseException {
456 List<T> matching = getSymbol(g, symbolName);
457 if (matching.size() == 1)
458 return matching.get(0);
459 if (matching.size() == 0)
461 throw new NoSingleResultException("There are multiple symbols with name '" + symbolName);
464 public T getPossibleSymbol(ReadGraph g, String symbolName) throws DatabaseException {
465 List<T> matching = getSymbol(g, symbolName);
466 if (matching.size() == 1)
467 return matching.get(0);
472 * Returns all symbols on the diagram
475 * @throws DatabaseException
477 public List<T> getSymbols(ReadGraph g) throws DatabaseException {
478 ModelingResources m = ModelingResources.getInstance(g);
479 List<T> matching = new ArrayList<T>();
480 for (Resource element : OrderedSetUtils.toList(g, diagram)) {
481 Resource component = g.getPossibleObject(element, m.ElementToComponent);
482 matching.add(getSymbol(g,element, component));
488 * Returns symbols that have matching name.
490 * Prioritizes L0.HasLabel over L0.HasName (If symbol has label, it is used for comparison).
492 * ';' character acts as a splitter, allowing input parameter and a label contain multiple identifiers.
499 * @throws DatabaseException
501 public List<T> getSymbol(ReadGraph g, String symbolName) throws DatabaseException {
502 ModelingResources m = ModelingResources.getInstance(g);
503 Layer0 b = Layer0.getInstance(g);
504 List<T> matching = new ArrayList<T>();
506 String splitId[] = symbolName.split(";");
508 for (Resource element : OrderedSetUtils.toList(g, diagram)) {
509 Resource component = g.getPossibleObject(element, m.ElementToComponent);
510 if (component == null)
511 component = g.getPossibleObject(element, m.HasParentComponent);
512 if (component == null)
514 String label = g.getPossibleRelatedValue(component, b.HasLabel);
515 String name = g.getRelatedValue(component, b.HasName);
517 String splitLabel[] = label.split(";");
519 boolean match = true;
520 for (int i = 0; i < splitId.length; i++) {
521 if(!Arrays.contains(splitLabel, splitId[i])) {
527 matching.add(getSymbol(g,element, component));
529 if (label.equals(symbolName) || name.equals(symbolName)) {
530 matching.add(getSymbol(g,element, component));
536 for (String split : splitId) {
537 if (name.equals(split)) {
538 matching.add(getSymbol(g,element, component));
548 * Returns a single symbol matching given name and type. If matching symbol cannot be found, returns null.
549 * Throws a NoSingleResultException if the re are multiple symbols matching the criteria.
555 * @throws DatabaseException
557 public T getSingleSymbol(ReadGraph g, String symbolName, Resource symbolType) throws DatabaseException {
558 List<T> matching = getSymbol(g, symbolName, symbolType);
559 if (matching.size() == 1)
560 return matching.get(0);
561 if (matching.size() == 0)
563 throw new NoSingleResultException("There are multiple symbols with name '" + symbolName + "' and type " + symbolType);
566 public T getPossibleSymbol(ReadGraph g, String symbolName, Resource symbolType) throws DatabaseException {
567 List<T> matching = getSymbol(g, symbolName, symbolType);
568 if (matching.size() == 1)
569 return matching.get(0);
574 * Returns symbols matching given name and type.
580 * @throws DatabaseException
582 public List<T> getSymbol(ReadGraph g, String symbolName, Resource symbolType) throws DatabaseException {
584 List<T> nameMatching = getSymbol(g, symbolName);
585 List<T> matching = new ArrayList<T>();
586 for (T s : nameMatching) {
587 if ((g.isInstanceOf(s.getElement(), symbolType)||
588 g.isInstanceOf(s.getComponent(), symbolType))) {
595 public Symbol getFlag(ReadGraph g, Resource flagType, String symbolName, Resource symbolType) throws DatabaseException {
596 Layer0 b = Layer0.getInstance(g);
597 DiagramResource dr = DiagramResource.getInstance(g);
598 List<Symbol> matching = new ArrayList<Symbol>();
599 for (Resource flag : OrderedSetUtils.toList(g, diagram)) {
600 if (!g.isInstanceOf(flag, dr.Flag))
602 if (!g.getSingleObject(flag, dr.HasFlagType).equals(flagType))
604 Symbol flagSymbol = getSymbol(g,flag, null);
606 Symbol connectedSymbol = flagSymbol.getDiagramSingleConnected(g, dr.Flag_ConnectionPoint);
607 Resource component = connectedSymbol.getComponent();
608 if (component == null)
610 String label = g.getPossibleRelatedValue(component, b.HasLabel);
611 String name = g.getRelatedValue(component, b.HasName);
613 String splitId[] = symbolName.split(";");
614 if (splitId.length > 1) {
615 String splitLabel[] = label.split(";");
616 boolean match = true;
617 for (int i = 0; i < splitId.length; i++) {
618 if(!Arrays.contains(splitLabel, splitId[i])) {
624 if (g.isInstanceOf(connectedSymbol.getElement(), symbolType)||
625 g.isInstanceOf(component, symbolType)) {
626 matching.add(getSymbol(g,flag, null));
631 if (label.equals(symbolName) || name.equals(symbolName)) {
632 if (g.isInstanceOf(connectedSymbol.getElement(), symbolType)||
633 g.isInstanceOf(component, symbolType)) {
634 matching.add(getSymbol(g,flag, null));
641 if (name.equals(symbolName)) {
642 if (g.isInstanceOf(connectedSymbol.getElement(), symbolType)||
643 g.isInstanceOf(component, symbolType)) {
644 matching.add(getSymbol(g,flag, null));
650 if (matching.size() == 1)
651 return matching.get(0);
652 if (matching.size() == 0)
654 throw new NoSingleResultException("There are multiple flags connecting symbol with name '" + symbolName + "' and type " + symbolType);
658 public PageDesc getPageDesc(ReadGraph g) throws DatabaseException {
659 PageDesc pDesc = DiagramGraphUtil.getPageDesc(g, diagram, PageDesc.DEFAULT);
664 public boolean equals(Object o) {
667 if (this.getClass() != o.getClass())
669 Diagram other = (Diagram)o;
670 return diagram.equals(other.diagram);
674 public int hashCode() {
675 return diagram.hashCode();
678 void merge(Symbol to, Symbol from) {
679 Resource element = from.getElement();
680 for (Symbol s : elementToSymbolMap.values()) {
681 if (s.element.equals(element)) {
682 s.element = to.element;
683 s.component = to.component;
686 from.element = to.element;
687 from.component = to.component;
691 * Returns a model containing the diagram.
694 * @throws DatabaseException
696 public Resource getModel(ReadGraph g) throws DatabaseException {
697 Layer0 l0 = Layer0.getInstance(g);
698 SimulationResource sim = SimulationResource.getInstance(g);
699 Resource r = composite;
701 Resource parent = g.getSingleObject(r, l0.PartOf);
702 if (g.isInstanceOf(parent, sim.Model))