Skip to content

Commit

Permalink
Allow @context to appear anywhere in JSON-LD document
Browse files Browse the repository at this point in the history
  • Loading branch information
Greg McFall committed Nov 5, 2013
1 parent 4ce5f6d commit 7ac9856
Show file tree
Hide file tree
Showing 12 changed files with 882 additions and 15 deletions.
2 changes: 1 addition & 1 deletion semantictools-jsonld/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
<groupId>org.semantictools</groupId>
<artifactId>semantictools-jsonld</artifactId>
<packaging>jar</packaging>
<version>1.22</version>
<version>1.24</version>
<name>SemanticTools JSON-LD</name>
<description>
A library used for JSON-LD processing
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@

public interface LdObject extends LdNode {

void setContext(LdContext context);

/**
* Returns the JSON-LD context that governs this object.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import org.semantictools.jsonld.impl.LdContextManagerImpl;
import org.semantictools.jsonld.impl.LdParserImpl;
import org.semantictools.jsonld.impl.LdPublisherPipeline;
import org.semantictools.jsonld.impl.LdTreeReader;
import org.semantictools.jsonld.impl.LdValidationServiceImpl;
import org.semantictools.jsonld.io.ErrorHandler;
import org.semantictools.jsonld.io.LdContextReader;
Expand Down Expand Up @@ -160,7 +161,7 @@ private LdContextWriter getContextWriter() {
private LdParser getLdParser() {
if (jsonldParser == null) {
LdContextReader reader = new EnhancedLdContextReader(getContextEnhancer(), getContextReader());
jsonldParser = new LdParserImpl(reader);
jsonldParser = new LdTreeReader(reader);
}
return jsonldParser;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,10 +84,14 @@ public LdObject asObject() throws ClassCastException {

@Override
public LdContext getContext() {
if (context == null && owner != null && owner.getOwner()!=null) {
return owner.getOwner().getContext();
}
return context;
}

void setContext(LdContext context) {
@Override
public void setContext(LdContext context) {
this.context = context;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,272 @@
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;
if (contextNode == null) {
context = parent.getContext();
}
if (contextNode != null) {
try {
context = contextReader.parseContext(contextNode);

} catch (Exception e) {
throw new LdParseException(e);
}
}
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;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -168,19 +168,19 @@ private void validateObject(String path, RandomAccessObject object, LdClass dr)
Integer minCardinality = restriction.getMinCardinality();
Integer maxCardinality = restriction.getMaxCardinality();


LdContext context = object.getNode().getContext();

int cardinality = getCardinality(field);

if (minCardinality != null) {
validateMinCardinality(object.getNode(), propertyURI, fieldPath, minCardinality, cardinality);
validateMinCardinality(context, object.getNode(), propertyURI, fieldPath, minCardinality, cardinality);
}

if (maxCardinality != null) {
validateMaxCardinality(fieldPath, maxCardinality, cardinality);
}

validateQualifiedRestrictions(fieldPath, restriction, field, cardinality);
validateQualifiedRestrictions(context, fieldPath, restriction, field, cardinality);

}

Expand All @@ -190,6 +190,7 @@ private void validateObject(String path, RandomAccessObject object, LdClass dr)


private void validateQualifiedRestrictions(
LdContext context,
String fieldPath, LdRestriction restriction, LdField field, int cardinality) {

List<LdQualifiedRestriction> list = restriction.listQualifiedRestrictions();
Expand All @@ -199,9 +200,14 @@ private void validateQualifiedRestrictions(

Integer minCardinality = qr.getMinCardinality();
Integer maxCardinality = qr.getMaxCardinality();

LdObject owner = field==null ? null : field.getOwner();
String propertyURI = field==null ? qr.getRestriction().getPropertyURI() : field.getPropertyURI();

LdContext c = (owner==null) ? context : owner.getContext();

if (minCardinality != null) {
validateMinCardinality(field.getOwner(), field.getPropertyURI(), fieldPath, minCardinality, cardinality);
validateMinCardinality(c, owner, propertyURI, fieldPath, minCardinality, cardinality);
}

if (maxCardinality != null) {
Expand Down Expand Up @@ -500,13 +506,13 @@ private String getLocalName(String propertyURI) {


private void validateMinCardinality(
LdContext context,
LdObject object, String propertyURI, String fieldPath, int minCardinality, int cardinality) {

/**
* Check to see if the minimum cardinality has an override in the
* JSON-LD context.
*/
LdContext context = object.getContext();
LdTerm term = context.getTerm(propertyURI);
if (term.getMinCardinality() != null) {
minCardinality = term.getMinCardinality();
Expand Down
Loading

0 comments on commit 7ac9856

Please sign in to comment.