package org.openlca.app.cloud.ui.compare.json;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map.Entry;
import java.util.Set;
import java.util.Stack;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonNull;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
public class JsonUtil {
public static JsonObject toJsonObject(JsonElement element) {
if (element == null)
return null;
if (!element.isJsonObject())
return null;
return element.getAsJsonObject();
}
public static JsonArray toJsonArray(JsonElement element) {
if (element == null)
return null;
if (!element.isJsonArray())
return null;
return element.getAsJsonArray();
}
public static JsonPrimitive toJsonPrimitive(JsonElement element) {
if (element == null)
return null;
if (!element.isJsonPrimitive())
return null;
return element.getAsJsonPrimitive();
}
public static JsonElement deepCopy(JsonElement element) {
if (element == null)
return null;
if (element.isJsonPrimitive())
return deepCopy(element.getAsJsonPrimitive());
if (element.isJsonArray())
return deepCopy(element.getAsJsonArray());
if (element.isJsonObject())
return deepCopy(element.getAsJsonObject());
return JsonNull.INSTANCE;
}
private static JsonArray deepCopy(JsonArray element) {
JsonArray copy = new JsonArray();
element.forEach((child) -> copy.add(deepCopy(child)));
return copy;
}
private static JsonObject deepCopy(JsonObject element) {
JsonObject copy = new JsonObject();
for (Entry<String, JsonElement> entry : element.entrySet())
copy.add(entry.getKey(), deepCopy(entry.getValue()));
return copy;
}
private static JsonPrimitive deepCopy(JsonPrimitive element) {
if (element.isBoolean())
return new JsonPrimitive(element.getAsBoolean());
if (element.isNumber())
return new JsonPrimitive(element.getAsNumber());
return new JsonPrimitive(element.getAsString());
}
public static boolean equal(String property, JsonElement e1,
JsonElement e2, ElementFinder finder) {
if (isNull(e1) && isNull(e2))
return true;
if (isNull(e1) || isNull(e2))
return false;
if (e1.isJsonPrimitive() && e2.isJsonPrimitive())
return equal(e1.getAsJsonPrimitive(), e2.getAsJsonPrimitive());
if (e1.isJsonArray() && e2.isJsonArray())
return equal(property, e1.getAsJsonArray(), e2.getAsJsonArray(),
finder);
if (e1.isJsonObject() && e2.isJsonObject())
return equal(property, e1.getAsJsonObject(), e2.getAsJsonObject(),
finder);
return false;
}
private static boolean equal(String property, JsonArray a1, JsonArray a2,
ElementFinder finder) {
if (a1.size() != a2.size())
return false;
Iterator<JsonElement> it1 = a1.iterator();
while (it1.hasNext()) {
JsonElement e1 = it1.next();
int index = finder.find(property, e1, a2);
if (index == -1)
return false;
JsonElement e2 = a2.get(index);
if (!equal(property, e1, e2, finder))
return false;
}
return true;
}
private static boolean equal(String property, JsonObject e1, JsonObject e2,
ElementFinder finder) {
Set<String> checked = new HashSet<>();
for (Entry<String, JsonElement> entry : e1.entrySet()) {
checked.add(entry.getKey());
if (finder.skipOnEqualsCheck(property, e1, entry.getKey()))
continue;
JsonElement element = entry.getValue();
JsonElement other = e2.get(entry.getKey());
if (!equal(entry.getKey(), element, other, finder))
return false;
}
for (Entry<String, JsonElement> entry : e2.entrySet()) {
if (checked.contains(entry.getKey()))
continue;
if (finder.skipOnEqualsCheck(property, e1, entry.getKey()))
continue;
JsonElement element = e1.get(entry.getKey());
JsonElement other = entry.getValue();
if (!equal(entry.getKey(), element, other, finder))
return false;
}
return true;
}
private static boolean equal(JsonPrimitive e1, JsonPrimitive e2) {
if (e1.isBoolean() && e2.isBoolean())
return e1.getAsBoolean() == e2.getAsBoolean();
if (e1.isNumber() && e2.isNumber())
return e1.getAsNumber().doubleValue() == e2.getAsNumber()
.doubleValue();
return e1.getAsString().equals(e2.getAsString());
}
public static boolean isNull(JsonElement element) {
if (element == null)
return true;
if (element.isJsonNull())
return true;
if (element.isJsonArray())
return element.getAsJsonArray().size() == 0;
if (element.isJsonObject())
return element.getAsJsonObject().entrySet().size() == 0;
if (element.isJsonPrimitive())
if (element.getAsJsonPrimitive().isNumber())
return element.getAsJsonPrimitive().getAsNumber() == null;
else if (element.getAsJsonPrimitive().isString())
return element.getAsJsonPrimitive().getAsString() == null;
return false;
}
public static String getString(JsonElement element, String property) {
JsonElement value = getValue(element, property);
if (value == null)
return null;
return value.getAsString();
}
public static double getDouble(JsonElement element, String property) {
return getDouble(element, property, 0d);
}
public static Double getDouble(JsonElement element, String property,
Double defaultValue) {
JsonElement value = getValue(element, property);
if (!value.isJsonPrimitive())
return defaultValue;
JsonPrimitive primitive = value.getAsJsonPrimitive();
if (primitive.isNumber())
return primitive.getAsDouble();
if (!primitive.isString())
return defaultValue;
try {
return Double.parseDouble(primitive.getAsString());
} catch (NumberFormatException e) {
return defaultValue;
}
}
private static JsonElement getValue(JsonElement element, String property) {
if (element == null)
return null;
if (!element.isJsonObject())
return null;
JsonObject object = element.getAsJsonObject();
if (property.contains(".")) {
String next = property.substring(0, property.indexOf('.'));
String rest = property.substring(property.indexOf('.') + 1);
return getValue(object.get(next), rest);
}
if (!object.has(property))
return null;
return object.get(property);
}
public static JsonArray replace(int index, JsonArray original,
JsonElement toReplace) {
JsonArray copy = new JsonArray();
for (int i = 0; i < original.size(); i++)
if (index == i)
copy.add(toReplace);
else
copy.add(original.get(i));
return copy;
}
public static JsonArray remove(int index, JsonArray original) {
JsonArray copy = new JsonArray();
for (int i = 0; i < original.size(); i++)
if (index != i)
copy.add(original.get(i));
return copy;
}
public static int find(JsonElement element, JsonArray array,
String... fields) {
if (array == null || array.size() == 0)
return -1;
if (element.isJsonPrimitive())
return findPrimitive(element.getAsJsonPrimitive(), array);
if (fields == null)
return -1;
if (!element.isJsonObject())
return -1;
JsonObject object = element.getAsJsonObject();
String[] values = getValues(object, fields);
if (values == null)
return -1;
Iterator<JsonElement> iterator = array.iterator();
int index = 0;
while (iterator.hasNext()) {
JsonElement other = iterator.next();
if (!other.isJsonObject()) {
index++;
continue;
}
String[] otherValues = getValues(other.getAsJsonObject(), fields);
if (equal(values, otherValues))
return index;
index++;
}
return -1;
}
private static int findPrimitive(JsonPrimitive element, JsonArray array) {
Iterator<JsonElement> iterator = array.iterator();
int index = 0;
while (iterator.hasNext()) {
JsonElement next = iterator.next();
if (!next.isJsonPrimitive()) {
index++;
continue;
}
JsonPrimitive other = next.getAsJsonPrimitive();
if (other.equals(element))
return index;
index++;
}
return -1;
}
private static String get(JsonObject object, String... path) {
if (path == null)
return null;
if (path.length == 0)
return null;
Stack<String> stack = new Stack<>();
for (int i = path.length - 1; i >= 0; i--)
stack.add(path[i]);
while (stack.size() > 1) {
String next = stack.pop();
if (!object.has(next))
return null;
object = object.get(next).getAsJsonObject();
}
JsonElement value = object.get(stack.pop());
if (value == null || value.isJsonNull())
return null;
if (!value.isJsonPrimitive())
return null;
if (value.getAsJsonPrimitive().isNumber())
return Double.toString(value.getAsNumber().doubleValue());
if (value.getAsJsonPrimitive().isBoolean())
return value.getAsBoolean() ? "true" : "false";
return value.getAsString();
}
private static String[] getValues(JsonObject object, String[] fields) {
String[] values = new String[fields.length];
for (int i = 0; i < fields.length; i++)
values[i] = get(object, fields[i].split("\\."));
return values;
}
private static boolean equal(String[] a1, String[] a2) {
if (a1 == null && a2 == null)
return true;
if (a1 == null || a2 == null)
return false;
if (a1.length != a2.length)
return false;
for (int i = 0; i < a1.length; i++)
if (a1[i] == a2[i])
continue;
else if (a1[i] == null)
return false;
else if (!a1[i].equals(a2[i]))
return false;
return true;
}
public static abstract class ElementFinder {
protected abstract String[] getComparisonFields(String property);
protected abstract boolean skipOnEqualsCheck(String parentProperty, JsonElement element, String property);
public int find(String property, JsonElement element, JsonArray array) {
return JsonUtil.find(element, array, getComparisonFields(property));
}
}
}