-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
+package org.simantics.diagram.flag;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.simantics.databoard.Bindings;
+import org.simantics.db.ReadGraph;
+import org.simantics.db.Resource;
+import org.simantics.db.Statement;
+import org.simantics.db.WriteGraph;
+import org.simantics.db.common.utils.OrderedSetUtils;
+import org.simantics.db.exception.DatabaseException;
+import org.simantics.db.layer0.util.RemoverUtil;
+import org.simantics.diagram.content.ConnectionUtil;
+import org.simantics.diagram.stubs.DiagramResource;
+import org.simantics.g2d.elementclass.FlagClass.Type;
+import org.simantics.layer0.Layer0;
+import org.simantics.modeling.ModelingResources;
+import org.simantics.structural.stubs.StructuralResource2;
+
+/**
+ * A class that handles joining of diagram-local flag pairs into a direct
+ * connections.
+ *
+ * @author Tuukka Lehtonen
+ */
+public class Joiner {
+
+ Layer0 L0;
+ DiagramResource DIA;
+ StructuralResource2 STR;
+ ModelingResources MOD;
+
+ ConnectionUtil cu;
+
+ public Joiner(ReadGraph graph) {
+ this.L0 = Layer0.getInstance(graph);
+ this.DIA = DiagramResource.getInstance(graph);
+ this.STR = StructuralResource2.getInstance(graph);
+ this.MOD = ModelingResources.getInstance(graph);
+ this.cu = new ConnectionUtil(graph);
+ }
+
+ public void joinLocal(WriteGraph graph, Collection<Resource> flags) throws DatabaseException {
+ // Only those flags that are known to be removed during the joining of
+ // two flags are added to this set to prevent processing them multiple times.
+ Set<Resource> removed = new HashSet<Resource>();
+ for (Resource flag1 : flags) {
+ Collection<Resource> flag1Counterparts = FlagUtil.getCounterparts(graph, flag1);
+ for (Resource flag2 : flag1Counterparts) {
+ boolean flag1Removed = FlagUtil.countCounterparts(graph, flag1) <= 1;
+ boolean flag2Removed = FlagUtil.countCounterparts(graph, flag2) <= 1;
+ if (flag1Removed && !removed.add(flag1))
+ continue;
+ if (flag2Removed && !removed.add(flag2))
+ continue;
+ boolean switchFlags = !flag1Removed && flag2Removed;
+ Resource f1 = switchFlags ? flag2 : flag1;
+ Resource f2 = switchFlags ? flag1 : flag2;
+ joinFlagPair(graph, f1, f2);
+ }
+ }
+ }
+
+ private boolean joinFlagPair(WriteGraph graph, Resource flag1, Resource flag2) throws DatabaseException {
+ Type type1 = FlagUtil.getFlagType(graph, flag1);
+ Type type2 = FlagUtil.getFlagType(graph, flag2);
+
+ Resource connector1 = null;
+ Resource connector2 = null;
+
+ Resource connection1 = null;
+ Resource connection2 = null;
+
+ // #7781: prevent joining of flags where one of them or both
+ // have no connections to them, i.e. are disconnected.
+ // First ensure that both flags are connected to something,
+ // even considering joining them.
+ for (Resource connector : graph.getObjects(flag1, STR.IsConnectedTo)) {
+ connector1 = graph.getPossibleObject(connector, DIA.AreConnected);
+ connection1 = ConnectionUtil.getConnection(graph, connector1);
+ }
+ for (Resource connector : graph.getObjects(flag2, STR.IsConnectedTo)) {
+ connector2 = graph.getPossibleObject(connector, DIA.AreConnected);
+ connection2 = ConnectionUtil.getConnection(graph, connector2);
+ }
+ if (connection1 == null || connector1 == null || connection2 == null || connector2 == null)
+ return false;
+
+ // If a flag has more than 1 counterpart it must not be
+ // removed because it is a merged flag that has multiple
+ // connection joins attached to it.
+ boolean removeFlag1 = FlagUtil.countCounterparts(graph, flag1) <= 1;
+ boolean removeFlag2 = FlagUtil.countCounterparts(graph, flag2) <= 1;
+
+ // Disconnect flags from their respective edges
+ // This code relies on the fact that flag terminals are
+ // functional and can only be connected once. This implies
+ // that their :DIA.Connectors cannot have more than one
+ // AreConnected relation.
+ if (removeFlag1) {
+ for (Resource connector : graph.getObjects(flag1, STR.IsConnectedTo)) {
+ connector1 = graph.getSingleObject(connector, DIA.AreConnected);
+ connection1 = ConnectionUtil.getConnection(graph, connector1);
+ cu.removeConnectionPart(connector);
+ }
+ }
+ if (removeFlag2) {
+ for (Resource connector : graph.getObjects(flag2, STR.IsConnectedTo)) {
+ connector2 = graph.getSingleObject(connector, DIA.AreConnected);
+ connection2 = ConnectionUtil.getConnection(graph, connector2);
+ cu.removeConnectionPart(connector);
+ }
+ }
+
+ // Decide which connection to remove. The strategy is:
+ // * always keep the connection that has an ElementToComponent relation.
+ // * if there are no ElementToComponent relations, keep the connection on the OutputFlag side.
+ // * if flag type information is not available, keep connection1.
+ Resource connectionToKeep = connection1;
+ Resource connectionToRemove = connection2;
+ Resource hasElementToComponent1 = graph.getPossibleObject(connection1, MOD.ElementToComponent);
+ Resource hasElementToComponent2 = graph.getPossibleObject(connection2, MOD.ElementToComponent);
+ if (hasElementToComponent1 != null && hasElementToComponent2 != null)
+ throw new UnsupportedOperationException("Both flag are connected with connections that have mapped components, can't decide which connection to remove in join operation");
+ if (hasElementToComponent2 != null
+ || (type1 != Type.Out && type2 == Type.Out)) {
+ connectionToKeep = connection2;
+ connectionToRemove = connection1;
+ }
+
+ // Remove connection join and flags when necessary
+ if (removeFlag1)
+ for (Resource diagram : OrderedSetUtils.getOwnerLists(graph, flag1, DIA.Diagram))
+ OrderedSetUtils.remove(graph, diagram, flag1);
+ if (removeFlag2)
+ for (Resource diagram : OrderedSetUtils.getOwnerLists(graph, flag2, DIA.Diagram))
+ OrderedSetUtils.remove(graph, diagram, flag2);
+ FlagUtil.disconnectFlag(graph, flag1);
+ double[] transform1 = graph.getRelatedValue(flag1, DIA.HasTransform, Bindings.DOUBLE_ARRAY);
+ double[] transform2 = graph.getRelatedValue(flag2, DIA.HasTransform, Bindings.DOUBLE_ARRAY);
+ if (removeFlag1)
+ RemoverUtil.remove(graph, flag1);
+ if (removeFlag2)
+ RemoverUtil.remove(graph, flag2);
+
+ // Move connector from connection to remove to other connection
+ for(Statement connectorStat : graph.getStatements(connectionToRemove, DIA.HasConnector)) {
+ graph.deny(connectorStat);
+ graph.claim(connectionToKeep, connectorStat.getPredicate(), connectorStat.getObject());
+ }
+ for(Resource node : graph.getObjects(connectionToRemove, DIA.HasInteriorRouteNode)) {
+ graph.deny(node, DIA.HasInteriorRouteNode_Inverse, connectionToRemove);
+ graph.claim(node, DIA.HasInteriorRouteNode_Inverse, connectionToKeep);
+ }
+
+ // Remove obsolete connection
+ cu.removeConnection(connectionToRemove);
+
+ // Reconnect respective edges
+ if(graph.isInstanceOf(connector1, DIA.RouteLine) && graph.isInstanceOf(connector2, DIA.RouteLine)
+ && graph.getRelatedValue(connector1, DIA.IsHorizontal) == graph.getRelatedValue(connector2, DIA.IsHorizontal)) {
+ boolean horizontal = graph.getRelatedValue(connector1, DIA.IsHorizontal);
+
+ double position;
+ if(horizontal)
+ position = 0.5 * (transform1[4] + transform2[4]);
+ else
+ position = 0.5 * (transform1[5] + transform2[5]);
+
+ Resource intermediateRouteLine = graph.newResource();
+ graph.claim(intermediateRouteLine, L0.InstanceOf, DIA.RouteLine);
+ graph.claimLiteral(intermediateRouteLine, DIA.IsHorizontal, !horizontal);
+ graph.claimLiteral(intermediateRouteLine, DIA.HasPosition, position);
+ graph.claim(connectionToKeep, DIA.HasInteriorRouteNode, intermediateRouteLine);
+ graph.claim(connector1, DIA.AreConnected, intermediateRouteLine);
+ graph.claim(connector2, DIA.AreConnected, intermediateRouteLine);
+ }
+ else
+ graph.claim(connector1, DIA.AreConnected, connector2);
+
+ return true;
+ }
+
+ public static void joinFlagsLocal(WriteGraph graph, Collection<Resource> flags) throws DatabaseException {
+ new Joiner(graph).joinLocal(graph, flags);
+ }
+