package org.simantics.interop.diagram; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.Stack; import org.simantics.databoard.Bindings; import org.simantics.db.ReadGraph; import org.simantics.db.RequestProcessor; import org.simantics.db.Resource; import org.simantics.db.WriteGraph; import org.simantics.db.common.procedure.adapter.TransientCacheListener; import org.simantics.db.common.request.ResourceRead; import org.simantics.db.common.utils.OrderedSetUtils; import org.simantics.db.exception.DatabaseException; import org.simantics.db.exception.ManyObjectsForFunctionalRelationException; import org.simantics.db.exception.NoSingleResultException; import org.simantics.db.exception.ServiceException; import org.simantics.db.layer0.adapter.Template; import org.simantics.diagram.stubs.DiagramResource; import org.simantics.diagram.synchronization.graph.DiagramGraphUtil; import org.simantics.g2d.page.DiagramDesc; import org.simantics.layer0.Layer0; import org.simantics.modeling.ModelingResources; import org.simantics.operation.Layer0X; import org.simantics.simulation.ontology.SimulationResource; import org.simantics.structural.stubs.StructuralResource2; import org.simantics.utils.datastructures.Arrays; import org.simantics.utils.page.PageDesc; /** * @author Marko Luukkainen */ public abstract class Diagram { public static boolean DO_NO_MIX_SECTION_NAMES = false; public static boolean USE_MERGE_CONNECTS = true; private Resource composite; private Resource diagram; private Map indexMap = new HashMap(); private Map elementToSymbolMap = new HashMap(); private Map> compositeToDiagramMap = new HashMap>(); protected Diagram(ReadGraph g, Resource composite, Resource diagram) throws DatabaseException { this.composite = composite; this.diagram = diagram; } protected abstract Diagram construct(ReadGraph g, Resource composite, Resource diagram) throws DatabaseException; protected abstract T constructSymbol(ReadGraph g, Resource element, Resource component) throws DatabaseException; public Resource getComposite() { return composite; } public Resource getDiagram() { return diagram; } public void clearCaches() { for (Diagram d : compositeToDiagramMap.values()) { for (T s : d.elementToSymbolMap.values()) s.dispose(); d.elementToSymbolMap.clear(); } compositeToDiagramMap.clear(); } public String generateName(ReadGraph g, Resource type, String name) throws DatabaseException { Layer0 l0 = Layer0.getInstance(g); Layer0X l0x = Layer0X.getInstance(g); String prefix = g.getPossibleRelatedValue(type, l0x.HasGeneratedNamePrefix); if (prefix == null || prefix.length() == 0) prefix = g.getRelatedValue(type, l0.HasName); prefix += "_"; Set reserved = null; if (name != null) { reserved = getAllModuleNamesForModel(g, composite); int index = 0; String realname = name + "_" + prefix; while (true) { String toTry = realname + Integer.toString(index); if (!reserved.contains(toTry)) break; index++; } realname += Integer.toString(index); return realname; } Integer index = indexMap.get(type); if (index != null) { index++; } else { if (reserved == null) reserved = getAllModuleNamesForModel(g, composite); index = 0; while (true) { String toTry = prefix + Integer.toString(index); if (!reserved.contains(toTry)) break; index++; } } indexMap.put(type, index); return prefix + index; } private Set getAllModuleNamesForModel(ReadGraph graph, final Resource res) throws DatabaseException { Resource model = DiagramUtils.getModel(graph, res); Resource configuration = DiagramUtils.getConfiguration(graph, model); Set names = new HashSet(); if (configuration == null) return names; Collection composites = getAllComposites(graph, configuration); for (Resource composite : composites) { names.addAll(getAllModuleNamesForComposite(graph, composite)); } return names; } protected abstract Resource getGraphicalCompositeType(ReadGraph graph) throws DatabaseException; protected abstract Resource getFolderType(ReadGraph graph) throws DatabaseException; @SuppressWarnings("unchecked") private Collection getAllComposites(RequestProcessor processor, Resource configuration) throws DatabaseException { return (Set)processor.syncRequest(new ResourceRead(configuration) { @Override public Object perform(ReadGraph graph) throws DatabaseException { Layer0 l0 = Layer0.getInstance(graph); Set composites = new HashSet(); Stack resources = new Stack(); Resource graphicalCompositeType = getGraphicalCompositeType(graph); Resource folderType = getFolderType(graph); resources.add(resource); while (!resources.isEmpty()) { Resource r = resources.pop(); if (graph.isInstanceOf(r, graphicalCompositeType)) { composites.add(r); } else if (folderType != null && graph.isInstanceOf(r, folderType)){ resources.addAll(graph.getObjects(r, l0.ConsistsOf)); } } return composites; } }, TransientCacheListener.instance()); } @SuppressWarnings("unchecked") private Set getAllModuleNamesForComposite(RequestProcessor processor, Resource composite) throws DatabaseException { return (Set)processor.syncRequest(new ResourceRead(composite) { @Override public Object perform(ReadGraph graph) throws DatabaseException { Layer0 l0 = Layer0.getInstance(graph); Set names = new HashSet(); Collection components = graph.getObjects(resource, l0.ConsistsOf); for (Resource component : components) { String n = graph.getPossibleRelatedValue(component, l0.HasName, Bindings.STRING); if (n != null) names.add(n); } return names; } }, TransientCacheListener.instance()); } public Diagram createDiagram(WriteGraph g,String name, Resource compositeType, Resource parent) throws DatabaseException { return createDiagram(g, name, null, null,compositeType, parent); } /** * Creates a new Diagram * @param g * @param name name of the diagram * @param compositeType composite type of the diagram * @param parent resource where diagram is added * @return * @throws DatabaseException */ @SuppressWarnings("deprecation") public Diagram createDiagram(WriteGraph g,String name, DiagramDesc desc, Template tpl, Resource compositeType, Resource parent) throws DatabaseException { Layer0 l0 = Layer0.getInstance(g); Layer0X l0x = Layer0X.getInstance(g); ModelingResources m = ModelingResources.getInstance(g); DiagramResource dia = DiagramResource.getInstance(g); // create composite Resource composite = g.newResource(); g.claim(composite, l0.InstanceOf, compositeType); // create diagram Resource diagramType = getDiagramFromComposite(g, compositeType); Resource diagram = OrderedSetUtils.create(g, diagramType); g.claim(composite, m.CompositeToDiagram, diagram); // link to parent and set name g.claimLiteral(composite, l0.HasName, name); g.claim(composite, l0.PartOf, parent); // activate mapping Resource mapping = g.newResource(); g.claim(mapping, l0.InstanceOf, m.DiagramToCompositeMapping); g.claim(diagram, l0x.HasTrigger, mapping); if (tpl != null) { Map params = new HashMap(); params.put("diagram", diagram); tpl.apply(g, params); } // Make diagram part of a dummy container library attached to the parent // composite if it's not already part of something. // This gives the diagram a proper URI without connecting it directly to the composite with ConsistsOf. // This would cause problems because a diagram is a structural composite/component also. g.claimLiteral(diagram, l0.HasName, name, Bindings.STRING); Resource container = g.newResource(); g.claim(container, l0.InstanceOf, null, dia.DiagramContainer); g.addLiteral(container, l0.HasName, l0.NameOf, l0.String, "__CONTAINER__", Bindings.STRING); g.claim(container, l0.ConsistsOf, diagram); g.claim(composite, l0.ConsistsOf, container); if (desc != null) DiagramGraphUtil.setDiagramDesc(g, diagram, desc); Diagram diag = construct(g, composite, diagram); diag.compositeToDiagramMap = compositeToDiagramMap; compositeToDiagramMap.put(composite, diag); return diag; } public Diagram fromExisting(ReadGraph g, Resource diagram) throws DatabaseException { ModelingResources m = ModelingResources.getInstance(g); StructuralResource2 s = StructuralResource2.getInstance(g); Resource composite = null; if (g.isInstanceOf(diagram, s.Composite)) { composite = diagram; diagram = g.getPossibleObject(composite, m.CompositeToDiagram); if (diagram == null) return null; } else { composite = g.getPossibleObject(diagram, m.DiagramToComposite); if (composite == null) return null; } Diagram diag = compositeToDiagramMap.get(composite); if (diag != null) return diag; diag = construct(g,composite, diagram); diag.compositeToDiagramMap = compositeToDiagramMap; compositeToDiagramMap.put(composite, diag); return diag; } /** * Returns diagram type from composite type * @param g * @param compositeType * @return diagram type * @throws NoSingleResultException * @throws ManyObjectsForFunctionalRelationException * @throws ServiceException */ public Resource getDiagramFromComposite(ReadGraph g, Resource compositeType) throws DatabaseException { ModelingResources m = ModelingResources.getInstance(g); Collection diagramTemplates = g.getAssertedObjects(compositeType, m.HasModelingTemplate); for (Resource diagramTemplate : diagramTemplates) { Resource diagramType = g.getPossibleObject(diagramTemplate, m.HasDiagramType); if (diagramType != null) return diagramType; } throw new RuntimeException("Cannot find diagramType for composite " + compositeType); } /** * Adds new symbol to a diagram * @param g * @param symbolType * @return * @throws DatabaseException */ public T addSymbol(WriteGraph g, Resource symbolType) throws DatabaseException { return addSymbol(g, symbolType, 0, 0); } /** * Adds new symbol to a diagram * @param g * @param symbolType * @param name * @return * @throws DatabaseException */ public T addSymbol(WriteGraph g, Resource symbolType, String name) throws DatabaseException { return addSymbol(g, symbolType, name, 0, 0); } /** * Adds new symbol to a diagram * @param g * @param diagramConf Diagram configuration * @param symbolType type of the new symbol * @param x * @param y * @return configuration of the new Symbol * @throws DatabaseException */ public T addSymbol(WriteGraph g, Resource symbolType, double x, double y) throws DatabaseException { return addSymbol(g, symbolType,null,x,y); } /** * Adds new symbol to a diagram * @param g * @param elementType * @param name * @param x * @param y * @return * @throws DatabaseException */ public T addSymbol(WriteGraph g, Resource elementType, String name, double x, double y) throws DatabaseException { if (elementType == null) throw new NullPointerException("Element type is null"); Layer0 l0 = Layer0.getInstance(g); DiagramResource d = DiagramResource.getInstance(g); ModelingResources m = ModelingResources.getInstance(g); Resource componentType = null; if (g.isInheritedFrom(elementType, d.DefinedElement)) componentType = Symbol.getComponentTypeFromSymbolType(g, elementType); else { componentType = elementType; elementType = Symbol.getSymbolTypeFromComponentType(g,componentType); } Resource element = g.newResource(); g.claim(element, l0.InstanceOf, elementType); Resource module = g.newResource(); g.claim(module, l0.InstanceOf, componentType); g.claim(module, m.ComponentToElement, element); g.claim(module, m.Mapped, module); String id = generateName(g, componentType, name); g.claimLiteral(module, l0.HasName, id); g.claimLiteral(element, l0.HasName, id); OrderedSetUtils.add(g, this.diagram, element); g.claim(this.diagram, l0.ConsistsOf, element); g.claim(this.composite, l0.ConsistsOf, module); T s = constructSymbol(g, element, module); s.setSymbolTranslation(g, x, y); elementToSymbolMap.put(element, s); return s; } /** * Returns a Symbol for given resource. * @param g * @param element Element or Component of the Symbol. * @return * @throws ManyObjectsForFunctionalRelationException * @throws ServiceException * @throws NoSingleResultException */ public T getSymbol(ReadGraph g, Resource element) throws DatabaseException { ModelingResources mr = ModelingResources.getInstance(g); DiagramResource dr = DiagramResource.getInstance(g); if (g.isInstanceOf(element, dr.Element)) return getSymbol(g, element, g.getPossibleObject(element, mr.ElementToComponent)); else return getSymbol(g, g.getSingleObject(element, mr.ComponentToElement), element); } /** * Returns a Symbol for given resource. * @param g * @param element Element Resource of the Symbol. * @param component Component Resource of the symbol. * @return */ public T getSymbol(ReadGraph g, Resource element, Resource component) throws DatabaseException { T s = elementToSymbolMap.get(element); if (s == null) { // TODO : check that symbol actually belongs to this diagram s = constructSymbol(g, element, component); elementToSymbolMap.put(element, s); } else { if (s.component == null) s.component = component; else if (!s.component.equals(component)) { // FIXME : ? return constructSymbol(g, element, component); } } return s; } /** * Adds new element to a diagram * @param g * @param symbolType type of the element. * @param x * @param y * @return * @throws DatabaseException */ public T addElement(WriteGraph g, Resource symbolType, double x, double y) throws DatabaseException { Layer0 l0 = Layer0.getInstance(g); Resource element = g.newResource(); g.claim(element, l0.InstanceOf, symbolType); DiagramUtils.addElement(g, this, element); T s = constructSymbol(g,element,null); s.setSymbolTranslation(g, x, y); elementToSymbolMap.put(element, s); return s; } public T getSingleSymbol(ReadGraph g, String symbolName) throws DatabaseException { List matching = getSymbol(g, symbolName); if (matching.size() == 1) return matching.get(0); if (matching.size() == 0) return null; throw new NoSingleResultException("There are multiple symbols with name '" + symbolName); } public T getPossibleSymbol(ReadGraph g, String symbolName) throws DatabaseException { List matching = getSymbol(g, symbolName); if (matching.size() == 1) return matching.get(0); return null; } /** * Returns all symbols on the diagram * @param g * @return * @throws DatabaseException */ public List getSymbols(ReadGraph g) throws DatabaseException { ModelingResources m = ModelingResources.getInstance(g); List matching = new ArrayList(); for (Resource element : OrderedSetUtils.toList(g, diagram)) { Resource component = g.getPossibleObject(element, m.ElementToComponent); matching.add(getSymbol(g,element, component)); } return matching; } /** * Returns symbols that have matching name. * * Prioritizes L0.HasLabel over L0.HasName (If symbol has label, it is used for comparison). * * ';' character acts as a splitter, allowing input parameter and a label contain multiple identifiers. * * All the splits * * @param g * @param symbolName * @return * @throws DatabaseException */ public List getSymbol(ReadGraph g, String symbolName) throws DatabaseException { ModelingResources m = ModelingResources.getInstance(g); Layer0 b = Layer0.getInstance(g); List matching = new ArrayList(); String splitId[] = symbolName.split(";"); for (Resource element : OrderedSetUtils.toList(g, diagram)) { Resource component = g.getPossibleObject(element, m.ElementToComponent); if (component == null) component = g.getPossibleObject(element, m.HasParentComponent); if (component == null) continue; String label = g.getPossibleRelatedValue(component, b.HasLabel); String name = g.getRelatedValue(component, b.HasName); if (label != null) { String splitLabel[] = label.split(";"); boolean match = true; for (int i = 0; i < splitId.length; i++) { if(!Arrays.contains(splitLabel, splitId[i])) { match = false; break; } } if (match) { matching.add(getSymbol(g,element, component)); } else { if (label.equals(symbolName) || name.equals(symbolName)) { matching.add(getSymbol(g,element, component)); } } } else { for (String split : splitId) { if (name.equals(split)) { matching.add(getSymbol(g,element, component)); } } } } return matching; } /** * Returns a single symbol matching given name and type. If matching symbol cannot be found, returns null. * Throws a NoSingleResultException if the re are multiple symbols matching the criteria. * * @param g * @param symbolName * @param symbolType * @return * @throws DatabaseException */ public T getSingleSymbol(ReadGraph g, String symbolName, Resource symbolType) throws DatabaseException { List matching = getSymbol(g, symbolName, symbolType); if (matching.size() == 1) return matching.get(0); if (matching.size() == 0) return null; throw new NoSingleResultException("There are multiple symbols with name '" + symbolName + "' and type " + symbolType); } public T getPossibleSymbol(ReadGraph g, String symbolName, Resource symbolType) throws DatabaseException { List matching = getSymbol(g, symbolName, symbolType); if (matching.size() == 1) return matching.get(0); return null; } /** * Returns symbols matching given name and type. * * @param g * @param symbolName * @param symbolType * @return * @throws DatabaseException */ public List getSymbol(ReadGraph g, String symbolName, Resource symbolType) throws DatabaseException { List nameMatching = getSymbol(g, symbolName); List matching = new ArrayList(); for (T s : nameMatching) { if ((g.isInstanceOf(s.getElement(), symbolType)|| g.isInstanceOf(s.getComponent(), symbolType))) { matching.add(s); } } return matching; } public Symbol getFlag(ReadGraph g, Resource flagType, String symbolName, Resource symbolType) throws DatabaseException { Layer0 b = Layer0.getInstance(g); DiagramResource dr = DiagramResource.getInstance(g); List matching = new ArrayList(); for (Resource flag : OrderedSetUtils.toList(g, diagram)) { if (!g.isInstanceOf(flag, dr.Flag)) continue; if (!g.getSingleObject(flag, dr.HasFlagType).equals(flagType)) continue; Symbol flagSymbol = getSymbol(g,flag, null); Symbol connectedSymbol = flagSymbol.getDiagramSingleConnected(g, dr.Flag_ConnectionPoint); Resource component = connectedSymbol.getComponent(); if (component == null) continue; String label = g.getPossibleRelatedValue(component, b.HasLabel); String name = g.getRelatedValue(component, b.HasName); if (label != null) { String splitId[] = symbolName.split(";"); if (splitId.length > 1) { String splitLabel[] = label.split(";"); boolean match = true; for (int i = 0; i < splitId.length; i++) { if(!Arrays.contains(splitLabel, splitId[i])) { match = false; break; } } if (match) { if (g.isInstanceOf(connectedSymbol.getElement(), symbolType)|| g.isInstanceOf(component, symbolType)) { matching.add(getSymbol(g,flag, null)); } } } else { if (label.equals(symbolName) || name.equals(symbolName)) { if (g.isInstanceOf(connectedSymbol.getElement(), symbolType)|| g.isInstanceOf(component, symbolType)) { matching.add(getSymbol(g,flag, null)); } } } } else { if (name.equals(symbolName)) { if (g.isInstanceOf(connectedSymbol.getElement(), symbolType)|| g.isInstanceOf(component, symbolType)) { matching.add(getSymbol(g,flag, null)); } } } } if (matching.size() == 1) return matching.get(0); if (matching.size() == 0) return null; throw new NoSingleResultException("There are multiple flags connecting symbol with name '" + symbolName + "' and type " + symbolType); } public PageDesc getPageDesc(ReadGraph g) throws DatabaseException { PageDesc pDesc = DiagramGraphUtil.getPageDesc(g, diagram, PageDesc.DEFAULT); return pDesc; } @Override public boolean equals(Object o) { if (o == null) return false; if (this.getClass() != o.getClass()) return false; Diagram other = (Diagram)o; return diagram.equals(other.diagram); } @Override public int hashCode() { return diagram.hashCode(); } void merge(Symbol to, Symbol from) { Resource element = from.getElement(); for (Symbol s : elementToSymbolMap.values()) { if (s.element.equals(element)) { s.element = to.element; s.component = to.component; } } from.element = to.element; from.component = to.component; } /** * Returns a model containing the diagram. * @param g * @return * @throws DatabaseException */ public Resource getModel(ReadGraph g) throws DatabaseException { Layer0 l0 = Layer0.getInstance(g); SimulationResource sim = SimulationResource.getInstance(g); Resource r = composite; while (true) { Resource parent = g.getSingleObject(r, l0.PartOf); if (g.isInstanceOf(parent, sim.Model)) return parent; r = parent; } } }