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