You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jena.apache.org by cl...@apache.org on 2017/06/04 11:39:23 UTC
[2/6] jena git commit: Initial implementation with tests
Initial implementation with tests
Project: http://git-wip-us.apache.org/repos/asf/jena/repo
Commit: http://git-wip-us.apache.org/repos/asf/jena/commit/7d96286e
Tree: http://git-wip-us.apache.org/repos/asf/jena/tree/7d96286e
Diff: http://git-wip-us.apache.org/repos/asf/jena/diff/7d96286e
Branch: refs/heads/master
Commit: 7d96286e1b49dc170604dd76f81abfc3f397541a
Parents: a2fb93c
Author: Claude Warren <cl...@apache.org>
Authored: Sun Jun 4 12:07:56 2017 +0100
Committer: Claude Warren <cl...@apache.org>
Committed: Sun Jun 4 12:07:56 2017 +0100
----------------------------------------------------------------------
.../arq/querybuilder/AbstractQueryBuilder.java | 5 +
.../jena/arq/querybuilder/UpdateBuilder.java | 1027 ++++++++++++++++++
.../arq/querybuilder/handlers/WhereHandler.java | 23 +-
.../updatebuilder/PrefixHandler.java | 115 ++
.../updatebuilder/QBQuadHolder.java | 79 ++
.../querybuilder/updatebuilder/QuadHolder.java | 46 +
.../updatebuilder/QuadIteratorBuilder.java | 180 +++
.../updatebuilder/SingleQuadHolder.java | 97 ++
.../updatebuilder/WhereProcessor.java | 433 ++++++++
.../src/test/java/Example10.java | 104 ++
.../querybuilder/UpdateBuilderExampleTests.java | 659 +++++++++++
.../arq/querybuilder/UpdateBuilderTest.java | 411 +++++++
12 files changed, 3175 insertions(+), 4 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/jena/blob/7d96286e/jena-extras/jena-querybuilder/src/main/java/org/apache/jena/arq/querybuilder/AbstractQueryBuilder.java
----------------------------------------------------------------------
diff --git a/jena-extras/jena-querybuilder/src/main/java/org/apache/jena/arq/querybuilder/AbstractQueryBuilder.java b/jena-extras/jena-querybuilder/src/main/java/org/apache/jena/arq/querybuilder/AbstractQueryBuilder.java
index ed18272..17be323 100644
--- a/jena-extras/jena-querybuilder/src/main/java/org/apache/jena/arq/querybuilder/AbstractQueryBuilder.java
+++ b/jena-extras/jena-querybuilder/src/main/java/org/apache/jena/arq/querybuilder/AbstractQueryBuilder.java
@@ -50,6 +50,7 @@ import org.apache.jena.sparql.path.P_Link;
import org.apache.jena.sparql.path.Path;
import org.apache.jena.sparql.path.PathParser;
import org.apache.jena.sparql.syntax.ElementGroup;
+import org.apache.jena.sparql.syntax.ElementSubQuery;
import org.apache.jena.sparql.util.ExprUtils;
import org.apache.jena.sparql.util.NodeFactoryExtra ;
@@ -132,6 +133,10 @@ public abstract class AbstractQueryBuilder<T extends AbstractQueryBuilder<T>>
return NodeFactory.createLiteral(LiteralLabelFactory.createTypedLiteral(o));
}
+ public ElementSubQuery asSubQuery() {
+ return getWhereHandler().makeSubQuery( this );
+ }
+
/**
* Make a triple path from the objects.
*
http://git-wip-us.apache.org/repos/asf/jena/blob/7d96286e/jena-extras/jena-querybuilder/src/main/java/org/apache/jena/arq/querybuilder/UpdateBuilder.java
----------------------------------------------------------------------
diff --git a/jena-extras/jena-querybuilder/src/main/java/org/apache/jena/arq/querybuilder/UpdateBuilder.java b/jena-extras/jena-querybuilder/src/main/java/org/apache/jena/arq/querybuilder/UpdateBuilder.java
new file mode 100644
index 0000000..93aeae6
--- /dev/null
+++ b/jena-extras/jena-querybuilder/src/main/java/org/apache/jena/arq/querybuilder/UpdateBuilder.java
@@ -0,0 +1,1027 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.arq.querybuilder;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Function;
+
+import org.apache.jena.arq.querybuilder.clauses.PrologClause;
+import org.apache.jena.arq.querybuilder.clauses.WhereClause;
+import org.apache.jena.arq.querybuilder.handlers.WhereHandler;
+import org.apache.jena.arq.querybuilder.updatebuilder.PrefixHandler;
+import org.apache.jena.arq.querybuilder.updatebuilder.QBQuadHolder;
+import org.apache.jena.arq.querybuilder.updatebuilder.QuadHolder;
+import org.apache.jena.arq.querybuilder.updatebuilder.SingleQuadHolder;
+import org.apache.jena.arq.querybuilder.updatebuilder.WhereProcessor;
+import org.apache.jena.graph.FrontsTriple;
+import org.apache.jena.graph.Node;
+import org.apache.jena.graph.NodeFactory;
+import org.apache.jena.graph.Triple;
+import org.apache.jena.query.QueryParseException;
+import org.apache.jena.rdf.model.Resource;
+import org.apache.jena.shared.PrefixMapping;
+import org.apache.jena.sparql.core.Quad;
+import org.apache.jena.sparql.core.TriplePath;
+import org.apache.jena.sparql.core.Var;
+import org.apache.jena.sparql.expr.Expr;
+import org.apache.jena.sparql.lang.sparql_11.ParseException;
+import org.apache.jena.sparql.modify.request.QuadAcc;
+import org.apache.jena.sparql.modify.request.QuadDataAcc;
+import org.apache.jena.sparql.modify.request.UpdateDataDelete;
+import org.apache.jena.sparql.modify.request.UpdateDataInsert;
+import org.apache.jena.sparql.modify.request.UpdateDeleteWhere;
+import org.apache.jena.sparql.modify.request.UpdateModify;
+import org.apache.jena.update.Update;
+import org.apache.jena.update.UpdateRequest;
+import org.apache.jena.util.iterator.ExtendedIterator;
+import org.apache.jena.util.iterator.NiceIterator;
+import org.apache.jena.vocabulary.RDF;
+
+/**
+ * Class to build update requests.
+ *
+ */
+public class UpdateBuilder {
+
+ private final PrefixHandler prefixHandler;
+ private final WhereProcessor whereProcessor;
+ private List<QuadHolder> inserts = new ArrayList<QuadHolder>();
+ private List<QuadHolder> deletes = new ArrayList<QuadHolder>();
+ private Map<Var, Node> values;
+ private Node with;
+
+ /**
+ * Constructor.
+ */
+ public UpdateBuilder() {
+ this.prefixHandler = new PrefixHandler();
+ this.whereProcessor = new WhereProcessor(prefixHandler);
+ this.values = new HashMap<Var, Node>();
+ this.with = null;
+ }
+
+ /**
+ * Constructor. Uses the prefixes from the prolog clause. <b>May modify the
+ * contents of the prefix mapping in the prolog handler</b>
+ *
+ * @param prologClause
+ * the default prefixes for this builder.
+ */
+ public UpdateBuilder(PrologClause<?> prologClause) {
+ this(prologClause.getPrologHandler().getPrefixes());
+ }
+
+ /**
+ * Constructor. Uses the specified prefix mapping. <b>May modify the
+ * contents of the prefix mapping</b>
+ *
+ * @param pMap
+ * the prefix mapping to use.
+ */
+ public UpdateBuilder(PrefixMapping pMap) {
+ this.prefixHandler = new PrefixHandler(pMap);
+ this.whereProcessor = new WhereProcessor(prefixHandler);
+ }
+
+ // conver a collection of QuadHolder to an iterator on quads.
+ private ExtendedIterator<Quad> getQuads(Collection<QuadHolder> holders) {
+ ExtendedIterator<Quad> result = NiceIterator.emptyIterator();
+ for (QuadHolder holder : holders) {
+ result = result.andThen(holder.setValues(values).getQuads());
+ }
+ return result;
+ }
+
+ /**
+ * Build the update.
+ *
+ * <b>Note: the update does not include the prefix statements</b> use
+ * buildRequest() or appendTo() methods to include the prefix statements.
+ *
+ * @return the update.
+ */
+ public Update build() {
+
+ if (deletes.isEmpty() && inserts.isEmpty()) {
+ throw new IllegalStateException("At least one delete or insert must be specified");
+ }
+
+ if (whereProcessor.isEmpty()) {
+ return buildNoWhere();
+ }
+ return buildWhere();
+ }
+
+ /**
+ * Build as an UpdateRequest with prefix mapping set.
+ *
+ * @return a new UpdateRequest
+ */
+ public UpdateRequest buildRequest() {
+ UpdateRequest req = new UpdateRequest(build());
+ req.setPrefixMapping(prefixHandler.getPrefixes());
+ return req;
+ }
+
+ /**
+ * Appends the new Update to the UpdateRequest.
+ *
+ * @param req
+ * the UpdateRequest to append this Update to.
+ * @return the req parameter for chaining.
+ */
+ public UpdateRequest appendTo(UpdateRequest req) {
+ req.add(build());
+ for (Map.Entry<String, String> entry : prefixHandler.getPrefixes().getNsPrefixMap().entrySet()) {
+ req.setPrefix(entry.getKey(), entry.getValue());
+ }
+ return req;
+ }
+
+ // build updates without where clauses
+ private Update buildNoWhere() {
+ if (inserts.isEmpty()) {
+ QuadDataAcc quadData = new QuadDataAcc(getQuads(deletes).mapWith(new Function<Quad, Quad>() {
+ @Override
+ public Quad apply(Quad arg0) {
+ return check(arg0);
+ }
+ }).toList());
+ return new UpdateDataDelete(quadData);
+ }
+ if (deletes.isEmpty()) {
+ QuadDataAcc quadData = new QuadDataAcc(getQuads(inserts).mapWith(new Function<Quad, Quad>() {
+
+ @Override
+ public Quad apply(Quad t) {
+ return check(t);
+ }
+
+ }).toList());
+ return new UpdateDataInsert(quadData);
+ }
+
+ throw new IllegalStateException("Can not have both insert and delete without a where clause");
+ }
+
+ // build updates with where clauses
+ private Update buildWhere() {
+
+ // if (inserts.isEmpty()) {
+ // QuadAcc quadAcc = new QuadAcc(getQuads(deletes).toList());
+ // UpdateDeleteWhere retval = new UpdateDeleteWhere(quadAcc);
+ // return retval;
+ // }
+
+ UpdateModify retval = new UpdateModify();
+ if (with != null)
+ {
+ Node graph = values.get(with);
+ if (graph == null) {
+ graph = with;
+ }
+ retval.setWithIRI(graph);
+ }
+ QuadAcc acc;
+ Iterator<Quad> iter;
+
+ if (!inserts.isEmpty()) {
+ retval.setHasInsertClause(true);
+ acc = retval.getInsertAcc();
+ iter = getQuads(inserts);
+ while (iter.hasNext()) {
+ acc.addQuad(iter.next());
+ }
+ }
+ if (!deletes.isEmpty()) {
+ retval.setHasDeleteClause(true);
+ acc = retval.getDeleteAcc();
+
+ iter = getQuads(deletes);
+ while (iter.hasNext()) {
+ acc.addQuad(iter.next());
+ }
+ }
+
+ retval.setElement(whereProcessor.setVars(values));
+
+ return retval;
+
+ }
+
+ /**
+ * Convert the object to a node.
+ *
+ * Shorthand for AbstractQueryBuilder.makeNode( o, prefixes )
+ *
+ * @see AbstractQueryBuilder#makeNode(Object)
+ *
+ * @param o
+ * the object to convert to a node.
+ * @return the Node.
+ */
+ public Node makeNode(Object o) {
+ return AbstractQueryBuilder.makeNode(o, prefixHandler.getPrefixes());
+ }
+
+ /**
+ * Convert the object to a node.
+ *
+ * Shorthand for AbstractQueryBuilder.makeVar( o )
+ *
+ * @see AbstractQueryBuilder#makeVar(Object)
+ *
+ * @param o
+ * the object to convert to a var.
+ * @return the Var.
+ */
+
+ public Var makeVar(Object o) {
+ return AbstractQueryBuilder.makeVar(o);
+ }
+
+ /**
+ * Quote a string.
+ *
+ * Shorthand for AbstractQueryBuilder.quote( s )
+ *
+ * @see AbstractQueryBuilder#quote(String)
+ *
+ *
+ * @param s
+ * the string to quote.
+ * @return the quoted string.
+ */
+ public String quote(String s) {
+ return AbstractQueryBuilder.quote(s);
+ }
+
+ /**
+ * Add a quad to the insert statement.
+ *
+ * Arguments are converted to nodes using the makeNode() method.
+ *
+ * @see #makeNode(Object)
+ * @param g
+ * the graph
+ * @param s
+ * the subject
+ * @param p
+ * the predicate
+ * @param o
+ * the object
+ * @return this builder for chaining.
+ */
+ public UpdateBuilder addInsert(Object g, Object s, Object p, Object o) {
+ return addInsert(new Quad(makeNode(g), makeNode(s), makeNode(p), makeNode(o)));
+ }
+
+ /**
+ * Add a quad to the insert statement.
+ *
+ *
+ * @param quad
+ * the quad to add.
+ * @return this builder for chaining.
+ */
+ public UpdateBuilder addInsert(Quad quad) {
+ inserts.add(new SingleQuadHolder(quad));
+ return this;
+ }
+
+ /**
+ * Add a triple to the insert statement.
+ *
+ * Arguments are converted to nodes using the makeNode() method.
+ *
+ * @see #makeNode(Object)
+ * @param s
+ * the subject
+ * @param p
+ * the predicate
+ * @param o
+ * the object
+ * @return this builder for chaining.
+ */
+ public UpdateBuilder addInsert(Object s, Object p, Object o) {
+ addInsert(new Triple(makeNode(s), makeNode(p), makeNode(o)));
+ return this;
+ }
+
+ /**
+ * Add a triple to the insert statement.
+ *
+ * @param t
+ * the triple to add.
+ * @return this builder for chaining.
+ */
+ public UpdateBuilder addInsert(Triple t) {
+ inserts.add(new SingleQuadHolder( t ));
+ return this;
+ }
+
+ /**
+ * Add a triple in a specified graph to the insert statement.
+ *
+ * The graph object is converted by a call to makeNode().
+ *
+ * @see #makeNode(Object)
+ * @param g
+ * the graph for the triple.
+ * @param t
+ * the triple to add.
+ * @return this builder for chaining.
+ */
+ public UpdateBuilder addInsert(Object g, Triple t) {
+ Quad q = new Quad(AbstractQueryBuilder.makeNode(g, prefixHandler.getPrefixes()), t);
+ inserts.add(new SingleQuadHolder(q));
+ return this;
+ }
+
+ /**
+ * Add the statements from the where clause in the specified query builder
+ * to the insert statement.
+ *
+ * @see #makeNode(Object)
+ * @param queryBuilder
+ * The query builder to extract the where clause from.
+ * @return this builder for chaining.
+ */
+ public UpdateBuilder addInsert(AbstractQueryBuilder<?> queryBuilder) {
+ inserts.add(new QBQuadHolder( queryBuilder));
+ return this;
+ }
+
+ /**
+ * Add the statements from the where clause in the specified query builder
+ * to the insert statements for the specified graph.
+ *
+ * The graph object is converted by a call to makeNode().
+ *
+ * @see #makeNode(Object)
+ * @param graph
+ * the graph to add the statements to.
+ * @param queryBuilder
+ * The query builder to extract the where clause from.
+ * @return this builder for chaining.
+ */
+ public UpdateBuilder addInsert(Object graph, AbstractQueryBuilder<?> queryBuilder) {
+ inserts.add(new QBQuadHolder(AbstractQueryBuilder.makeNode(graph, prefixHandler.getPrefixes()), queryBuilder));
+ return this;
+ }
+
+ /**
+ * Add a quad to the delete statement.
+ *
+ * Arguments are converted to nodes using the makeNode() method.
+ *
+ * @see #makeNode(Object)
+ * @param g
+ * the graph
+ * @param s
+ * the subject
+ * @param p
+ * the predicate
+ * @param o
+ * the object
+ * @return this builder for chaining.
+ */
+ public UpdateBuilder addDelete(Object g, Object s, Object p, Object o) {
+ return addDelete(new Quad(AbstractQueryBuilder.makeNode(g, prefixHandler.getPrefixes()),
+ AbstractQueryBuilder.makeNode(s, prefixHandler.getPrefixes()),
+ AbstractQueryBuilder.makeNode(p, prefixHandler.getPrefixes()),
+ AbstractQueryBuilder.makeNode(o, prefixHandler.getPrefixes())));
+ }
+
+ /**
+ * Add a quad to the delete statement.
+ *
+ *
+ * @param quad
+ * the quad to add.
+ * @return this builder for chaining.
+ */
+ public UpdateBuilder addDelete(Quad quad) {
+ deletes.add(new SingleQuadHolder(quad));
+ return this;
+ }
+
+ /**
+ * Add a triple to the delete statement.
+ *
+ * Arguments are converted to nodes using the makeNode() method.
+ *
+ * @see #makeNode(Object)
+ * @param s
+ * the subject
+ * @param p
+ * the predicate
+ * @param o
+ * the object
+ * @return this builder for chaining.
+ */
+ public UpdateBuilder addDelete(Object s, Object p, Object o) {
+ addDelete(new Triple(AbstractQueryBuilder.makeNode(s, prefixHandler.getPrefixes()),
+ AbstractQueryBuilder.makeNode(p, prefixHandler.getPrefixes()),
+ AbstractQueryBuilder.makeNode(o, prefixHandler.getPrefixes())));
+ return this;
+ }
+
+ /**
+ * Add a triple to the delete statement.
+ *
+ *
+ * @param t
+ * the triple to add.
+ * @return this builder for chaining.
+ */
+ public UpdateBuilder addDelete(Triple t) {
+ deletes.add(new SingleQuadHolder(t));
+ return this;
+ }
+
+ /**
+ * Add a triple in a specified graph to the delete statement.
+ *
+ * The graph object is converted by a call to makeNode().
+ *
+ * @see #makeNode(Object)
+ * @param g
+ * the graph for the triple.
+ * @param t
+ * the triple to add.
+ * @return this builder for chaining.
+ */
+ public UpdateBuilder addDelete(Object g, Triple t) {
+ Quad q = new Quad(AbstractQueryBuilder.makeNode(g, prefixHandler.getPrefixes()), t);
+ deletes.add(new SingleQuadHolder(q));
+ return this;
+ }
+
+ /**
+ * Add the statements from the where clause in the specified query builder
+ * to the delete statement.
+ *
+ * @see #makeNode(Object)
+ * @param queryBuilder
+ * The query builder to extract the where clause from.
+ * @return this builder for chaining.
+ */
+ public UpdateBuilder addDelete(AbstractQueryBuilder<?> queryBuilder) {
+ deletes.add(new QBQuadHolder( queryBuilder));
+ return this;
+ }
+
+ /**
+ * Add the statements from the where clause in the specified query builder
+ * to the delete statements for the specified graph.
+ *
+ * The graph object is converted by a call to makeNode().
+ *
+ * @see #makeNode(Object)
+ * @param graph
+ * the graph to add the statements to.
+ * @param queryBuilder
+ * The query builder to extract the where clause from.
+ * @return this builder for chaining.
+ */
+ public UpdateBuilder addDelete(Object graph, AbstractQueryBuilder<?> queryBuilder) {
+ deletes.add(new QBQuadHolder(AbstractQueryBuilder.makeNode(graph, prefixHandler.getPrefixes()), queryBuilder));
+ return this;
+ }
+
+ /**
+ * Add the prefix to the prefix mapping.
+ *
+ * @param pfx
+ * the prefix to add.
+ * @param uri
+ * the uri for the prefix.
+ * @return this builder for chaining
+ */
+ public UpdateBuilder addPrefix(String pfx, Resource uri) {
+ return addPrefix(pfx, uri.getURI());
+ }
+
+ /**
+ * Add the prefix to the prefix mapping.
+ *
+ * @param pfx
+ * the prefix to add.
+ * @param uri
+ * the uri for the prefix.
+ * @return this builder for chaining
+ */
+ public UpdateBuilder addPrefix(String pfx, Node uri) {
+ return addPrefix(pfx, uri.getURI());
+ }
+
+ /**
+ * Add the prefix to the prefix mapping.
+ *
+ * @param pfx
+ * the prefix to add.
+ * @param uri
+ * the uri for the prefix.
+ * @return this builder for chaining
+ */
+ public UpdateBuilder addPrefix(String pfx, String uri) {
+ prefixHandler.addPrefix(pfx, uri);
+ return this;
+ }
+
+ /**
+ * Add the prefixes to the prefix mapping.
+ *
+ * @param prefixes
+ * the prefixes to add.
+ * @return this builder for chaining
+ */
+
+ public UpdateBuilder addPrefixes(Map<String, String> prefixes) {
+ prefixHandler.addPrefixes(prefixes);
+ return this;
+ }
+
+ /**
+ * Get an ExprFactory that uses the prefixes from this builder.
+ *
+ * @return the EpxressionFactory.
+ */
+ public ExprFactory getExprFactory() {
+ return prefixHandler.getExprFactory();
+ }
+
+ /**
+ * Set a variable replacement. During build all instances of var in the
+ * query will be replaced with value. If value is null the replacement is
+ * cleared.
+ *
+ * @param var
+ * The variable to replace
+ * @param value
+ * The value to replace it with or null to remove the
+ * replacement.
+ */
+ public void setVar(Var var, Node value) {
+ if (value == null) {
+ values.remove(var);
+ } else {
+ values.put(var, value);
+ }
+ }
+
+ /**
+ * Set a variable replacement. During build all instances of var in the
+ * query will be replaced with value. If value is null the replacement is
+ * cleared.
+ *
+ * See {@link #makeVar} for conversion of the var param. See
+ * {@link #makeNode} for conversion of the value param.
+ *
+ * @param var
+ * The variable to replace.
+ * @param value
+ * The value to replace it with or null to remove the
+ * replacement.
+ */
+ public void setVar(Object var, Object value) {
+ if (value == null) {
+ setVar(AbstractQueryBuilder.makeVar(var), null);
+ } else {
+ setVar(AbstractQueryBuilder.makeVar(var),
+ AbstractQueryBuilder.makeNode(value, prefixHandler.getPrefixes()));
+ }
+ }
+
+ private Quad check(Quad q) {
+ if (Var.isVar(q.getGraph()))
+ throw new QueryParseException("Variables not permitted in data quad", -1, -1);
+ if (Var.isVar(q.getSubject()) || Var.isVar(q.getPredicate()) || Var.isVar(q.getObject()))
+ throw new QueryParseException("Variables not permitted in data quad", -1, -1);
+ if (q.getSubject().isLiteral())
+ throw new QueryParseException("Literals not allowed as subjects in data", -1, -1);
+ return q;
+ }
+
+ /**
+ * Add all where attributes from the Where Handler argument.
+ *
+ * @param whereHandler
+ * The Where Handler to copy from.
+ */
+ public UpdateBuilder addAll(WhereHandler whereHandler) {
+ whereProcessor.addAll(whereHandler);
+ return this;
+ }
+
+ /**
+ * Add the triple path to the where clause
+ *
+ * @param t
+ * The triple path to add.
+ * @throws IllegalArgumentException
+ * If the triple path is not a valid triple path for a where
+ * clause.
+ */
+ public UpdateBuilder addWhere(TriplePath t) throws IllegalArgumentException {
+ whereProcessor.addWhere(t);
+ return this;
+ }
+
+ /**
+ * Add the triple path to the where clause
+ *
+ * @param t
+ * The triple path to add.
+ * @throws IllegalArgumentException
+ * If the triple path is not a valid triple path for a where
+ * clause.
+ */
+ public UpdateBuilder addWhere(WhereClause<?> whereClause) throws IllegalArgumentException {
+ whereProcessor.addAll(whereClause.getWhereHandler());
+ return this;
+ }
+ /**
+ * Add an optional triple to the where clause
+ *
+ * @param t
+ * The triple path to add.
+ * @return The Builder for chaining.
+ * @throws IllegalArgumentException
+ * If the triple is not a valid triple for a where clause.
+ */
+ public UpdateBuilder addOptional(TriplePath t) throws IllegalArgumentException {
+ whereProcessor.addOptional(t);
+ return this;
+ }
+
+ /**
+ * Add the contents of a where handler as an optional statement.
+ *
+ * @param whereHandler
+ * The where handler to use as the optional statement.
+ */
+ public UpdateBuilder addOptional(WhereHandler whereHandler) {
+ whereProcessor.addOptional(whereHandler);
+ return this;
+ }
+
+ /**
+ * Add an expression string as a filter.
+ *
+ * @param expression
+ * The expression string to add.
+ * @return The Builder for chaining.
+ * @throws ParseException
+ * If the expression can not be parsed.
+ */
+ public UpdateBuilder addFilter(String expression) throws ParseException {
+ whereProcessor.addFilter(expression);
+ return this;
+ }
+
+ /**
+ * Add a subquery to the where clause.
+ *
+ * @param subQuery
+ * The sub query to add.
+ * @return The Builder for chaining.
+ */
+ public UpdateBuilder addSubQuery(AbstractQueryBuilder<?> subQuery) {
+ whereProcessor.addSubQuery(subQuery);
+ return this;
+ }
+
+ /**
+ * Add a union to the where clause.
+ *
+ * @param subQuery
+ * The subquery to add as the union.
+ * @return The Builder for chaining.
+ */
+ public UpdateBuilder addUnion(AbstractQueryBuilder<?> subQuery) {
+ whereProcessor.addUnion(subQuery);
+ return this;
+ }
+
+ /**
+ * Add a graph to the where clause.
+ *
+ * @param graph
+ * The name of the graph.
+ * @param subQuery
+ * The where handler that defines the graph.
+ */
+ public UpdateBuilder addGraph(Node graph, WhereHandler subQuery) {
+ whereProcessor.addGraph(graph, subQuery);
+ return this;
+ }
+
+ /**
+ * Add a binding to the where clause.
+ *
+ * @param expr
+ * The expression to bind.
+ * @param var
+ * The variable to bind it to.
+ */
+ public UpdateBuilder addBind(Expr expr, Var var) {
+ whereProcessor.addBind(expr, var);
+ return this;
+ }
+
+ /**
+ * Add a binding to the where clause.
+ *
+ * @param expression
+ * The expression to bind.
+ * @param var
+ * The variable to bind it to.
+ * @throws ParseException
+ */
+ public UpdateBuilder addBind(String expression, Var var) throws ParseException {
+ whereProcessor.addBind(expression, var);
+ return this;
+ }
+
+ /**
+ * Create a list node from a list of objects as per RDF Collections.
+ *
+ * http://www.w3.org/TR/2013/REC-sparql11-query-20130321/#collections
+ *
+ * See {@link AbstractQueryBuilder#makeNode} for conversion of the param
+ * values.
+ * <p>
+ * usage:
+ * <ul>
+ * <li>list( param1, param2, param3, ... )</li>
+ * <li>addWhere( list( param1, param2, param3, ... ), p, o )</li>
+ * <li>addOptional( list( param1, param2, param3, ... ), p, o )</li>
+ * </ul>
+ * </p>
+ *
+ * @param objs
+ * the list of objects for the list.
+ *
+ * @param objs
+ * the list of objects for the list.
+ * @return the first blank node in the list.
+ */
+ public Node list(Object... objs) {
+ Node retval = NodeFactory.createBlankNode();
+ Node lastObject = retval;
+ for (int i = 0; i < objs.length; i++) {
+ Node n = makeNode(objs[i]);
+ addWhere(new TriplePath(new Triple(lastObject, RDF.first.asNode(), n)));
+ if (i + 1 < objs.length) {
+ Node nextObject = NodeFactory.createBlankNode();
+ addWhere(new TriplePath(new Triple(lastObject, RDF.rest.asNode(), nextObject)));
+ lastObject = nextObject;
+ } else {
+ addWhere(new TriplePath(new Triple(lastObject, RDF.rest.asNode(), RDF.nil.asNode())));
+ }
+
+ }
+
+ return retval;
+ }
+
+ /**
+ * Adds a triple to the where clause.
+ *
+ * @param t
+ * The triple path to add
+ * @return The Builder for chaining.
+ */
+ public UpdateBuilder addWhere(Triple t) {
+ return addWhere(new TriplePath(t));
+ }
+
+ /**
+ * Adds a triple to the where clause.
+ *
+ * @param t
+ * The triple to add
+ * @return The Builder for chaining.
+ */
+ public UpdateBuilder addWhere(FrontsTriple t) {
+ return addWhere(t.asTriple());
+ }
+
+ /**
+ * Adds a triple or triple path to the where clause.
+ *
+ * See {@link AbstractQueryBuilder#makeTriplePath} for conversion of the
+ * param values.
+ *
+ * @param s
+ * The subject.
+ * @param p
+ * The predicate.
+ * @param o
+ * The object.
+ * @return The Builder for chaining.
+ */
+ public UpdateBuilder addWhere(Object s, Object p, Object o) {
+ return addWhere(new Triple(makeNode(s), makeNode(p), makeNode(o)));
+ }
+
+ /**
+ * Adds an optional triple to the where clause.
+ *
+ * @param t
+ * The triple to add
+ * @return The Builder for chaining.
+ */
+ public UpdateBuilder addOptional(Triple t) {
+ return addOptional(new TriplePath(t));
+ }
+
+ /**
+ * Adds an optional triple as to the where clause.
+ *
+ * @param t
+ * The triple to add
+ * @return The Builder for chaining.
+ */
+ public UpdateBuilder addOptional(FrontsTriple t) {
+ return addOptional(t.asTriple());
+ }
+
+ /**
+ * Adds an optional triple or triple path to the where clause.
+ *
+ * See {@link AbstractQueryBuilder#makeTriplePath} for conversion of the
+ * param values.
+ *
+ * @param s
+ * The subject.
+ * @param p
+ * The predicate.
+ * @param o
+ * The object.
+ * @return The Builder for chaining.
+ */
+ public UpdateBuilder addOptional(Object s, Object p, Object o) {
+ return addOptional(new Triple(makeNode(s), makeNode(p), makeNode(o)));
+ }
+
+ /**
+ * Adds an optional group pattern to the where clause.
+ *
+ * @param t
+ * The select builder to add as an optional pattern
+ * @return The Builder for chaining.
+ */
+ public UpdateBuilder addOptional(AbstractQueryBuilder<?> t) {
+ whereProcessor.addOptional(t.getWhereHandler());
+ return this;
+ }
+
+ /**
+ * Adds a filter to the where clause
+ *
+ * Use ExprFactory or NodeValue static or the AbstractQueryBuilder.makeExpr
+ * methods to create the expression.
+ *
+ * @see ExprFactory
+ * @see org.apache.jena.sparql.expr.NodeValue
+ * @see AbstractQueryBuilder#makeExpr(String)
+ *
+ * @param expression
+ * the expression to evaluate for the filter.
+ * @return @return The Builder for chaining.
+ */
+ public UpdateBuilder addFilter(Expr expression) {
+ whereProcessor.addFilter(expression);
+ return this;
+ }
+
+ /**
+ * Add a graph statement to the query as per
+ * http://www.w3.org/TR/2013/REC-sparql11
+ * -query-20130321/#rGraphGraphPattern.
+ *
+ * See {@link AbstractQueryBuilder#makeNode} for conversion of the graph
+ * param.
+ *
+ * @param graph
+ * The iri or variable identifying the graph.
+ * @param subQuery
+ * The graph to add.
+ * @return This builder for chaining.
+ */
+ public UpdateBuilder addGraph(Object graph, AbstractQueryBuilder<?> subQuery) {
+ whereProcessor.addGraph(makeNode(graph), subQuery.getWhereHandler());
+ return this;
+ }
+
+ /**
+ * Add a bind statement to the query *
+ * http://www.w3.org/TR/2013/REC-sparql11-query-20130321/#rGraphGraphPattern.
+ *
+ * @param expression
+ * The expression to bind to the var.
+ * @param var
+ * The variable to bind to.
+ * @return This builder for chaining.
+ */
+ public UpdateBuilder addBind(Expr expression, Object var) {
+ whereProcessor.addBind(expression, makeVar(var));
+ return this;
+ }
+
+ /**
+ * Add a bind statement to the query
+ * http://www.w3.org/TR/2013/REC-sparql11-query-20130321/#rGraphGraphPattern.
+ *
+ * @param expression
+ * The expression to bind to the var.
+ * @param var
+ * The variable to bind to.
+ * @return This builder for chaining.
+ * @throws ParseException
+ */
+ public UpdateBuilder addBind(String expression, Object var) throws ParseException {
+ whereProcessor.addBind(expression, makeVar(var));
+ return this;
+ }
+
+ /**
+ * Add a minus clause to the query.
+ *
+ * https://www.w3.org/TR/2013/REC-sparql11-query-20130321/#rMinusGraphPattern
+ *
+ * @param t
+ * The select builder to add as a minus pattern
+ * @return this builder for chaining
+ */
+ public UpdateBuilder addMinus(AbstractQueryBuilder<?> t) {
+ whereProcessor.addMinus(t);
+ return this;
+ }
+
+ /**
+ * Specify the graph for all inserts and deletes.
+ *
+ *
+ * @See Quad#defaultGraphNodeGenerated
+ * @param iri
+ * the IRI for the graph to use.
+ * @return this builder for chaining.
+ */
+ public UpdateBuilder with(Object iri) {
+ if (iri == null) {
+ with = null;
+ }
+ Node n = makeNode(iri);
+ if (n.isLiteral()) {
+ throw new IllegalArgumentException(String.format("IRI '%s' must not be a literal", iri));
+ }
+ with = n;
+ return this;
+ }
+
+ /**
+ * Create a DeleteWhere from the where clause.
+ * @return a DeleteWhere update.
+ */
+ public UpdateDeleteWhere buildDeleteWhere()
+ {
+ QuadAcc quadAcc = new QuadAcc( whereProcessor.getQuads().toList() );
+ return new UpdateDeleteWhere( quadAcc );
+ }
+
+ /**
+ * Create a DeleteWhere from the where clause.
+ * @param queryBuilder the query builder to extract the where clause from.
+ * @return a DeleteWhere update.
+ */
+ public UpdateDeleteWhere buildDeleteWhere( AbstractQueryBuilder<?> queryBuilder)
+ {
+ QuadAcc quadAcc = new QuadAcc( new QBQuadHolder( queryBuilder ).getQuads().toList() );
+ return new UpdateDeleteWhere( quadAcc );
+ }
+}
http://git-wip-us.apache.org/repos/asf/jena/blob/7d96286e/jena-extras/jena-querybuilder/src/main/java/org/apache/jena/arq/querybuilder/handlers/WhereHandler.java
----------------------------------------------------------------------
diff --git a/jena-extras/jena-querybuilder/src/main/java/org/apache/jena/arq/querybuilder/handlers/WhereHandler.java b/jena-extras/jena-querybuilder/src/main/java/org/apache/jena/arq/querybuilder/handlers/WhereHandler.java
index b5f04ac..95d8bc7 100644
--- a/jena-extras/jena-querybuilder/src/main/java/org/apache/jena/arq/querybuilder/handlers/WhereHandler.java
+++ b/jena-extras/jena-querybuilder/src/main/java/org/apache/jena/arq/querybuilder/handlers/WhereHandler.java
@@ -57,6 +57,15 @@ public class WhereHandler implements Handler {
public WhereHandler(Query query) {
this.query = query;
}
+
+ /**
+ * Get the query pattern from this where handler.
+ * @return the query pattern
+ */
+ public Element getQueryPattern()
+ {
+ return query.getQueryPattern();
+ }
/**
* Add all where attributes from the Where Handler argument.
@@ -273,7 +282,7 @@ public class WhereHandler implements Handler {
* The sub query to convert
* @return THe converted element.
*/
- private ElementSubQuery makeSubQuery(AbstractQueryBuilder<?> subQuery) {
+ public ElementSubQuery makeSubQuery(AbstractQueryBuilder<?> subQuery) {
Query q = new Query();
SelectHandler sh = subQuery.getHandlerBlock().getSelectHandler();
if (sh != null)
@@ -421,12 +430,18 @@ public class WhereHandler implements Handler {
return retval;
}
- public void addMinus( AbstractQueryBuilder<?> t )
+ /**
+ * Add a minus operation to the where clause.
+ * The prolog will be updated with the prefixes from the abstract query builder.
+ *
+ * @param qb the abstract builder that defines the data to subtract.
+ */
+ public void addMinus( AbstractQueryBuilder<?> qb )
{
PrologHandler ph = new PrologHandler(query);
- ph.addPrefixes( t.getPrologHandler().getPrefixes() );
+ ph.addPrefixes( qb.getPrologHandler().getPrefixes() );
ElementGroup clause = getClause();
- ElementMinus minus = new ElementMinus(t.getWhereHandler().getClause());
+ ElementMinus minus = new ElementMinus(qb.getWhereHandler().getClause());
clause.addElement(minus);
}
}
http://git-wip-us.apache.org/repos/asf/jena/blob/7d96286e/jena-extras/jena-querybuilder/src/main/java/org/apache/jena/arq/querybuilder/updatebuilder/PrefixHandler.java
----------------------------------------------------------------------
diff --git a/jena-extras/jena-querybuilder/src/main/java/org/apache/jena/arq/querybuilder/updatebuilder/PrefixHandler.java b/jena-extras/jena-querybuilder/src/main/java/org/apache/jena/arq/querybuilder/updatebuilder/PrefixHandler.java
new file mode 100644
index 0000000..5713e04
--- /dev/null
+++ b/jena-extras/jena-querybuilder/src/main/java/org/apache/jena/arq/querybuilder/updatebuilder/PrefixHandler.java
@@ -0,0 +1,115 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jena.arq.querybuilder.updatebuilder;
+
+import java.util.Map;
+
+import org.apache.jena.arq.querybuilder.ExprFactory;
+import org.apache.jena.shared.PrefixMapping ;
+
+/**
+ * The prefix handler for the updatebuilder class
+ *
+ */
+public class PrefixHandler {
+
+ // the prefix mapping we are handling
+ private final PrefixMapping pMap;
+
+ /**
+ * Constructor.
+ * <b>May modify the contents of the provided prefix mapping</b>
+ * @param pMap The prefix map to handle.
+ */
+ public PrefixHandler(PrefixMapping pMap) {
+ this.pMap = pMap;
+ }
+
+ /**
+ * Constructor. Creates and empty prefix mapping
+ */
+ public PrefixHandler() {
+ this.pMap = PrefixMapping.Factory.create();
+ }
+
+ /**
+ * get the canonical prefix name.
+ *
+ * Removes ':' from the end of the name if present.
+ *
+ * @param x The prefix name
+ * @return The prefix name with the trialing ':' removed.
+ */
+ private static String canonicalPfx(String x) {
+ if (x.endsWith(":"))
+ return x.substring(0, x.length() - 1);
+ return x;
+ }
+
+
+ /**
+ * Add a prefix to the prefix mapping.
+ * @param pfx The prefix to add.
+ * @param uri The uri to resolve the prefix to.
+ */
+ public void addPrefix(String pfx, String uri) {
+ pMap.setNsPrefix(canonicalPfx(pfx), uri);
+ }
+
+ /**
+ * Clear the prefix mapping.
+ */
+ public void clearPrefixes() {
+ pMap.clearNsPrefixMap();
+ }
+
+ /**
+ * Add the map of prefixes to the query prefixes.
+ * @param prefixes The map of prefixs to URIs.
+ */
+ public void addPrefixes(Map<String, String> prefixes) {
+ for (Map.Entry<String, String> e : prefixes.entrySet()) {
+ addPrefix(e.getKey(), e.getValue());
+ }
+ }
+
+ /**
+ * Get the prefix mapping
+ * @return the prefix mapping object.
+ */
+ public PrefixMapping getPrefixes() {
+ return pMap;
+ }
+
+ /**
+ * Get the expression factory based on the prefix mapping.
+ * @return an Expression Factory.
+ */
+ public ExprFactory getExprFactory() {
+ return new ExprFactory( pMap );
+ }
+
+ /**
+ * Add prefixes from a prefix mapping.
+ * @param prefixes THe prefix mapping to add from.
+ */
+ public void addPrefixes(PrefixMapping prefixes) {
+ pMap.setNsPrefixes(prefixes);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/jena/blob/7d96286e/jena-extras/jena-querybuilder/src/main/java/org/apache/jena/arq/querybuilder/updatebuilder/QBQuadHolder.java
----------------------------------------------------------------------
diff --git a/jena-extras/jena-querybuilder/src/main/java/org/apache/jena/arq/querybuilder/updatebuilder/QBQuadHolder.java b/jena-extras/jena-querybuilder/src/main/java/org/apache/jena/arq/querybuilder/updatebuilder/QBQuadHolder.java
new file mode 100644
index 0000000..d6584dd
--- /dev/null
+++ b/jena-extras/jena-querybuilder/src/main/java/org/apache/jena/arq/querybuilder/updatebuilder/QBQuadHolder.java
@@ -0,0 +1,79 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jena.arq.querybuilder.updatebuilder;
+
+import java.util.Map;
+
+import org.apache.jena.arq.querybuilder.AbstractQueryBuilder;
+import org.apache.jena.graph.Node;
+import org.apache.jena.query.Query;
+import org.apache.jena.sparql.core.Quad;
+import org.apache.jena.sparql.core.Var;
+import org.apache.jena.util.iterator.ExtendedIterator;
+
+/**
+ * An QuadHolder that manages AbstractQueryBuilder data.
+ *
+ */
+public class QBQuadHolder implements QuadHolder {
+
+ private final AbstractQueryBuilder<?> qb;
+ private Node defaultGraphName;
+
+ /**
+ * Constructor.
+ * @param graph the default graph name for the triples
+ * @param qb the AbstractQueryBuilder that is providing the triples.
+ */
+ public QBQuadHolder( Node graph, AbstractQueryBuilder<?> qb )
+ {
+ this.qb = qb;
+ this.defaultGraphName = graph;
+ }
+
+ /**
+ * Constructor.
+ * Uses Quad.defaultGraphNodeGenerated for the graph name.
+ *
+ * @see Quad#defaultGraphNodeGenerated
+ * @param qb the AbstractQueryBuilder that is providing the triples.
+ */
+ public QBQuadHolder( AbstractQueryBuilder<?> qb )
+ {
+ this( Quad.defaultGraphNodeGenerated, qb );
+ }
+
+ @Override
+ public ExtendedIterator<Quad> getQuads() {
+ Query q = qb.build();
+ QuadIteratorBuilder builder = new QuadIteratorBuilder(defaultGraphName);
+ q.getQueryPattern().visit(builder);
+ return builder.iter;
+ }
+
+ @Override
+ public QuadHolder setValues(Map<Var, Node> values) {
+ qb.clearValues();
+ for (Map.Entry<Var, Node> entry : values.entrySet())
+ {
+ qb.setVar(entry.getKey(), entry.getValue());
+ }
+ return this;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/jena/blob/7d96286e/jena-extras/jena-querybuilder/src/main/java/org/apache/jena/arq/querybuilder/updatebuilder/QuadHolder.java
----------------------------------------------------------------------
diff --git a/jena-extras/jena-querybuilder/src/main/java/org/apache/jena/arq/querybuilder/updatebuilder/QuadHolder.java b/jena-extras/jena-querybuilder/src/main/java/org/apache/jena/arq/querybuilder/updatebuilder/QuadHolder.java
new file mode 100644
index 0000000..607898d
--- /dev/null
+++ b/jena-extras/jena-querybuilder/src/main/java/org/apache/jena/arq/querybuilder/updatebuilder/QuadHolder.java
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jena.arq.querybuilder.updatebuilder;
+
+import java.util.Map;
+
+import org.apache.jena.graph.Node;
+import org.apache.jena.sparql.core.Quad;
+import org.apache.jena.sparql.core.Var;
+import org.apache.jena.util.iterator.ExtendedIterator;
+
+/**
+ * An interface that defines a holder of quads.
+ *
+ */
+public interface QuadHolder {
+
+ /**
+ * Get an extended iterator over the quads this holder holds.
+ * @return the extended iterator.
+ */
+ public ExtendedIterator<Quad> getQuads();
+
+ /**
+ * Apply values to the variables in the quads held by this holder.
+ * May return this holder or a new holder instance.
+ * @param values the values to set.
+ * @return a QuadHolder in which the variables have been replaced.
+ */
+ public QuadHolder setValues(Map<Var, Node> values);
+}
http://git-wip-us.apache.org/repos/asf/jena/blob/7d96286e/jena-extras/jena-querybuilder/src/main/java/org/apache/jena/arq/querybuilder/updatebuilder/QuadIteratorBuilder.java
----------------------------------------------------------------------
diff --git a/jena-extras/jena-querybuilder/src/main/java/org/apache/jena/arq/querybuilder/updatebuilder/QuadIteratorBuilder.java b/jena-extras/jena-querybuilder/src/main/java/org/apache/jena/arq/querybuilder/updatebuilder/QuadIteratorBuilder.java
new file mode 100644
index 0000000..cc7c2ea
--- /dev/null
+++ b/jena-extras/jena-querybuilder/src/main/java/org/apache/jena/arq/querybuilder/updatebuilder/QuadIteratorBuilder.java
@@ -0,0 +1,180 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jena.arq.querybuilder.updatebuilder;
+
+import java.util.function.Function;
+
+import org.apache.jena.graph.Node;
+import org.apache.jena.graph.Triple;
+import org.apache.jena.query.Query;
+import org.apache.jena.query.QueryParseException;
+import org.apache.jena.sparql.core.Quad;
+import org.apache.jena.sparql.core.TriplePath;
+import org.apache.jena.sparql.syntax.Element;
+import org.apache.jena.sparql.syntax.ElementAssign;
+import org.apache.jena.sparql.syntax.ElementBind;
+import org.apache.jena.sparql.syntax.ElementData;
+import org.apache.jena.sparql.syntax.ElementDataset;
+import org.apache.jena.sparql.syntax.ElementExists;
+import org.apache.jena.sparql.syntax.ElementFilter;
+import org.apache.jena.sparql.syntax.ElementGroup;
+import org.apache.jena.sparql.syntax.ElementMinus;
+import org.apache.jena.sparql.syntax.ElementNamedGraph;
+import org.apache.jena.sparql.syntax.ElementNotExists;
+import org.apache.jena.sparql.syntax.ElementOptional;
+import org.apache.jena.sparql.syntax.ElementPathBlock;
+import org.apache.jena.sparql.syntax.ElementService;
+import org.apache.jena.sparql.syntax.ElementSubQuery;
+import org.apache.jena.sparql.syntax.ElementTriplesBlock;
+import org.apache.jena.sparql.syntax.ElementUnion;
+import org.apache.jena.sparql.syntax.ElementVisitor;
+import org.apache.jena.util.iterator.ExtendedIterator;
+import org.apache.jena.util.iterator.WrappedIterator;
+
+/**
+ * A class to construct a quad iterator from the elements.
+ *
+ */
+class QuadIteratorBuilder implements ElementVisitor {
+ // the default graph name
+ private final Node defaultGraph;
+ // the extended iterator we will add to.
+ ExtendedIterator<Quad> iter = WrappedIterator.emptyIterator();
+
+ // a function to map triples to quads using the default graph name.
+ private Function<Triple,Quad> MAP =
+ new Function<Triple,Quad>(){
+
+ @Override
+ public Quad apply(Triple triple) {
+ return new Quad( defaultGraph, triple );
+ }
+ };
+
+ /**
+ * Constructor.
+ * @param defaultGraph the default graph name.
+ */
+ QuadIteratorBuilder(Node defaultGraph)
+ {
+ this.defaultGraph = defaultGraph;
+ }
+
+
+ @Override
+ public void visit(ElementTriplesBlock el) {
+ iter = iter.andThen( WrappedIterator.create(el.getPattern().getList().iterator())
+ .mapWith( MAP ));
+ }
+
+ @Override
+ public void visit(ElementPathBlock el) {
+ for (TriplePath pth : el.getPattern().getList())
+ {
+ if ( ! pth.isTriple())
+ {
+ throw new QueryParseException("Paths not permitted in data quad", -1, -1) ;
+ }
+ }
+ iter = iter.andThen(
+ WrappedIterator.create(el.getPattern().getList().iterator())
+ .mapWith( pth -> pth.asTriple() )
+ .mapWith( MAP ));
+ }
+
+ @Override
+ public void visit(ElementFilter el) {
+ throw new QueryParseException("Paths not permitted in data quad", -1, -1) ;
+ }
+
+ @Override
+ public void visit(ElementAssign el) {
+ throw new QueryParseException("element assignment not permitted in data quad", -1, -1) ;
+ }
+
+ @Override
+ public void visit(ElementBind el) {
+ throw new QueryParseException("bind not permitted in data quad", -1, -1) ;
+
+ }
+
+ @Override
+ public void visit(ElementData el) {
+ throw new QueryParseException("element data not permitted in data quad", -1, -1) ;
+ }
+
+ @Override
+ public void visit(ElementUnion el) {
+ for (Element e : el.getElements())
+ {
+ e.visit( this );
+ }
+ }
+
+ @Override
+ public void visit(ElementOptional el) {
+ throw new QueryParseException("optional not permitted in data quad", -1, -1) ;
+ }
+
+ @Override
+ public void visit(ElementGroup el) {
+ for (Element e : el.getElements())
+ {
+ e.visit( this );
+ }
+ }
+
+ @Override
+ public void visit(ElementDataset el) {
+ iter = iter.andThen(el.getDataset().find());
+ }
+
+ @Override
+ public void visit(ElementNamedGraph el) {
+ QuadIteratorBuilder bldr = new QuadIteratorBuilder( el.getGraphNameNode());
+ el.getElement().visit(bldr);
+ iter = iter.andThen( bldr.iter );
+ }
+
+ @Override
+ public void visit(ElementExists el) {
+ throw new QueryParseException("exists not permitted in data quad", -1, -1) ;
+ }
+
+ @Override
+ public void visit(ElementNotExists el) {
+ throw new QueryParseException("not exists not permitted in data quad", -1, -1) ;
+ }
+
+ @Override
+ public void visit(ElementMinus el) {
+ throw new QueryParseException("minus not permitted in data quad", -1, -1) ;
+ }
+
+ @Override
+ public void visit(ElementService el) {
+ throw new QueryParseException("service not permitted in data quad", -1, -1) ;
+ }
+
+ @Override
+ public void visit(ElementSubQuery el) {
+ Query q = el.getQuery();
+ q.getQueryPattern().visit( this );
+ }
+
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/jena/blob/7d96286e/jena-extras/jena-querybuilder/src/main/java/org/apache/jena/arq/querybuilder/updatebuilder/SingleQuadHolder.java
----------------------------------------------------------------------
diff --git a/jena-extras/jena-querybuilder/src/main/java/org/apache/jena/arq/querybuilder/updatebuilder/SingleQuadHolder.java b/jena-extras/jena-querybuilder/src/main/java/org/apache/jena/arq/querybuilder/updatebuilder/SingleQuadHolder.java
new file mode 100644
index 0000000..234de60
--- /dev/null
+++ b/jena-extras/jena-querybuilder/src/main/java/org/apache/jena/arq/querybuilder/updatebuilder/SingleQuadHolder.java
@@ -0,0 +1,97 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jena.arq.querybuilder.updatebuilder;
+
+import java.util.Map;
+import org.apache.jena.graph.Node;
+import org.apache.jena.graph.Triple;
+import org.apache.jena.sparql.core.Quad;
+import org.apache.jena.sparql.core.Var;
+import org.apache.jena.util.iterator.ExtendedIterator;
+import org.apache.jena.util.iterator.SingletonIterator;
+
+/**
+ * A QuadHolder implementation for a single quad.
+ */
+public class SingleQuadHolder implements QuadHolder{
+
+ private Quad quad;
+ private Quad updated;
+
+ /**
+ * Constructor for a single quad.
+ * @param quad the quad to hold.
+ */
+ public SingleQuadHolder( Quad quad )
+ {
+ this.quad = quad;
+ }
+
+ /**
+ * Constructor from a triple
+ * Uses Quad.defaultGraphNodeGenerated for the graph name.
+ *
+ * @see Quad#defaultGraphNodeGenerated
+ * @param triple the triple to convert to a quad.
+ */
+ public SingleQuadHolder( Triple triple )
+ {
+ this.quad = new Quad( Quad.defaultGraphNodeGenerated, triple );
+ }
+
+
+ /**
+ * Constructor from a triple
+ * @param graph the graph name to use for the triple
+ * @param triple the triple to convert to a quad.
+ */
+ public SingleQuadHolder( Node graph, Triple triple )
+ {
+ this.quad = new Quad( graph, triple );
+ }
+
+ @Override
+ public ExtendedIterator<Quad> getQuads() {
+ return new SingletonIterator<Quad>(updated==null?quad:updated);
+ }
+
+ // convert variable if in map
+ private Node mapValue( Node n, Map<Var, Node> values)
+ {
+ Node retval = null;
+ if (n.isVariable())
+ {
+ Var v = Var.alloc(n);
+ retval = values.get(v);
+ }
+ return retval==null?n:retval;
+ }
+
+ @Override
+ public QuadHolder setValues(Map<Var, Node> values) {
+ updated = new Quad(
+ mapValue( quad.getGraph(), values),
+ mapValue( quad.getSubject(), values),
+ mapValue( quad.getPredicate(), values),
+ mapValue( quad.getObject(), values)
+ );
+ return this;
+ }
+
+
+}
http://git-wip-us.apache.org/repos/asf/jena/blob/7d96286e/jena-extras/jena-querybuilder/src/main/java/org/apache/jena/arq/querybuilder/updatebuilder/WhereProcessor.java
----------------------------------------------------------------------
diff --git a/jena-extras/jena-querybuilder/src/main/java/org/apache/jena/arq/querybuilder/updatebuilder/WhereProcessor.java b/jena-extras/jena-querybuilder/src/main/java/org/apache/jena/arq/querybuilder/updatebuilder/WhereProcessor.java
new file mode 100644
index 0000000..b6e40e4
--- /dev/null
+++ b/jena-extras/jena-querybuilder/src/main/java/org/apache/jena/arq/querybuilder/updatebuilder/WhereProcessor.java
@@ -0,0 +1,433 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jena.arq.querybuilder.updatebuilder;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import org.apache.jena.arq.querybuilder.AbstractQueryBuilder;
+import org.apache.jena.arq.querybuilder.clauses.SelectClause;
+import org.apache.jena.arq.querybuilder.handlers.WhereHandler;
+import org.apache.jena.arq.querybuilder.rewriters.ElementRewriter;
+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.sparql.core.Quad;
+import org.apache.jena.sparql.core.TriplePath;
+import org.apache.jena.sparql.core.Var;
+import org.apache.jena.sparql.expr.Expr;
+import org.apache.jena.sparql.lang.sparql_11.ParseException;
+import org.apache.jena.sparql.syntax.*;
+import org.apache.jena.sparql.util.ExprUtils;
+import org.apache.jena.util.iterator.ExtendedIterator;
+import org.apache.jena.util.iterator.NiceIterator;
+import org.apache.jena.vocabulary.RDF;
+
+/**
+ * The where processor. Generally handles update where clause.
+ *
+ * @see <a href=
+ * "http://www.w3.org/TR/2013/REC-sparql11-query-20130321/#rGroupGraphPattern">
+ * SPARQL 11 Query Language - Group Graph Pattern</a>
+ *
+ */
+public class WhereProcessor implements QuadHolder {
+
+ private Element whereClause;
+ private final PrefixHandler prefixHandler;
+
+ /**
+ * Constructor.
+ *
+ * @param prefixHandler
+ * the prefix handler to use.
+ */
+ public WhereProcessor(PrefixHandler prefixHandler) {
+ this.prefixHandler = prefixHandler;
+ }
+
+ /**
+ * True if there are no elements in the where processor.
+ *
+ * @return true if there are no elements.
+ */
+ public boolean isEmpty() {
+ return whereClause == null || (whereClause instanceof ElementGroup && ((ElementGroup) whereClause).isEmpty());
+ }
+
+ @Override
+ public ExtendedIterator<Quad> getQuads() {
+ return getQuads( Quad.defaultGraphNodeGenerated);
+ }
+
+ public ExtendedIterator<Quad> getQuads( Node defaultGraphName ) {
+ if (isEmpty())
+ {
+ return NiceIterator.emptyIterator();
+ }
+ QuadIteratorBuilder builder = new QuadIteratorBuilder(defaultGraphName);
+ whereClause.visit(builder);
+ return builder.iter;
+ }
+ /**
+ * Add all where attributes from the Where Handler argument.
+ *
+ * @param whereHandler
+ * The Where Handler to copy from.
+ */
+ public void addAll(WhereHandler whereHandler) {
+
+ Element e = whereHandler.getQueryPattern();
+ if (e != null) {
+ // clone the Element
+ ElementRewriter rewriter = new ElementRewriter(Collections.emptyMap());
+ e.visit(rewriter);
+ Element clone = rewriter.getResult();
+
+ if (whereClause == null) {
+ whereClause = clone;
+ } else {
+ ElementGroup eg = null;
+ if (whereClause instanceof ElementGroup) {
+ eg = (ElementGroup) whereClause;
+ } else {
+ eg = new ElementGroup();
+ eg.addElement(whereClause);
+ }
+ if (clone instanceof ElementGroup) {
+ for (Element ele : ((ElementGroup) clone).getElements()) {
+ eg.addElement(ele);
+ }
+ } else {
+ eg.addElement(clone);
+ }
+ whereClause = eg;
+ }
+ }
+ }
+
+ /**
+ * Get the element group for the clause. if The element group is not set,
+ * create and set it.
+ *
+ * Public for ExprFactory use.
+ *
+ * @return The element group.
+ */
+ public ElementGroup getClause() {
+ Element e = whereClause;
+ if (e == null) {
+ e = new ElementGroup();
+ whereClause = e;
+ }
+ if (e instanceof ElementGroup) {
+ return (ElementGroup) e;
+ }
+
+ ElementGroup eg = new ElementGroup();
+ eg.addElement(e);
+ whereClause = eg;
+ return eg;
+ }
+
+ /**
+ * Test that a triple is valid. Throws an IllegalArgumentException if the
+ * triple is not valid.
+ *
+ * @param t
+ * The trip to test.
+ */
+ private static void testTriple(TriplePath t) {
+ // verify Triple is valid
+ boolean validSubject = t.getSubject().isURI() || t.getSubject().isBlank() || t.getSubject().isVariable()
+ || t.getSubject().equals(Node.ANY);
+ boolean validPredicate;
+
+ if (t.isTriple()) {
+ validPredicate = t.getPredicate().isURI() || t.getPredicate().isVariable()
+ || t.getPredicate().equals(Node.ANY);
+ } else {
+ validPredicate = t.getPath() != null;
+ }
+
+ boolean validObject = t.getObject().isURI() || t.getObject().isLiteral() || t.getObject().isBlank()
+ || t.getObject().isVariable() || t.getObject().equals(Node.ANY);
+
+ if (!validSubject || !validPredicate || !validObject) {
+ StringBuilder sb = new StringBuilder();
+ if (!validSubject) {
+ sb.append(String.format("Subject (%s) must be a URI, blank, variable, or a wildcard. %n",
+ t.getSubject()));
+ }
+ if (!validPredicate) {
+ sb.append(String.format("Predicate (%s) must be a Path, URI , variable, or a wildcard. %n",
+ t.getPredicate()));
+ }
+ if (!validObject) {
+ sb.append(String.format("Object (%s) must be a URI, literal, blank, , variable, or a wildcard. %n",
+ t.getObject()));
+ }
+ if (!validSubject || !validPredicate) {
+ sb.append(String.format("Is a prefix missing? Prefix must be defined before use. %n"));
+ }
+ throw new IllegalArgumentException(sb.toString());
+ }
+ }
+
+ /**
+ * Add the triple path to the where clause
+ *
+ * @param t
+ * The triple path to add.
+ * @throws IllegalArgumentException
+ * If the triple path is not a valid triple path for a where
+ * clause.
+ */
+ public void addWhere(TriplePath t) throws IllegalArgumentException {
+ testTriple(t);
+ ElementGroup eg = getClause();
+ List<Element> lst = eg.getElements();
+ if (lst.isEmpty()) {
+ ElementPathBlock epb = new ElementPathBlock();
+ epb.addTriple(t);
+ eg.addElement(epb);
+ } else {
+ Element e = lst.get(lst.size() - 1);
+ if (e instanceof ElementTriplesBlock && t.isTriple()) {
+ ElementTriplesBlock etb = (ElementTriplesBlock) e;
+ etb.addTriple(t.asTriple());
+ } else if (e instanceof ElementPathBlock) {
+ ElementPathBlock epb = (ElementPathBlock) e;
+ epb.addTriple(t);
+ } else {
+ ElementPathBlock etb = new ElementPathBlock();
+ etb.addTriple(t);
+ eg.addElement(etb);
+ }
+
+ }
+ }
+
+ /**
+ * Add an optional triple to the where clause
+ *
+ * @param t
+ * The triple path to add.
+ * @throws IllegalArgumentException
+ * If the triple is not a valid triple for a where clause.
+ */
+ public void addOptional(TriplePath t) throws IllegalArgumentException {
+ testTriple(t);
+ ElementPathBlock epb = new ElementPathBlock();
+ epb.addTriple(t);
+ ElementOptional opt = new ElementOptional(epb);
+ getClause().addElement(opt);
+ }
+
+ /**
+ * Add the contents of a where handler as an optional statement.
+ *
+ * @param whereHandler
+ * The where handler to use as the optional statement.
+ */
+ public void addOptional(WhereHandler whereHandler) {
+ getClause().addElement(new ElementOptional(whereHandler.getClause()));
+ }
+
+ /**
+ * Add an expression string as a filter.
+ *
+ * @param expression
+ * The expression string to add.
+ * @throws ParseException
+ * If the expression can not be parsed.
+ */
+ public void addFilter(String expression) throws ParseException {
+ getClause().addElement(new ElementFilter(parseExpr(expression)));
+ }
+
+ private Expr parseExpr(String expression) {
+ Query query = new Query();
+ query.setPrefixMapping(prefixHandler.getPrefixes());
+ return ExprUtils.parse(query, expression, true);
+
+ }
+
+ /**
+ * add an expression as a filter.
+ *
+ * @param expr
+ * The expression to add.
+ */
+ public void addFilter(Expr expr) {
+ getClause().addElement(new ElementFilter(expr));
+ }
+
+ /**
+ * Add a subquery to the where clause.
+ *
+ * @param subQuery
+ * The sub query to add.
+ */
+ public void addSubQuery(AbstractQueryBuilder<?> subQuery) {
+ getClause().addElement(subQuery.asSubQuery());
+ }
+
+ /**
+ * Add a union to the where clause.
+ *
+ * @param subQuery
+ * The subquery to add as the union.
+ */
+ public void addUnion(AbstractQueryBuilder<?> subQuery) {
+ ElementUnion union = null;
+ ElementGroup clause = getClause();
+ // if the last element is a union make sure we add to it.
+ if (!clause.isEmpty()) {
+ Element lastElement = clause.getElements().get(clause.getElements().size() - 1);
+ if (lastElement instanceof ElementUnion) {
+ union = (ElementUnion) lastElement;
+ } else {
+ // clauses is not empty and is not a union so it is the left
+ // side of the union.
+ union = new ElementUnion();
+ union.addElement(clause);
+ whereClause = union;
+ }
+ } else {
+ // add the union as the first element in the clause.
+ union = new ElementUnion();
+ clause.addElement(union);
+ }
+ // if there are projected vars then do a full blown subquery
+ // otherwise just add the clause.
+ if (subQuery instanceof SelectClause && ((SelectClause<?>) subQuery).getVars().size() > 0) {
+ union.addElement(subQuery.asSubQuery());
+ } else {
+ prefixHandler.addPrefixes(subQuery.getPrologHandler().getPrefixes());
+ union.addElement(subQuery.getWhereHandler().getClause());
+ }
+
+ }
+
+ /**
+ * Add a graph to the where clause.
+ *
+ * @param graph
+ * The name of the graph.
+ * @param subQuery
+ * The where handler that defines the graph.
+ */
+ public void addGraph(Node graph, WhereHandler subQuery) {
+ getClause().addElement(new ElementNamedGraph(graph, subQuery.getClause()));
+ }
+
+ /**
+ * Add a binding to the where clause.
+ *
+ * @param expr
+ * The expression to bind.
+ * @param var
+ * The variable to bind it to.
+ */
+ public void addBind(Expr expr, Var var) {
+ getClause().addElement(new ElementBind(var, expr));
+ }
+
+ /**
+ * Add a binding to the where clause.
+ *
+ * @param expression
+ * The expression to bind.
+ * @param var
+ * The variable to bind it to.
+ * @throws ParseException
+ */
+ public void addBind(String expression, Var var) throws ParseException {
+ getClause().addElement(new ElementBind(var, parseExpr(expression)));
+ }
+
+ /**
+ * replace the vars in the expressions with the nodes in the values map.
+ * Vars not listed in the values map are not changed.
+ *
+ * Will return null if the whereClause is null.
+ *
+ * @param values
+ * the value map to use
+ * @return A new Element instance with the values changed.
+ */
+ public Element setVars(Map<Var, Node> values) {
+ if (values.isEmpty() || whereClause == null) {
+ return whereClause;
+ }
+ ElementRewriter r = new ElementRewriter(values);
+ whereClause.visit(r);
+ return r.getResult();
+
+ }
+
+ @Override
+ public QuadHolder setValues(Map<Var, Node> values)
+ {
+ setVars( values );
+ return this;
+ }
+
+ /**
+ * Create a list node from a list of objects as per RDF Collections.
+ *
+ * http://www.w3.org/TR/2013/REC-sparql11-query-20130321/#collections
+ *
+ * @param objs
+ * the list of objects for the list.
+ * @return the first blank node in the list.
+ */
+ public Node list(Object... objs) {
+ Node retval = NodeFactory.createBlankNode();
+ Node lastObject = retval;
+ for (int i = 0; i < objs.length; i++) {
+ Node n = AbstractQueryBuilder.makeNode(objs[i], prefixHandler.getPrefixes());
+ addWhere(new TriplePath(new Triple(lastObject, RDF.first.asNode(), n)));
+ if (i + 1 < objs.length) {
+ Node nextObject = NodeFactory.createBlankNode();
+ addWhere(new TriplePath(new Triple(lastObject, RDF.rest.asNode(), nextObject)));
+ lastObject = nextObject;
+ } else {
+ addWhere(new TriplePath(new Triple(lastObject, RDF.rest.asNode(), RDF.nil.asNode())));
+ }
+
+ }
+
+ return retval;
+ }
+
+ /**
+ * Add a minus operation to the where clause.
+ * The prefixes will be updated with the prefixes from the abstract query builder.
+ *
+ * @param qb the abstract builder that defines the data to subtract.
+ */
+ public void addMinus(AbstractQueryBuilder<?> qb) {
+ prefixHandler.addPrefixes(qb.getPrologHandler().getPrefixes());
+ ElementGroup clause = getClause();
+ ElementMinus minus = new ElementMinus(qb.getWhereHandler().getClause());
+ clause.addElement(minus);
+ }
+}
http://git-wip-us.apache.org/repos/asf/jena/blob/7d96286e/jena-extras/jena-querybuilder/src/test/java/Example10.java
----------------------------------------------------------------------
diff --git a/jena-extras/jena-querybuilder/src/test/java/Example10.java b/jena-extras/jena-querybuilder/src/test/java/Example10.java
new file mode 100644
index 0000000..7acbd7c
--- /dev/null
+++ b/jena-extras/jena-querybuilder/src/test/java/Example10.java
@@ -0,0 +1,104 @@
+
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import org.apache.jena.datatypes.xsd.XSDDatatype;
+import org.apache.jena.graph.Node;
+import org.apache.jena.graph.NodeFactory;
+import org.apache.jena.query.Dataset;
+import org.apache.jena.query.DatasetFactory;
+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.RDFNode;
+import org.apache.jena.rdf.model.Resource;
+import org.apache.jena.rdf.model.ResourceFactory;
+import org.apache.jena.update.UpdateAction;
+import org.apache.jena.update.UpdateFactory;
+import org.apache.jena.update.UpdateRequest;
+import org.apache.jena.vocabulary.DCTypes;
+import org.apache.jena.vocabulary.DC_11;
+import org.junit.Test;
+
+public class Example10 {
+
+ /**
+ * Example 10:
+ *
+ * @see https://www.w3.org/TR/sparql11-update/#example_10
+ */
+ @Test
+ public void example10()
+ {
+
+ Resource book1 = ResourceFactory.createResource( "http://example/book1" );
+ Resource book3 = ResourceFactory.createResource( "http://example/book3" );
+ Resource book4 = ResourceFactory.createResource( "http://example/book4" );
+
+ Literal d1996 = ResourceFactory.createTypedLiteral("1996-01-01T00:00:00-02:00", XSDDatatype.XSDdateTime);
+
+ Node graphName1 = NodeFactory.createURI("http://example/bookStore");
+ Node graphName2 = NodeFactory.createURI("http://example/bookStore2");
+
+ Model m1 = ModelFactory.createDefaultModel();
+ m1.add( book1, DC_11.title, "Fundamentals of Compiler Design");
+ m1.add( book1, DC_11.date, d1996 );
+ m1.add( book1, DC_11.date, DCTypes.PhysicalObject );
+
+ m1.add( book3, DC_11.title, "SPARQL 1.1 Tutorial" );
+
+ Model m2 = ModelFactory.createDefaultModel();
+ m2.add( book4, DC_11.title, "SPARQL 1.1 Tutorial" );
+
+ Dataset ds = DatasetFactory.create();
+ ds.addNamedModel(graphName1.getURI(), m1);
+ ds.addNamedModel(graphName2.getURI(), m2);
+
+ String s = "PREFIX dc: <http://purl.org/dc/elements/1.1/>\n"
+ + "PREFIX dcmitype: <http://purl.org/dc/dcmitype/>\n"
+ + "PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>\n"
+ + "INSERT\n"
+ + " { GRAPH <http://example/bookStore2> { ?book ?p ?v } }\n"
+ + "WHERE\n"
+ + " { GRAPH <http://example/bookStore>\n"
+ + " { ?book dc:date ?date .\n"
+ + " FILTER ( ?date < \"2000-01-01T00:00:00-02:00\"^^xsd:dateTime )\n"
+ + " ?book ?p ?v\n"
+ + " }"
+ + " } ;\n\n"
+ + "WITH <http://example/bookStore>\n"
+ + "DELETE\n"
+ + " { ?book ?p ?v }\n"
+ + "WHERE\n"
+ + " { ?book dc:date ?date ;\n"
+ + " dc:type dcmitype:PhysicalObject .\n"
+ + " FILTER ( ?date < \"2000-01-01T00:00:00-02:00\"^^xsd:dateTime )\n"
+ + " ?book ?p ?v\n"
+ + " } ";
+ UpdateRequest req = UpdateFactory.create(s);
+
+ System.out.println( req );
+
+ UpdateAction.execute( req, ds );
+
+ m1 = ds.getNamedModel( graphName1.getURI());
+ assertEquals( "DELETE failed", 1, m1.listStatements().toList().size());
+
+ assertTrue( m1.contains( book3, DC_11.title, "SPARQL 1.1 Tutorial"));
+
+
+ m2 = ds.getNamedModel( graphName2.getURI());
+ assertEquals( "Insert failed", 4, m2.listStatements().toList().size());
+
+
+ assertEquals( 3, m1.listStatements( book1, null, (RDFNode)null).toList().size());
+ assertTrue( m2.contains( book1, DC_11.title, "Fundamentals of Compiler Design"));
+ assertTrue( m2.contains( book1, DC_11.date, d1996 ));
+ assertTrue( m2.contains( book1, DC_11.date, DCTypes.PhysicalObject ));
+
+ assertEquals( 1, m1.listStatements( book4, null, (RDFNode)null).toList().size());
+ assertTrue( m1.contains( book4, DC_11.title, "SPARQL 1.1 Tutorial"));
+
+ }
+}