package edu.isi.karma.util;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Map;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;
public class JSONLDUtilSimple {
private static JSONLDReducerComparatorSimple comparator;
static {
comparator = new JSONLDReducerComparatorSimple();
}
private JSONLDUtilSimple() {
}
public static JSONObject mergeJSONObjects(String left, String right, Map<String, String> provenanceProperties) throws ParseException
{
JSONParser parser = new JSONParser();
return mergeJSONObjects((JSONObject)parser.parse(left), (JSONObject)parser.parse(right), provenanceProperties);
}
public static JSONObject mergeJSONObjects(Iterator<String> iterator) throws ParseException {
return mergeJSONObjects(iterator, new HashMap<String, String>());
}
public static JSONObject mergeJSONObjects(Iterator<String> iterator, Map<String, String> provenanceProperties) throws ParseException {
JSONParser parser = new JSONParser();
JSONObject accumulatorObject = new JSONObject();
while(iterator.hasNext())
{
String value = iterator.next();
JSONObject object = (JSONObject)parser.parse(value);
accumulatorObject = mergeJSONObjects(accumulatorObject, object, provenanceProperties);
}
return accumulatorObject;
}
public static JSONObject mergeJSONObjects(JSONObject left, JSONObject right, Map<String, String> provenanceProperties)
{
mergeJSONObjectsInner(left, right, provenanceProperties);
return left;
}
private static boolean mergeJSONObjectsInner(JSONObject left, JSONObject right, Map<String, String> provenanceProperties)
{
boolean rightAdded = false;
ArrayList<String> names = new ArrayList<>(right.keySet());
//Move provenance properties to the end of the list
for(String provProp: provenanceProperties.keySet()) {
if(names.contains(provProp)) {
names.remove(provProp);
names.add(provProp);
}
}
for(Object rawName : names)
{
String name = (String) rawName;
if(!left.containsKey(name))
{
Object rightObj = right.get(name);
left.put(name, rightObj);
if(rightObj instanceof String)
rightAdded = true;
}
else
{
Object leftObject = left.get(name);
Object rightObject = right.get(name);
if(leftObject instanceof JSONArray)
{
if(rightObject instanceof JSONArray)
{
rightAdded = rightAdded | mergeArrays(left, name, rightAdded, (JSONArray) leftObject, (JSONArray) rightObject, provenanceProperties);
}
else
{
JSONArray newRightArray = new JSONArray();
newRightArray.add(rightObject);
rightAdded = rightAdded | mergeArrays(left, name, rightAdded, (JSONArray) leftObject, newRightArray, provenanceProperties);
}
}
else
{
if(rightObject instanceof JSONArray)
{
JSONArray newLeftArray = new JSONArray();
newLeftArray.add(leftObject);
rightAdded = rightAdded | mergeArrays(left, name, rightAdded, newLeftArray, (JSONArray)rightObject, provenanceProperties);
}
else
{
JSONArray newLeftArray = new JSONArray();
JSONArray newRightArray = new JSONArray();
newLeftArray.add(leftObject);
newRightArray.add(rightObject);
rightAdded = rightAdded | mergeArrays(left, name, rightAdded, newLeftArray, newRightArray, provenanceProperties);
}
}
}
}
return rightAdded;
}
protected static boolean mergeArrays(JSONObject left, String name, boolean newAdded,
JSONArray leftArray, JSONArray rightArray, Map<String, String> provenanceProperties) {
LinkedList<Object> newArrayBuilder = new LinkedList<Object>();
int leftIndex = 0;
int rightIndex = 0;
boolean rightAdded = false;
boolean provenanceDateProperty = false;
//If it is a provenance property and no new information is added, we do not need to add the property
if(provenanceProperties.containsKey(name) && !newAdded) {
String provType = provenanceProperties.get(name);
if(provType == "date") {
//For dates, we would like to min, max date
provenanceDateProperty = true;
} else {
return false;
}
}
while(leftIndex < leftArray.size() && rightIndex < rightArray.size() )
{
int result = comparator.compare(leftArray.get(leftIndex),rightArray.get(rightIndex));
if(result < 0)
{
newArrayBuilder.add(leftArray.get(leftIndex++));
}
else if (result == 0)
{
Object tempLeft = leftArray.get(leftIndex++);
Object tempRight = rightArray.get(rightIndex++);
ObjectRightAdded mergedResultAdded = mergeStringsAndJSONObjects(
tempLeft, tempRight, provenanceProperties);
Object mergedResult = mergedResultAdded.object;
rightAdded = rightAdded | mergedResultAdded.rightAdded;
newArrayBuilder.add(mergedResult);
}
else
{
Object rightObj = rightArray.get(rightIndex++);
newArrayBuilder.add(rightObj);
if(rightObj instanceof String)
rightAdded = true;
}
}
while(leftIndex < leftArray.size())
{
newArrayBuilder.add(leftArray.get(leftIndex++));
}
while(rightIndex < rightArray.size())
{
Object rightObj = rightArray.get(rightIndex++);
newArrayBuilder.add(rightObj);
if(rightObj instanceof String)
rightAdded = true;
}
if(newArrayBuilder.size() > 1 || name.equals("a"))
{
JSONArray newArray = new JSONArray();
if(!provenanceDateProperty) {
newArray.addAll(newArrayBuilder);
} else {
String min = null, max = null;
for(Object obj : newArrayBuilder) {
String s = obj.toString();
if(min == null || s.compareTo(min) < 0)
min = s;
if(max == null || s.compareTo(max) > 0)
max = s;
}
newArray.add(min);
if(min != max)
newArray.add(max);
}
left.put(name, newArray);
}
else if(newArrayBuilder.size() == 1)
{
left.put(name, newArrayBuilder.get(0));
}
return rightAdded;
}
private static ObjectRightAdded mergeStringsAndJSONObjects(Object tempLeft,
Object tempRight, Map<String, String> provenanceProperties) {
Object mergedResult = null;
boolean rightAdded = false;
if(tempLeft instanceof String && tempRight instanceof String)
{
mergedResult = tempLeft;
}
else if(tempLeft instanceof JSONObject && tempRight instanceof String)
{
mergedResult = tempLeft;
}
else if(tempLeft instanceof String && tempRight instanceof JSONObject)
{
mergedResult = tempRight;
rightAdded = false; //We add right, but since its an object, the object will contain prov and we dont need to count it as merged
}
else if(tempLeft instanceof JSONObject && tempRight instanceof JSONObject)
{
// rightAdded = rightAdded | mergeJSONObjectsInner((JSONObject)tempLeft, (JSONObject)tempRight, provenanceProperties);
mergeJSONObjectsInner((JSONObject)tempLeft, (JSONObject)tempRight, provenanceProperties);
mergedResult = tempLeft;
rightAdded = false; //We add right, but since its an object, the object will contain prov and we dont need to count it as merged
}
else {
if (tempLeft instanceof String) {
mergedResult = tempRight.toString();
if(tempRight instanceof String)
rightAdded = true;
}
else {
mergedResult = tempLeft.toString();
}
}
return new ObjectRightAdded(mergedResult, rightAdded);
}
private static class ObjectRightAdded {
public Object object;
public boolean rightAdded;
public ObjectRightAdded(Object object, boolean rightAdded) {
this.object = object;
this.rightAdded = rightAdded;
}
}
}