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 if (configuration == null)
128 Collection<Resource> composites = getAllComposites(graph, configuration);
129 for (Resource composite : composites) {
130 names.addAll(getAllModuleNamesForComposite(graph, composite));
135 protected abstract Resource getGraphicalCompositeType(ReadGraph graph) throws DatabaseException;
136 protected abstract Resource getFolderType(ReadGraph graph) throws DatabaseException;
138 @SuppressWarnings("unchecked")
139 private Collection<Resource> getAllComposites(RequestProcessor processor, Resource configuration) throws DatabaseException {
140 return (Set<Resource>)processor.syncRequest(new ResourceRead<Object>(configuration) {
142 public Object perform(ReadGraph graph) throws DatabaseException {
143 Layer0 l0 = Layer0.getInstance(graph);
144 Set<Resource> composites = new HashSet<Resource>();
145 Stack<Resource> resources = new Stack<Resource>();
146 Resource graphicalCompositeType = getGraphicalCompositeType(graph);
147 Resource folderType = getFolderType(graph);
148 resources.add(resource);
149 while (!resources.isEmpty()) {
150 Resource r = resources.pop();
151 if (graph.isInstanceOf(r, graphicalCompositeType)) {
153 } else if (folderType != null && graph.isInstanceOf(r, folderType)){
154 resources.addAll(graph.getObjects(r, l0.ConsistsOf));
159 }, TransientCacheListener.instance());
162 @SuppressWarnings("unchecked")
163 private Set<String> getAllModuleNamesForComposite(RequestProcessor processor, Resource composite) throws DatabaseException {
164 return (Set<String>)processor.syncRequest(new ResourceRead<Object>(composite) {
166 public Object perform(ReadGraph graph) throws DatabaseException {
167 Layer0 l0 = Layer0.getInstance(graph);
168 Set<String> names = new HashSet<String>();
169 Collection<Resource> components = graph.getObjects(resource, l0.ConsistsOf);
170 for (Resource component : components) {
171 String n = graph.getPossibleRelatedValue(component, l0.HasName, Bindings.STRING);
177 }, TransientCacheListener.instance());
181 public Diagram<T> createDiagram(WriteGraph g,String name, Resource compositeType, Resource parent) throws DatabaseException {
182 return createDiagram(g, name, null, null,compositeType, parent);
188 * Creates a new Diagram
190 * @param name name of the diagram
191 * @param compositeType composite type of the diagram
192 * @param parent resource where diagram is added
194 * @throws DatabaseException
196 @SuppressWarnings("deprecation")
197 public Diagram<T> createDiagram(WriteGraph g,String name, DiagramDesc desc, Template tpl, Resource compositeType, Resource parent) throws DatabaseException {
198 Layer0 l0 = Layer0.getInstance(g);
199 Layer0X l0x = Layer0X.getInstance(g);
200 ModelingResources m = ModelingResources.getInstance(g);
201 DiagramResource dia = DiagramResource.getInstance(g);
204 Resource composite = g.newResource();
205 g.claim(composite, l0.InstanceOf, compositeType);
208 Resource diagramType = getDiagramFromComposite(g, compositeType);
210 Resource diagram = OrderedSetUtils.create(g, diagramType);
212 g.claim(composite, m.CompositeToDiagram, diagram);
214 // link to parent and set name
215 g.claimLiteral(composite, l0.HasName, name);
216 g.claim(composite, l0.PartOf, parent);
219 Resource mapping = g.newResource();
220 g.claim(mapping, l0.InstanceOf, m.DiagramToCompositeMapping);
221 g.claim(diagram, l0x.HasTrigger, mapping);
224 Map<String,Object> params = new HashMap<String, Object>();
225 params.put("diagram", diagram);
226 tpl.apply(g, params);
230 // Make diagram part of a dummy container library attached to the parent
231 // composite if it's not already part of something.
232 // This gives the diagram a proper URI without connecting it directly to the composite with ConsistsOf.
233 // This would cause problems because a diagram is a structural composite/component also.
234 g.claimLiteral(diagram, l0.HasName, name, Bindings.STRING);
235 Resource container = g.newResource();
236 g.claim(container, l0.InstanceOf, null, dia.DiagramContainer);
237 g.addLiteral(container, l0.HasName, l0.NameOf, l0.String, "__CONTAINER__", Bindings.STRING);
238 g.claim(container, l0.ConsistsOf, diagram);
239 g.claim(composite, l0.ConsistsOf, container);
242 DiagramGraphUtil.setDiagramDesc(g, diagram, desc);
244 Diagram<T> diag = construct(g, composite, diagram);
245 diag.compositeToDiagramMap = compositeToDiagramMap;
246 compositeToDiagramMap.put(composite, diag);
250 public Diagram<T> fromExisting(ReadGraph g, Resource diagram) throws DatabaseException {
251 ModelingResources m = ModelingResources.getInstance(g);
252 StructuralResource2 s = StructuralResource2.getInstance(g);
253 DiagramResource d = DiagramResource.getInstance(g);
254 Resource composite = null;
255 if (g.isInstanceOf(diagram, s.Composite)) {
257 diagram = g.getPossibleObject(composite, m.CompositeToDiagram);
260 } else if (g.isInheritedFrom(diagram, d.DefinedElement)) {
261 // TODO : Defined Element behaves differently to regular configuration diagrams; should we have separate implementation?
263 diagram = g.getPossibleObject(composite, s.IsDefinedBy);
267 composite = g.getPossibleObject(diagram, m.DiagramToComposite);
268 if (composite == null)
272 Diagram<T> diag = compositeToDiagramMap.get(composite);
276 diag = construct(g,composite, diagram);
277 diag.compositeToDiagramMap = compositeToDiagramMap;
278 compositeToDiagramMap.put(composite, diag);
289 * Returns diagram type from composite type
291 * @param compositeType
292 * @return diagram type
293 * @throws NoSingleResultException
294 * @throws ManyObjectsForFunctionalRelationException
295 * @throws ServiceException
297 public Resource getDiagramFromComposite(ReadGraph g, Resource compositeType) throws DatabaseException {
298 ModelingResources m = ModelingResources.getInstance(g);
299 Collection<Resource> diagramTemplates = g.getAssertedObjects(compositeType, m.HasModelingTemplate);
300 for (Resource diagramTemplate : diagramTemplates) {
301 Resource diagramType = g.getPossibleObject(diagramTemplate, m.HasDiagramType);
302 if (diagramType != null)
306 throw new RuntimeException("Cannot find diagramType for composite " + compositeType);
313 * Adds new symbol to a diagram
317 * @throws DatabaseException
319 public T addSymbol(WriteGraph g, Resource symbolType) throws DatabaseException {
320 return addSymbol(g, symbolType, 0, 0);
324 * Adds new symbol to a diagram
329 * @throws DatabaseException
331 public T addSymbol(WriteGraph g, Resource symbolType, String name) throws DatabaseException {
332 return addSymbol(g, symbolType, name, 0, 0);
336 * Adds new symbol to a diagram
338 * @param diagramConf Diagram configuration
339 * @param symbolType type of the new symbol
342 * @return configuration of the new Symbol
343 * @throws DatabaseException
345 public T addSymbol(WriteGraph g, Resource symbolType, double x, double y) throws DatabaseException {
346 return addSymbol(g, symbolType,null,x,y);
350 * Adds new symbol to a diagram
357 * @throws DatabaseException
359 public T addSymbol(WriteGraph g, Resource elementType, String name, double x, double y) throws DatabaseException {
360 if (elementType == null)
361 throw new NullPointerException("Element type is null");
362 Layer0 l0 = Layer0.getInstance(g);
364 DiagramResource d = DiagramResource.getInstance(g);
365 ModelingResources m = ModelingResources.getInstance(g);
367 Resource componentType = null;
368 if (g.isInheritedFrom(elementType, d.DefinedElement))
369 componentType = Symbol.getComponentTypeFromSymbolType(g, elementType);
371 componentType = elementType;
372 elementType = Symbol.getSymbolTypeFromComponentType(g,componentType);
376 Resource element = g.newResource();
377 g.claim(element, l0.InstanceOf, elementType);
379 Resource module = g.newResource();
380 g.claim(module, l0.InstanceOf, componentType);
382 g.claim(module, m.ComponentToElement, element);
383 g.claim(module, m.Mapped, module);
385 String id = generateName(g, componentType, name);
386 g.claimLiteral(module, l0.HasName, id);
387 g.claimLiteral(element, l0.HasName, id);
389 OrderedSetUtils.add(g, this.diagram, element);
390 g.claim(this.diagram, l0.ConsistsOf, element);
391 g.claim(this.composite, l0.ConsistsOf, module);
393 T s = constructSymbol(g, element, module);
394 s.setSymbolTranslation(g, x, y);
396 elementToSymbolMap.put(element, s);
401 * Returns a Symbol for given resource.
403 * @param element Element or Component of the Symbol.
405 * @throws ManyObjectsForFunctionalRelationException
406 * @throws ServiceException
407 * @throws NoSingleResultException
409 public T getSymbol(ReadGraph g, Resource element) throws DatabaseException {
410 ModelingResources mr = ModelingResources.getInstance(g);
411 DiagramResource dr = DiagramResource.getInstance(g);
412 StructuralResource2 s = StructuralResource2.getInstance(g);
413 if (g.isInstanceOf(element, dr.Element))
414 return getSymbol(g, element, g.getPossibleObject(element, mr.ElementToComponent));
415 else if (g.isSubrelationOf(element, s.IsConnectedTo)) {
416 // TODO : Defined Element
417 return getSymbol(g, g.getSingleObject(element, dr.HasConnectionPoint_Inverse),element);
420 return getSymbol(g, g.getSingleObject(element, mr.ComponentToElement), element);
424 * Returns a Symbol for given resource.
426 * @param element Element Resource of the Symbol.
427 * @param component Component Resource of the symbol.
430 public T getSymbol(ReadGraph g, Resource element, Resource component) throws DatabaseException {
431 T s = elementToSymbolMap.get(element);
433 // TODO : check that symbol actually belongs to this diagram
434 s = constructSymbol(g, element, component);
435 elementToSymbolMap.put(element, s);
437 if (s.component == null)
438 s.component = component;
439 else if (!s.component.equals(component)) {
441 return constructSymbol(g, element, component);
448 * Adds new element to a diagram
450 * @param symbolType type of the element.
454 * @throws DatabaseException
456 public T addElement(WriteGraph g, Resource symbolType, double x, double y) throws DatabaseException {
457 Layer0 l0 = Layer0.getInstance(g);
459 Resource element = g.newResource();
460 g.claim(element, l0.InstanceOf, symbolType);
462 DiagramUtils.addElement(g, this, element);
464 T s = constructSymbol(g,element,null);
466 s.setSymbolTranslation(g, x, y);
468 elementToSymbolMap.put(element, s);
472 public T getSingleSymbol(ReadGraph g, String symbolName) throws DatabaseException {
473 List<T> matching = getSymbol(g, symbolName);
474 if (matching.size() == 1)
475 return matching.get(0);
476 if (matching.size() == 0)
478 throw new NoSingleResultException("There are multiple symbols with name '" + symbolName);
481 public T getPossibleSymbol(ReadGraph g, String symbolName) throws DatabaseException {
482 List<T> matching = getSymbol(g, symbolName);
483 if (matching.size() == 1)
484 return matching.get(0);
489 * Returns all symbols on the diagram
492 * @throws DatabaseException
494 public List<T> getSymbols(ReadGraph g) throws DatabaseException {
495 ModelingResources m = ModelingResources.getInstance(g);
496 List<T> matching = new ArrayList<T>();
497 for (Resource element : OrderedSetUtils.toList(g, diagram)) {
498 Resource component = g.getPossibleObject(element, m.ElementToComponent);
499 matching.add(getSymbol(g,element, component));
505 * Returns symbols that have matching name.
507 * Prioritizes L0.HasLabel over L0.HasName (If symbol has label, it is used for comparison).
509 * ';' character acts as a splitter, allowing input parameter and a label contain multiple identifiers.
516 * @throws DatabaseException
518 public List<T> getSymbol(ReadGraph g, String symbolName) throws DatabaseException {
519 ModelingResources m = ModelingResources.getInstance(g);
520 Layer0 b = Layer0.getInstance(g);
521 List<T> matching = new ArrayList<T>();
523 String splitId[] = symbolName.split(";");
525 for (Resource element : OrderedSetUtils.toList(g, diagram)) {
526 Resource component = g.getPossibleObject(element, m.ElementToComponent);
527 if (component == null)
528 component = g.getPossibleObject(element, m.HasParentComponent);
529 if (component == null)
531 String label = g.getPossibleRelatedValue(component, b.HasLabel);
532 String name = g.getRelatedValue(component, b.HasName);
534 String splitLabel[] = label.split(";");
536 boolean match = true;
537 for (int i = 0; i < splitId.length; i++) {
538 if(!Arrays.contains(splitLabel, splitId[i])) {
544 matching.add(getSymbol(g,element, component));
546 if (label.equals(symbolName) || name.equals(symbolName)) {
547 matching.add(getSymbol(g,element, component));
553 for (String split : splitId) {
554 if (name.equals(split)) {
555 matching.add(getSymbol(g,element, component));
565 * Returns a single symbol matching given name and type. If matching symbol cannot be found, returns null.
566 * Throws a NoSingleResultException if the re are multiple symbols matching the criteria.
572 * @throws DatabaseException
574 public T getSingleSymbol(ReadGraph g, String symbolName, Resource symbolType) throws DatabaseException {
575 List<T> matching = getSymbol(g, symbolName, symbolType);
576 if (matching.size() == 1)
577 return matching.get(0);
578 if (matching.size() == 0)
580 throw new NoSingleResultException("There are multiple symbols with name '" + symbolName + "' and type " + symbolType);
583 public T getPossibleSymbol(ReadGraph g, String symbolName, Resource symbolType) throws DatabaseException {
584 List<T> matching = getSymbol(g, symbolName, symbolType);
585 if (matching.size() == 1)
586 return matching.get(0);
591 * Returns symbols matching given name and type.
597 * @throws DatabaseException
599 public List<T> getSymbol(ReadGraph g, String symbolName, Resource symbolType) throws DatabaseException {
601 List<T> nameMatching = getSymbol(g, symbolName);
602 List<T> matching = new ArrayList<T>();
603 for (T s : nameMatching) {
604 if ((g.isInstanceOf(s.getElement(), symbolType)||
605 g.isInstanceOf(s.getComponent(), symbolType))) {
612 public Symbol getFlag(ReadGraph g, Resource flagType, String symbolName, Resource symbolType) throws DatabaseException {
613 Layer0 b = Layer0.getInstance(g);
614 DiagramResource dr = DiagramResource.getInstance(g);
615 List<Symbol> matching = new ArrayList<Symbol>();
616 for (Resource flag : OrderedSetUtils.toList(g, diagram)) {
617 if (!g.isInstanceOf(flag, dr.Flag))
619 if (!g.getSingleObject(flag, dr.HasFlagType).equals(flagType))
621 Symbol flagSymbol = getSymbol(g,flag, null);
623 Symbol connectedSymbol = flagSymbol.getDiagramSingleConnected(g, dr.Flag_ConnectionPoint);
624 Resource component = connectedSymbol.getComponent();
625 if (component == null)
627 String label = g.getPossibleRelatedValue(component, b.HasLabel);
628 String name = g.getRelatedValue(component, b.HasName);
630 String splitId[] = symbolName.split(";");
631 if (splitId.length > 1) {
632 String splitLabel[] = label.split(";");
633 boolean match = true;
634 for (int i = 0; i < splitId.length; i++) {
635 if(!Arrays.contains(splitLabel, splitId[i])) {
641 if (g.isInstanceOf(connectedSymbol.getElement(), symbolType)||
642 g.isInstanceOf(component, symbolType)) {
643 matching.add(getSymbol(g,flag, null));
648 if (label.equals(symbolName) || name.equals(symbolName)) {
649 if (g.isInstanceOf(connectedSymbol.getElement(), symbolType)||
650 g.isInstanceOf(component, symbolType)) {
651 matching.add(getSymbol(g,flag, null));
658 if (name.equals(symbolName)) {
659 if (g.isInstanceOf(connectedSymbol.getElement(), symbolType)||
660 g.isInstanceOf(component, symbolType)) {
661 matching.add(getSymbol(g,flag, null));
667 if (matching.size() == 1)
668 return matching.get(0);
669 if (matching.size() == 0)
671 throw new NoSingleResultException("There are multiple flags connecting symbol with name '" + symbolName + "' and type " + symbolType);
675 public PageDesc getPageDesc(ReadGraph g) throws DatabaseException {
676 PageDesc pDesc = DiagramGraphUtil.getPageDesc(g, diagram, PageDesc.DEFAULT);
681 public boolean equals(Object o) {
684 if (this.getClass() != o.getClass())
686 Diagram other = (Diagram)o;
687 return diagram.equals(other.diagram);
691 public int hashCode() {
692 return diagram.hashCode();
695 void merge(Symbol to, Symbol from) {
696 Resource element = from.getElement();
697 for (Symbol s : elementToSymbolMap.values()) {
698 if (s.element.equals(element)) {
699 s.element = to.element;
700 s.component = to.component;
703 from.element = to.element;
704 from.component = to.component;
708 * Returns a model containing the diagram.
711 * @throws DatabaseException
713 public Resource getModel(ReadGraph g) throws DatabaseException {
714 Layer0 l0 = Layer0.getInstance(g);
715 SimulationResource sim = SimulationResource.getInstance(g);
716 Resource r = composite;
718 Resource parent = g.getSingleObject(r, l0.PartOf);
719 if (g.isInstanceOf(parent, sim.Model))