1 /*******************************************************************************
2 * Copyright (c) 2011 Association for Decentralized Information Management in
4 * All rights reserved. This program and the accompanying materials
5 * are made available under the terms of the Eclipse Public License v1.0
6 * which accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/epl-v10.html
10 * VTT Technical Research Centre of Finland - initial API and implementation
11 *******************************************************************************/
12 package org.simantics.diagram.connection.delta;
14 import gnu.trove.map.hash.THashMap;
16 import java.io.Serializable;
17 import java.util.ArrayList;
18 import java.util.Collection;
20 import org.simantics.diagram.connection.RouteGraph;
21 import org.simantics.diagram.connection.RouteLine;
22 import org.simantics.diagram.connection.RouteLink;
23 import org.simantics.diagram.connection.RoutePoint;
24 import org.simantics.diagram.connection.RouteTerminal;
27 * Calculates the delta between two route graphs. Only
28 * persistent lines are used in the computation. Lines,
29 * links and terminals are matched by their data attribute.
30 * If data is null, the node is not matched any node in
31 * the other node. Two lines differ, if they have a
32 * different orientation or position. Two terminals differ,
33 * if they are linked to different route lines ({@link RouteTerminal#getLine()}).
34 * @author Hannu Niemistö
36 public class RouteGraphDelta implements Serializable {
38 private static final long serialVersionUID = 5011201407852172263L;
40 ArrayList<RouteLine> linesOnlyInLeft = new ArrayList<RouteLine>();
41 ArrayList<RouteLine> linesOnlyInRight = new ArrayList<RouteLine>();
42 ArrayList<RouteLinePair> linesThatDiffer = new ArrayList<RouteLinePair>();
43 ArrayList<RouteLink> linksOnlyInLeft = new ArrayList<RouteLink>();
44 ArrayList<RouteLink> linksOnlyInRight = new ArrayList<RouteLink>();
45 ArrayList<RouteTerminal> terminalsOnlyInLeft = new ArrayList<RouteTerminal>();
46 ArrayList<RouteTerminal> terminalsOnlyInRight = new ArrayList<RouteTerminal>();
47 ArrayList<RouteTerminalPair> terminalsThatDiffer = new ArrayList<RouteTerminalPair>();
49 public static class RouteLinePair implements Serializable {
50 private static final long serialVersionUID = 382349562756381086L;
52 public final RouteLine left;
53 public final RouteLine right;
55 public RouteLinePair(RouteLine left, RouteLine right) {
61 public static class RouteTerminalPair implements Serializable {
62 private static final long serialVersionUID = 5286896101190626944L;
64 public final RouteTerminal left;
65 public final RouteTerminal right;
67 public RouteTerminalPair(RouteTerminal left, RouteTerminal right) {
73 public Collection<RouteLine> getLinesOnlyInLeft() {
74 return linesOnlyInLeft;
77 public Collection<RouteLine> getLinesOnlyInRight() {
78 return linesOnlyInRight;
81 public ArrayList<RouteLinePair> getLinesThatDiffer() {
82 return linesThatDiffer;
85 public Collection<RouteLink> getLinksOnlyInLeft() {
86 return linksOnlyInLeft;
89 public Collection<RouteLink> getLinksOnlyInRight() {
90 return linksOnlyInRight;
93 public ArrayList<RouteTerminal> getTerminalsOnlyInLeft() {
94 return terminalsOnlyInLeft;
97 public ArrayList<RouteTerminal> getTerminalsOnlyInRight() {
98 return terminalsOnlyInRight;
101 public ArrayList<RouteTerminalPair> getTerminalsThatDiffer() {
102 return terminalsThatDiffer;
105 private static class KeyPair {
109 public KeyPair(Object a, Object b) {
115 public int hashCode() {
116 return 31*a.hashCode() + b.hashCode();
120 public boolean equals(Object obj) {
123 if(obj == null || obj.getClass() != KeyPair.class)
125 KeyPair other = (KeyPair)obj;
126 return a.equals(other.a) && b.equals(other.b);
130 public RouteGraphDelta(RouteGraph left, RouteGraph right) {
132 THashMap<Object, RouteLine> lineMap =
133 new THashMap<Object, RouteLine>();
134 for(RouteLine line : left.getLines()) {
135 Object data = line.getData();
137 linesOnlyInLeft.add(line);
139 lineMap.put(data, line);
141 for(RouteLine line : right.getLines()) {
142 Object data = line.getData();
144 linesOnlyInRight.add(line);
146 RouteLine other =lineMap.remove(data);
148 linesOnlyInRight.add(line);
149 else if(line.getPosition() != other.getPosition() ||
150 line.isHorizontal() != other.isHorizontal())
151 linesThatDiffer.add(new RouteLinePair(other, line));
154 linesOnlyInLeft.addAll(lineMap.values());
157 THashMap<KeyPair, RouteLink> linkMap =
158 new THashMap<KeyPair, RouteLink>();
159 for(RouteLine a : left.getLines()) {
160 for(RoutePoint point : a.getPoints()) {
161 if(!(point instanceof RouteLink))
163 RouteLink link = (RouteLink)point;
164 RouteLine b = link.getB();
165 if(a == link.getA() && !b.isTransient()) {
166 Object dataA = a.getData();
167 Object dataB = b.getData();
168 if(dataA == null || dataB == null)
169 linksOnlyInLeft.add(link);
171 linkMap.put(new KeyPair(dataA, dataB), link);
175 for(RouteLine a : right.getLines()) {
176 for(RoutePoint point : a.getPoints()) {
177 if(!(point instanceof RouteLink))
179 RouteLink link = (RouteLink)point;
180 RouteLine b = link.getB();
181 if(a == link.getA() && !b.isTransient()) {
182 Object dataA = a.getData();
183 Object dataB = b.getData();
184 if(dataA == null || dataB == null
185 || (linkMap.remove(new KeyPair(dataA, dataB)) == null
186 && linkMap.remove(new KeyPair(dataB, dataA)) == null))
187 linksOnlyInRight.add(link);
191 linksOnlyInLeft.addAll(linkMap.values());
194 THashMap<Object, RouteTerminal> terminalMap =
195 new THashMap<Object, RouteTerminal>();
196 for(RouteTerminal terminal : left.getTerminals()) {
197 Object data = terminal.getData();
199 terminalsOnlyInLeft.add(terminal);
201 terminalMap.put(data, terminal);
203 for(RouteTerminal terminal : right.getTerminals()) {
204 Object data = terminal.getData();
206 terminalsOnlyInRight.add(terminal);
208 RouteTerminal other = terminalMap.remove(data);
210 terminalsOnlyInRight.add(terminal);
211 else if(!terminalsEqual(other, terminal))
212 terminalsThatDiffer.add(new RouteTerminalPair(other, terminal));
215 terminalsOnlyInLeft.addAll(terminalMap.values());
219 private static boolean terminalsEqual(RouteTerminal left, RouteTerminal right) {
221 return right == null;
224 RouteLine leftLine = left.getLine();
225 RouteLine rightLine = right.getLine();
227 return rightLine == null;
228 if(rightLine == null)
230 Object leftData = leftLine.getData();
231 Object rightData = rightLine.getData();
232 if(leftData == null || rightData == null)
233 return false; // if both lines have null data, they are still not equal
234 return leftData.equals(rightData);
237 public void print() {
238 System.out.println("=== Delta ===");
239 if(!linesOnlyInLeft.isEmpty() || !linksOnlyInLeft.isEmpty()
240 || !terminalsOnlyInLeft.isEmpty()) {
241 System.out.println("Only in the left route graph:");
242 for(RouteLine line : linesOnlyInLeft)
243 System.out.println(" line " + line.getData());
244 for(RouteLink link : linksOnlyInLeft)
245 System.out.println(" <" + link.getA().getData() + "," + link.getB().getData() + ">");
246 for(RouteTerminal terminal : terminalsOnlyInLeft)
247 System.out.println(" terminal " + terminal.getData());
249 if(!linesOnlyInRight.isEmpty() || !linksOnlyInRight.isEmpty()
250 || !terminalsOnlyInRight.isEmpty()) {
251 System.out.println("Only in the right route graph:");
252 for(RouteLine line : linesOnlyInRight)
253 System.out.println(" line " + line.getData());
254 for(RouteLink link : linksOnlyInRight)
255 System.out.println(" <" + link.getA().getData() + "," + link.getB().getData() + ">");
256 for(RouteTerminal terminal : terminalsOnlyInRight)
257 System.out.println(" terminal " + terminal.getData());
259 if(!linesThatDiffer.isEmpty() || !terminalsThatDiffer.isEmpty()) {
260 System.out.println("Differing:");
261 for(RouteLinePair pair : linesThatDiffer)
262 System.out.println(" " + pair.left.getData() + " <> " + pair.right.getData());
263 for(RouteTerminalPair pair : terminalsThatDiffer)
264 System.out.println(" " + pair.left.getData() + " (" + pair.left.getX() + "," + pair.left.getY() + ") <> " + pair.right.getData() + " (" + pair.right.getX() + "," + pair.right.getY() + ")");
268 public boolean isEmpty() {
270 linesOnlyInLeft.isEmpty() && linksOnlyInLeft.isEmpty() && terminalsOnlyInLeft.isEmpty() &&
271 linesOnlyInRight.isEmpty() && linksOnlyInRight.isEmpty() && terminalsOnlyInRight.isEmpty() &&
272 linesThatDiffer.isEmpty() && terminalsThatDiffer.isEmpty();