Skip to content

Instantly share code, notes, and snippets.

@hank-cp
Last active June 27, 2019 02:47
Show Gist options
  • Save hank-cp/3db40faed1dd9f02ababd86c2c9eaf8d to your computer and use it in GitHub Desktop.
Save hank-cp/3db40faed1dd9f02ababd86c2c9eaf8d to your computer and use it in GitHub Desktop.
Javers complex @valueobject comparator
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import org.javers.core.diff.changetype.map.*;
import org.javers.core.diff.custom.CustomPropertyComparator;
import org.javers.core.json.JsonConverter;
import org.javers.core.metamodel.object.GlobalId;
import org.javers.core.metamodel.property.Property;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Stack;
import java.util.stream.Collectors;
public abstract class NestedObjectComparator<T> implements CustomPropertyComparator<T, MapChange> {
private JsonConverter jsonConverter;
public void setJsonConverter(JsonConverter jsonConverter) {
this.jsonConverter = jsonConverter;
}
@Override
public MapChange compare(T old, T current,
GlobalId affectedId, Property property) {
assert(jsonConverter != null);
JsonElement oldJson = jsonConverter.toJsonElement(old);
JsonElement currentJson = jsonConverter.toJsonElement(current);
List<EntryChange> changes = parseChanges(oldJson, currentJson);
if (changes == null || changes.size <= 0) return null;
return new MapChange(affectedId, property.getName(), changes);
}
private List<EntryChange> parseChanges(JsonElement old, JsonElement current) {
List<EntryChange> changes = new ArrayList<>();
Stack<String> fieldPath = new Stack<>();
resolveChanges(old, current, changes, fieldPath);
return changes;
}
private void resolveChanges(JsonElement old, JsonElement current, List<EntryChange> changes, Stack<String> fieldPath) {
String path = fieldPath.stream().collect(Collectors.joining("."));
if ((old == null || old.isJsonNull())
&& (current == null || current.isJsonNull())) return;
if (old != null && !old.isJsonNull()
&& (current == null || current.isJsonNull())) {
// add new field
resolveChanges(old, changes, fieldPath, false);
} else if (current != null && !current.isJsonNull()
&& (old == null || old.isJsonNull())) {
// remove field
resolveChanges(current, changes, fieldPath, true);
} else {
// field aligned
if (current.isJsonPrimitive()) {
if (!Objects.equals(old.getAsString(), current.getAsString())) {
changes.add(new EntryValueChange(path, old.getAsString(), current.getAsString()));
}
} else if (current.isJsonArray()) {
JsonArray oldArray = old.getAsJsonArray();
JsonArray currentArray = current.getAsJsonArray();
for (int i=0; i<oldArray.size(); i++) {
fieldPath.push(Integer.toString(i));
if (i < currentArray.size()) {
resolveChanges(oldArray.get(i), currentArray.get(i), changes, fieldPath);
} else {
resolveChanges(oldArray.get(i), changes, fieldPath, false);
}
fieldPath.pop();
}
for (int i=oldArray.size(); i<currentArray.size(); i++) {
fieldPath.push(Integer.toString(i));
resolveChanges(currentArray.get(i), changes, fieldPath, true);
fieldPath.pop();
}
} else if (current.isJsonObject()) {
JsonObject oldObj = old.getAsJsonObject();
JsonObject currentObj = current.getAsJsonObject();
oldObj.entrySet().forEach(oldEntry -> {
String key = oldEntry.getKey();
fieldPath.push(key);
if (currentObj.has(key)) {
resolveChanges(oldEntry.getValue(), currentObj.get(key), changes, fieldPath);
} else {
resolveChanges(oldEntry.getValue(), changes, fieldPath, false);
}
fieldPath.pop();
});
currentObj.entrySet().forEach(currentEntry -> {
String key = currentEntry.getKey();
fieldPath.push(key);
if (!currentObj.has(key)) {
resolveChanges(currentEntry.getValue(), changes, fieldPath, true);
}
fieldPath.pop();
});
}
}
}
private void resolveChanges(JsonElement json, List<EntryChange> changes, Stack<String> fieldPath, boolean addOrRemove) {
String path = fieldPath.stream().collect(Collectors.joining("."));
if (json.isJsonPrimitive()) {
EntryAddOrRemove entryChange = addOrRemove
? new EntryAdded(path, json.getAsString())
: new EntryRemoved(path, json.getAsString());
changes.add(entryChange);
} else if (json.isJsonArray()) {
JsonArray array = json.getAsJsonArray();
for (int i=0; i<array.size(); i++) {
fieldPath.push(Integer.toString(i));
resolveChanges(array.get(i), changes, fieldPath, addOrRemove);
fieldPath.pop();
}
} else if (json.isJsonObject()) {
JsonObject obj = json.getAsJsonObject();
obj.entrySet().forEach(entry -> {
fieldPath.push(entry.getKey());
resolveChanges(entry.getValue(), changes, fieldPath, addOrRemove);
fieldPath.pop();
});
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment