/******************************************************************************* * Copyright 2012 Pearson Education * * 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.semantictools.context.renderer; import java.io.File; import java.io.IOException; import java.io.OutputStream; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import org.codehaus.jackson.JsonGenerationException; import org.codehaus.jackson.JsonGenerator; import org.codehaus.jackson.JsonNode; import org.codehaus.jackson.PrettyPrinter; import org.codehaus.jackson.map.ObjectMapper; import org.codehaus.jackson.map.ObjectWriter; import org.codehaus.jackson.map.SerializationConfig; import org.codehaus.jackson.node.ArrayNode; import org.codehaus.jackson.node.JsonNodeFactory; import org.codehaus.jackson.node.ObjectNode; import org.semantictools.frame.api.TypeManager; import org.semantictools.frame.model.OntologyInfo; import com.hp.hpl.jena.rdf.model.Literal; import com.hp.hpl.jena.rdf.model.Property; import com.hp.hpl.jena.rdf.model.RDFList; import com.hp.hpl.jena.rdf.model.RDFNode; import com.hp.hpl.jena.rdf.model.Resource; import com.hp.hpl.jena.rdf.model.Statement; import com.hp.hpl.jena.vocabulary.RDF; public class SampleWriter { private TypeManager typeManager; private JsonNodeFactory factory = JsonNodeFactory.instance; private Map<String, OntologyInfo> uri2OntologyInfo = new HashMap<String, OntologyInfo>(); private Map<String, ObjectNode> uri2Resource = new HashMap<String, ObjectNode>(); public SampleWriter(TypeManager typeManager) { this.typeManager = typeManager; } public void add(Resource resource) { String id = getIdentifier(resource); ObjectNode node = uri2Resource.get(id); if (node == null) { node = factory.objectNode(); uri2Resource.put(id, node); node.put("@id", id); addType(node, resource); addProperties(node, resource); } } public void write(File file) throws IOException { ObjectNode universe = createUniverse(); ObjectMapper mapper = new ObjectMapper(); mapper.configure(SerializationConfig.Feature.INDENT_OUTPUT, true); mapper.writeValue(file, universe); } public void write(OutputStream output) throws IOException { ObjectNode universe = createUniverse(); ObjectMapper mapper = new ObjectMapper(); ObjectWriter writer = mapper.writer(new MyPrettyPrinter()); mapper.configure(SerializationConfig.Feature.INDENT_OUTPUT, true); writer.writeValue(output, universe); } static class MyPrettyPrinter implements PrettyPrinter { private static enum Container { OBJECT, ARRAY } private int indent=0; private int tabSize=2; private List<Container> stack = new ArrayList<Container>(); private void pushIndent(Container container) { indent += tabSize; stack.add(container); } private void popIndent() { indent -= tabSize; stack.remove(stack.size()-1); } private Container getContainer() { return stack.size()>1 ? stack.get(stack.size()-2) : Container.OBJECT; } private void indent(JsonGenerator g) throws IOException { for (int i=0; i<indent; i++) { g.writeRaw(' '); } } @Override public void writeRootValueSeparator(JsonGenerator jg) throws IOException, JsonGenerationException { jg.writeRaw('\n'); } @Override public void writeStartObject(JsonGenerator jg) throws IOException, JsonGenerationException { jg.writeRaw("{"); pushIndent(Container.OBJECT); } @Override public void writeEndObject(JsonGenerator jg, int nrOfEntries) throws IOException, JsonGenerationException { jg.writeRaw("\n"); popIndent(); indent(jg); jg.writeRaw("}"); } @Override public void writeObjectEntrySeparator(JsonGenerator jg) throws IOException, JsonGenerationException { jg.writeRaw(",\n"); indent(jg); } @Override public void writeObjectFieldValueSeparator(JsonGenerator jg) throws IOException, JsonGenerationException { jg.writeRaw(" : "); } @Override public void writeStartArray(JsonGenerator jg) throws IOException, JsonGenerationException { jg.writeRaw("["); pushIndent(Container.ARRAY); } @Override public void writeEndArray(JsonGenerator jg, int nrOfValues) throws IOException, JsonGenerationException { jg.writeRaw('\n'); popIndent(); indent(jg); jg.writeRaw("]"); } @Override public void writeArrayValueSeparator(JsonGenerator jg) throws IOException, JsonGenerationException { jg.writeRaw(",\n"); indent(jg); } @Override public void beforeArrayValues(JsonGenerator jg) throws IOException, JsonGenerationException { jg.writeRaw(" \n"); indent(jg); } @Override public void beforeObjectEntries(JsonGenerator jg) throws IOException, JsonGenerationException { switch (getContainer()) { case OBJECT : jg.writeRaw('\n'); indent(jg); break; case ARRAY : jg.writeRaw(' '); break; } } } private ObjectNode createUniverse() { ObjectNode universe = factory.objectNode(); universe.put("@context", createContext()); universe.put("database", createDatabase()); return universe; } private ArrayNode createDatabase() { ArrayNode array = factory.arrayNode(); List<Map.Entry<String, ObjectNode>> list = new ArrayList<Map.Entry<String,ObjectNode>>(uri2Resource.entrySet()); Collections.sort(list, new Comparator<Map.Entry<String,ObjectNode>>() { @Override public int compare(Entry<String, ObjectNode> a, Entry<String, ObjectNode> b) { return a.getKey().compareTo(b.getKey()); } }); for (Entry<String, ObjectNode> entry : list) { array.add(entry.getValue()); } return array; } private ObjectNode createContext() { ObjectNode node = factory.objectNode(); List<OntologyInfo> list = new ArrayList<OntologyInfo>(uri2OntologyInfo.values()); Collections.sort(list, new Comparator<OntologyInfo>() { @Override public int compare(OntologyInfo a, OntologyInfo b) { return a.getPrefix().compareTo(b.getPrefix()); } }); for (OntologyInfo info : list) { node.put(info.getPrefix(), info.getNamespaceUri()); } return node; } private String getIdentifier(Resource resource) { String result = resource.isURIResource() ? resource.getURI() : resource.isAnon() ? "_:" + resource.getId().getLabelString() : null; if (result == null) { throw new IllegalArgumentException("Resource does not have an identifier"); } return result; } private void addProperties(ObjectNode node, Resource resource) { Iterator<Statement> sequence = resource.listProperties(); while (sequence.hasNext()) { Statement s = sequence.next(); Property p = s.getPredicate(); if (p.equals(RDF.type)) continue; RDFNode object = s.getObject(); String fieldName = getCurie(p); JsonNode fieldValue = node.get(fieldName); if (fieldValue == null) { addField(node, fieldName, object); } else if (fieldValue.isArray()) { // We already have more than one value for the given // field name, so add the new value to the existing array. addElement((ArrayNode) fieldValue, object); } else { // The object already has exactly one value for the given // field name. Replace that value with an array that // contains the previous value plus the new value. ArrayNode array = factory.arrayNode(); node.remove(fieldName); node.put(fieldName, array); array.add(fieldValue); addElement(array, object); } } } private void addElement(ArrayNode array, RDFNode value) { if (value.isLiteral()) { JsonNode jsonValue = toJsonNode(value.asLiteral()); array.add(jsonValue); } else if (value.isResource()){ Resource resource = value.asResource(); String id = getIdentifier(resource); array.add(id); add(resource); } else { throw new IllegalArgumentException("The elements in an array must be a literal or a resource"); } } private void addField(ObjectNode node, String fieldName, RDFNode object) { if (object.isLiteral()) { addLiteral(node, fieldName, object.asLiteral()); } else if (object.isResource()) { addResource(node, fieldName, object.asResource()); } } private void addResource(ObjectNode node, String fieldName, Resource resource) { if (resource.canAs(RDFList.class)) { addList(node, fieldName, resource.as(RDFList.class)); } else { node.put(fieldName, getIdentifier(resource)); add(resource); } } private void addList(ObjectNode node, String fieldName, RDFList list) { ArrayNode array = factory.arrayNode(); node.put(fieldName, array); List<RDFNode> valueList = list.asJavaList(); for (RDFNode value : valueList) { addElement(array, value); } } private JsonNode toJsonNode(Literal literal) { JsonNode node = null; Object value = literal.getValue(); if (value instanceof Boolean) { node = factory.booleanNode(literal.getBoolean()); } else if (value instanceof Byte) { node = factory.numberNode(literal.getByte()); } else if (value instanceof Double) { node = factory.numberNode(literal.getDouble()); } else if (value instanceof Float) { node = factory.numberNode(literal.getFloat()); } else if (value instanceof Integer) { node = factory.numberNode(literal.getInt()); } else if (value instanceof Long) { node = factory.numberNode(literal.getLong()); } else if (value instanceof Short) { node = factory.numberNode(literal.getShort()); } else { node = factory.textNode(value.toString()); } return node; } private void addLiteral(ObjectNode node, String fieldName, Literal literal) { JsonNode value = toJsonNode(literal); node.put(fieldName, value); } private void addType(ObjectNode node, Resource resource) { List<Statement> list = resource.listProperties(RDF.type).toList(); if (list.size() == 1) { String value = getCurie(list.get(0).getResource()); node.put("@type", value); } else if (list.size()>1) { ArrayNode array = factory.arrayNode(); node.put("@type", array); for (Statement s : list) { Resource type = s.getResource(); String curie = getCurie(type); array.add(curie); } } } private String getCurie(Resource resource) { String namespace = resource.getNameSpace(); OntologyInfo info = getOntologyInfo(namespace); if (info != null) { return info.getPrefix() + ":" + resource.getLocalName(); } return resource.getURI(); } private OntologyInfo getOntologyInfo(String namespace) { OntologyInfo info = uri2OntologyInfo.get(namespace); if (info == null) { info = typeManager.getOntologyByNamespaceUri(namespace); if (info != null) { uri2OntologyInfo.put(namespace, info); } } return info; } }