package org.aksw.jena_sparql_api.shape;
import java.util.Map.Entry;
import org.aksw.gson.utils.JsonVisitor;
import org.aksw.gson.utils.JsonWalker;
import org.aksw.jena_sparql_api.concepts.Relation;
import org.aksw.jena_sparql_api.utils.Vars;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonNull;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
import org.apache.jena.graph.Node;
import org.apache.jena.graph.NodeFactory;
import org.apache.jena.shared.PrefixMapping;
import org.apache.jena.shared.impl.PrefixMappingImpl;
import org.apache.jena.sparql.core.Prologue;
import org.apache.jena.sparql.expr.E_Equals;
import org.apache.jena.sparql.expr.Expr;
import org.apache.jena.sparql.expr.ExprVar;
import org.apache.jena.sparql.expr.NodeValue;
import org.apache.jena.sparql.syntax.ElementFilter;
import org.apache.jena.sparql.util.ExprUtils;
/**
* shape: true
*
* shape: 'rdfs:label'
*
*
* shape: ['rdf:type', 'rdfs:label']
*
* shape: {
* 'rdf:type': true # Fetch rdf:type triples together with all outgoing triples of rdf:type
* }
*
* shape: {
* 'rdf:type': false # Fetch rdf:type triples, but no further reachable triples
* }
*
* shape: {
* '-rdf:type': ... # Prefix with '-' to navigate in inverse direction (should replace '>' which we used so far)
* }
*
* shape: {
* '~?p = rdf:type && langMatches(lang(?o), "en")' // Prefix with ~ to use a sparql expression
* }
*
* Special attributes start with '$':
* $filter: Set a concept for filtering the set of reached resources
*
* note:
* ['rdf:type'] is equivalent to { 'rdf:type': false }
*
* shape: {
* 'rdf:type': {
* $filter: '?s | ?s a owl:Class' // Only fetch types that are owl:Classes (i.e. exclude e.g. SKOS concepts),
* $predicates: ['rdfs:label']
* }
* }
*
* Macro symbols:
* shape: '{@literal @}spatial'
*
* At {@literal @}spatial will extended with its definition.
*
*
* @author raven
*
*/
public class ResourceShapeParserJson
implements JsonVisitor<Void>
{
protected ResourceShapeBuilder builder;
public ResourceShapeBuilder getBuilder() {
return builder;
}
public ResourceShapeParserJson() {
this(new PrefixMappingImpl());
}
public ResourceShapeParserJson(PrefixMapping prefixMapping) {
this(new ResourceShapeBuilder(prefixMapping));
}
public ResourceShapeParserJson(Prologue prologue) {
this(new ResourceShapeBuilder(prologue));
}
public ResourceShapeParserJson(ResourceShapeBuilder builder) {
this.builder = builder;
//this.builder = new ResourceShapeBuilder(prefixMapping);
}
/**
* String must be of format
* [-] [~] str
*
* -: If present, assume inverse direction
* ~: If present, str is assumed to be a SPARQL expression. Otherwise, a property URI is assumed
*
*
* @param str
* @return
*/
public static StepRelation parseStep(String str, PrefixMapping prefixMapping) {
str = str.trim();
// Check the first character
char c = str.charAt(0);
boolean isInverse = c == '-';
if(isInverse) {
str = str.substring(1);
}
c = str.charAt(0);
boolean isExpr = c == '~';
if(isExpr) {
str = str.substring(1);
}
Expr expr;
if(isExpr) {
expr = ExprUtils.parse(str, prefixMapping);
} else {
String p = prefixMapping.expandPrefix(str);
Node np = NodeFactory.createURI(p);
expr = new E_Equals(new ExprVar(Vars.p), NodeValue.makeNode(np));
}
Relation relation = new Relation(new ElementFilter(expr), Vars.p, Vars.o);
StepRelation result = new StepRelation(relation, isInverse);
return result;
}
public static ResourceShape parse(JsonElement json) {
ResourceShapeParserJson parser = new ResourceShapeParserJson();
JsonWalker.visit(json, parser);
ResourceShape result = parser.getBuilder().getResourceShape();
return result;
}
public static ResourceShape parse(JsonElement json, ResourceShapeBuilder builder) {
ResourceShapeParserJson parser = new ResourceShapeParserJson(builder);
JsonWalker.visit(json, parser);
ResourceShape result = builder.getResourceShape();
return result;
}
@Override
public Void visit(JsonNull json) {
return null;
}
@Override
public Void visit(JsonObject json) {
PrefixMapping pm = builder.getPrefixMapping();
for(Entry<String, JsonElement> entry : json.entrySet()) {
String str = entry.getKey();
JsonElement e = entry.getValue();
StepRelation step = parseStep(str, pm);
ResourceShapeBuilder subBilder = builder.nav(step);
JsonWalker.visit(e, this);
parse(e, subBilder);
}
return null;
}
@Override
public Void visit(JsonArray json) {
for(JsonElement item : json) {
JsonWalker.walk(item, this);
}
return null;
}
@Override
public Void visit(JsonPrimitive json) {
PrefixMapping pm = builder.getPrefixMapping();
if(json.isBoolean()) {
Boolean tf = json.getAsBoolean();
if(tf == true) {
builder.nav(NodeValue.TRUE, true);
}
}
else if (json.isString()) { // fetch a single property
String str = json.getAsString();
StepRelation step = parseStep(str, pm);
builder.nav(step);
}
return null;
}
}