package services;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import com.fasterxml.jackson.databind.node.ObjectNode;
import helpers.JsonLdConstants;
import models.Resource;
import org.apache.commons.io.IOUtils;
import org.apache.jena.query.DatasetFactory;
import org.apache.jena.query.QueryExecution;
import org.apache.jena.query.QueryExecutionFactory;
import org.apache.jena.query.QueryFactory;
import org.apache.jena.query.QuerySolution;
import org.apache.jena.query.ResultSet;
import org.apache.jena.rdf.model.Model;
import org.apache.jena.rdf.model.ModelFactory;
import org.apache.jena.rdf.model.NodeIterator;
import org.apache.jena.riot.JsonLDWriteContext;
import org.apache.jena.riot.Lang;
import org.apache.jena.riot.RDFDataMgr;
import org.apache.jena.riot.RDFFormat;
import org.apache.jena.riot.WriterDatasetRIOT;
import org.apache.jena.riot.system.RiotLib;
import org.apache.jena.shared.Lock;
import org.apache.jena.sparql.core.DatasetGraph;
import org.apache.jena.vocabulary.RDF;
import play.Logger;
import services.repository.TriplestoreRepository;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
/**
* Created by fo on 23.03.16.
*/
public class ResourceFramer {
public static Resource resourceFromModel(Model aModel, String aId) throws IOException {
String describeStatement = String.format(TriplestoreRepository.EXTENDED_DESCRIPTION, aId);
Model dbstate = ModelFactory.createDefaultModel();
aModel.enterCriticalSection(Lock.READ);
dbstate.enterCriticalSection(Lock.WRITE);
try {
try (QueryExecution queryExecution = QueryExecutionFactory.create(QueryFactory.create(describeStatement),
aModel)) {
queryExecution.execDescribe(dbstate);
ByteArrayOutputStream unframed = new ByteArrayOutputStream();
RDFDataMgr.write(unframed, dbstate, RDFFormat.JSONLD_EXPAND_PRETTY);
unframed.close();
NodeIterator types = aModel.listObjectsOfProperty(aModel.createResource(aId), RDF.type);
if (types.hasNext()) {
DatasetGraph g = DatasetFactory.create(dbstate).asDatasetGraph();
JsonLDWriteContext ctx = new JsonLDWriteContext();
String context = "{ \"@context\": \"https://oerworldmap.org/assets/json/context.json\"}";
ctx.setJsonLDContext(context);
ByteArrayOutputStream boas = new ByteArrayOutputStream();
WriterDatasetRIOT w = RDFDataMgr.createDatasetWriter(RDFFormat.JSONLD_COMPACT_PRETTY);
w.write(boas, g, RiotLib.prefixMap(g), null, ctx);
ObjectMapper objectMapper = new ObjectMapper();
JsonNode jsonNode = objectMapper.readTree(boas.toByteArray());
if (jsonNode.has(JsonLdConstants.GRAPH)) {
ArrayNode graphs = (ArrayNode) jsonNode.get(JsonLdConstants.GRAPH);
for (JsonNode graph : graphs) {
if (graph.get(JsonLdConstants.ID).asText().equals(aId)) {
ObjectNode result = (ObjectNode) buildTree(graph, graphs);
result.put(JsonLdConstants.CONTEXT, "https://oerworldmap.org/assets/json/context.json");
Logger.debug("Framed " + aId);
return Resource.fromJson(result);
}
}
} else {
ObjectNode result = (ObjectNode) jsonNode;
result.put(JsonLdConstants.CONTEXT, "https://oerworldmap.org/assets/json/context.json");
Logger.debug("Framed " + aId);
return Resource.fromJson(result);
}
}
}
} finally {
dbstate.leaveCriticalSection();
aModel.leaveCriticalSection();
}
return null;
}
private static JsonNode buildTree(JsonNode graph, ArrayNode graphs) {
if (graph.isArray()) {
ArrayNode result = new ArrayNode(JsonNodeFactory.instance);
Iterator<JsonNode> elements = graph.elements();
while (elements.hasNext()) {
JsonNode element = elements.next();
result.add(buildTree(element, graphs));
}
return result;
} else if (graph.isObject()) {
ObjectNode result = new ObjectNode(JsonNodeFactory.instance);
Iterator<String> fieldNames = graph.fieldNames();
while(fieldNames.hasNext()) {
String fieldName = fieldNames.next();
if (! (fieldName.equals(JsonLdConstants.ID) && graph.get(fieldName).asText().startsWith("_:"))) {
result.set(fieldName, link(graph.get(fieldName), graphs));
}
}
return result;
} else {
return graph;
}
}
private static JsonNode link(JsonNode ref, ArrayNode graphs) {
JsonNode graph;
if (ref.has(JsonLdConstants.ID)) {
graph = getGraph(ref.get(JsonLdConstants.ID).asText(), graphs);
} else {
graph = ref;
}
List<String> linkProperties = Arrays.asList("@id", "@type", "@value", "@language", "name", "image", "location",
"startDate", "endDate");
if (graph != null && graph.isArray()) {
ArrayNode result = new ArrayNode(JsonNodeFactory.instance);
Iterator<JsonNode> elements = graph.elements();
while (elements.hasNext()) {
JsonNode element = elements.next();
result.add(link(element, graphs));
}
return result;
} else if (graph != null && graph.isObject()) {
if (graph.get(JsonLdConstants.ID) != null && graph.get(JsonLdConstants.ID).asText().startsWith("_:")) {
return buildTree(graph, graphs);
}
ObjectNode result = new ObjectNode(JsonNodeFactory.instance);
Iterator<String> fieldNames = graph.fieldNames();
while(fieldNames.hasNext()) {
String fieldName = fieldNames.next();
if (linkProperties.contains(fieldName)) {
JsonNode value = graph.get(fieldName);
if (value.isObject() || value.isArray()) {
result.set(fieldName, link(value, graphs));
} else {
result.set(fieldName, value);
}
}
}
return result;
} else {
return graph;
}
}
private static JsonNode getGraph(String aId, ArrayNode graphs) {
for (JsonNode graph: graphs) {
if (graph.get(JsonLdConstants.ID).asText().equals(aId)) {
return graph;
}
}
return new ObjectNode(JsonNodeFactory.instance);
}
public static List<Resource> flatten(Resource resource) throws IOException {
Model model = ModelFactory.createDefaultModel();
RDFDataMgr.read(model, IOUtils.toInputStream(resource.toString(), StandardCharsets.UTF_8), Lang.JSONLD);
List<Resource> resources = new ArrayList<>();
String subjectsQuery = "SELECT DISTINCT ?s WHERE { ?s ?p ?o . FILTER isIRI(?s) }";
try (QueryExecution queryExecution = QueryExecutionFactory.create(QueryFactory.create(subjectsQuery), model)) {
ResultSet resultSet = queryExecution.execSelect();
while (resultSet.hasNext()) {
QuerySolution querySolution = resultSet.next();
String subject = querySolution.get("s").toString();
resources.add(resourceFromModel(model, subject));
}
}
return resources;
}
}