// TODO extract and evaluate filter criteria (such as e.g. 'path') specified in 'filter' parameter
String path = "/";
try {
final JsopBuilder buff = new JsopBuilder();
final RevisionProvider rp = rep.getRevisionStore();
// maps (key: id of target node, value: path/to/target)
// for tracking added/removed nodes; this allows us
// to detect 'move' operations
final HashMap<Id, String> addedNodes = new HashMap<Id, String>();
final HashMap<Id, String> removedNodes = new HashMap<Id, String>();
NodeState node1, node2;
try {
node1 = rep.getNodeState(fromRevisionId, path);
} catch (NotFoundException e) {
node1 = null;
}
try {
node2 = rep.getNodeState(toRevisionId, path);
} catch (NotFoundException e) {
node2 = null;
}
if (node1 == null) {
if (node2 != null) {
buff.tag('+').key(path).object();
toJson(buff, node2, Integer.MAX_VALUE, 0, -1, false);
return buff.endObject().newline().toString();
} else {
throw new MicroKernelException("path doesn't exist in the specified revisions: " + path);
}
} else if (node2 == null) {
buff.tag('-');
buff.value(path);
return buff.newline().toString();
}
TraversingNodeDiffHandler diffHandler = new TraversingNodeDiffHandler() {
@Override
public void propertyAdded(PropertyState after) {
buff.tag('+').
key(PathUtils.concat(getCurrentPath(), after.getName())).
encodedValue(after.getEncodedValue()).
newline();
}
@Override
public void propertyChanged(PropertyState before, PropertyState after) {
buff.tag('^').
key(PathUtils.concat(getCurrentPath(), after.getName())).
encodedValue(after.getEncodedValue()).
newline();
}
@Override
public void propertyDeleted(PropertyState before) {
// since property and node deletions can't be distinguished
// using the "- <path>" notation we're representing
// property deletions as "^ <path>:null"
buff.tag('^').
key(PathUtils.concat(getCurrentPath(), before.getName())).
value(null).
newline();
}
@Override
public void childNodeAdded(String name, NodeState after) {
addedNodes.put(rp.getId(after), PathUtils.concat(getCurrentPath(), name));
buff.tag('+').
key(PathUtils.concat(getCurrentPath(), name)).object();
toJson(buff, after, Integer.MAX_VALUE, 0, -1, false);
buff.endObject().newline();
}
@Override
public void childNodeDeleted(String name, NodeState before) {
removedNodes.put(rp.getId(before), PathUtils.concat(getCurrentPath(), name));
buff.tag('-');
buff.value(PathUtils.concat(getCurrentPath(), name));
buff.newline();
}
};
diffHandler.start(node1, node2, path);
// check if this commit includes 'move' operations
// by building intersection of added and removed nodes
addedNodes.keySet().retainAll(removedNodes.keySet());
if (!addedNodes.isEmpty()) {
// this commit includes 'move' operations
removedNodes.keySet().retainAll(addedNodes.keySet());
// addedNodes & removedNodes now only contain information about moved nodes
// re-build the diff in a 2nd pass, this time representing moves correctly
buff.resetWriter();
// TODO refactor code, avoid duplication
diffHandler = new TraversingNodeDiffHandler() {
@Override
public void propertyAdded(PropertyState after) {
buff.tag('+').
key(PathUtils.concat(getCurrentPath(), after.getName())).
encodedValue(after.getEncodedValue()).
newline();
}
@Override
public void propertyChanged(PropertyState before, PropertyState after) {
buff.tag('^').
key(PathUtils.concat(getCurrentPath(), after.getName())).
encodedValue(after.getEncodedValue()).
newline();
}
@Override
public void propertyDeleted(PropertyState before) {
// since property and node deletions can't be distinguished
// using the "- <path>" notation we're representing
// property deletions as "^ <path>:null"
buff.tag('^').
key(PathUtils.concat(getCurrentPath(), before.getName())).
value(null).
newline();
}
@Override
public void childNodeAdded(String name, NodeState after) {
if (addedNodes.containsKey(rp.getId(after))) {
// moved node, will be processed separately
return;
}
buff.tag('+').
key(PathUtils.concat(getCurrentPath(), name)).object();
toJson(buff, after, Integer.MAX_VALUE, 0, -1, false);
buff.endObject().newline();
}
@Override
public void childNodeDeleted(String name, NodeState before) {
if (addedNodes.containsKey(rp.getId(before))) {
// moved node, will be processed separately
return;
}
buff.tag('-');
buff.value(PathUtils.concat(getCurrentPath(), name));