package org.aksw.jena_sparql_api.changeset; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import org.aksw.commons.collections.diff.Diff; import org.aksw.commons.util.strings.StringUtils; import org.aksw.jena_sparql_api.core.QueryExecutionFactory; import org.aksw.jena_sparql_api.core.SparqlServiceReference; import org.aksw.jena_sparql_api.core.utils.UpdateRequestUtils; import org.aksw.jena_sparql_api.lookup.LookupService; import org.aksw.jena_sparql_api.lookup.LookupServiceSparqlQuery; import org.aksw.jena_sparql_api.lookup.LookupServiceTransformValue; import org.aksw.jena_sparql_api.mapper.BindingMapperProjectVar; import org.aksw.jena_sparql_api.mapper.FunctionBindingMapper; import org.aksw.jena_sparql_api.update.DiffQuadUtils; import org.aksw.jena_sparql_api.utils.GraphUtils; import org.aksw.jena_sparql_api.utils.NodeUtils; import org.aksw.jena_sparql_api.utils.QueryUtils; import org.aksw.jena_sparql_api.utils.ResultSetPart; import org.aksw.jena_sparql_api.utils.SetGraph; import org.aksw.jena_sparql_api.utils.Vars; import org.apache.http.auth.UsernamePasswordCredentials; import org.apache.jena.graph.Graph; import org.apache.jena.graph.Node; import org.apache.jena.graph.NodeFactory; import org.apache.jena.graph.Triple; import org.apache.jena.query.Query; import org.apache.jena.query.QueryFactory; import org.apache.jena.query.Syntax; import org.apache.jena.rdf.model.Literal; import org.apache.jena.rdf.model.Model; import org.apache.jena.rdf.model.ModelFactory; import org.apache.jena.rdf.model.Property; import org.apache.jena.rdf.model.RDFNode; import org.apache.jena.rdf.model.Resource; import org.apache.jena.rdf.model.ResourceFactory; import org.apache.jena.rdf.model.Statement; import org.apache.jena.sparql.core.Quad; import org.apache.jena.sparql.expr.E_Equals; import org.apache.jena.sparql.expr.ExprVar; import org.apache.jena.sparql.expr.NodeValue; import org.apache.jena.sparql.graph.GraphFactory; import org.apache.jena.sparql.util.ModelUtils; import org.apache.jena.update.UpdateRequest; import org.apache.jena.util.iterator.ExtendedIterator; import org.apache.jena.vocabulary.RDF; import com.google.common.base.Function; import com.google.common.base.Functions; // TODO A vocubulary class, name it properly class V { public static final String ns = "http://example.org/"; public static final Resource RdfDataset = ResourceFactory.createResource(ns + " RdfDataset"); public static final Property endpoint = ResourceFactory.createProperty(ns + " endpoint"); public static final Property target = ResourceFactory.createProperty(ns + " target"); public static final Property username = ResourceFactory.createProperty(ns + "username"); public static final Property password = ResourceFactory.createProperty(ns + "password"); } class ResourceUtils { public static Resource createSubResource(Resource parent, String subName) { String str = parent.getURI() + "/" + subName; Model m = parent.getModel(); Resource result; if(m == null) { result = ResourceFactory.createResource(str); } else { result = m.createResource(str); } return result; } } public class ChangeSetUtils { public static final Query queryMostRecentChangeSet = QueryFactory.parse(new Query(), "Prefix cs: <http://purl.org/vocab/changeset/schema#> Select ?s ?o ?y ?z { ?s cs:subjectOfChange ?o . Optional { ?x cs:precedingChangeSet ?s } . Optional { ?s cs:service ?y } . Optional { ?s cs:graph ?z } . Filter(!Bound(?x)) }", "http://example.org/", Syntax.syntaxARQ); public static String createHash(SparqlServiceReference serviceRef) { String result = serviceRef.getServiceURL() + serviceRef.getDefaultGraphURIs() + serviceRef.getNamedGraphURIs(); result = StringUtils.md5Hash(result); return result; } public static void writeServiceReference(SparqlServiceReference serviceRef, String targetGraph, Resource root, Model model) throws IllegalAccessException { Resource dgs = ResourceUtils.createSubResource(root, "defaultGraphs"); Resource ngs = ResourceUtils.createSubResource(root, "namedGraphs"); // An update can only go to a single graph model.add(root, RDF.type, V.RdfDataset); model.add(root, V.endpoint, model.createResource(serviceRef.getServiceURL())); model.add(root, V.target, model.createResource(targetGraph)); UsernamePasswordCredentials credentials = serviceRef.getCredentials(); //if(auth instanceof SimpleAuthenticator) { if(credentials != null) { //SimpleAuthenticator a = (SimpleAuthenticator)auth; //String username = (String)FieldUtils.readField(a, "username" , true); //char[] password = (char[])FieldUtils.readField(a, "password" , true); model.add(root, V.username, model.createLiteral(credentials.getUserName())); model.add(root, V.password, model.createLiteral(credentials.getPassword())); } } public static LookupService<Node, Node> createLookupServiceMostRecentChangeSet(QueryExecutionFactory qef, Node service, Node graph) { Query query = queryMostRecentChangeSet; if(service != null || graph != null) { query = query.cloneQuery(); } if(service != null) { QueryUtils.injectFilter(query, new E_Equals(new ExprVar(Vars.y), NodeValue.makeNode(service))); //QueryUtils.injectElement(query, ElementUtils.createElement(new Triple(Vars.s, NodeFactory.createURI("http://purl.org/vocab/changeset/schema#service"), Vars.y))); //query.getProject().add(Vars.y); // new E_Equals(new ExprVar(Vars.y), new ExprVar(graph))); } if(graph != null) { QueryUtils.injectFilter(query, new E_Equals(new ExprVar(Vars.z), NodeValue.makeNode(graph))); //QueryUtils.injectElement(query, ElementUtils.createElement(new Triple(Vars.s, NodeFactory.createURI("http://purl.org/vocab/changeset/schema#graph"), Vars.z))); } LookupService<Node, ResultSetPart> ls = new LookupServiceSparqlQuery(qef, query, Vars.o); // We filter by o, but project by s LookupService<Node, Node> result = LookupServiceTransformValue.create(ls, Functions.compose( FunctionBindingMapper.create(BindingMapperProjectVar.create(Vars.s)), FunctionResultSetPartFirstRow.fn)); return result; } /** * Check if all triples' subject equal the subjectOfChange * @param cs * @return */ public static boolean isValid(ChangeSet cs) { String str = cs.getSubjectOfChange(); Node s = NodeFactory.createURI(str); boolean isValidAdded = isSubjectOfAllTriples(s, cs.getAddition()); boolean isValidRemoved = isSubjectOfAllTriples(s, cs.getAddition()); boolean result = isValidAdded && isValidRemoved; return result; } public static boolean isSubjectOfTriple(Node s, Triple triple) { boolean result = triple.getSubject().equals(s); return result; } public static boolean isSubjectOfAllTriples(Node s, Graph g) { boolean result = true; ExtendedIterator<Triple> it = g.find(Node.ANY, Node.ANY, Node.ANY); try { while(it.hasNext()) { Triple triple = it.next(); // TODO We could implement this as a filter boolean isValid = isSubjectOfTriple(s, triple); if(!isValid) { result = false; break; } } } finally { it.close(); } return result; } public static void writeLangMap(Model model, Resource s, Property p, Map<String, String> langToText) { if(langToText != null) { for(Entry<String, String> entry : langToText.entrySet()) { String lang = entry.getKey(); String text = entry.getValue(); // TODO Is this check needed? Literal o = lang == null || lang.trim().isEmpty() ? model.createLiteral(text) : model.createLiteral(text, lang); model.add(s, p, o); } } } public static void writeReifiedGraph(Model model, Graph graph, Function<Triple, Node> tripleToSubject) { ExtendedIterator<Triple> it = graph.find(Node.ANY, Node.ANY, Node.ANY); try { while(it.hasNext()) { Triple triple = it.next(); Node s = tripleToSubject.apply(triple); writeReifiedTriple(model, s, triple); } } finally { it.close(); } } public static void writeReifiedTriple(Model model, Node s, Triple triple) { RDFNode tmp = ModelUtils.convertGraphNodeToRDFNode(s, model); Resource n = tmp.asResource(); Statement stmt = ModelUtils.tripleToStatement(model, triple); writeReifiedStatement(model, n, stmt); } public static void writeReifiedStatement(Model model, Resource s, Statement stmt) { model.add(s, RDF.type, RDF.Statement); model.add(s, RDF.subject, stmt.getSubject()); model.add(s, RDF.predicate, stmt.getPredicate()); model.add(s, RDF.object, stmt.getObject()); } public static void add(Model model, Resource s, Property p, RDFNode o) { if(s != null && p != null && o != null) { model.add(s, p, o); } } public static void write(Model model, ChangeSet cs) { Resource s = model.createResource(cs.getUri()); model.add(s, RDF.type, CS.ChangeSet); //writeLangMap(model, s, CS.changeReason, cs.getChangeReason()); ChangeSetMetadata md = cs.getMetadata(); model.add(s, CS.changeReason, model.createLiteral(md.getChangeReason())); model.add(s, CS.createdDate, model.createTypedLiteral(md.getCreatedDate())); model.add(s, CS.creatorName, model.createLiteral(md.getCreatorName())); model.add(s, CS.subjectOfChange, model.createResource(cs.getSubjectOfChange())); if(cs.getService() != null) { model.add(s, CS.service, model.createResource(cs.getService())); } if(cs.getGraph() != null) { model.add(s, CS.graph, model.createResource(cs.getGraph())); } //model.add(s, CS.datasetSet); if(cs.getPrecedingChangeSet() != null) { model.add(s, CS.precedingChangeSet, model.createResource(cs.getPrecedingChangeSet())); } String prefix = "http://example.org/"; for(Triple triple : SetGraph.wrap(cs.getAddition())) { String uri = prefix + FN_TripleToMd5.fn.apply(triple); Resource o = ResourceFactory.createResource(uri); Statement stmt = ModelUtils.tripleToStatement(model, triple); writeReifiedStatement(model, o, stmt); model.add(s, CS.addition, o); model.add(s, CS.statement, o); } for(Triple triple : SetGraph.wrap(cs.getRemoval())) { String uri = prefix + FN_TripleToMd5.fn.apply(triple); Resource o = ResourceFactory.createResource(uri); Statement stmt = ModelUtils.tripleToStatement(model, triple); writeReifiedStatement(model, o, stmt); model.add(s, CS.removal, o); model.add(s, CS.statement, o); } } // public static UpdateRequest createUpdateRequest(ChangeSetMetadata metadata, // QueryExecutionFactory qef, // Diff<? extends Iterable<Quad>> diff, // String prefix) { // Diff<Graph> d = new Diff<Graph>(GraphFactory.createGraphMem(), GraphFactory.createGraphMem(), null); // // for(Quad quad : diff.getAdded()) { // d.getAdded().add(quad.asTriple()); // } // // for(Quad quad : diff.getRemoved()) { // d.getRemoved().add(quad.asTriple()); // } // // UpdateRequest result = createUpdateRequestGraph(metadata, qef, d, prefix); // return result; // } public static UpdateRequest createUpdateRequest(ChangeSetMetadata metadata, QueryExecutionFactory qef, Diff<? extends Iterable<Quad>> quadDiff, String prefix) { Map<Node, Diff<Set<Triple>>> diff = DiffQuadUtils.partitionQuads(quadDiff); //createUpdateRequest(metadata, qef, diff, prefix, serviceUri, graphUri); throw new UnsupportedOperationException(); } public static UpdateRequest createUpdateRequestGraph(ChangeSetMetadata metadata, QueryExecutionFactory qef, Diff<Set<Triple>> diff, String prefix, String serviceUri, String graphUri) { Map<Node, ChangeSet> subjectToChangeSet = createChangeSets(qef, serviceUri, graphUri, metadata, diff, prefix); Model model = ModelFactory.createDefaultModel(); for(ChangeSet cs : subjectToChangeSet.values()) { write(model, cs); System.out.println("v ==================="); model.write(System.out, "TURTLE"); System.out.println("^ -------------------"); } UpdateRequest result = UpdateRequestUtils.createUpdateRequest(model, null); return result; } // public Map<Node, ChangeSet> apply(Diff<Graph> diff) { public static Map<Node, ChangeSet> createChangeSets(QueryExecutionFactory qef, String serviceUri, String graphUri, ChangeSetMetadata metadata, Diff<Set<Triple>> diff, String prefix) { Node service = NodeUtils.asNullableNode(serviceUri); Node graph = NodeUtils.asNullableNode(graphUri); LookupService<Node, Node> precedingChangeSetLs = ChangeSetUtils.createLookupServiceMostRecentChangeSet(qef, service, graph); Set<Triple> added = diff.getAdded(); Set<Triple> removed = diff.getRemoved(); Map<Node, Graph> subjectToAdded = GraphUtils.indexBySubject(added); Map<Node, Graph> subjectToRemoved = GraphUtils.indexBySubject(removed); Set<Node> subjects = new HashSet<Node>(); subjects.addAll(subjectToAdded.keySet()); subjects.addAll(subjectToRemoved.keySet()); // Perform a lookup of latest changesets for the given subjects and create // a writeable copy of the map Map<Node, Node> tmp = precedingChangeSetLs.apply(subjects); Map<Node, Node> subjectToRecentChangeSet = new HashMap<Node, Node>(tmp); Map<Node, ChangeSet> result = new HashMap<Node, ChangeSet>(); for(Node s : subjects) { String subjectOfChange = s.getURI(); Node precedingId = subjectToRecentChangeSet.get(s); String precedingChangeSet = precedingId == null ? null : precedingId.getURI(); //" " + service + " " + graph + " " + String localName = StringUtils.urlEncode(subjectOfChange) + "-" + StringUtils.md5Hash("" + precedingId); String uri = prefix + localName; // Update the preceding changeset Node nextId = NodeFactory.createURI(uri); subjectToRecentChangeSet.put(s, nextId); Graph addedGraph = subjectToAdded.get(s); Graph removedGraph = subjectToRemoved.get(s); addedGraph = addedGraph == null ? GraphFactory.createGraphMem() : addedGraph; removedGraph = removedGraph == null ? GraphFactory.createGraphMem() : removedGraph; ChangeSet cs = new ChangeSet(metadata, uri, precedingChangeSet, subjectOfChange, addedGraph, removedGraph, serviceUri, graphUri); result.put(s, cs); } return result; } public static void enrichWithSource(Model model, Node g, SparqlServiceReference ssr) { model.write(System.out, "TTL"); Set<Resource> rs = model.listSubjectsWithProperty(RDF.type, CS.ChangeSet).toSet(); // TODO Auto-generated method stub } // public static Query createQueryPrecedingChangeSet() { //String str = "Prefix cs: <http://purl.org/vocab/changeset/schema#> Select ?s { ?s cs:subjectOfChange <" + uri + "> ; cs:createdDate ?d } Order By Desc(?d) Limit 1"; // Get the changeset for a subject that does not (yet) occurr as a preceding changeset /* Query query = new Query(); query.setQuerySelectType(); query.getProject().add(Vars.s); ElementGroup queryPattern = new ElementGroup() ElementTriplesBlock b1 = new ElementTRip query.setQueryPattern(queryPattern); */ //String queryString = "{ Select ?s { ?s <http://purl.org/vocab/changeset/schema#subjectOfChange> ?o . Optional { ?x <http://purl.org/vocab/changeset/schema#precedingChangeSet> ?s } . Filter(!Bound(?x)) } Order By Desc(?d) Limit 1 }"; //Concept concept = Concept.create(queryString, "s"); //LookupService ls = new LookupServiceSparqlQuery(qef, ) //} }