1 /*******************************************************************************
\r
2 * Copyright (c) 2011 Association for Decentralized Information Management in
\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
10 * VTT Technical Research Centre of Finland - initial API and implementation
\r
11 *******************************************************************************/
\r
12 package org.simantics.diagram.connection.delta;
\r
14 import gnu.trove.map.hash.THashMap;
\r
16 import java.io.Serializable;
\r
17 import java.util.ArrayList;
\r
18 import java.util.Collection;
\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
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
36 public class RouteGraphDelta implements Serializable {
\r
38 private static final long serialVersionUID = 5011201407852172263L;
\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
49 public static class RouteLinePair implements Serializable {
\r
50 private static final long serialVersionUID = 382349562756381086L;
\r
52 public final RouteLine left;
\r
53 public final RouteLine right;
\r
55 public RouteLinePair(RouteLine left, RouteLine right) {
\r
61 public static class RouteTerminalPair implements Serializable {
\r
62 private static final long serialVersionUID = 5286896101190626944L;
\r
64 public final RouteTerminal left;
\r
65 public final RouteTerminal right;
\r
67 public RouteTerminalPair(RouteTerminal left, RouteTerminal right) {
\r
73 public Collection<RouteLine> getLinesOnlyInLeft() {
\r
74 return linesOnlyInLeft;
\r
77 public Collection<RouteLine> getLinesOnlyInRight() {
\r
78 return linesOnlyInRight;
\r
81 public ArrayList<RouteLinePair> getLinesThatDiffer() {
\r
82 return linesThatDiffer;
\r
85 public Collection<RouteLink> getLinksOnlyInLeft() {
\r
86 return linksOnlyInLeft;
\r
89 public Collection<RouteLink> getLinksOnlyInRight() {
\r
90 return linksOnlyInRight;
\r
93 public ArrayList<RouteTerminal> getTerminalsOnlyInLeft() {
\r
94 return terminalsOnlyInLeft;
\r
97 public ArrayList<RouteTerminal> getTerminalsOnlyInRight() {
\r
98 return terminalsOnlyInRight;
\r
101 public ArrayList<RouteTerminalPair> getTerminalsThatDiffer() {
\r
102 return terminalsThatDiffer;
\r
105 private static class KeyPair {
\r
109 public KeyPair(Object a, Object b) {
\r
115 public int hashCode() {
\r
116 return 31*a.hashCode() + b.hashCode();
\r
120 public boolean equals(Object obj) {
\r
123 if(obj == null || obj.getClass() != KeyPair.class)
\r
125 KeyPair other = (KeyPair)obj;
\r
126 return a.equals(other.a) && b.equals(other.b);
\r
130 public RouteGraphDelta(RouteGraph left, RouteGraph right) {
\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
137 linesOnlyInLeft.add(line);
\r
139 lineMap.put(data, line);
\r
141 for(RouteLine line : right.getLines()) {
\r
142 Object data = line.getData();
\r
144 linesOnlyInRight.add(line);
\r
146 RouteLine other =lineMap.remove(data);
\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
154 linesOnlyInLeft.addAll(lineMap.values());
\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
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
171 linkMap.put(new KeyPair(dataA, dataB), link);
\r
175 for(RouteLine a : right.getLines()) {
\r
176 for(RoutePoint point : a.getPoints()) {
\r
177 if(!(point instanceof RouteLink))
\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
191 linksOnlyInLeft.addAll(linkMap.values());
\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
199 terminalsOnlyInLeft.add(terminal);
\r
201 terminalMap.put(data, terminal);
\r
203 for(RouteTerminal terminal : right.getTerminals()) {
\r
204 Object data = terminal.getData();
\r
206 terminalsOnlyInRight.add(terminal);
\r
208 RouteTerminal other = terminalMap.remove(data);
\r
210 terminalsOnlyInRight.add(terminal);
\r
211 else if(!terminalsEqual(other, terminal))
\r
212 terminalsThatDiffer.add(new RouteTerminalPair(other, terminal));
\r
215 terminalsOnlyInLeft.addAll(terminalMap.values());
\r
219 private static boolean terminalsEqual(RouteTerminal left, RouteTerminal right) {
\r
221 return right == null;
\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
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
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
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
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
268 public boolean isEmpty() {
\r
270 linesOnlyInLeft.isEmpty() && linksOnlyInLeft.isEmpty() && terminalsOnlyInLeft.isEmpty() &&
\r
271 linesOnlyInRight.isEmpty() && linksOnlyInRight.isEmpty() && terminalsOnlyInRight.isEmpty() &&
\r
272 linesThatDiffer.isEmpty() && terminalsThatDiffer.isEmpty();
\r