package org.aksw.jena_sparql_api.shape; import java.util.List; import java.util.Map; import java.util.Map.Entry; import org.aksw.jena_sparql_api.concepts.Relation; import org.aksw.jena_sparql_api.utils.Vars; import org.apache.jena.graph.Node; import org.apache.jena.graph.NodeFactory; import org.apache.jena.shared.PrefixMapping; 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; /* * http://www.grappa.univ-lille3.fr/~staworko/papers/staworko-arxiv-shex.pdf * * E := e | a | E* | E1 or E2 | E1 and E2 * * e = empty expression * and = unordered concatenation * or = disjunction * * = Kleene star * * E? = (e or E) * E+ = (E and E*) * */ // NOTE Maybe we should use the term silhouette to distinguish our approach from the shapes // a silhouette expression could be shortened to 'silex'. // What is the semantics of a shex?????? // [[E]]_D // The evaluation of a silex E over a dataset D yields a graph // This means, that a silex must be converted to a sparql query first interface Shex { } // Note: could also be modeled as a constant of type ShEx class ShexEmpty implements Shex { } class ShexNav implements Shex { private Shex base; private String predicate; private boolean isInverse; } abstract class ShexBase2 { private Shex left; private Shex right; } /** * Union of two shex expressions * * @author raven * */ class ShexUnion extends ShexBase2 { } /** * * @author raven * */ class ShexAnd extends ShexBase2 { } /** * 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 ResourceShapeParserJsonObject { public static final String geo = "http://www.w3.org/2003/01/geo/wgs84_pos#"; public static final String geom = "http://geovocab.org/geometry#"; public static final String ogc = "http://www.opengis.net/ont/geosparql#asWKT"; // TODO: All these things should be resource shape objects that must be combinable public static final String wgs84 = "['geo:lat', 'geo:long']"; public static final String wgs84geometry = "['geo:geometry']"; public static final String geoSparqlLgd = "[geom:geometry: 'ogc:AsWkt']"; private PrefixMapping prefixMapping; public ResourceShapeParserJsonObject(PrefixMapping prefixMapping) { this.prefixMapping = 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 ResourceShape parse(Object obj) { ResourceShapeBuilder builder = new ResourceShapeBuilder(prefixMapping); parse(obj, builder); ResourceShape result = builder.getResourceShape(); return result; } public ResourceShape parse(Object obj, ResourceShapeBuilder builder) { if(obj == null) { // nothing to do } if(obj instanceof Boolean) { // true -> fetch all properties Boolean tf = (Boolean)obj; if(tf == true) { builder.nav(NodeValue.TRUE, true); } } else if (obj instanceof String) { // fetch a single property String str = (String)obj; StepRelation step = parseStep(str, prefixMapping); builder.nav(step); } else if (obj instanceof List) { // fetch an array of properties (possibly nested)c List<?> list = (List<?>)obj; for(Object item : list) { parse(item, builder); } } else if (obj instanceof Map) { // @SuppressWarnings("unchecked") Map<String, Object> map = (Map<String, Object>)obj; for(Entry<String, Object> entry : map.entrySet()) { String str = entry.getKey(); Object o = entry.getValue(); StepRelation step = parseStep(str, prefixMapping); ResourceShapeBuilder subBilder = builder.nav(step); parse(o, subBilder); } } else { throw new RuntimeException("Unsupported argument: " + obj); } return null; } }