Node to = getNode(path, Revision.fromString(toRevisionId));
if (from == null || to == null) {
// TODO implement correct behavior if the node does't/didn't exist
throw new MicroKernelException("Diff is only supported if the node exists in both cases");
}
JsopWriter w = new JsopStream();
for (String p : from.getPropertyNames()) {
// changed or removed properties
String fromValue = from.getProperty(p);
String toValue = to.getProperty(p);
if (!fromValue.equals(toValue)) {
w.tag('^').key(p);
if (toValue == null) {
w.value(toValue);
} else {
w.encodedValue(toValue).newline();
}
}
}
for (String p : to.getPropertyNames()) {
// added properties
if (from.getProperty(p) == null) {
w.tag('^').key(p).encodedValue(to.getProperty(p)).newline();
}
}
Revision fromRev = Revision.fromString(fromRevisionId);
Revision toRev = Revision.fromString(toRevisionId);
// TODO this does not work well for large child node lists
// use a MongoDB index instead
Children fromChildren = getChildren(path, fromRev, Integer.MAX_VALUE);
Children toChildren = getChildren(path, toRev, Integer.MAX_VALUE);
Set<String> childrenSet = new HashSet<String>(toChildren.children);
for (String n : fromChildren.children) {
if (!childrenSet.contains(n)) {
w.tag('-').value(n).newline();
} else {
Node n1 = getNode(n, fromRev);
Node n2 = getNode(n, toRev);
// this is not fully correct:
// a change is detected if the node changed recently,
// even if the revisions are well in the past
// if this is a problem it would need to be changed
if (!n1.getId().equals(n2.getId())) {
w.tag('^').key(n).object().endObject().newline();
}
}
}
childrenSet = new HashSet<String>(fromChildren.children);
for (String n : toChildren.children) {
if (!childrenSet.contains(n)) {
w.tag('+').key(n).object().endObject().newline();
}
}
return w.toString();
}