package org.semantictools.jsonld.impl;
import java.io.IOException;
import java.io.InputStream;
import java.util.Iterator;
import java.util.Map.Entry;
import org.codehaus.jackson.JsonNode;
import org.codehaus.jackson.JsonParseException;
import org.codehaus.jackson.JsonParser.Feature;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.node.ArrayNode;
import org.codehaus.jackson.node.ObjectNode;
import org.semantictools.jsonld.LdContainerType;
import org.semantictools.jsonld.LdContext;
import org.semantictools.jsonld.LdField;
import org.semantictools.jsonld.LdLiteral;
import org.semantictools.jsonld.LdNode;
import org.semantictools.jsonld.LdObject;
import org.semantictools.jsonld.LdTerm;
import org.semantictools.jsonld.io.LdContextReader;
import org.semantictools.jsonld.io.LdParseException;
import org.semantictools.jsonld.io.LdParser;
public class LdTreeReader implements LdParser {
private LdContextReader contextReader;
public LdTreeReader(LdContextReader contextReader) {
this.contextReader = contextReader;
}
@Override
public LdNode parse(InputStream input) throws LdParseException, IOException {
ObjectMapper mapper = new ObjectMapper();
mapper.configure(Feature.ALLOW_COMMENTS, true);
JsonNode node = mapper.readTree(input);
if (! (node instanceof ObjectNode)) {
throw new LdParseException("JSON-LD document must have an object as the root element");
}
LdNode result = parseNode(node, null, null, null);
return result;
}
LdNode parseNode(JsonNode node, LdObjectImpl parent, LdTerm term, LdField owner) throws JsonParseException, IOException, LdParseException {
LdNode result = null;
if ( node instanceof ObjectNode) {
result = parseObject((ObjectNode) node, parent, owner);
} else if (node.isBoolean()) {
LdLiteral literal = new LdLiteral();
literal.setBooleanValue(node.getBooleanValue());
result = literal;
} else if (node.isFloatingPointNumber()) {
LdLiteral literal = new LdLiteral();
literal.setDoubleValue(node.getDoubleValue());
result = literal;
} else if (node.isLong() || node.isInt()) {
LdLiteral literal = new LdLiteral();
literal.setLongValue(node.getLongValue());
result = literal;
} else if (node.isTextual()) {
result = parseStringNode(node.getTextValue(), parent, term);
} else if (node.isArray()) {
result = parseArray((ArrayNode)node, parent, term, owner);
}
return result;
}
private LdNode parseStringNode(String text, LdObject parent, LdTerm term) throws JsonParseException, IOException {
LdNode result = null;
String type = (term==null) ? null : term.getRawTypeIRI();
if ("@id".equals(type)) {
LdContext context = (parent==null) ? null : parent.getContext();
LdObjectImpl object = new LdObjectImpl(context);
object.setId(text);
result = object;
} else {
LdLiteral literal = new LdLiteral();
literal.setStringValue(text);
result = literal;
}
return result;
}
private LdNode parseArray(ArrayNode array, LdObjectImpl parent, LdTerm term, LdField owner) throws JsonParseException, IOException, LdParseException {
LdList list = new LdList(LdContainerType.SET);
parseElements(array, term, parent, list, owner);
return list;
}
private void parseElements(ArrayNode array, LdTerm term, LdObjectImpl parent, LdList list,
LdField field) throws JsonParseException, IOException, LdParseException {
for (int i=0; i<array.size(); i++) {
JsonNode node = array.get(i);
list.add(parseNode(node, parent, term, field));
}
}
private LdNode parseObject(ObjectNode node, LdObjectImpl parent, LdField owner) throws LdParseException, JsonParseException, IOException {
JsonNode value = node.get("@value");
if (value != null) {
return parseExtendedValue(node, parent, owner);
}
JsonNode contextNode = node.get("@context");
LdContext context = null;
LdContext parentContext = (parent==null) ? null : parent.getContext();
if (contextNode != null) {
try {
context = contextReader.parseContext(contextNode);
context.setParentContext(parentContext);
} catch (Exception e) {
throw new LdParseException(e);
}
} else {
context = parentContext;
}
if (context == null) {
context = new LdContext();
}
LdObjectImpl object = new LdObjectImpl(context);
object.setOwner(owner);
parseFields(node, object, (ObjectNode)node, parent);
return object;
}
private LdNode parseExtendedValue(ObjectNode object, LdObjectImpl parent,
LdField owner) {
LdLiteral literal = new LdLiteral();
Iterator<Entry<String,JsonNode>> sequence = object.getFields();
while (sequence.hasNext()) {
Entry<String,JsonNode> entry = sequence.next();
String fieldName = entry.getKey();
JsonNode node = entry.getValue();
if (fieldName.equals("@language")) {
literal.setLanguage(node.getTextValue());
} else if (fieldName.equals("@type")) {
literal.setType(node.getTextValue());
} else if ("@value".equals(fieldName)) {
if (node.isBoolean()) {
literal.setBooleanValue(node.getBooleanValue());
} else if (node.isFloatingPointNumber()) {
literal.setDoubleValue(node.getDoubleValue());
} else if (node.isLong()) {
literal.setLongValue(literal.getLongValue());
} else if (node.isTextual()) {
literal.setStringValue(node.getTextValue());
}
}
}
return literal;
}
private void parseFields(ObjectNode json, LdObjectImpl object, ObjectNode node,
LdObjectImpl parent) throws JsonParseException, IOException, LdParseException {
FieldList fieldList = new FieldList();
object.setFieldList(fieldList);
LdContext context = object.getContext();
Iterator<Entry<String,JsonNode>> sequence = json.getFields();
while (sequence.hasNext()) {
Entry<String,JsonNode> entry = sequence.next();
String fieldName = entry.getKey();
JsonNode value = entry.getValue();
if ("@context".equals(fieldName)) {
continue;
} else if ("@id".equals(fieldName)) {
object.setId(value.getTextValue());
} else if ("@type".equals(fieldName)) {
object.setRawType(value.getTextValue());
} else {
LdTerm term = (context==null) ? null : context.getTerm(fieldName);
LinkedLdField field = new LinkedLdField(object);
// If the field name contains a '#', '/', or ':'
// then the simple name is substring after that delimiter.
int delim = fieldName.lastIndexOf('#');
if (delim < 0) {
delim = fieldName.lastIndexOf('/');
}
if (delim < 0) {
delim = fieldName.lastIndexOf(':');
}
if (delim >= 0) {
field.setLocalName(fieldName.substring(delim+1));
} else {
field.setLocalName(fieldName);
}
String propertyIRI = (context == null) ? fieldName : context.expand(fieldName);
field.setPropertyURI(propertyIRI);
LdNode v = parseNode(value, object, term, field);
field.setValue(v);
setValueOwner(field);
setType(field, fieldName, context);
object.fieldList.add(field);
}
}
}
/**
* Set the type of the given field based on the given context, but only if the
* field does not already have the type declared.
*/
private void setType(LinkedLdField field, String fieldName, LdContext context) {
if (context == null) return;
LdNode value = field.getValue();
if (value == null) return;
LdTerm term = context.getTerm(fieldName);
if (term == null) return;
if (value instanceof LdObjectImpl) {
LdObjectImpl object = (LdObjectImpl) value;
if (object.getTypeIRI() != null) return;
object.setTypeIRI(term.getTypeIRI());
} else if (value instanceof LdLiteral) {
LdLiteral literal = (LdLiteral) value;
if (literal.getType() == null) {
literal.setType(term.getTypeIRI());
}
}
}
private void setValueOwner(LdField field) {
LdNode value = field.getValue();
if (value instanceof LdObjectImpl) {
LdObjectImpl object = (LdObjectImpl) value;
object.setOwner(field);
} else if (value instanceof LdContainerImpl) {
LdContainerImpl container = (LdContainerImpl) value;
container.setOwner(field);
}
}
@Override
public void setStreaming(boolean streaming) {
}
@Override
public boolean isStreaming() {
return false;
}
}