Sync git svn branch with SVN repository r33176.
authorTuukka Lehtonen <tuukka.lehtonen@semantum.fi>
Wed, 7 Sep 2016 12:50:57 +0000 (15:50 +0300)
committerTuukka Lehtonen <tuukka.lehtonen@semantum.fi>
Wed, 7 Sep 2016 12:50:57 +0000 (15:50 +0300)
refs #6677

bundles/org.simantics.diagram/src/org/simantics/diagram/flag/FlagUtil.java
bundles/org.simantics.diagram/src/org/simantics/diagram/flag/Joiner.java
bundles/org.simantics.diagram/src/org/simantics/diagram/handler/ConnectionSplitAndJoin.java

index 6f884e06afbf8651906977be9ac17df04802eb90..27dc9e307e69038ac40daa53f5c24dc8057b412e 100644 (file)
@@ -247,14 +247,25 @@ public final class FlagUtil {
         RemoverUtil.remove(graph, flag);\r
     }\r
 \r
+    /**\r
+     * @param graph\r
+     * @param flag\r
+     * @return <code>true</code> only if the specified flag is joined only\r
+     *         within the single diagram it resides in\r
+     * @throws DatabaseException\r
+     */\r
     public static boolean isJoinedInSingleDiagram(ReadGraph graph, Resource flag) throws DatabaseException {\r
-        DiagramResource DIA = DiagramResource.getInstance(graph);\r
-        Resource counterpart = getPossibleCounterpart(graph, flag);\r
-        if (counterpart == null)\r
+        Collection<Resource> counterparts = getCounterparts(graph, flag);\r
+        if (counterparts.isEmpty())\r
             return false;\r
-        return !Collections.disjoint(\r
-                OrderedSetUtils.getOwnerLists(graph, flag, DIA.Diagram),\r
-                OrderedSetUtils.getOwnerLists(graph, counterpart, DIA.Diagram));\r
+        DiagramResource DIA = DiagramResource.getInstance(graph);\r
+        Collection<Resource> flagDiagrams = OrderedSetUtils.getOwnerLists(graph, flag, DIA.Diagram);\r
+        for (Resource counterpart : counterparts) {\r
+            if (Collections.disjoint(flagDiagrams,\r
+                    OrderedSetUtils.getOwnerLists(graph, counterpart, DIA.Diagram)))\r
+                return false;\r
+        }\r
+        return true;\r
     }\r
 \r
     public static boolean isJoinedBetweenDiagrams(ReadGraph graph, Resource flag) throws DatabaseException {\r
index cc02b763801d2493972ec166cb3215c156a81d4d..490a770dfb631e4a24567aada7f24011747a7413 100644 (file)
@@ -1,6 +1,5 @@
 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
@@ -33,127 +32,157 @@ public class Joiner {
     StructuralResource2 STR;\r
     ModelingResources   MOD;\r
 \r
+    ConnectionUtil cu;\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
+        this.cu = new ConnectionUtil(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
+        // Only those flags that are known to be removed during the joining of\r
+        // two flags are added to this set to prevent processing them multiple times.\r
+        Set<Resource> removed = new HashSet<Resource>();\r
+        for (Resource flag1 : flags) {\r
+            Collection<Resource> flag1Counterparts = FlagUtil.getCounterparts(graph, flag1);\r
+            for (Resource flag2 : flag1Counterparts) {\r
+                boolean flag1Removed = FlagUtil.countCounterparts(graph, flag1) <= 1;\r
+                boolean flag2Removed = FlagUtil.countCounterparts(graph, flag2) <= 1;\r
+                if (flag1Removed && !removed.add(flag1))\r
+                    continue;\r
+                if (flag2Removed && !removed.add(flag2))\r
+                    continue;\r
+                boolean switchFlags = !flag1Removed && flag2Removed;\r
+                Resource f1 = switchFlags ? flag2 : flag1;\r
+                Resource f2 = switchFlags ? flag1 : flag2;\r
+                joinFlagPair(graph, f1, f2);\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
+        }\r
+    }\r
+\r
+    private boolean joinFlagPair(WriteGraph graph, Resource flag1, Resource flag2) throws DatabaseException {\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\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
+            return false;\r
+\r
+        // If a flag has more than 1 counterpart it must not be\r
+        // removed because it is a merged flag that has multiple\r
+        // connection joins attached to it.\r
+        boolean removeFlag1 = FlagUtil.countCounterparts(graph, flag1) <= 1;\r
+        boolean removeFlag2 = FlagUtil.countCounterparts(graph, flag2) <= 1;\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
+        if (removeFlag1) {\r
             for (Resource connector : graph.getObjects(flag1, STR.IsConnectedTo)) {\r
-                connector1 = graph.getPossibleObject(connector, DIA.AreConnected);\r
+                connector1 = graph.getSingleObject(connector, DIA.AreConnected);\r
                 connection1 = ConnectionUtil.getConnection(graph, connector1);\r
                 cu.removeConnectionPart(connector);\r
             }\r
+        }\r
+        if (removeFlag2) {\r
             for (Resource connector : graph.getObjects(flag2, STR.IsConnectedTo)) {\r
-                connector2 = graph.getPossibleObject(connector, DIA.AreConnected);\r
+                connector2 = graph.getSingleObject(connector, DIA.AreConnected);\r
                 connection2 = ConnectionUtil.getConnection(graph, connector2);\r
                 cu.removeConnectionPart(connector);\r
             }\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
+        // 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
+        // Remove connection join and flags when necessary\r
+        if (removeFlag1)\r
             for (Resource diagram : OrderedSetUtils.getOwnerLists(graph, flag1, DIA.Diagram))\r
                 OrderedSetUtils.remove(graph, diagram, flag1);\r
+        if (removeFlag2)\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
+        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
+        if (removeFlag1)\r
             RemoverUtil.remove(graph, flag1);\r
+        if (removeFlag2)\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
+        // 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
+        // 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
-                graph.claim(connector1, DIA.AreConnected, connector2);\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
+        return true;\r
     }\r
-    \r
+\r
     public static void joinFlagsLocal(WriteGraph graph, Collection<Resource> flags) throws DatabaseException {\r
         new Joiner(graph).joinLocal(graph, flags);\r
     }\r
index 9e7a1637ebe2eba7ec0605b969de441f3ac3117c..d46c427320c7b06e8c13a6a77112202146a6d50f 100644 (file)
@@ -29,6 +29,7 @@ import org.simantics.db.Session;
 import org.simantics.db.WriteGraph;\r
 import org.simantics.db.common.request.IndexRoot;\r
 import org.simantics.db.common.request.WriteRequest;\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.content.EdgeResource;\r
@@ -150,13 +151,20 @@ public class ConnectionSplitAndJoin extends DynamicMenuContribution {
             Resource r = AdaptionUtils.adaptToSingle(s, Resource.class);\r
             if (r == null || !graph.isInstanceOf(r, DIA.Flag))\r
                 return Collections.emptyList();\r
-            Resource counterpart = FlagUtil.getPossibleCounterpart(graph, r);\r
-            if (counterpart == null)\r
+            if (!isConnectedToSomething(graph, r))\r
                 return Collections.emptyList();\r
-            if (!FlagUtil.isJoinedInSingleDiagram(graph, r))\r
-                return Collections.emptyList();\r
-            if (!isConnectedToSomething(graph, r) || !isConnectedToSomething(graph, counterpart))\r
+            Collection<Resource> counterparts = FlagUtil.getCounterparts(graph, r);\r
+            if (counterparts.isEmpty())\r
                 return Collections.emptyList();\r
+            Collection<Resource> flagDiagrams = OrderedSetUtils.getOwnerLists(graph, r, DIA.Diagram);\r
+            for (Resource counterpart : counterparts) {\r
+                boolean joinedWithinSingleDiagram = !Collections.disjoint(flagDiagrams,\r
+                        OrderedSetUtils.getOwnerLists(graph, counterpart, DIA.Diagram));\r
+                if (!joinedWithinSingleDiagram)\r
+                    return Collections.emptyList();\r
+                if (!isConnectedToSomething(graph, counterpart))\r
+                    return Collections.emptyList();\r
+            }\r
             result.add(r);\r
         }\r
         return result;\r