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 Resource composite = null;
254 if (g.isInstanceOf(diagram, s.Composite)) {
256 diagram = g.getPossibleObject(composite, m.CompositeToDiagram);
260 composite = g.getPossibleObject(diagram, m.DiagramToComposite);
261 if (composite == null)
265 Diagram<T> diag = compositeToDiagramMap.get(composite);
269 diag = construct(g,composite, diagram);
270 diag.compositeToDiagramMap = compositeToDiagramMap;
271 compositeToDiagramMap.put(composite, diag);
280 * Returns diagram type from composite type
282 * @param compositeType
283 * @return diagram type
284 * @throws NoSingleResultException
285 * @throws ManyObjectsForFunctionalRelationException
286 * @throws ServiceException
288 public Resource getDiagramFromComposite(ReadGraph g, Resource compositeType) throws DatabaseException {
289 ModelingResources m = ModelingResources.getInstance(g);
290 Collection<Resource> diagramTemplates = g.getAssertedObjects(compositeType, m.HasModelingTemplate);
291 for (Resource diagramTemplate : diagramTemplates) {
292 Resource diagramType = g.getPossibleObject(diagramTemplate, m.HasDiagramType);
293 if (diagramType != null)
297 throw new RuntimeException("Cannot find diagramType for composite " + compositeType);
304 * Adds new symbol to a diagram
308 * @throws DatabaseException
310 public T addSymbol(WriteGraph g, Resource symbolType) throws DatabaseException {
311 return addSymbol(g, symbolType, 0, 0);
315 * Adds new symbol to a diagram
320 * @throws DatabaseException
322 public T addSymbol(WriteGraph g, Resource symbolType, String name) throws DatabaseException {
323 return addSymbol(g, symbolType, name, 0, 0);
327 * Adds new symbol to a diagram
329 * @param diagramConf Diagram configuration
330 * @param symbolType type of the new symbol
333 * @return configuration of the new Symbol
334 * @throws DatabaseException
336 public T addSymbol(WriteGraph g, Resource symbolType, double x, double y) throws DatabaseException {
337 return addSymbol(g, symbolType,null,x,y);
341 * Adds new symbol to a diagram
348 * @throws DatabaseException
350 public T addSymbol(WriteGraph g, Resource elementType, String name, double x, double y) throws DatabaseException {
351 if (elementType == null)
352 throw new NullPointerException("Element type is null");
353 Layer0 l0 = Layer0.getInstance(g);
355 DiagramResource d = DiagramResource.getInstance(g);
356 ModelingResources m = ModelingResources.getInstance(g);
358 Resource componentType = null;
359 if (g.isInheritedFrom(elementType, d.DefinedElement))
360 componentType = Symbol.getComponentTypeFromSymbolType(g, elementType);
362 componentType = elementType;
363 elementType = Symbol.getSymbolTypeFromComponentType(g,componentType);
367 Resource element = g.newResource();
368 g.claim(element, l0.InstanceOf, elementType);
370 Resource module = g.newResource();
371 g.claim(module, l0.InstanceOf, componentType);
373 g.claim(module, m.ComponentToElement, element);
374 g.claim(module, m.Mapped, module);
376 String id = generateName(g, componentType, name);
377 g.claimLiteral(module, l0.HasName, id);
378 g.claimLiteral(element, l0.HasName, id);
380 OrderedSetUtils.add(g, this.diagram, element);
381 g.claim(this.diagram, l0.ConsistsOf, element);
382 g.claim(this.composite, l0.ConsistsOf, module);
384 T s = constructSymbol(g, element, module);
385 s.setSymbolTranslation(g, x, y);
387 elementToSymbolMap.put(element, s);
392 * Returns a Symbol for given resource.
394 * @param element Element or Component of the Symbol.
396 * @throws ManyObjectsForFunctionalRelationException
397 * @throws ServiceException
398 * @throws NoSingleResultException
400 public T getSymbol(ReadGraph g, Resource element) throws DatabaseException {
401 ModelingResources mr = ModelingResources.getInstance(g);
402 DiagramResource dr = DiagramResource.getInstance(g);
403 if (g.isInstanceOf(element, dr.Element))
404 return getSymbol(g, element, g.getPossibleObject(element, mr.ElementToComponent));
406 return getSymbol(g, g.getSingleObject(element, mr.ComponentToElement), element);
410 * Returns a Symbol for given resource.
412 * @param element Element Resource of the Symbol.
413 * @param component Component Resource of the symbol.
416 public T getSymbol(ReadGraph g, Resource element, Resource component) throws DatabaseException {
417 T s = elementToSymbolMap.get(element);
419 // TODO : check that symbol actually belongs to this diagram
420 s = constructSymbol(g, element, component);
421 elementToSymbolMap.put(element, s);
423 if (s.component == null)
424 s.component = component;
425 else if (!s.component.equals(component)) {
427 return constructSymbol(g, element, component);
434 * Adds new element to a diagram
436 * @param symbolType type of the element.
440 * @throws DatabaseException
442 public T addElement(WriteGraph g, Resource symbolType, double x, double y) throws DatabaseException {
443 Layer0 l0 = Layer0.getInstance(g);
445 Resource element = g.newResource();
446 g.claim(element, l0.InstanceOf, symbolType);
448 DiagramUtils.addElement(g, this, element);
450 T s = constructSymbol(g,element,null);
452 s.setSymbolTranslation(g, x, y);
454 elementToSymbolMap.put(element, s);
458 public T getSingleSymbol(ReadGraph g, String symbolName) throws DatabaseException {
459 List<T> matching = getSymbol(g, symbolName);
460 if (matching.size() == 1)
461 return matching.get(0);
462 if (matching.size() == 0)
464 throw new NoSingleResultException("There are multiple symbols with name '" + symbolName);
467 public T getPossibleSymbol(ReadGraph g, String symbolName) throws DatabaseException {
468 List<T> matching = getSymbol(g, symbolName);
469 if (matching.size() == 1)
470 return matching.get(0);
475 * Returns all symbols on the diagram
478 * @throws DatabaseException
480 public List<T> getSymbols(ReadGraph g) throws DatabaseException {
481 ModelingResources m = ModelingResources.getInstance(g);
482 List<T> matching = new ArrayList<T>();
483 for (Resource element : OrderedSetUtils.toList(g, diagram)) {
484 Resource component = g.getPossibleObject(element, m.ElementToComponent);
485 matching.add(getSymbol(g,element, component));
491 * Returns symbols that have matching name.
493 * Prioritizes L0.HasLabel over L0.HasName (If symbol has label, it is used for comparison).
495 * ';' character acts as a splitter, allowing input parameter and a label contain multiple identifiers.
502 * @throws DatabaseException
504 public List<T> getSymbol(ReadGraph g, String symbolName) throws DatabaseException {
505 ModelingResources m = ModelingResources.getInstance(g);
506 Layer0 b = Layer0.getInstance(g);
507 List<T> matching = new ArrayList<T>();
509 String splitId[] = symbolName.split(";");
511 for (Resource element : OrderedSetUtils.toList(g, diagram)) {
512 Resource component = g.getPossibleObject(element, m.ElementToComponent);
513 if (component == null)
514 component = g.getPossibleObject(element, m.HasParentComponent);
515 if (component == null)
517 String label = g.getPossibleRelatedValue(component, b.HasLabel);
518 String name = g.getRelatedValue(component, b.HasName);
520 String splitLabel[] = label.split(";");
522 boolean match = true;
523 for (int i = 0; i < splitId.length; i++) {
524 if(!Arrays.contains(splitLabel, splitId[i])) {
530 matching.add(getSymbol(g,element, component));
532 if (label.equals(symbolName) || name.equals(symbolName)) {
533 matching.add(getSymbol(g,element, component));
539 for (String split : splitId) {
540 if (name.equals(split)) {
541 matching.add(getSymbol(g,element, component));
551 * Returns a single symbol matching given name and type. If matching symbol cannot be found, returns null.
552 * Throws a NoSingleResultException if the re are multiple symbols matching the criteria.
558 * @throws DatabaseException
560 public T getSingleSymbol(ReadGraph g, String symbolName, Resource symbolType) throws DatabaseException {
561 List<T> matching = getSymbol(g, symbolName, symbolType);
562 if (matching.size() == 1)
563 return matching.get(0);
564 if (matching.size() == 0)
566 throw new NoSingleResultException("There are multiple symbols with name '" + symbolName + "' and type " + symbolType);
569 public T getPossibleSymbol(ReadGraph g, String symbolName, Resource symbolType) throws DatabaseException {
570 List<T> matching = getSymbol(g, symbolName, symbolType);
571 if (matching.size() == 1)
572 return matching.get(0);
577 * Returns symbols matching given name and type.
583 * @throws DatabaseException
585 public List<T> getSymbol(ReadGraph g, String symbolName, Resource symbolType) throws DatabaseException {
587 List<T> nameMatching = getSymbol(g, symbolName);
588 List<T> matching = new ArrayList<T>();
589 for (T s : nameMatching) {
590 if ((g.isInstanceOf(s.getElement(), symbolType)||
591 g.isInstanceOf(s.getComponent(), symbolType))) {
598 public Symbol getFlag(ReadGraph g, Resource flagType, String symbolName, Resource symbolType) throws DatabaseException {
599 Layer0 b = Layer0.getInstance(g);
600 DiagramResource dr = DiagramResource.getInstance(g);
601 List<Symbol> matching = new ArrayList<Symbol>();
602 for (Resource flag : OrderedSetUtils.toList(g, diagram)) {
603 if (!g.isInstanceOf(flag, dr.Flag))
605 if (!g.getSingleObject(flag, dr.HasFlagType).equals(flagType))
607 Symbol flagSymbol = getSymbol(g,flag, null);
609 Symbol connectedSymbol = flagSymbol.getDiagramSingleConnected(g, dr.Flag_ConnectionPoint);
610 Resource component = connectedSymbol.getComponent();
611 if (component == null)
613 String label = g.getPossibleRelatedValue(component, b.HasLabel);
614 String name = g.getRelatedValue(component, b.HasName);
616 String splitId[] = symbolName.split(";");
617 if (splitId.length > 1) {
618 String splitLabel[] = label.split(";");
619 boolean match = true;
620 for (int i = 0; i < splitId.length; i++) {
621 if(!Arrays.contains(splitLabel, splitId[i])) {
627 if (g.isInstanceOf(connectedSymbol.getElement(), symbolType)||
628 g.isInstanceOf(component, symbolType)) {
629 matching.add(getSymbol(g,flag, null));
634 if (label.equals(symbolName) || name.equals(symbolName)) {
635 if (g.isInstanceOf(connectedSymbol.getElement(), symbolType)||
636 g.isInstanceOf(component, symbolType)) {
637 matching.add(getSymbol(g,flag, null));
644 if (name.equals(symbolName)) {
645 if (g.isInstanceOf(connectedSymbol.getElement(), symbolType)||
646 g.isInstanceOf(component, symbolType)) {
647 matching.add(getSymbol(g,flag, null));
653 if (matching.size() == 1)
654 return matching.get(0);
655 if (matching.size() == 0)
657 throw new NoSingleResultException("There are multiple flags connecting symbol with name '" + symbolName + "' and type " + symbolType);
661 public PageDesc getPageDesc(ReadGraph g) throws DatabaseException {
662 PageDesc pDesc = DiagramGraphUtil.getPageDesc(g, diagram, PageDesc.DEFAULT);
667 public boolean equals(Object o) {
670 if (this.getClass() != o.getClass())
672 Diagram other = (Diagram)o;
673 return diagram.equals(other.diagram);
677 public int hashCode() {
678 return diagram.hashCode();
681 void merge(Symbol to, Symbol from) {
682 Resource element = from.getElement();
683 for (Symbol s : elementToSymbolMap.values()) {
684 if (s.element.equals(element)) {
685 s.element = to.element;
686 s.component = to.component;
689 from.element = to.element;
690 from.component = to.component;
694 * Returns a model containing the diagram.
697 * @throws DatabaseException
699 public Resource getModel(ReadGraph g) throws DatabaseException {
700 Layer0 l0 = Layer0.getInstance(g);
701 SimulationResource sim = SimulationResource.getInstance(g);
702 Resource r = composite;
704 Resource parent = g.getSingleObject(r, l0.PartOf);
705 if (g.isInstanceOf(parent, sim.Model))