--- /dev/null
+/*******************************************************************************\r
+ * Copyright (c) 2012 Association for Decentralized Information Management in\r
+ * Industry THTH ry.\r
+ * All rights reserved. This program and the accompanying materials\r
+ * are made available under the terms of the Eclipse Public License v1.0\r
+ * which accompanies this distribution, and is available at\r
+ * http://www.eclipse.org/legal/epl-v10.html\r
+ *\r
+ * Contributors:\r
+ * VTT Technical Research Centre of Finland - initial API and implementation\r
+ *******************************************************************************/\r
+package org.simantics.modeling.flags;\r
+\r
+import gnu.trove.map.hash.THashMap;\r
+import gnu.trove.set.hash.THashSet;\r
+\r
+import java.util.ArrayList;\r
+import java.util.Iterator;\r
+import java.util.List;\r
+\r
+import org.simantics.db.ReadGraph;\r
+import org.simantics.db.Resource;\r
+import org.simantics.db.Statement;\r
+import org.simantics.db.WriteGraph;\r
+import org.simantics.db.common.request.IndexRoot;\r
+import org.simantics.db.common.utils.NameUtils;\r
+import org.simantics.db.common.utils.OrderedSetUtils;\r
+import org.simantics.db.exception.DatabaseException;\r
+import org.simantics.diagram.content.ConnectionUtil;\r
+import org.simantics.diagram.stubs.DiagramResource;\r
+import org.simantics.layer0.Layer0;\r
+import org.simantics.modeling.ModelingResources;\r
+import org.simantics.scl.commands.Commands;\r
+import org.simantics.scl.runtime.tuple.Tuple2;\r
+import org.simantics.structural.stubs.StructuralResource2;\r
+\r
+public class MergeFlags {\r
+\r
+ private static class CP {\r
+ public final Resource component;\r
+ public final Resource connectionPoint;\r
+ \r
+ public CP(Resource component, Resource connectionPoint) {\r
+ super();\r
+ this.component = component;\r
+ this.connectionPoint = connectionPoint;\r
+ }\r
+\r
+ @Override\r
+ public int hashCode() {\r
+ return component.hashCode() + 31 * connectionPoint.hashCode();\r
+ }\r
+\r
+ @Override\r
+ public boolean equals(Object obj) {\r
+ if (this == obj)\r
+ return true;\r
+ if (obj == null)\r
+ return false;\r
+ if (getClass() != obj.getClass())\r
+ return false;\r
+ CP other = (CP) obj;\r
+ return component.equals(other.component) \r
+ && connectionPoint.equals(other.connectionPoint);\r
+ }\r
+ }\r
+ \r
+ public static String validateForMerge(ReadGraph g,\r
+ List<Resource> flags) throws DatabaseException {\r
+ if(flags.size() <= 1)\r
+ return "At least two flags must be chosen.";\r
+ \r
+ DiagramResource DIA = DiagramResource.getInstance(g);\r
+ StructuralResource2 STR = StructuralResource2.getInstance(g);\r
+ \r
+ for(Resource flag : flags)\r
+ if(!g.hasStatement(flag, DIA.FlagIsJoinedBy))\r
+ return "All flags are not joined to other flags.";\r
+\r
+ List<Resource> connectors = getPossibleRelated(g, flags, DIA.Flag_ConnectionPoint);\r
+ for(Resource connector : connectors) {\r
+ if(connector == null)\r
+ return "All flags are not connected";\r
+ }\r
+ \r
+ List<Resource> connections = getConnection(g, flags, connectors);\r
+ for(Resource connection : connections) {\r
+ if(connection == null)\r
+ return "Invalid flag. Didn't find configuration connection.";\r
+ }\r
+ \r
+ THashSet<Resource> uniqueConnections = new THashSet<Resource>(connections.size());\r
+ for(Resource connection : connections) {\r
+ uniqueConnections.add(connection);\r
+ }\r
+ \r
+ if(uniqueConnections.size() == 1)\r
+ return null;\r
+ \r
+ Iterator<Resource> it = uniqueConnections.iterator();\r
+ THashSet<CP> cps = getConnectionPoints(g, STR, it.next());\r
+ while(it.hasNext()) {\r
+ cps.retainAll(getConnectionPoints(g, STR, it.next()));\r
+ if(cps.isEmpty())\r
+ return "Flags are not connected to a common terminal.";\r
+ }\r
+ \r
+ return null;\r
+ }\r
+ \r
+ private static THashSet<CP> getConnectionPoints(ReadGraph g, StructuralResource2 STR, Resource connection) throws DatabaseException {\r
+ THashSet<CP> result = new THashSet<CP>(); \r
+ for(Statement stat : g.getStatements(connection, STR.Connects))\r
+ result.add(new CP(stat.getObject(), g.getInverse(stat.getPredicate())));\r
+ return result;\r
+ }\r
+ \r
+ /**\r
+ * Merges all flags in the given list to one or two flags.\r
+ * @param g\r
+ * @param flags to join\r
+ * @return Error message or empty string if the operation succeeded.\r
+ */\r
+ public static String merge(WriteGraph g, List<Resource> flags) throws DatabaseException {\r
+ return (String)Commands.get(g, "Simantics/Flag/mergeFlags").execute(g, g.syncRequest(new IndexRoot(flags.get(0))), flags);\r
+ }\r
+ \r
+ public static String mergeWithoutMetadata(WriteGraph g, List<Resource> flags) throws DatabaseException {\r
+ THashMap<Tuple2, ArrayList<Resource>> groups = \r
+ new THashMap<Tuple2, ArrayList<Resource>>();\r
+ \r
+ DiagramResource DIA = DiagramResource.getInstance(g);\r
+ StructuralResource2 STR = StructuralResource2.getInstance(g);\r
+ for(Resource flag : flags) {\r
+ Resource connector = g.getSingleObject(flag, DIA.Flag_ConnectionPoint);\r
+ Resource connection = null;\r
+ for(Resource temp : g.getObjects(connector, STR.Connects))\r
+ if(!temp.equals(flag)) {\r
+ connection = temp;\r
+ break;\r
+ }\r
+ if(connection == null)\r
+ continue;\r
+ \r
+ Resource flagType = g.getSingleObject(flag, DIA.HasFlagType);\r
+ Resource connectionType = g.getPossibleObject(connection, STR.HasConnectionType);\r
+ \r
+ Tuple2 tuple = new Tuple2(flagType, connectionType);\r
+ ArrayList<Resource> group = groups.get(tuple);\r
+ if(group == null) {\r
+ group = new ArrayList<Resource>();\r
+ groups.put(tuple, group);\r
+ }\r
+ group.add(flag);\r
+ }\r
+ \r
+ String errorMessage = "";\r
+ for(ArrayList<Resource> group : groups.values()) {\r
+ if(group.size() > 1) {\r
+ String temp = mergeAux(g, group);\r
+ if(temp != null)\r
+ errorMessage = temp;\r
+ }\r
+ }\r
+ return errorMessage;\r
+ }\r
+ \r
+ /**\r
+ * Merges all flags in the given list to the first flag of the list.\r
+ * @param g\r
+ * @param flags to join\r
+ * @return Error message or null if the operation succeeded.\r
+ */\r
+ private static String mergeAux(WriteGraph g, List<Resource> flags) throws DatabaseException {\r
+ if(flags.size() <= 1)\r
+ return null; // Nothing to do\r
+ \r
+ DiagramResource DIA = DiagramResource.getInstance(g);\r
+ StructuralResource2 STR = StructuralResource2.getInstance(g);\r
+ \r
+ // Find connectors\r
+ List<Resource> connectors = getPossibleRelated(g, flags, DIA.Flag_ConnectionPoint);\r
+ for(Resource connector : connectors) {\r
+ if(connector == null)\r
+ return "All flags are not connected";\r
+ } \r
+ \r
+ // Find configuration connections\r
+ List<Resource> connections = getConnection(g, flags, connectors);\r
+ for(Resource connection : connections) {\r
+ if(connection == null)\r
+ return "Invalid flag. Didn't find configuration connection.";\r
+ }\r
+ \r
+ // Choose canonical flag and connection where other flags are merged to\r
+ Resource canonicalFlag = flags.get(0);\r
+ Resource canonicalConnection = connections.get(0);\r
+ \r
+ // Do the merging\r
+ for(int i=1;i<flags.size();++i) {\r
+ Resource flag = flags.get(i);\r
+ Resource connection = connections.get(i);\r
+ \r
+ // Replace references in joins to the canonical flag and connection\r
+ for(Resource join : g.getObjects(flag, DIA.FlagIsJoinedBy)) {\r
+ g.denyStatement(flag, DIA.FlagIsJoinedBy, join);\r
+ g.claim(canonicalFlag, DIA.FlagIsJoinedBy, join);\r
+ g.denyStatement(join, STR.Joins, connection);\r
+ g.claim(join, STR.Joins, canonicalConnection);\r
+ }\r
+ \r
+ // Remove flag and its connector\r
+ removeElement(g, flag);\r
+ g.deny(connectors.get(i));\r
+ }\r
+ \r
+ // Clean up connections (remove extra route lines and complete connections)\r
+ THashSet<Resource> uniqueConnections = new THashSet<Resource>(connections.size());\r
+ for(int i=1;i<connections.size();++i)\r
+ uniqueConnections.add(connections.get(i));\r
+ \r
+ for(Resource connection : uniqueConnections)\r
+ cleanUpConnection(g, connection);\r
+ \r
+ return null;\r
+ }\r
+\r
+ private static void cleanUpConnection(WriteGraph g, Resource connection) throws DatabaseException {\r
+ DiagramResource DIA = DiagramResource.getInstance(g);\r
+ StructuralResource2 STR = StructuralResource2.getInstance(g);\r
+ ModelingResources MOD = ModelingResources.getInstance(g);\r
+ \r
+ Resource diagramConnection = g.getSingleObject(connection, MOD.ConnectionToDiagramConnection);\r
+ \r
+ // If connection is degenerated, remove it\r
+ if(g.getObjects(connection, STR.IsJoinedBy).size() == 0 && \r
+ g.getObjects(connection, STR.Connects).size() <= 1) {\r
+ g.deny(connection);\r
+ // Garbage collection removes interior route nodes etc. stuff\r
+ //removeElement(g, diagramConnection);\r
+ // platform #4473: Remove connection completely to not leave behind stray connectors.\r
+ new ConnectionUtil(g).removeConnection(diagramConnection);\r
+ }\r
+ // Otherwise just remove degenerated route nodes\r
+ else {\r
+ boolean modifiedSomething = true;\r
+ while(modifiedSomething) { // loop until no modification are made (O(n^2) algorithm)\r
+ modifiedSomething = false;\r
+ for(Resource routeNode : g.getObjects(diagramConnection, DIA.HasInteriorRouteNode))\r
+ if(g.getObjects(routeNode, DIA.AreConnected).size() <= 1) {\r
+ g.deny(routeNode);\r
+ modifiedSomething = true;\r
+ }\r
+ }\r
+ }\r
+ }\r
+ \r
+ private static void removeElement(WriteGraph g, Resource element) throws DatabaseException {\r
+ OrderedSetUtils.remove(g, OrderedSetUtils.getSingleOwnerList(g, element), element);\r
+ g.deny(element);\r
+ }\r
+\r
+ private static List<Resource> getConnection(ReadGraph g, List<Resource> flags, List<Resource> connectors) throws DatabaseException {\r
+ ArrayList<Resource> result = new ArrayList<Resource>(flags.size());\r
+ for(int i=0;i<flags.size();++i)\r
+ result.add(getConnection(g, flags.get(i), connectors.get(i)));\r
+ return result;\r
+ }\r
+ \r
+ private static Resource getConnection(ReadGraph g, Resource flag, Resource connector) throws DatabaseException {\r
+ StructuralResource2 STR = StructuralResource2.getInstance(g);\r
+ ModelingResources MOD = ModelingResources.getInstance(g);\r
+ for(Resource diagramConnection : g.getObjects(connector, STR.Connects)) {\r
+ if(!flag.equals(diagramConnection)) {\r
+ return g.getPossibleObject(diagramConnection, MOD.DiagramConnectionToConnection);\r
+ }\r
+ }\r
+ return null;\r
+ }\r
+ \r
+ private static List<Resource> getPossibleRelated(ReadGraph g, List<Resource> subjects, Resource relation) throws DatabaseException {\r
+ ArrayList<Resource> result = new ArrayList<Resource>(subjects.size());\r
+ for(int i=0;i<subjects.size();++i)\r
+ result.add(g.getPossibleObject(subjects.get(i), relation));\r
+ return result;\r
+ }\r
+\r
+ public static void expandFlagSet(ReadGraph graph, List<Resource> flags) throws DatabaseException {\r
+ DiagramResource DIA = DiagramResource.getInstance(graph);\r
+ StructuralResource2 STR = StructuralResource2.getInstance(graph);\r
+ \r
+ THashSet<Resource> connectionSet = new THashSet<Resource>();\r
+ \r
+ for(Resource flag : flags) {\r
+ for(Resource connector : graph.getObjects(flag, STR.IsConnectedTo))\r
+ for(Resource connection : graph.getObjects(connector, STR.Connects))\r
+ if(!connection.equals(flag))\r
+ connectionSet.add(connection);\r
+ }\r
+ \r
+ for(Resource connection : connectionSet.toArray(new Resource[connectionSet.size()])) {\r
+ for(Resource connector : graph.getObjects(connection, STR.IsConnectedTo))\r
+ for(Statement stat : graph.getStatements(connector, STR.Connects))\r
+ if(!stat.getObject().equals(connection))\r
+ for(Resource connector2 : graph.getObjects(stat.getObject(), graph.getInverse(stat.getPredicate())))\r
+ if(!connector2.equals(connector))\r
+ for(Resource connection2 : graph.getObjects(connector2, STR.Connects))\r
+ if(graph.isInstanceOf(connection2, DIA.Connection))\r
+ connectionSet.add(connection2);\r
+ }\r
+ \r
+ THashSet<Resource> visited = new THashSet<Resource>(flags);\r
+ \r
+ for(Resource connection : connectionSet) {\r
+ visited.add(connection);\r
+ for(Resource connector : graph.getObjects(connection, STR.IsConnectedTo))\r
+ for(Resource flag : graph.getObjects(connector, STR.Connects))\r
+ if(visited.add(flag) && graph.isInstanceOf(flag, DIA.Flag)\r
+ && graph.hasStatement(flag, DIA.FlagIsJoinedBy))\r
+ flags.add(flag);\r
+ } \r
+ } \r
+ \r
+ public static void collectFlagGroupsInComposite(ReadGraph g, Resource composite, ArrayList<ArrayList<Resource>> groups) throws DatabaseException {\r
+ DiagramResource DIA = DiagramResource.getInstance(g);\r
+ ModelingResources MOD = ModelingResources.getInstance(g);\r
+ Layer0 L0 = Layer0.getInstance(g);\r
+ \r
+ Resource diagram = g.getPossibleObject(composite, MOD.CompositeToDiagram);\r
+ if(diagram == null)\r
+ return;\r
+ \r
+ THashSet<Resource> flags = new THashSet<Resource>();\r
+ for(Resource element : g.getObjects(diagram, L0.ConsistsOf))\r
+ if(g.isInstanceOf(element, DIA.Flag) && g.hasStatement(element, DIA.FlagIsJoinedBy))\r
+ flags.add(element);\r
+ \r
+ for(Resource flag : flags.toArray(new Resource[flags.size()])) \r
+ if(flags.contains(flag)) {\r
+ ArrayList<Resource> group = new ArrayList<Resource>();\r
+ group.add(flag);\r
+ expandFlagSet(g, group);\r
+ flags.removeAll(group);\r
+ if(group.size() > 1)\r
+ groups.add(group);\r
+ }\r
+ }\r
+ \r
+ public static void expandCompositeSet(ReadGraph g, THashSet<Resource> composites) throws DatabaseException {\r
+ for(Resource composite : composites.toArray(new Resource[composites.size()]))\r
+ expandCompositeSet(g, composite, composites);\r
+ }\r
+\r
+ private static void expandCompositeSet(ReadGraph g, Resource composite,\r
+ THashSet<Resource> composites) throws DatabaseException {\r
+ Layer0 L0 = Layer0.getInstance(g);\r
+ StructuralResource2 STR = StructuralResource2.getInstance(g);\r
+ for(Resource child : g.getObjects(composite, L0.ConsistsOf))\r
+ if(g.isInstanceOf(child, STR.Composite))\r
+ if(composites.add(child))\r
+ expandCompositeSet(g, child, composites);\r
+ }\r
+ \r
+ public static String mergeFlags (WriteGraph graph, Resource diagram ) throws DatabaseException {\r
+ \r
+ ArrayList<ArrayList<Resource>> groups = new ArrayList<ArrayList<Resource>>();\r
+ MergeFlags.collectFlagGroupsInComposite(graph, diagram, groups);\r
+ for(ArrayList<Resource> group : groups) {\r
+ MergeFlags.merge(graph, group);\r
+ }\r
+\r
+ return "Merged flags in diagram resource: " + diagram.toString() + " and name: " + NameUtils.getSafeName(graph, diagram);\r
+ }\r
+ \r
+ public static void expandFlags(WriteGraph graph, Resource diagram) throws DatabaseException {\r
+ ArrayList<Resource> groups = new ArrayList<Resource>();\r
+ ExpandFlags.collectGroupedFlags(graph, diagram, groups);\r
+ for(Resource group : groups) {\r
+ ExpandFlags.expandFlag(graph, group);\r
+ }\r
+ } \r
+}\r