/*******************************************************************************
* Copyright (c) 2007, 2009 Borland Software Corporation and others.
*
* 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:
* Borland Software Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.m2m.tests.qvt.oml.api.framework.comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.m2m.tests.qvt.oml.api.framework.comparator.edit.ChangeNodeEdit;
import org.eclipse.m2m.tests.qvt.oml.api.framework.comparator.edit.CompositeEdit;
import org.eclipse.m2m.tests.qvt.oml.api.framework.comparator.edit.ContentChangeEdit;
import org.eclipse.m2m.tests.qvt.oml.api.framework.comparator.edit.RefChangeEdit;
import org.eclipse.m2m.tests.qvt.oml.api.framework.comparator.edit.TreeEdit;
import org.eclipse.m2m.tests.qvt.oml.api.framework.comparator.tree.ComparatorTreeNode;
import org.eclipse.m2m.tests.qvt.oml.api.framework.comparator.tree.RefSizeContentChange;
/**
* @author pkobiakov
* uses Selkow's algorithm
*/
public class TreeComparator {
public TreeComparator() {
myCmp = new HashMap<ObjectPair, TreeEdit>();
}
public TreeEdit compare(ComparatorTreeNode left, ComparatorTreeNode right) {
TreeEdit edit = compareStructure(left, right);
if(edit.getCost() != 0) {
return edit;
}
edit = compareReferences(left, right);
if(edit.getCost() != 0) {
return edit;
}
return TreeEdit.NULL_EDIT;
}
private TreeEdit compareStructure(ComparatorTreeNode left, ComparatorTreeNode right) {
int numLeftChildren = left.getChildren().size();
int numRightChildren = right.getChildren().size();
TreeEdit[][] edits = new TreeEdit[numLeftChildren+1][numRightChildren+1];
edits[0][0] = new ChangeNodeEdit(left, right);
registerEdit(left, right, edits[0][0]);
for(int i = 1; i <= numLeftChildren; i++) {
ComparatorTreeNode leftChild = left.getChildren().get(i-1);
edits[i][0] = new CompositeEdit(edits[i-1][0], leftChild.getCumulativeDeleteEdit());
}
for(int j = 1; j <= numRightChildren; j++) {
ComparatorTreeNode rightChild = right.getChildren().get(j-1);
edits[0][j] = new CompositeEdit(edits[0][j-1], rightChild.getCumulativeInsertEdit());
}
for(int i = 1; i <= numLeftChildren; i++) {
ComparatorTreeNode leftChild = left.getChildren().get(i-1);
for(int j = 1; j <= numRightChildren; j++) {
ComparatorTreeNode rightChild = right.getChildren().get(j-1);
TreeEdit change = new CompositeEdit(edits[i-1][j-1], compareStructure(leftChild, rightChild));
TreeEdit delete = new CompositeEdit(edits[i-1][j], leftChild.getCumulativeDeleteEdit());
TreeEdit insert = new CompositeEdit(edits[i][j-1], rightChild.getCumulativeInsertEdit());
if(change.getCost() <= delete.getCost() && change.getCost() <= insert.getCost()) {
edits[i][j] = change;
}
else if(delete.getCost() <= insert.getCost()) {
edits[i][j] = delete;
}
else {
edits[i][j] = insert;
}
}
}
//System.err.println("*** compare " + left + "<->" + right + "=" + edits[numLeftChildren][numRightChildren].getCost());
return edits[numLeftChildren][numRightChildren];
}
private TreeEdit compareReferences(ComparatorTreeNode left, ComparatorTreeNode right) {
class RefTraverser {
TreeEdit compareReferences(ComparatorTreeNode left, ComparatorTreeNode right) {
List<ComparatorTreeNode> leftRefs = left.getNoncontainmentReferences();
List<ComparatorTreeNode> rightRefs = right.getNoncontainmentReferences();
if(leftRefs.size() != rightRefs.size()) {
ContentChangeEdit edit = new ContentChangeEdit(left, right, new RefSizeContentChange(left, leftRefs, rightRefs, leftRefs.size() - rightRefs.size()));
return edit;
}
for(int i = 0; i < leftRefs.size(); i++) {
ComparatorTreeNode leftRef = leftRefs.get(i);
ComparatorTreeNode rightRef = rightRefs.get(i);
TreeEdit edit = getEdit(leftRef, rightRef);
if(edit.getCost() != 0) {
return new RefChangeEdit(left, right, edit);
}
}
for(int i = 0; i < left.getChildren().size(); i++) {
ComparatorTreeNode leftChild = left.getChildren().get(i);
ComparatorTreeNode rightChild = right.getChildren().get(i);
TreeEdit edit = compareReferences(leftChild, rightChild);
if(edit.getCost() != 0) {
return edit;
}
}
return TreeEdit.NULL_EDIT;
}
}
return new RefTraverser().compareReferences(left, right);
}
private void registerEdit(ComparatorTreeNode left, ComparatorTreeNode right, TreeEdit edit) {
myCmp.put(new ObjectPair(left, right), edit);
}
private TreeEdit getEdit(ComparatorTreeNode left, ComparatorTreeNode right) {
TreeEdit edit = myCmp.get(new ObjectPair(left, right));
if(edit == null) {
// must be something outside our tree, like PrimitiveType
edit = left.equals(right) ? TreeEdit.NULL_EDIT : new ChangeNodeEdit(left, right);
registerEdit(left, right, edit);
}
return edit;
}
private final Map<ObjectPair, TreeEdit> myCmp;
}
class ObjectPair {
public ObjectPair(ComparatorTreeNode first, ComparatorTreeNode second) {
myFirst = first;
mySecond = second;
}
@Override
public boolean equals(Object o) {
if(o instanceof ObjectPair == false) {
return false;
}
ObjectPair pair = (ObjectPair)o;
return myFirst.equals(pair.myFirst) && mySecond.equals(pair.mySecond);
}
@Override
public int hashCode() {
return 17 + myFirst.hashCode()*37 + mySecond.hashCode();
}
private final ComparatorTreeNode myFirst;
private final ComparatorTreeNode mySecond;
}