]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.diagram.connection/src/org/simantics/diagram/connection/delta/RouteGraphDelta.java
Migrated source code from Simantics SVN
[simantics/platform.git] / bundles / org.simantics.diagram.connection / src / org / simantics / diagram / connection / delta / RouteGraphDelta.java
1 /*******************************************************************************\r
2  * Copyright (c) 2011 Association for Decentralized Information Management in\r
3  * Industry THTH ry.\r
4  * All rights reserved. This program and the accompanying materials\r
5  * are made available under the terms of the Eclipse Public License v1.0\r
6  * which accompanies this distribution, and is available at\r
7  * http://www.eclipse.org/legal/epl-v10.html\r
8  *\r
9  * Contributors:\r
10  *     VTT Technical Research Centre of Finland - initial API and implementation\r
11  *******************************************************************************/\r
12 package org.simantics.diagram.connection.delta;\r
13 \r
14 import gnu.trove.map.hash.THashMap;\r
15 \r
16 import java.io.Serializable;\r
17 import java.util.ArrayList;\r
18 import java.util.Collection;\r
19 \r
20 import org.simantics.diagram.connection.RouteGraph;\r
21 import org.simantics.diagram.connection.RouteLine;\r
22 import org.simantics.diagram.connection.RouteLink;\r
23 import org.simantics.diagram.connection.RoutePoint;\r
24 import org.simantics.diagram.connection.RouteTerminal;\r
25 \r
26 /**\r
27  * Calculates the delta between two route graphs. Only\r
28  * persistent lines are used in the computation. Lines,\r
29  * links and terminals are matched by their data attribute.\r
30  * If data is null, the node is not matched any node in\r
31  * the other node. Two lines differ, if they have a \r
32  * different orientation or position. Two terminals differ,\r
33  * if they are linked to different route lines ({@link RouteTerminal#getLine()}).  \r
34  * @author Hannu Niemistö\r
35  */\r
36 public class RouteGraphDelta implements Serializable {\r
37 \r
38     private static final long serialVersionUID = 5011201407852172263L;\r
39 \r
40     ArrayList<RouteLine> linesOnlyInLeft = new ArrayList<RouteLine>();\r
41     ArrayList<RouteLine> linesOnlyInRight = new ArrayList<RouteLine>();\r
42     ArrayList<RouteLinePair> linesThatDiffer = new ArrayList<RouteLinePair>();\r
43     ArrayList<RouteLink> linksOnlyInLeft = new ArrayList<RouteLink>();\r
44     ArrayList<RouteLink> linksOnlyInRight = new ArrayList<RouteLink>();\r
45     ArrayList<RouteTerminal> terminalsOnlyInLeft = new ArrayList<RouteTerminal>();\r
46     ArrayList<RouteTerminal> terminalsOnlyInRight = new ArrayList<RouteTerminal>();\r
47     ArrayList<RouteTerminalPair> terminalsThatDiffer = new ArrayList<RouteTerminalPair>();\r
48     \r
49     public static class RouteLinePair implements Serializable {\r
50         private static final long serialVersionUID = 382349562756381086L;\r
51 \r
52         public final RouteLine left;\r
53         public final RouteLine right;\r
54 \r
55         public RouteLinePair(RouteLine left, RouteLine right) {\r
56             this.left = left;\r
57             this.right = right;\r
58         }\r
59     }\r
60     \r
61     public static class RouteTerminalPair implements Serializable {\r
62         private static final long serialVersionUID = 5286896101190626944L;\r
63 \r
64         public final RouteTerminal left;\r
65         public final RouteTerminal right;\r
66 \r
67         public RouteTerminalPair(RouteTerminal left, RouteTerminal right) {\r
68             this.left = left;\r
69             this.right = right;\r
70         }\r
71     }\r
72     \r
73     public Collection<RouteLine> getLinesOnlyInLeft() {\r
74         return linesOnlyInLeft;\r
75     }\r
76     \r
77     public Collection<RouteLine> getLinesOnlyInRight() {\r
78         return linesOnlyInRight;\r
79     }\r
80     \r
81     public ArrayList<RouteLinePair> getLinesThatDiffer() {\r
82         return linesThatDiffer;\r
83     }\r
84     \r
85     public Collection<RouteLink> getLinksOnlyInLeft() {\r
86         return linksOnlyInLeft;\r
87     }\r
88     \r
89     public Collection<RouteLink> getLinksOnlyInRight() {\r
90         return linksOnlyInRight;\r
91     }\r
92     \r
93     public ArrayList<RouteTerminal> getTerminalsOnlyInLeft() {\r
94         return terminalsOnlyInLeft;\r
95     }\r
96     \r
97     public ArrayList<RouteTerminal> getTerminalsOnlyInRight() {\r
98         return terminalsOnlyInRight;\r
99     }\r
100     \r
101     public ArrayList<RouteTerminalPair> getTerminalsThatDiffer() {\r
102         return terminalsThatDiffer;\r
103     }\r
104     \r
105     private static class KeyPair {\r
106         final Object a;\r
107         final Object b;\r
108         \r
109         public KeyPair(Object a, Object b) {\r
110             this.a = a;\r
111             this.b = b;\r
112         }\r
113         \r
114         @Override\r
115         public int hashCode() {\r
116             return 31*a.hashCode() + b.hashCode();\r
117         }\r
118         \r
119         @Override\r
120         public boolean equals(Object obj) {\r
121             if(obj == this)\r
122                 return true;\r
123             if(obj == null || obj.getClass() != KeyPair.class)\r
124                 return false;\r
125             KeyPair other = (KeyPair)obj;\r
126             return a.equals(other.a) && b.equals(other.b);\r
127         }\r
128     }\r
129     \r
130     public RouteGraphDelta(RouteGraph left, RouteGraph right) {\r
131         {\r
132             THashMap<Object, RouteLine> lineMap = \r
133                     new THashMap<Object, RouteLine>();\r
134             for(RouteLine line : left.getLines()) {\r
135                 Object data = line.getData();\r
136                 if(data == null)\r
137                     linesOnlyInLeft.add(line);\r
138                 else\r
139                     lineMap.put(data, line);\r
140             }\r
141             for(RouteLine line : right.getLines()) {\r
142                 Object data = line.getData();\r
143                 if(data == null)\r
144                     linesOnlyInRight.add(line);\r
145                 else {\r
146                     RouteLine other =lineMap.remove(data);\r
147                     if(other == null)\r
148                         linesOnlyInRight.add(line);\r
149                     else if(line.getPosition() != other.getPosition() ||\r
150                             line.isHorizontal() != other.isHorizontal())\r
151                         linesThatDiffer.add(new RouteLinePair(other, line));\r
152                 }\r
153             }\r
154             linesOnlyInLeft.addAll(lineMap.values());\r
155         }\r
156         {\r
157             THashMap<KeyPair, RouteLink> linkMap = \r
158                     new THashMap<KeyPair, RouteLink>();\r
159             for(RouteLine a : left.getLines()) {\r
160                 for(RoutePoint point : a.getPoints()) {\r
161                     if(!(point instanceof RouteLink))\r
162                         continue;\r
163                     RouteLink link = (RouteLink)point;\r
164                     RouteLine b = link.getB();\r
165                     if(a == link.getA() && !b.isTransient()) {\r
166                         Object dataA = a.getData();\r
167                         Object dataB = b.getData();\r
168                         if(dataA == null || dataB == null)\r
169                             linksOnlyInLeft.add(link);\r
170                         else\r
171                             linkMap.put(new KeyPair(dataA, dataB), link);\r
172                     }\r
173                 }\r
174             }\r
175             for(RouteLine a : right.getLines()) {\r
176                 for(RoutePoint point : a.getPoints()) {\r
177                     if(!(point instanceof RouteLink))\r
178                         continue;\r
179                     RouteLink link = (RouteLink)point;\r
180                     RouteLine b = link.getB();\r
181                     if(a == link.getA() && !b.isTransient()) {\r
182                         Object dataA = a.getData();\r
183                         Object dataB = b.getData();\r
184                         if(dataA == null || dataB == null\r
185                                 || (linkMap.remove(new KeyPair(dataA, dataB)) == null\r
186                                  && linkMap.remove(new KeyPair(dataB, dataA)) == null))\r
187                             linksOnlyInRight.add(link);\r
188                     }\r
189                 }\r
190             }\r
191             linksOnlyInLeft.addAll(linkMap.values());\r
192         }   \r
193         {\r
194             THashMap<Object, RouteTerminal> terminalMap = \r
195                     new THashMap<Object, RouteTerminal>();\r
196             for(RouteTerminal terminal : left.getTerminals()) {\r
197                 Object data = terminal.getData();\r
198                 if(data == null)\r
199                     terminalsOnlyInLeft.add(terminal);\r
200                 else\r
201                     terminalMap.put(data, terminal);\r
202             }\r
203             for(RouteTerminal terminal : right.getTerminals()) {\r
204                 Object data = terminal.getData();\r
205                 if(data == null)\r
206                     terminalsOnlyInRight.add(terminal);\r
207                 else {\r
208                     RouteTerminal other = terminalMap.remove(data);\r
209                     if(other == null)\r
210                         terminalsOnlyInRight.add(terminal);\r
211                     else if(!terminalsEqual(other, terminal))\r
212                         terminalsThatDiffer.add(new RouteTerminalPair(other, terminal));\r
213                 }\r
214             }\r
215             terminalsOnlyInLeft.addAll(terminalMap.values());\r
216         }\r
217     }\r
218     \r
219     private static boolean terminalsEqual(RouteTerminal left, RouteTerminal right) {\r
220         if(left == null)\r
221             return right == null;\r
222         if(right == null)\r
223             return false;\r
224         RouteLine leftLine = left.getLine();\r
225         RouteLine rightLine = right.getLine();\r
226         if(leftLine == null)\r
227             return rightLine == null;\r
228         if(rightLine == null)\r
229             return false;\r
230         Object leftData = leftLine.getData();\r
231         Object rightData = rightLine.getData();\r
232         if(leftData == null || rightData == null) \r
233             return false; // if both lines have null data, they are still not equal\r
234         return leftData.equals(rightData);\r
235     }\r
236     \r
237     public void print() {\r
238         System.out.println("=== Delta ===");\r
239         if(!linesOnlyInLeft.isEmpty() || !linksOnlyInLeft.isEmpty()\r
240                 || !terminalsOnlyInLeft.isEmpty()) {\r
241             System.out.println("Only in the left route graph:");\r
242             for(RouteLine line : linesOnlyInLeft)\r
243                 System.out.println("    line " + line.getData());\r
244             for(RouteLink link : linksOnlyInLeft)\r
245                 System.out.println("    <" + link.getA().getData() + "," + link.getB().getData() + ">");\r
246             for(RouteTerminal terminal : terminalsOnlyInLeft)\r
247                 System.out.println("    terminal " + terminal.getData());\r
248         }\r
249         if(!linesOnlyInRight.isEmpty() || !linksOnlyInRight.isEmpty()\r
250                 || !terminalsOnlyInRight.isEmpty()) {\r
251             System.out.println("Only in the right route graph:");\r
252             for(RouteLine line : linesOnlyInRight)\r
253                 System.out.println("    line " + line.getData());\r
254             for(RouteLink link : linksOnlyInRight)\r
255                 System.out.println("    <" + link.getA().getData() + "," + link.getB().getData() + ">");\r
256             for(RouteTerminal terminal : terminalsOnlyInRight)\r
257                 System.out.println("    terminal " + terminal.getData());\r
258         }\r
259         if(!linesThatDiffer.isEmpty() || !terminalsThatDiffer.isEmpty()) {\r
260             System.out.println("Differing:");\r
261             for(RouteLinePair pair : linesThatDiffer)\r
262                 System.out.println("    " + pair.left.getData() + " <> " + pair.right.getData());\r
263             for(RouteTerminalPair pair : terminalsThatDiffer)\r
264                 System.out.println("    " + pair.left.getData() + " (" + pair.left.getX() + "," + pair.left.getY() + ") <> " + pair.right.getData() + " (" + pair.right.getX() + "," + pair.right.getY() + ")");\r
265         }\r
266     }\r
267 \r
268     public boolean isEmpty() {\r
269         return\r
270                 linesOnlyInLeft.isEmpty() && linksOnlyInLeft.isEmpty() && terminalsOnlyInLeft.isEmpty() &&\r
271                 linesOnlyInRight.isEmpty() && linksOnlyInRight.isEmpty() && terminalsOnlyInRight.isEmpty() &&\r
272                 linesThatDiffer.isEmpty() && terminalsThatDiffer.isEmpty();\r
273     }\r
274 \r
275 }\r