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);
203 Resource composite = g.newResource();
204 g.claim(composite, l0.InstanceOf, compositeType);
207 Resource diagramType = getDiagramFromComposite(g, compositeType);
209 Resource diagram = OrderedSetUtils.create(g, diagramType);
211 g.claim(composite, m.CompositeToDiagram, diagram);
213 // link to parent and set name
214 g.claimLiteral(composite, l0.HasName, name);
215 g.claim(composite, l0.PartOf, parent);
218 Resource mapping = g.newResource();
219 g.claim(mapping, l0.InstanceOf, m.DiagramToCompositeMapping);
220 g.claim(diagram, l0x.HasTrigger, mapping);
223 Map<String,Object> params = new HashMap<String, Object>();
224 params.put("diagram", diagram);
225 tpl.apply(g, params);
229 // Make diagram part of a dummy container library attached to the parent
230 // composite if it's not already part of something.
231 // This gives the diagram a proper URI without connecting it directly to the composite with ConsistsOf.
232 // This would cause problems because a diagram is a structural composite/component also.
233 g.claimLiteral(diagram, l0.HasName, name, Bindings.STRING);
234 Resource container = g.newResource();
235 g.claim(container, l0.InstanceOf, null, l0.Library);
236 g.addLiteral(container, l0.HasName, l0.NameOf, l0.String, "__CONTAINER__", Bindings.STRING);
237 g.claim(container, l0.ConsistsOf, diagram);
238 g.claim(composite, l0.ConsistsOf, container);
241 DiagramGraphUtil.setDiagramDesc(g, diagram, desc);
243 Diagram<T> diag = construct(g, composite, diagram);
244 diag.compositeToDiagramMap = compositeToDiagramMap;
245 compositeToDiagramMap.put(composite, diag);
249 public Diagram<T> fromExisting(ReadGraph g, Resource diagram) throws DatabaseException {
250 ModelingResources m = ModelingResources.getInstance(g);
251 StructuralResource2 s = StructuralResource2.getInstance(g);
252 Resource composite = null;
253 if (g.isInstanceOf(diagram, s.Composite)) {
255 diagram = g.getPossibleObject(composite, m.CompositeToDiagram);
259 composite = g.getPossibleObject(diagram, m.DiagramToComposite);
260 if (composite == null)
264 Diagram<T> diag = compositeToDiagramMap.get(composite);
268 diag = construct(g,composite, diagram);
269 diag.compositeToDiagramMap = compositeToDiagramMap;
270 compositeToDiagramMap.put(composite, diag);
279 * Returns diagram type from composite type
281 * @param compositeType
282 * @return diagram type
283 * @throws NoSingleResultException
284 * @throws ManyObjectsForFunctionalRelationException
285 * @throws ServiceException
287 public Resource getDiagramFromComposite(ReadGraph g, Resource compositeType) throws DatabaseException {
288 ModelingResources m = ModelingResources.getInstance(g);
289 Collection<Resource> diagramTemplates = g.getAssertedObjects(compositeType, m.HasModelingTemplate);
290 for (Resource diagramTemplate : diagramTemplates) {
291 Resource diagramType = g.getPossibleObject(diagramTemplate, m.HasDiagramType);
292 if (diagramType != null)
296 throw new RuntimeException("Cannot find diagramType for composite " + compositeType);
303 * Adds new symbol to a diagram
307 * @throws DatabaseException
309 public T addSymbol(WriteGraph g, Resource symbolType) throws DatabaseException {
310 return addSymbol(g, symbolType, 0, 0);
314 * Adds new symbol to a diagram
319 * @throws DatabaseException
321 public T addSymbol(WriteGraph g, Resource symbolType, String name) throws DatabaseException {
322 return addSymbol(g, symbolType, name, 0, 0);
326 * Adds new symbol to a diagram
328 * @param diagramConf Diagram configuration
329 * @param symbolType type of the new symbol
332 * @return configuration of the new Symbol
333 * @throws DatabaseException
335 public T addSymbol(WriteGraph g, Resource symbolType, double x, double y) throws DatabaseException {
336 return addSymbol(g, symbolType,null,x,y);
340 * Adds new symbol to a diagram
347 * @throws DatabaseException
349 public T addSymbol(WriteGraph g, Resource elementType, String name, double x, double y) throws DatabaseException {
350 if (elementType == null)
351 throw new NullPointerException("Element type is null");
352 Layer0 l0 = Layer0.getInstance(g);
354 DiagramResource d = DiagramResource.getInstance(g);
355 ModelingResources m = ModelingResources.getInstance(g);
357 Resource componentType = null;
358 if (g.isInheritedFrom(elementType, d.DefinedElement))
359 componentType = Symbol.getComponentTypeFromSymbolType(g, elementType);
361 componentType = elementType;
362 elementType = Symbol.getSymbolTypeFromComponentType(g,componentType);
366 Resource element = g.newResource();
367 g.claim(element, l0.InstanceOf, elementType);
369 Resource module = g.newResource();
370 g.claim(module, l0.InstanceOf, componentType);
372 g.claim(module, m.ComponentToElement, element);
373 g.claim(module, m.Mapped, module);
375 String id = generateName(g, componentType, name);
376 g.claimLiteral(module, l0.HasName, id);
377 g.claimLiteral(element, l0.HasName, id);
379 OrderedSetUtils.add(g, this.diagram, element);
380 g.claim(this.diagram, l0.ConsistsOf, element);
381 g.claim(this.composite, l0.ConsistsOf, module);
383 T s = constructSymbol(g, element, module);
384 s.setSymbolTranslation(g, x, y);
386 elementToSymbolMap.put(element, s);
391 * Returns a Symbol for given resource.
393 * @param element Element or Component of the Symbol.
395 * @throws ManyObjectsForFunctionalRelationException
396 * @throws ServiceException
397 * @throws NoSingleResultException
399 public T getSymbol(ReadGraph g, Resource element) throws DatabaseException {
400 ModelingResources mr = ModelingResources.getInstance(g);
401 DiagramResource dr = DiagramResource.getInstance(g);
402 if (g.isInstanceOf(element, dr.Element))
403 return getSymbol(g, element, g.getPossibleObject(element, mr.ElementToComponent));
405 return getSymbol(g, g.getSingleObject(element, mr.ComponentToElement), element);
409 * Returns a Symbol for given resource.
411 * @param element Element Resource of the Symbol.
412 * @param component Component Resource of the symbol.
415 public T getSymbol(ReadGraph g, Resource element, Resource component) throws DatabaseException {
416 T s = elementToSymbolMap.get(element);
418 // TODO : check that symbol actually belongs to this diagram
419 s = constructSymbol(g, element, component);
420 elementToSymbolMap.put(element, s);
422 if (s.component == null)
423 s.component = component;
424 else if (!s.component.equals(component)) {
426 return constructSymbol(g, element, component);
433 * Adds new element to a diagram
435 * @param symbolType type of the element.
439 * @throws DatabaseException
441 public T addElement(WriteGraph g, Resource symbolType, double x, double y) throws DatabaseException {
442 Layer0 l0 = Layer0.getInstance(g);
444 Resource element = g.newResource();
445 g.claim(element, l0.InstanceOf, symbolType);
447 DiagramUtils.addElement(g, this, element);
449 T s = constructSymbol(g,element,null);
451 s.setSymbolTranslation(g, x, y);
453 elementToSymbolMap.put(element, s);
457 public T getSingleSymbol(ReadGraph g, String symbolName) throws DatabaseException {
458 List<T> matching = getSymbol(g, symbolName);
459 if (matching.size() == 1)
460 return matching.get(0);
461 if (matching.size() == 0)
463 throw new NoSingleResultException("There are multiple symbols with name '" + symbolName);
466 public T getPossibleSymbol(ReadGraph g, String symbolName) throws DatabaseException {
467 List<T> matching = getSymbol(g, symbolName);
468 if (matching.size() == 1)
469 return matching.get(0);
474 * Returns all symbols on the diagram
477 * @throws DatabaseException
479 public List<T> getSymbols(ReadGraph g) throws DatabaseException {
480 ModelingResources m = ModelingResources.getInstance(g);
481 List<T> matching = new ArrayList<T>();
482 for (Resource element : OrderedSetUtils.toList(g, diagram)) {
483 Resource component = g.getPossibleObject(element, m.ElementToComponent);
484 matching.add(getSymbol(g,element, component));
490 * Returns symbols that have matching name.
492 * Prioritizes L0.HasLabel over L0.HasName (If symbol has label, it is used for comparison).
494 * ';' character acts as a splitter, allowing input parameter and a label contain multiple identifiers.
501 * @throws DatabaseException
503 public List<T> getSymbol(ReadGraph g, String symbolName) throws DatabaseException {
504 ModelingResources m = ModelingResources.getInstance(g);
505 Layer0 b = Layer0.getInstance(g);
506 List<T> matching = new ArrayList<T>();
508 String splitId[] = symbolName.split(";");
510 for (Resource element : OrderedSetUtils.toList(g, diagram)) {
511 Resource component = g.getPossibleObject(element, m.ElementToComponent);
512 if (component == null)
513 component = g.getPossibleObject(element, m.HasParentComponent);
514 if (component == null)
516 String label = g.getPossibleRelatedValue(component, b.HasLabel);
517 String name = g.getRelatedValue(component, b.HasName);
519 String splitLabel[] = label.split(";");
521 boolean match = true;
522 for (int i = 0; i < splitId.length; i++) {
523 if(!Arrays.contains(splitLabel, splitId[i])) {
529 matching.add(getSymbol(g,element, component));
531 if (label.equals(symbolName) || name.equals(symbolName)) {
532 matching.add(getSymbol(g,element, component));
538 for (String split : splitId) {
539 if (name.equals(split)) {
540 matching.add(getSymbol(g,element, component));
550 * Returns a single symbol matching given name and type. If matching symbol cannot be found, returns null.
551 * Throws a NoSingleResultException if the re are multiple symbols matching the criteria.
557 * @throws DatabaseException
559 public T getSingleSymbol(ReadGraph g, String symbolName, Resource symbolType) throws DatabaseException {
560 List<T> matching = getSymbol(g, symbolName, symbolType);
561 if (matching.size() == 1)
562 return matching.get(0);
563 if (matching.size() == 0)
565 throw new NoSingleResultException("There are multiple symbols with name '" + symbolName + "' and type " + symbolType);
568 public T getPossibleSymbol(ReadGraph g, String symbolName, Resource symbolType) throws DatabaseException {
569 List<T> matching = getSymbol(g, symbolName, symbolType);
570 if (matching.size() == 1)
571 return matching.get(0);
576 * Returns symbols matching given name and type.
582 * @throws DatabaseException
584 public List<T> getSymbol(ReadGraph g, String symbolName, Resource symbolType) throws DatabaseException {
586 List<T> nameMatching = getSymbol(g, symbolName);
587 List<T> matching = new ArrayList<T>();
588 for (T s : nameMatching) {
589 if ((g.isInstanceOf(s.getElement(), symbolType)||
590 g.isInstanceOf(s.getComponent(), symbolType))) {
597 public Symbol getFlag(ReadGraph g, Resource flagType, String symbolName, Resource symbolType) throws DatabaseException {
598 Layer0 b = Layer0.getInstance(g);
599 DiagramResource dr = DiagramResource.getInstance(g);
600 List<Symbol> matching = new ArrayList<Symbol>();
601 for (Resource flag : OrderedSetUtils.toList(g, diagram)) {
602 if (!g.isInstanceOf(flag, dr.Flag))
604 if (!g.getSingleObject(flag, dr.HasFlagType).equals(flagType))
606 Symbol flagSymbol = getSymbol(g,flag, null);
608 Symbol connectedSymbol = flagSymbol.getDiagramSingleConnected(g, dr.Flag_ConnectionPoint);
609 Resource component = connectedSymbol.getComponent();
610 if (component == null)
612 String label = g.getPossibleRelatedValue(component, b.HasLabel);
613 String name = g.getRelatedValue(component, b.HasName);
615 String splitId[] = symbolName.split(";");
616 if (splitId.length > 1) {
617 String splitLabel[] = label.split(";");
618 boolean match = true;
619 for (int i = 0; i < splitId.length; i++) {
620 if(!Arrays.contains(splitLabel, splitId[i])) {
626 if (g.isInstanceOf(connectedSymbol.getElement(), symbolType)||
627 g.isInstanceOf(component, symbolType)) {
628 matching.add(getSymbol(g,flag, null));
633 if (label.equals(symbolName) || name.equals(symbolName)) {
634 if (g.isInstanceOf(connectedSymbol.getElement(), symbolType)||
635 g.isInstanceOf(component, symbolType)) {
636 matching.add(getSymbol(g,flag, null));
643 if (name.equals(symbolName)) {
644 if (g.isInstanceOf(connectedSymbol.getElement(), symbolType)||
645 g.isInstanceOf(component, symbolType)) {
646 matching.add(getSymbol(g,flag, null));
652 if (matching.size() == 1)
653 return matching.get(0);
654 if (matching.size() == 0)
656 throw new NoSingleResultException("There are multiple flags connecting symbol with name '" + symbolName + "' and type " + symbolType);
660 public PageDesc getPageDesc(ReadGraph g) throws DatabaseException {
661 PageDesc pDesc = DiagramGraphUtil.getPageDesc(g, diagram, PageDesc.DEFAULT);
666 public boolean equals(Object o) {
669 if (this.getClass() != o.getClass())
671 Diagram other = (Diagram)o;
672 return diagram.equals(other.diagram);
676 public int hashCode() {
677 return diagram.hashCode();
680 void merge(Symbol to, Symbol from) {
681 Resource element = from.getElement();
682 for (Symbol s : elementToSymbolMap.values()) {
683 if (s.element.equals(element)) {
684 s.element = to.element;
685 s.component = to.component;
688 from.element = to.element;
689 from.component = to.component;
693 * Returns a model containing the diagram.
696 * @throws DatabaseException
698 public Resource getModel(ReadGraph g) throws DatabaseException {
699 Layer0 l0 = Layer0.getInstance(g);
700 SimulationResource sim = SimulationResource.getInstance(g);
701 Resource r = composite;
703 Resource parent = g.getSingleObject(r, l0.PartOf);
704 if (g.isInstanceOf(parent, sim.Model))