JDIL.java
/*
* Copyright (c) 2007-2017 MetaSolutions AB
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.entrystore.rest.util.jdil;
import org.eclipse.rdf4j.model.BNode;
import org.eclipse.rdf4j.model.IRI;
import org.eclipse.rdf4j.model.Literal;
import org.eclipse.rdf4j.model.Model;
import org.eclipse.rdf4j.model.Resource;
import org.eclipse.rdf4j.model.Statement;
import org.eclipse.rdf4j.model.Value;
import org.eclipse.rdf4j.model.impl.LinkedHashModel;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import static org.eclipse.rdf4j.model.util.Values.*;
/**
*
* @author Matthias Palmer , Eric Johansson
*
*/
public class JDIL {
private Namespaces namespaces;
static Logger log = LoggerFactory.getLogger(JDIL.class);
public JDIL(Namespaces namespaces) {
this.namespaces = namespaces;
}
HashMap<String, String> globalNamespaces;
public String getAbbr(String uri) {
return namespaces.abbreviate(uri);
}
/**
* Converts a JDIL-JSON-object to a RDF graph.
* Example JDIL-JSON:
* {"metadata": {"dcterms:description":"nice data"}}
* @param jsonObject the JSONobject with the JDIL-JSON
* @return a new Graph
*/
public Model importJDILtoGraph(JSONObject jsonObject) {
try {
Model graph = new LinkedHashModel();
JDILParser.removeJDILStar(jsonObject);
this.JDILtoGraph(jsonObject, graph, null, new HashMap<String, BNode>());
return graph;
} catch (JSONException e) {
e.printStackTrace();
}
return null;
}
private void JDILtoGraph(JSONObject jsonObject, Model graph, Resource subject, Map<String, BNode> bnodeResolver) throws JSONException {
if (subject == null) { //Assume there is a @id.
subject = iri(namespaces.expand(jsonObject.getString("@id")));
}
Iterator keyIt = jsonObject.keys();
while(keyIt.hasNext()) {
String key = keyIt.next().toString();
if (key.startsWith("@")) {
//One of the builtin that should be ignored, e.g. @id or @namespaces
continue;
}
IRI spred = iri(namespaces.expand(key));
Object obj= jsonObject.opt(key);
// Recurse (base pattern)
if (obj instanceof JSONObject) {
//single property
extract(graph, subject, spred, obj, bnodeResolver);
} else if (obj instanceof JSONArray) {
//Repeated properties as array
JSONArray array = (JSONArray) obj;
for (int i = 0; i < array.length(); i++) {
obj = array.get(i);
extract(graph, subject, spred, obj, bnodeResolver);
}
} else {
//Take care of literal.
graph.add(subject, spred, literal(obj.toString()));
}
}
}
private void extract(Model graph, Resource subject, IRI spred, Object obj, Map<String, BNode> bnodeResolver) throws JSONException {
if (((JSONObject) obj).has("@id") && !((JSONObject) obj).has("@isBlank")) {
//Take care of object
IRI sobj = iri(namespaces.expand(((JSONObject)obj).getString("@id")));
graph.add(subject, spred, sobj);
JDILtoGraph((JSONObject)obj, graph, sobj, bnodeResolver);
} else {
if (((JSONObject) obj).has("@isBlank")) {
String bid = ((JSONObject)obj).getString("@id");
BNode sobj = null;
if (bid == null) {
sobj = bnode();
} else if (bnodeResolver.containsKey(bid)){
sobj = bnodeResolver.get(bid);
} else {
sobj = bnode();
bnodeResolver.put(bid, sobj);
}
graph.add(subject, spred, sobj);
JDILtoGraph((JSONObject)obj, graph, sobj, bnodeResolver);
} else {
//Take care of literal as object.
exportLiteral((JSONObject) obj, graph, subject, spred);
}
}
}
private void exportLiteral(JSONObject obj, Model graph, Resource subject, IRI predicate) {
Object objVal= obj.opt("@value");
Object objLang= obj.opt("@language");
Object objType= obj.opt("@datatype");
if(objLang != null) { //value + language
graph.add(subject, predicate, literal((String)objVal,(String)objLang));
} else if(objType != null) { //value + type
graph.add(subject, predicate, literal((String) objVal, iri(namespaces.expand((String)objType))));
} else { //value
graph.add(subject, predicate, literal((String)objVal));
}
}
/**
* pseudo code
*
* @param graph
* @return
*/
public JSONObject exportRelationGraphToJSON(Model graph) {
JSONObject result = new JSONObject();
//HashMap<String, HashMap<String, ArrayList<String>>> objRelations = new HashMap<String, HashMap<String,ArrayList<String>>>();
//HashMap<String, ArrayList<String>> subjRelations = new HashMap<String,ArrayList<String>>();
//ArrayList<String> predRelations = new ArrayList<String>();
// { Obj1 : { subj1: pred1, subj2: [pred2, pred4] }
//
// { "res1" : { subj1: pred1, subj2: [pred2, pred4] },
for (Statement statement : graph) {
String obj = namespaces.abbreviate(statement.getObject().stringValue());
String subj = namespaces.abbreviate(statement.getSubject().stringValue());
String pred = namespaces.abbreviate(statement.getPredicate().stringValue());
JSONObject objJSON = null;
try {
objJSON = result.getJSONObject(obj);
} catch (JSONException e) {
objJSON = new JSONObject();
try {
result.put(obj, objJSON);
} catch (JSONException e1) {
}
}
try {
objJSON.append(subj, pred);
} catch (JSONException e1) {
}
}
return result;
}
public JSONObject exportGraphToJDIL(Model graph, Resource root) {
try {
HashMap<Resource, JSONObject> res2Jdil= new HashMap<Resource, JSONObject>();
HashSet<Resource> notRoots = new HashSet<Resource>();
for (Statement statement : graph) {
JSONObject subj = getOrCreateSubject(statement.getSubject(), res2Jdil);
String predicate = namespaces.abbreviate(statement.getPredicate().stringValue());
notRoots.add(statement.getPredicate());
Value value = statement.getObject();
if (value instanceof Resource) {
/*
* Create a new JDIL value to accumulate to the subject.
*/
JSONObject JDILValueObject = getOrCreateObject((Resource) value,res2Jdil);
subj.accumulate(predicate, JDILValueObject);
notRoots.add((Resource) value);
} else {
Literal lit = (Literal) value;
String language = lit.getLanguage().orElse(null);
IRI datatype = lit.getDatatype();
JSONObject object = new JSONObject();
object.accumulate("@value", value.stringValue());
if (language != null) {
object.accumulate("@language", language);
} else if (datatype != null) {
object.accumulate("@datatype", datatype.stringValue());
}
subj.accumulate(predicate, object);
}
}
if (root != null) {
JSONObject obj = res2Jdil.get(root);
cutLoops(obj, new HashSet());
return obj;
}
HashSet<Resource> roots = new HashSet<Resource>(res2Jdil.keySet());
roots.removeAll(notRoots);
if (roots.size() == 1) {
JSONObject obj = res2Jdil.get(roots.iterator().next());
cutLoops(obj, new HashSet());
return obj;
}
} catch (JSONException jse) {
log.error(jse.getMessage());
}
return null;
}
void cutLoops(JSONObject obj, HashSet above) {
if (obj == null) {
return;
}
above.add(obj);
for (Iterator it = obj.keys();it.hasNext();) {
String key = (String) it.next();
Object child = obj.opt(key);
if (child instanceof JSONObject) {
if (above.contains(child)) {
try {
JSONObject newChild = new JSONObject();
if (((JSONObject) child).has("@id")) {
newChild.put("@id", ((JSONObject) child).opt("@id"));
}
if (((JSONObject) child).has("@isBlank")) {
newChild.put("@isBlank", ((JSONObject) child).opt("@isBlank"));
}
obj.put(key, newChild);
} catch (JSONException e) {
}
} else {
cutLoops((JSONObject) child, above);
}
} else if (child instanceof JSONArray) {
JSONArray arr = (JSONArray) child;
for (int i = 0;i<arr.length(); i++) {
try {
Object arrChild = arr.get(i);
if (arrChild instanceof JSONObject) {
if (above.contains(arrChild)) {
JSONObject newChild = new JSONObject();
if (((JSONObject) arrChild).opt("@id") != null) {
newChild.put("@id", ((JSONObject) arrChild).opt("@id"));
} else {
newChild.put("@bid", ((JSONObject) arrChild).opt("@bid"));
}
arr.put(i, newChild);
} else {
cutLoops((JSONObject) arrChild, above);
}
}
} catch (JSONException e) {
e.printStackTrace();
}
}
}
}
}
JSONObject getOrCreateSubject(Resource resource, Map<Resource, JSONObject> res2Jdil) throws JSONException {
JSONObject jdil = res2Jdil.get(resource);
if (jdil == null) {
jdil = new JSONObject();
if (resource instanceof BNode) {
jdil.accumulate("@id", resource.stringValue());
jdil.accumulate("@isBlank", true);
} else {
jdil.accumulate("@id", namespaces.abbreviate(resource.stringValue()));
}
res2Jdil.put(resource, jdil);
}
return jdil;
}
JSONObject getOrCreateObject(Resource resource, Map<Resource, JSONObject> res2Jdil) throws JSONException {
JSONObject jdil = new JSONObject();
if (resource instanceof BNode) {
jdil.accumulate("@id", resource.stringValue());
jdil.accumulate("@isBlank", true);
} else {
jdil.accumulate("@id", namespaces.abbreviate(resource.stringValue()));
}
if (res2Jdil.get(resource) == null) {
res2Jdil.put(resource, jdil);
} else {
jdil = res2Jdil.get(resource);
}
return jdil;
}
public Namespaces getNamespaces() {
return namespaces;
}
}