package org.eclipse.uml2.diagram.common.wholediagram;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.xmi.XMLResource;
import org.eclipse.gmf.runtime.notation.Diagram;
import org.eclipse.gmf.runtime.notation.Edge;
import org.eclipse.gmf.runtime.notation.Node;
import org.eclipse.gmf.runtime.notation.View;
public class DiagramCompareSession {
private MatchRegistry<Node> myNodesRegistry = new MatchRegistry<Node>();
private MatchRegistry<Edge> myEdgesRegistry = new MatchRegistry<Edge>();
private final Matcher<Edge> myEdgeMatcher;
private final Matcher<Node> myNodeMatcher;
private ViewFilter myViewFilter = NOTHING_TO_IGNORE;
public DiagramCompareSession(Matcher<Node> nodeMatcher, Matcher<Edge> edgeMatcher){
myNodeMatcher = nodeMatcher;
myEdgeMatcher = edgeMatcher;
}
public void setViewFilter(ViewFilter filter){
myViewFilter = filter;
if (myViewFilter == null){
myViewFilter = NOTHING_TO_IGNORE;
}
}
public DiagramCompareSession(){
this(NODE_MATCHER, EDGE_MATCHER);
}
public MatchRegistry<Edge> getEdgesRegistry() {
return myEdgesRegistry;
}
public MatchRegistry<Node> getNodesRegistry() {
return myNodesRegistry;
}
public void clear(){
myNodesRegistry.clear();
myEdgesRegistry.clear();
}
public void compare(Diagram oldDiagram, Diagram newDiagram){
matchChildNodes(oldDiagram, newDiagram, myNodeMatcher);
List<Edge> oldEdges = filter(oldDiagram.getEdges(), Edge.class);
List<Edge> newEdges = filter(newDiagram.getEdges(), Edge.class);
myEdgesRegistry.doMatch(oldEdges, newEdges, myEdgeMatcher);
}
private void matchChildNodes(View oldRoot, View newRoot, Matcher<Node> nodesMatcher){
List<Node> oldChildren = filter(oldRoot.getChildren(), Node.class);
List<Node> newChildren = filter(newRoot.getChildren(), Node.class);
myNodesRegistry.doMatch(oldChildren, newChildren, nodesMatcher);
for (Node nextOld : oldChildren){
Node nextNew = myNodesRegistry.getMatch(nextOld);
if (nextNew != null){
matchChildNodes(nextOld, nextNew, nodesMatcher);
}
}
}
@SuppressWarnings("unchecked")
private <T extends View> List<T> filter(List list, Class<T> clazz){
List<T> result = new LinkedList<T>();
for (Object next : list){
if (clazz.isInstance(next) && !myViewFilter.ignore((View)next)){
result.add(clazz.cast(next));
}
}
return result;
}
public static class MatchRegistry<T> {
private final HashMap<T, T> myMatches = new HashMap<T, T>();
private final LinkedHashSet<T> myMissedOlds = new LinkedHashSet<T>();
private final LinkedHashSet<T> myNotMatchedNews = new LinkedHashSet<T>();
public void clear(){
myMatches.clear();
myMissedOlds.clear();
myNotMatchedNews.clear();
}
public void addAllMissedOld(List<T> oldTs) {
myMissedOlds.addAll(oldTs);
}
public void addAllNotMatchedNew(List<T> newTs) {
myNotMatchedNews.addAll(newTs);
}
public void addMatch(T oldT, T newT) {
myMatches.put(oldT, newT);
}
public void addMissedOld(T oldT) {
myMissedOlds.add(oldT);
}
public void addNotMatchedNew(T newT) {
myNotMatchedNews.add(newT);
}
public Set<T> getMissedOlds() {
return Collections.unmodifiableSet(myMissedOlds);
}
public Set<T> getNotMatchedNews() {
return Collections.unmodifiableSet(myNotMatchedNews);
}
public T getMatch(T oldT){
return myMatches.get(oldT);
}
public void doMatch(List<T> oldTs, List<T> newTs, Matcher<T> matcher){
LinkedList<T> oldCopy = new LinkedList<T>(oldTs);
LinkedList<T> newCopy = new LinkedList<T>(newTs);
for (Iterator<T> oldIt = oldCopy.iterator(); oldIt.hasNext();){
T nextOld = oldIt.next();
T nextNew = matcher.removeMatched(newCopy, nextOld);
if (nextNew != null){
addMatch(nextOld, nextNew);
oldIt.remove();
}
}
addAllMissedOld(oldCopy);
addAllNotMatchedNew(newCopy);
}
}
public static abstract class Matcher<T> {
public abstract boolean match(T oldT, T newT);
public T removeMatched(List<T> list, T pattern){
for (Iterator<T> it = list.iterator(); it.hasNext();){
T next = it.next();
if (this.match(pattern, next)){
it.remove();
return next;
}
}
return null;
}
}
public static interface ViewFilter {
public boolean ignore(View view);
}
private static boolean sameElement(EObject a, EObject b){
if (a == null && b == null){
return true;
}
if (a == null || b == null){
return false;
}
if (a.equals(b)){
return true;
}
Resource aRes = a.eResource();
Resource bRes = b.eResource();
if (false == aRes instanceof XMLResource){
return false;
}
if (false == bRes instanceof XMLResource){
return false;
}
String aID = ((XMLResource)aRes).getID(a);
String bID = ((XMLResource)bRes).getID(b);
return aID != null && aID.equals(bID);
}
private static boolean safeEquals(String a, String b){
return a == null ? b == null : a.equals(b);
}
public static final Matcher<View> VIEW_MATCHER = new Matcher<View>(){
public boolean match(View oldNode, View newNode) {
return sameElement(oldNode.getElement(), newNode.getElement()) &&
safeEquals(oldNode.getType(), newNode.getType());
}
};
public static final Matcher<Edge> EDGE_MATCHER = new Matcher<Edge>(){
public boolean match(Edge oldEdge, Edge newEdge) {
return VIEW_MATCHER.match(oldEdge, newEdge) && //
VIEW_MATCHER.match(oldEdge.getSource(), newEdge.getSource()) && //
VIEW_MATCHER.match(oldEdge.getTarget(), newEdge.getTarget());
}
};
public static final Matcher<Node> NODE_MATCHER = new Matcher<Node>(){
public boolean match(Node oldNode, Node newNode) {
return VIEW_MATCHER.match(oldNode, newNode);
}
};
public static final ViewFilter NOTHING_TO_IGNORE = new ViewFilter(){
public boolean ignore(View view) {
return false;
}
};
}