}
if (!toEl.isJsonObject()) {
throw new IllegalArgumentException("To is not a json object");
}
JzonObject from = (JzonObject) fromEl;
JzonObject to = (JzonObject) toEl;
Root fromRoot = new Root();
Root toRoot = new Root();
ArrayList<Leaf> fromLeaves = new ArrayList<Leaf>();
ArrayList<Leaf> toLeaves = new ArrayList<Leaf>();
HashMap<Integer, ArrNode> fromArrs = new HashMap<Integer, ArrNode>();
HashMap<Integer, ArrNode> toArrs = new HashMap<Integer, ArrNode>();
findLeaves(fromRoot, from, fromLeaves, fromArrs);
findLeaves(toRoot, to, toLeaves, toArrs);
IncavaDiff<Leaf> idiff = new IncavaDiff<Leaf>(fromLeaves, toLeaves);
List<IncavaEntry> diff = idiff.diff();
int delta = 0;
// be careful with direct use of indexOf: need instance equality, not equals!
for (IncavaEntry incavaEntry : diff) {
int deletes = Math.max(0, incavaEntry.getDeletedEnd() - incavaEntry.getDeletedStart() + 1);
int insertionIndex = (incavaEntry.getDeletedStart() > 0) ? incavaEntry.getDeletedStart() + delta - 1 : 0;
Leaf fromLeaf = (fromLeaves.size() > insertionIndex) ? fromLeaves.get(insertionIndex) : fromLeaves.get(fromLeaves.size() - 1);
for (int i = incavaEntry.getDeletedStart(); i < incavaEntry.getDeletedEnd() + 1; i++) {
// ensure not orphan
fromLeaf.recover(fromLeaves);
// proceed to delete
Leaf toLeaf = fromLeaves.get(i + delta);
fromLeaf.delete(toLeaf, null);
fromLeaf = toLeaf;
}
if (incavaEntry.getAddedEnd() < 0) {
continue;
}
fromLeaf = (fromLeaves.size() > insertionIndex) ? fromLeaves.get(insertionIndex) : fromLeaves.get(fromLeaves.size() - 1);
while (fromLeaf.oper == Oper.DELETE && insertionIndex > 0) {
// find a NOT deleted node for set / insertion - parent traversal will be done later
insertionIndex--;
fromLeaf = fromLeaves.get(insertionIndex);
}
for (int i = incavaEntry.getAddedStart(); i < incavaEntry.getAddedEnd() + 1; i++) {
// ensure not orphan
fromLeaf.recover(fromLeaves);
Leaf toLeaf = toLeaves.get(i);
if (deletes > 0) {
deletes--;
Leaf deleted = fromLeaves.get(incavaEntry.getDeletedStart() + delta + (i - incavaEntry.getAddedStart()));
deleted.recover(fromLeaves);
if (!fromLeaf.cancelDelete(deleted, toLeaf)) {
// couldn't cancel delete (different obj key): INSERT
fromLeaf.insert(toLeaf, null);
fromLeaves.add(insertionIndex + 1, toLeaf);
fromLeaf = toLeaf;
delta++;
} else {
// cancel delete: pure SET
fromLeaf = deleted;
}
} else {
// regular INSERT
fromLeaf.insert(toLeaf, null);
fromLeaves.add(insertionIndex + 1, toLeaf);
fromLeaf = toLeaf;
delta++;
}
insertionIndex++;
}
}
// recover all pending orphans: this could be easily optimized
int i = 0;
for (Leaf fromLeaf : fromLeaves) {
if (fromLeaf.isOrphan()) {
fromLeaf.recover(i, fromLeaves);
}
i++;
}
JzonObject patch = fromLeaves.iterator().next().patch();
// prints the new structure
// fromLeaves.iterator().next().print();
return patch;
}