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