--- /dev/null
+/*******************************************************************************\r
+ * Copyright (c) 2011 Association for Decentralized Information Management in\r
+ * Industry THTH ry.\r
+ * All rights reserved. This program and the accompanying materials\r
+ * are made available under the terms of the Eclipse Public License v1.0\r
+ * which accompanies this distribution, and is available at\r
+ * http://www.eclipse.org/legal/epl-v10.html\r
+ *\r
+ * Contributors:\r
+ * VTT Technical Research Centre of Finland - initial API and implementation\r
+ *******************************************************************************/\r
+package org.simantics.diagram.connection.delta;\r
+\r
+import gnu.trove.map.hash.THashMap;\r
+\r
+import java.io.Serializable;\r
+import java.util.ArrayList;\r
+import java.util.Collection;\r
+\r
+import org.simantics.diagram.connection.RouteGraph;\r
+import org.simantics.diagram.connection.RouteLine;\r
+import org.simantics.diagram.connection.RouteLink;\r
+import org.simantics.diagram.connection.RoutePoint;\r
+import org.simantics.diagram.connection.RouteTerminal;\r
+\r
+/**\r
+ * Calculates the delta between two route graphs. Only\r
+ * persistent lines are used in the computation. Lines,\r
+ * links and terminals are matched by their data attribute.\r
+ * If data is null, the node is not matched any node in\r
+ * the other node. Two lines differ, if they have a \r
+ * different orientation or position. Two terminals differ,\r
+ * if they are linked to different route lines ({@link RouteTerminal#getLine()}). \r
+ * @author Hannu Niemistö\r
+ */\r
+public class RouteGraphDelta implements Serializable {\r
+\r
+ private static final long serialVersionUID = 5011201407852172263L;\r
+\r
+ ArrayList<RouteLine> linesOnlyInLeft = new ArrayList<RouteLine>();\r
+ ArrayList<RouteLine> linesOnlyInRight = new ArrayList<RouteLine>();\r
+ ArrayList<RouteLinePair> linesThatDiffer = new ArrayList<RouteLinePair>();\r
+ ArrayList<RouteLink> linksOnlyInLeft = new ArrayList<RouteLink>();\r
+ ArrayList<RouteLink> linksOnlyInRight = new ArrayList<RouteLink>();\r
+ ArrayList<RouteTerminal> terminalsOnlyInLeft = new ArrayList<RouteTerminal>();\r
+ ArrayList<RouteTerminal> terminalsOnlyInRight = new ArrayList<RouteTerminal>();\r
+ ArrayList<RouteTerminalPair> terminalsThatDiffer = new ArrayList<RouteTerminalPair>();\r
+ \r
+ public static class RouteLinePair implements Serializable {\r
+ private static final long serialVersionUID = 382349562756381086L;\r
+\r
+ public final RouteLine left;\r
+ public final RouteLine right;\r
+\r
+ public RouteLinePair(RouteLine left, RouteLine right) {\r
+ this.left = left;\r
+ this.right = right;\r
+ }\r
+ }\r
+ \r
+ public static class RouteTerminalPair implements Serializable {\r
+ private static final long serialVersionUID = 5286896101190626944L;\r
+\r
+ public final RouteTerminal left;\r
+ public final RouteTerminal right;\r
+\r
+ public RouteTerminalPair(RouteTerminal left, RouteTerminal right) {\r
+ this.left = left;\r
+ this.right = right;\r
+ }\r
+ }\r
+ \r
+ public Collection<RouteLine> getLinesOnlyInLeft() {\r
+ return linesOnlyInLeft;\r
+ }\r
+ \r
+ public Collection<RouteLine> getLinesOnlyInRight() {\r
+ return linesOnlyInRight;\r
+ }\r
+ \r
+ public ArrayList<RouteLinePair> getLinesThatDiffer() {\r
+ return linesThatDiffer;\r
+ }\r
+ \r
+ public Collection<RouteLink> getLinksOnlyInLeft() {\r
+ return linksOnlyInLeft;\r
+ }\r
+ \r
+ public Collection<RouteLink> getLinksOnlyInRight() {\r
+ return linksOnlyInRight;\r
+ }\r
+ \r
+ public ArrayList<RouteTerminal> getTerminalsOnlyInLeft() {\r
+ return terminalsOnlyInLeft;\r
+ }\r
+ \r
+ public ArrayList<RouteTerminal> getTerminalsOnlyInRight() {\r
+ return terminalsOnlyInRight;\r
+ }\r
+ \r
+ public ArrayList<RouteTerminalPair> getTerminalsThatDiffer() {\r
+ return terminalsThatDiffer;\r
+ }\r
+ \r
+ private static class KeyPair {\r
+ final Object a;\r
+ final Object b;\r
+ \r
+ public KeyPair(Object a, Object b) {\r
+ this.a = a;\r
+ this.b = b;\r
+ }\r
+ \r
+ @Override\r
+ public int hashCode() {\r
+ return 31*a.hashCode() + b.hashCode();\r
+ }\r
+ \r
+ @Override\r
+ public boolean equals(Object obj) {\r
+ if(obj == this)\r
+ return true;\r
+ if(obj == null || obj.getClass() != KeyPair.class)\r
+ return false;\r
+ KeyPair other = (KeyPair)obj;\r
+ return a.equals(other.a) && b.equals(other.b);\r
+ }\r
+ }\r
+ \r
+ public RouteGraphDelta(RouteGraph left, RouteGraph right) {\r
+ {\r
+ THashMap<Object, RouteLine> lineMap = \r
+ new THashMap<Object, RouteLine>();\r
+ for(RouteLine line : left.getLines()) {\r
+ Object data = line.getData();\r
+ if(data == null)\r
+ linesOnlyInLeft.add(line);\r
+ else\r
+ lineMap.put(data, line);\r
+ }\r
+ for(RouteLine line : right.getLines()) {\r
+ Object data = line.getData();\r
+ if(data == null)\r
+ linesOnlyInRight.add(line);\r
+ else {\r
+ RouteLine other =lineMap.remove(data);\r
+ if(other == null)\r
+ linesOnlyInRight.add(line);\r
+ else if(line.getPosition() != other.getPosition() ||\r
+ line.isHorizontal() != other.isHorizontal())\r
+ linesThatDiffer.add(new RouteLinePair(other, line));\r
+ }\r
+ }\r
+ linesOnlyInLeft.addAll(lineMap.values());\r
+ }\r
+ {\r
+ THashMap<KeyPair, RouteLink> linkMap = \r
+ new THashMap<KeyPair, RouteLink>();\r
+ for(RouteLine a : left.getLines()) {\r
+ for(RoutePoint point : a.getPoints()) {\r
+ if(!(point instanceof RouteLink))\r
+ continue;\r
+ RouteLink link = (RouteLink)point;\r
+ RouteLine b = link.getB();\r
+ if(a == link.getA() && !b.isTransient()) {\r
+ Object dataA = a.getData();\r
+ Object dataB = b.getData();\r
+ if(dataA == null || dataB == null)\r
+ linksOnlyInLeft.add(link);\r
+ else\r
+ linkMap.put(new KeyPair(dataA, dataB), link);\r
+ }\r
+ }\r
+ }\r
+ for(RouteLine a : right.getLines()) {\r
+ for(RoutePoint point : a.getPoints()) {\r
+ if(!(point instanceof RouteLink))\r
+ continue;\r
+ RouteLink link = (RouteLink)point;\r
+ RouteLine b = link.getB();\r
+ if(a == link.getA() && !b.isTransient()) {\r
+ Object dataA = a.getData();\r
+ Object dataB = b.getData();\r
+ if(dataA == null || dataB == null\r
+ || (linkMap.remove(new KeyPair(dataA, dataB)) == null\r
+ && linkMap.remove(new KeyPair(dataB, dataA)) == null))\r
+ linksOnlyInRight.add(link);\r
+ }\r
+ }\r
+ }\r
+ linksOnlyInLeft.addAll(linkMap.values());\r
+ } \r
+ {\r
+ THashMap<Object, RouteTerminal> terminalMap = \r
+ new THashMap<Object, RouteTerminal>();\r
+ for(RouteTerminal terminal : left.getTerminals()) {\r
+ Object data = terminal.getData();\r
+ if(data == null)\r
+ terminalsOnlyInLeft.add(terminal);\r
+ else\r
+ terminalMap.put(data, terminal);\r
+ }\r
+ for(RouteTerminal terminal : right.getTerminals()) {\r
+ Object data = terminal.getData();\r
+ if(data == null)\r
+ terminalsOnlyInRight.add(terminal);\r
+ else {\r
+ RouteTerminal other = terminalMap.remove(data);\r
+ if(other == null)\r
+ terminalsOnlyInRight.add(terminal);\r
+ else if(!terminalsEqual(other, terminal))\r
+ terminalsThatDiffer.add(new RouteTerminalPair(other, terminal));\r
+ }\r
+ }\r
+ terminalsOnlyInLeft.addAll(terminalMap.values());\r
+ }\r
+ }\r
+ \r
+ private static boolean terminalsEqual(RouteTerminal left, RouteTerminal right) {\r
+ if(left == null)\r
+ return right == null;\r
+ if(right == null)\r
+ return false;\r
+ RouteLine leftLine = left.getLine();\r
+ RouteLine rightLine = right.getLine();\r
+ if(leftLine == null)\r
+ return rightLine == null;\r
+ if(rightLine == null)\r
+ return false;\r
+ Object leftData = leftLine.getData();\r
+ Object rightData = rightLine.getData();\r
+ if(leftData == null || rightData == null) \r
+ return false; // if both lines have null data, they are still not equal\r
+ return leftData.equals(rightData);\r
+ }\r
+ \r
+ public void print() {\r
+ System.out.println("=== Delta ===");\r
+ if(!linesOnlyInLeft.isEmpty() || !linksOnlyInLeft.isEmpty()\r
+ || !terminalsOnlyInLeft.isEmpty()) {\r
+ System.out.println("Only in the left route graph:");\r
+ for(RouteLine line : linesOnlyInLeft)\r
+ System.out.println(" line " + line.getData());\r
+ for(RouteLink link : linksOnlyInLeft)\r
+ System.out.println(" <" + link.getA().getData() + "," + link.getB().getData() + ">");\r
+ for(RouteTerminal terminal : terminalsOnlyInLeft)\r
+ System.out.println(" terminal " + terminal.getData());\r
+ }\r
+ if(!linesOnlyInRight.isEmpty() || !linksOnlyInRight.isEmpty()\r
+ || !terminalsOnlyInRight.isEmpty()) {\r
+ System.out.println("Only in the right route graph:");\r
+ for(RouteLine line : linesOnlyInRight)\r
+ System.out.println(" line " + line.getData());\r
+ for(RouteLink link : linksOnlyInRight)\r
+ System.out.println(" <" + link.getA().getData() + "," + link.getB().getData() + ">");\r
+ for(RouteTerminal terminal : terminalsOnlyInRight)\r
+ System.out.println(" terminal " + terminal.getData());\r
+ }\r
+ if(!linesThatDiffer.isEmpty() || !terminalsThatDiffer.isEmpty()) {\r
+ System.out.println("Differing:");\r
+ for(RouteLinePair pair : linesThatDiffer)\r
+ System.out.println(" " + pair.left.getData() + " <> " + pair.right.getData());\r
+ for(RouteTerminalPair pair : terminalsThatDiffer)\r
+ System.out.println(" " + pair.left.getData() + " (" + pair.left.getX() + "," + pair.left.getY() + ") <> " + pair.right.getData() + " (" + pair.right.getX() + "," + pair.right.getY() + ")");\r
+ }\r
+ }\r
+\r
+ public boolean isEmpty() {\r
+ return\r
+ linesOnlyInLeft.isEmpty() && linksOnlyInLeft.isEmpty() && terminalsOnlyInLeft.isEmpty() &&\r
+ linesOnlyInRight.isEmpty() && linksOnlyInRight.isEmpty() && terminalsOnlyInRight.isEmpty() &&\r
+ linesThatDiffer.isEmpty() && terminalsThatDiffer.isEmpty();\r
+ }\r
+\r
+}\r