/** * Makes a diff copy of two {@link State} maps. * * <p>The returned diff state contains only the key/values that changed. {@code null} values are * equivalent to absent values. * * <p>For values set to null or removed, the value is null. * * <p>When setting a delta, the old value is checked to know if the delta should be kept or if a * full value should be set instead. * * <p>For sub-documents, a recursive diff is returned. * * @return a {@link StateDiff} which, when applied to a, gives b. */ public static StateDiff diff(State a, State b) { StateDiff diff = new StateDiff(); for (Entry<String, Serializable> en : a.entrySet()) { Serializable va = en.getValue(); if (va == null) { // checked by loop on b continue; } String key = en.getKey(); Serializable vb = b.get(key); if (vb == null) { // value must be cleared diff.put(key, null); } else { // compare values Serializable elemDiff = diff(va, vb); if (elemDiff != NOP) { if (elemDiff instanceof Delta) { Delta delta = (Delta) elemDiff; Serializable deltaBase = delta.getBase(); if (!Objects.equals(va, deltaBase)) { // delta's base is not the old value // -> set a new value, don't use a delta update elemDiff = delta.getFullValue(); } // else delta's base is the in-database value // because base is consistent with old value, assume the delta is already properly // computed } diff.put(key, elemDiff); } } } for (Entry<String, Serializable> en : b.entrySet()) { String key = en.getKey(); Serializable va = a.get(key); if (va != null) { // already checked by loop on a continue; } Serializable vb = en.getValue(); if (!equalsLoose(null, vb)) { // value must be added diff.put(key, vb); } } return diff; }
private static final StateDiff stateDiff(Serializable... values) { assertTrue(values.length % 2 == 0); StateDiff diff = new StateDiff(); for (int i = 0; i < values.length; i += 2) { diff.put((String) values[i], values[i + 1]); } return diff; }