]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.diagram/src/org/simantics/diagram/flag/Joiner.java
Sync git svn branch with SVN repository r33176.
[simantics/platform.git] / bundles / org.simantics.diagram / src / org / simantics / diagram / flag / Joiner.java
1 package org.simantics.diagram.flag;\r
2 \r
3 import java.util.Collection;\r
4 import java.util.HashSet;\r
5 import java.util.Set;\r
6 \r
7 import org.simantics.databoard.Bindings;\r
8 import org.simantics.db.ReadGraph;\r
9 import org.simantics.db.Resource;\r
10 import org.simantics.db.Statement;\r
11 import org.simantics.db.WriteGraph;\r
12 import org.simantics.db.common.utils.OrderedSetUtils;\r
13 import org.simantics.db.exception.DatabaseException;\r
14 import org.simantics.db.layer0.util.RemoverUtil;\r
15 import org.simantics.diagram.content.ConnectionUtil;\r
16 import org.simantics.diagram.stubs.DiagramResource;\r
17 import org.simantics.g2d.elementclass.FlagClass.Type;\r
18 import org.simantics.layer0.Layer0;\r
19 import org.simantics.modeling.ModelingResources;\r
20 import org.simantics.structural.stubs.StructuralResource2;\r
21 \r
22 /**\r
23  * A class that handles joining of diagram-local flag pairs into a direct\r
24  * connections.\r
25  * \r
26  * @author Tuukka Lehtonen\r
27  */\r
28 public class Joiner {\r
29 \r
30     Layer0              L0;\r
31     DiagramResource     DIA;\r
32     StructuralResource2 STR;\r
33     ModelingResources   MOD;\r
34 \r
35     ConnectionUtil cu;\r
36 \r
37     public Joiner(ReadGraph graph) {\r
38         this.L0 = Layer0.getInstance(graph);\r
39         this.DIA = DiagramResource.getInstance(graph);\r
40         this.STR = StructuralResource2.getInstance(graph);\r
41         this.MOD = ModelingResources.getInstance(graph);\r
42         this.cu = new ConnectionUtil(graph);\r
43     }\r
44 \r
45     public void joinLocal(WriteGraph graph, Collection<Resource> flags) throws DatabaseException {\r
46         // Only those flags that are known to be removed during the joining of\r
47         // two flags are added to this set to prevent processing them multiple times.\r
48         Set<Resource> removed = new HashSet<Resource>();\r
49         for (Resource flag1 : flags) {\r
50             Collection<Resource> flag1Counterparts = FlagUtil.getCounterparts(graph, flag1);\r
51             for (Resource flag2 : flag1Counterparts) {\r
52                 boolean flag1Removed = FlagUtil.countCounterparts(graph, flag1) <= 1;\r
53                 boolean flag2Removed = FlagUtil.countCounterparts(graph, flag2) <= 1;\r
54                 if (flag1Removed && !removed.add(flag1))\r
55                     continue;\r
56                 if (flag2Removed && !removed.add(flag2))\r
57                     continue;\r
58                 boolean switchFlags = !flag1Removed && flag2Removed;\r
59                 Resource f1 = switchFlags ? flag2 : flag1;\r
60                 Resource f2 = switchFlags ? flag1 : flag2;\r
61                 joinFlagPair(graph, f1, f2);\r
62             }\r
63         }\r
64     }\r
65 \r
66     private boolean joinFlagPair(WriteGraph graph, Resource flag1, Resource flag2) throws DatabaseException {\r
67         Type type1 = FlagUtil.getFlagType(graph, flag1);\r
68         Type type2 = FlagUtil.getFlagType(graph, flag2);\r
69 \r
70         Resource connector1 = null;\r
71         Resource connector2 = null;\r
72 \r
73         Resource connection1 = null;\r
74         Resource connection2 = null;\r
75 \r
76         // #7781: prevent joining of flags where one of them or both\r
77         // have no connections to them, i.e. are disconnected.\r
78         // First ensure that both flags are connected to something,\r
79         // even considering joining them.\r
80         for (Resource connector : graph.getObjects(flag1, STR.IsConnectedTo)) {\r
81             connector1 = graph.getPossibleObject(connector, DIA.AreConnected);\r
82             connection1 = ConnectionUtil.getConnection(graph, connector1);\r
83         }\r
84         for (Resource connector : graph.getObjects(flag2, STR.IsConnectedTo)) {\r
85             connector2 = graph.getPossibleObject(connector, DIA.AreConnected);\r
86             connection2 = ConnectionUtil.getConnection(graph, connector2);\r
87         }\r
88         if (connection1 == null || connector1 == null || connection2 == null || connector2 == null)\r
89             return false;\r
90 \r
91         // If a flag has more than 1 counterpart it must not be\r
92         // removed because it is a merged flag that has multiple\r
93         // connection joins attached to it.\r
94         boolean removeFlag1 = FlagUtil.countCounterparts(graph, flag1) <= 1;\r
95         boolean removeFlag2 = FlagUtil.countCounterparts(graph, flag2) <= 1;\r
96 \r
97         // Disconnect flags from their respective edges\r
98         // This code relies on the fact that flag terminals are\r
99         // functional and can only be connected once. This implies\r
100         // that their :DIA.Connectors cannot have more than one\r
101         // AreConnected relation.\r
102         if (removeFlag1) {\r
103             for (Resource connector : graph.getObjects(flag1, STR.IsConnectedTo)) {\r
104                 connector1 = graph.getSingleObject(connector, DIA.AreConnected);\r
105                 connection1 = ConnectionUtil.getConnection(graph, connector1);\r
106                 cu.removeConnectionPart(connector);\r
107             }\r
108         }\r
109         if (removeFlag2) {\r
110             for (Resource connector : graph.getObjects(flag2, STR.IsConnectedTo)) {\r
111                 connector2 = graph.getSingleObject(connector, DIA.AreConnected);\r
112                 connection2 = ConnectionUtil.getConnection(graph, connector2);\r
113                 cu.removeConnectionPart(connector);\r
114             }\r
115         }\r
116 \r
117         // Decide which connection to remove. The strategy is:\r
118         // * always keep the connection that has an ElementToComponent relation.\r
119         // * if there are no ElementToComponent relations, keep the connection on the OutputFlag side.\r
120         // * if flag type information is not available, keep connection1.\r
121         Resource connectionToKeep = connection1;\r
122         Resource connectionToRemove = connection2;\r
123         Resource hasElementToComponent1 = graph.getPossibleObject(connection1, MOD.ElementToComponent);\r
124         Resource hasElementToComponent2 = graph.getPossibleObject(connection2, MOD.ElementToComponent);\r
125         if (hasElementToComponent1 != null && hasElementToComponent2 != null)\r
126             throw new UnsupportedOperationException("Both flag are connected with connections that have mapped components, can't decide which connection to remove in join operation");\r
127         if (hasElementToComponent2 != null\r
128                 || (type1 != Type.Out && type2 == Type.Out)) {\r
129             connectionToKeep = connection2;\r
130             connectionToRemove = connection1;\r
131         }\r
132 \r
133         // Remove connection join and flags when necessary\r
134         if (removeFlag1)\r
135             for (Resource diagram : OrderedSetUtils.getOwnerLists(graph, flag1, DIA.Diagram))\r
136                 OrderedSetUtils.remove(graph, diagram, flag1);\r
137         if (removeFlag2)\r
138             for (Resource diagram : OrderedSetUtils.getOwnerLists(graph, flag2, DIA.Diagram))\r
139                 OrderedSetUtils.remove(graph, diagram, flag2);\r
140         FlagUtil.disconnectFlag(graph, flag1);\r
141         double[] transform1 = graph.getRelatedValue(flag1, DIA.HasTransform, Bindings.DOUBLE_ARRAY);\r
142         double[] transform2 = graph.getRelatedValue(flag2, DIA.HasTransform, Bindings.DOUBLE_ARRAY);\r
143         if (removeFlag1)\r
144             RemoverUtil.remove(graph, flag1);\r
145         if (removeFlag2)\r
146             RemoverUtil.remove(graph, flag2);\r
147 \r
148         // Move connector from connection to remove to other connection\r
149         for(Statement connectorStat : graph.getStatements(connectionToRemove, DIA.HasConnector)) {\r
150             graph.deny(connectorStat);\r
151             graph.claim(connectionToKeep, connectorStat.getPredicate(), connectorStat.getObject());\r
152         }\r
153         for(Resource node : graph.getObjects(connectionToRemove, DIA.HasInteriorRouteNode)) {\r
154             graph.deny(node, DIA.HasInteriorRouteNode_Inverse, connectionToRemove);\r
155             graph.claim(node, DIA.HasInteriorRouteNode_Inverse, connectionToKeep);\r
156         }\r
157 \r
158         // Remove obsolete connection\r
159         cu.removeConnection(connectionToRemove);\r
160 \r
161         // Reconnect respective edges\r
162         if(graph.isInstanceOf(connector1, DIA.RouteLine) && graph.isInstanceOf(connector2, DIA.RouteLine)\r
163                 && graph.getRelatedValue(connector1, DIA.IsHorizontal) == graph.getRelatedValue(connector2, DIA.IsHorizontal)) {\r
164             boolean horizontal = graph.getRelatedValue(connector1, DIA.IsHorizontal);\r
165 \r
166             double position;\r
167             if(horizontal)\r
168                 position = 0.5 * (transform1[4] + transform2[4]);\r
169             else\r
170                 position = 0.5 * (transform1[5] + transform2[5]);\r
171 \r
172             Resource intermediateRouteLine = graph.newResource();\r
173             graph.claim(intermediateRouteLine, L0.InstanceOf, DIA.RouteLine);\r
174             graph.claimLiteral(intermediateRouteLine, DIA.IsHorizontal, !horizontal);\r
175             graph.claimLiteral(intermediateRouteLine, DIA.HasPosition, position);\r
176             graph.claim(connectionToKeep, DIA.HasInteriorRouteNode, intermediateRouteLine);\r
177             graph.claim(connector1, DIA.AreConnected, intermediateRouteLine);\r
178             graph.claim(connector2, DIA.AreConnected, intermediateRouteLine);\r
179         }\r
180         else\r
181             graph.claim(connector1, DIA.AreConnected, connector2);\r
182 \r
183         return true;\r
184     }\r
185 \r
186     public static void joinFlagsLocal(WriteGraph graph, Collection<Resource> flags) throws DatabaseException {\r
187         new Joiner(graph).joinLocal(graph, flags);\r
188     }\r
189 \r
190 }