]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.diagram/src/org/simantics/diagram/flag/Joiner.java
Migrated source code from Simantics SVN
[simantics/platform.git] / bundles / org.simantics.diagram / src / org / simantics / diagram / flag / Joiner.java
diff --git a/bundles/org.simantics.diagram/src/org/simantics/diagram/flag/Joiner.java b/bundles/org.simantics.diagram/src/org/simantics/diagram/flag/Joiner.java
new file mode 100644 (file)
index 0000000..cc02b76
--- /dev/null
@@ -0,0 +1,161 @@
+package org.simantics.diagram.flag;\r
+\r
+import java.util.ArrayDeque;\r
+import java.util.Collection;\r
+import java.util.HashSet;\r
+import java.util.Set;\r
+\r
+import org.simantics.databoard.Bindings;\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.utils.OrderedSetUtils;\r
+import org.simantics.db.exception.DatabaseException;\r
+import org.simantics.db.layer0.util.RemoverUtil;\r
+import org.simantics.diagram.content.ConnectionUtil;\r
+import org.simantics.diagram.stubs.DiagramResource;\r
+import org.simantics.g2d.elementclass.FlagClass.Type;\r
+import org.simantics.layer0.Layer0;\r
+import org.simantics.modeling.ModelingResources;\r
+import org.simantics.structural.stubs.StructuralResource2;\r
+\r
+/**\r
+ * A class that handles joining of diagram-local flag pairs into a direct\r
+ * connections.\r
+ * \r
+ * @author Tuukka Lehtonen\r
+ */\r
+public class Joiner {\r
+\r
+    Layer0              L0;\r
+    DiagramResource     DIA;\r
+    StructuralResource2 STR;\r
+    ModelingResources   MOD;\r
+\r
+    public Joiner(ReadGraph graph) {\r
+        this.L0 = Layer0.getInstance(graph);\r
+        this.DIA = DiagramResource.getInstance(graph);\r
+        this.STR = StructuralResource2.getInstance(graph);\r
+        this.MOD = ModelingResources.getInstance(graph);\r
+    }\r
+\r
+    public void joinLocal(WriteGraph graph, Collection<Resource> flags) throws DatabaseException {\r
+        ConnectionUtil cu = new ConnectionUtil(graph);\r
+        Set<Resource> visited = new HashSet<Resource>();\r
+        ArrayDeque<Resource> todo = new ArrayDeque<Resource>(flags);\r
+        while (!todo.isEmpty()) {\r
+            Resource flag1 = todo.poll();\r
+            Resource flag2 = FlagUtil.getPossibleCounterpart(graph, flag1);\r
+            if (flag2 == null || !visited.add(flag1) || !visited.add(flag2))\r
+                continue;\r
+\r
+            Type type1 = FlagUtil.getFlagType(graph, flag1);\r
+            Type type2 = FlagUtil.getFlagType(graph, flag2);\r
+\r
+            Resource connector1 = null;\r
+            Resource connector2 = null;\r
+\r
+            Resource connection1 = null;\r
+            Resource connection2 = null;\r
+\r
+            // #7781: prevent joining of flags where one of them or both are\r
+            // have no connections to them, i.e. are disconnected.\r
+            // First ensure that both flags are connected to something,\r
+            // even considering joining them.\r
+            for (Resource connector : graph.getObjects(flag1, STR.IsConnectedTo)) {\r
+                connector1 = graph.getPossibleObject(connector, DIA.AreConnected);\r
+                connection1 = ConnectionUtil.getConnection(graph, connector1);\r
+            }\r
+            for (Resource connector : graph.getObjects(flag2, STR.IsConnectedTo)) {\r
+                connector2 = graph.getPossibleObject(connector, DIA.AreConnected);\r
+                connection2 = ConnectionUtil.getConnection(graph, connector2);\r
+            }\r
+            if (connection1 == null || connector1 == null || connection2 == null || connector2 == null)\r
+                continue;\r
+\r
+            // Disconnect flags from their respective edges\r
+            // This code relies on the fact that flag terminals are\r
+            // functional and can only be connected once. This implies\r
+            // that their :DIA.Connectors cannot have more than one\r
+            // AreConnected relation.\r
+            for (Resource connector : graph.getObjects(flag1, STR.IsConnectedTo)) {\r
+                connector1 = graph.getPossibleObject(connector, DIA.AreConnected);\r
+                connection1 = ConnectionUtil.getConnection(graph, connector1);\r
+                cu.removeConnectionPart(connector);\r
+            }\r
+            for (Resource connector : graph.getObjects(flag2, STR.IsConnectedTo)) {\r
+                connector2 = graph.getPossibleObject(connector, DIA.AreConnected);\r
+                connection2 = ConnectionUtil.getConnection(graph, connector2);\r
+                cu.removeConnectionPart(connector);\r
+            }\r
+\r
+            // Decide which connection to remove. The strategy is:\r
+            // * always keep the connection that has an ElementToComponent relation.\r
+            // * if there are no ElementToComponent relations, keep the connection on the OutputFlag side.\r
+            // * if flag type information is not available, keep connection1.\r
+            Resource connectionToKeep = connection1;\r
+            Resource connectionToRemove = connection2;\r
+            Resource hasElementToComponent1 = graph.getPossibleObject(connection1, MOD.ElementToComponent);\r
+            Resource hasElementToComponent2 = graph.getPossibleObject(connection2, MOD.ElementToComponent);\r
+            if (hasElementToComponent1 != null && hasElementToComponent2 != null)\r
+                throw new UnsupportedOperationException("Both flag are connected with connections that have mapped components, can't decide which connection to remove in join operation");\r
+            if (hasElementToComponent2 != null\r
+                    || (type1 != Type.Out && type2 == Type.Out)) {\r
+                connectionToKeep = connection2;\r
+                connectionToRemove = connection1;\r
+            }\r
+\r
+            // Remove connection join and flags\r
+            for (Resource diagram : OrderedSetUtils.getOwnerLists(graph, flag1, DIA.Diagram))\r
+                OrderedSetUtils.remove(graph, diagram, flag1);\r
+            for (Resource diagram : OrderedSetUtils.getOwnerLists(graph, flag2, DIA.Diagram))\r
+                OrderedSetUtils.remove(graph, diagram, flag2);\r
+            FlagUtil.disconnectFlag(graph, flag1);            \r
+            double[] transform1 = graph.getRelatedValue(flag1, DIA.HasTransform, Bindings.DOUBLE_ARRAY);\r
+            double[] transform2 = graph.getRelatedValue(flag2, DIA.HasTransform, Bindings.DOUBLE_ARRAY);\r
+            RemoverUtil.remove(graph, flag1);\r
+            RemoverUtil.remove(graph, flag2);\r
+\r
+            // Move connector from connection to remove to other connection\r
+            for(Statement connectorStat : graph.getStatements(connectionToRemove, DIA.HasConnector)) {\r
+               graph.deny(connectorStat);\r
+                graph.claim(connectionToKeep, connectorStat.getPredicate(), connectorStat.getObject());\r
+            }\r
+            for(Resource node : graph.getObjects(connectionToRemove, DIA.HasInteriorRouteNode)) {\r
+                graph.deny(node, DIA.HasInteriorRouteNode_Inverse, connectionToRemove);\r
+                graph.claim(node, DIA.HasInteriorRouteNode_Inverse, connectionToKeep);\r
+            }\r
+\r
+            // Remove obsolete connection\r
+            cu.removeConnection(connectionToRemove);\r
+\r
+            // Reconnect respective edges\r
+            if(graph.isInstanceOf(connector1, DIA.RouteLine) && graph.isInstanceOf(connector2, DIA.RouteLine)\r
+                    && graph.getRelatedValue(connector1, DIA.IsHorizontal) == graph.getRelatedValue(connector2, DIA.IsHorizontal)) {\r
+                boolean horizontal = graph.getRelatedValue(connector1, DIA.IsHorizontal);\r
+                \r
+                double position;\r
+                if(horizontal)\r
+                    position = 0.5 * (transform1[4] + transform2[4]);\r
+                else\r
+                    position = 0.5 * (transform1[5] + transform2[5]);\r
+                \r
+                Resource intermediateRouteLine = graph.newResource();\r
+                graph.claim(intermediateRouteLine, L0.InstanceOf, DIA.RouteLine);\r
+                graph.claimLiteral(intermediateRouteLine, DIA.IsHorizontal, !horizontal);\r
+                graph.claimLiteral(intermediateRouteLine, DIA.HasPosition, position);\r
+                graph.claim(connectionToKeep, DIA.HasInteriorRouteNode, intermediateRouteLine);\r
+                graph.claim(connector1, DIA.AreConnected, intermediateRouteLine);\r
+                graph.claim(connector2, DIA.AreConnected, intermediateRouteLine);\r
+            }\r
+            else\r
+                graph.claim(connector1, DIA.AreConnected, connector2);\r
+        }\r
+    }\r
+    \r
+    public static void joinFlagsLocal(WriteGraph graph, Collection<Resource> flags) throws DatabaseException {\r
+        new Joiner(graph).joinLocal(graph, flags);\r
+    }\r
+\r
+}
\ No newline at end of file