http://git-wip-us.apache.org/repos/asf/jena/blob/3d70d735/jena-arq/src/main/java/org/apache/jena/query/ParameterizedSparqlString.java ---------------------------------------------------------------------- diff --git a/jena-arq/src/main/java/org/apache/jena/query/ParameterizedSparqlString.java b/jena-arq/src/main/java/org/apache/jena/query/ParameterizedSparqlString.java index b6d7460..e096688 100644 --- a/jena-arq/src/main/java/org/apache/jena/query/ParameterizedSparqlString.java +++ b/jena-arq/src/main/java/org/apache/jena/query/ParameterizedSparqlString.java @@ -1,1710 +1,1710 @@ -/** - * 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.query; - -import java.net.URL; -import java.util.ArrayList; -import java.util.Calendar; -import java.util.Collections; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.regex.MatchResult; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import org.apache.jena.atlas.lib.Pair; -import org.apache.jena.datatypes.RDFDatatype ; -import org.apache.jena.graph.Node ; -import org.apache.jena.graph.NodeFactory ; -import org.apache.jena.iri.IRI; -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.shared.PrefixMapping ; -import org.apache.jena.shared.impl.PrefixMappingImpl ; -import org.apache.jena.sparql.ARQException ; -import org.apache.jena.sparql.serializer.SerializationContext ; -import org.apache.jena.sparql.util.FmtUtils ; -import org.apache.jena.sparql.util.NodeFactoryExtra ; -import org.apache.jena.update.UpdateFactory ; -import org.apache.jena.update.UpdateRequest ; - -/** - * <p> - * A Parameterized SPARQL String is a SPARQL query/update into which values may - * be injected. - * </p> - * <h3>Injecting Values</h3> - * <p> - * Values may be injected in several ways: - * </p> - * <ul> - * <li>By treating a variable in the SPARQL string as a parameter</li> - * <li>Using JDBC style positional parameters</li> - * <li>Appending values directly to the command text being built</li> - * </ul> - * <h4>Variable Parameters</h3> - * <p> - * Any variable in the command may have a value injected to it, injecting a - * value replaces all usages of that variable in the command i.e. substitutes - * the variable for a constant, injection is done by textual substitution. - * </p> <h4>Positional Parameters</h4> - * <p> - * You can use JDBC style positional parameters if you prefer, a JDBC style - * parameter is a single {@code ?} followed by whitespace or certain punctuation - * characters (currently {@code ; , .}). Positional parameters have a unique - * index which reflects the order in which they appear in the string. Positional - * parameters use a zero based index. - * </p> - * <h4>Buffer Usage</h3> </p> Additionally you may use this purely as a - * {@link StringBuffer} replacement for creating queries since it provides a - * large variety of convenience methods for appending things either as-is or as - * nodes (which causes appropriate formatting to be applied). </p> - * <h3>Intended Usage</h3> - * <p> - * The intended usage of this is where using a {@link QuerySolutionMap} as - * initial bindings is either inappropriate or not possible e.g. - * </p> - * <ul> - * <li>Generating query/update strings in code without lots of error prone and - * messy string concatenation</li> - * <li>Preparing a query/update for remote execution</li> - * <li>Where you do not want to simply say some variable should have a certain - * value but rather wish to insert constants into the query/update in place of - * variables</li> - * <li>Defending against SPARQL injection when creating a query/update using - * some external input, see SPARQL Injection notes for limitations.</li> - * <li>Provide a more convenient way to prepend common prefixes to your query</li> - * </ul> - * <p> - * This class is useful for preparing both queries and updates hence the generic - * name as it provides programmatic ways to replace variables in the query with - * constants and to add prefix and base declarations. A {@link Query} or - * {@link UpdateRequest} can be created using the {@link #asQuery()} and - * {@link #asUpdate()} methods assuming the command an instance represents is - * actually valid as a query/update. - * </p> - * <h3>Warnings</h3> - * <ol> - * <li>Note that this class does not in any way check that your command is - * syntactically correct until such time as you try and parse it as a - * {@link Query} or {@link UpdateRequest}.</li> - * <li>Also note that injection is done purely based on textual replacement, it - * does not understand or respect variable scope in any way. For example if your - * command text contains sub queries you should ensure that variables within the - * sub query which you don't want replaced have distinct names from those in the - * outer query you do want replaced (or vice versa)</li> - * </ol> - * <h3>SPARQL Injection Notes</h3> - * <p> - * While this class was in part designed to prevent SPARQL injection it is by no - * means foolproof because it works purely at the textual level. The current - * version of the code addresses some possible attack vectors that the - * developers have identified but we do not claim to be sufficiently devious to - * have thought of and prevented every possible attack vector. - * </p> - * <p> - * Therefore we <strong>strongly</strong> recommend that users concerned about - * SPARQL Injection attacks perform their own validation on provided parameters - * and test their use of this class themselves prior to its use in any security - * conscious deployment. We also recommend that users do not use easily - * guess-able variable names for their parameters as these can allow a chained - * injection attack though generally speaking the code should prevent these. - * </p> - */ -public class ParameterizedSparqlString implements PrefixMapping { - - private Model model = ModelFactory.createDefaultModel(); - - private StringBuilder cmd = new StringBuilder(); - private String baseUri; - private Map<String, Node> params = new HashMap<>(); - private Map<Integer, Node> positionalParams = new HashMap<>(); - private PrefixMapping prefixes; - - /** - * Creates a new parameterized string - * - * @param command - * Raw Command Text - * @param map - * Initial Parameters to inject - * @param base - * Base URI - * @param prefixes - * Prefix Mapping - */ - public ParameterizedSparqlString(String command, QuerySolutionMap map, String base, PrefixMapping prefixes) { - if (command != null) - this.cmd.append(command); - this.setParams(map); - this.baseUri = (base != null && !base.equals("") ? base : null); - this.prefixes = new PrefixMappingImpl(); - if (prefixes != null) - this.prefixes.setNsPrefixes(prefixes); - } - - /** - * Creates a new parameterized string - * - * @param command - * Raw Command Text - * @param map - * Initial Parameters to inject - * @param base - * Base URI - */ - public ParameterizedSparqlString(String command, QuerySolutionMap map, String base) { - this(command, map, base, null); - } - - /** - * Creates a new parameterized string - * - * @param command - * Raw Command Text - * @param map - * Initial Parameters to inject - * @param prefixes - * Prefix Mapping - */ - public ParameterizedSparqlString(String command, QuerySolutionMap map, PrefixMapping prefixes) { - this(command, map, null, prefixes); - } - - /** - * Creates a new parameterized string - * - * @param command - * Raw Command Text - * @param map - * Initial Parameters to inject - */ - public ParameterizedSparqlString(String command, QuerySolutionMap map) { - this(command, map, null, null); - } - - /** - * Creates a new parameterized string - * - * @param command - * Raw Command Text - * @param base - * Base URI - * @param prefixes - * Prefix Mapping - */ - public ParameterizedSparqlString(String command, String base, PrefixMapping prefixes) { - this(command, null, base, prefixes); - } - - /** - * Creates a new parameterized string - * - * @param command - * Raw Command Text - * @param prefixes - * Prefix Mapping - */ - public ParameterizedSparqlString(String command, PrefixMapping prefixes) { - this(command, null, null, prefixes); - } - - /** - * Creates a new parameterized string - * - * @param command - * Raw Command Text - * @param base - * Base URI - */ - public ParameterizedSparqlString(String command, String base) { - this(command, null, base, null); - } - - /** - * Creates a new parameterized string - * - * @param command - * Raw Command Text - */ - public ParameterizedSparqlString(String command) { - this(command, null, null, null); - } - - /** - * Creates a new parameterized string - * - * @param map - * Initial Parameters to inject - * @param prefixes - * Prefix Mapping - */ - public ParameterizedSparqlString(QuerySolutionMap map, PrefixMapping prefixes) { - this(null, map, null, prefixes); - } - - /** - * Creates a new parameterized string - * - * @param map - * Initial Parameters to inject - */ - public ParameterizedSparqlString(QuerySolutionMap map) { - this(null, map, null, null); - } - - /** - * Creates a new parameterized string - * - * @param prefixes - * Prefix Mapping - */ - public ParameterizedSparqlString(PrefixMapping prefixes) { - this(null, null, null, prefixes); - } - - /** - * Creates a new parameterized string with an empty command text - */ - public ParameterizedSparqlString() { - this("", null, null, null); - } - - /** - * Sets the command text, overwriting any existing command text. If you want - * to append to the command text use one of the {@link #append(String)}, - * {@link #appendIri(String)}, {@link #appendLiteral(String)} or - * {@link #appendNode(Node)} methods instead - * - * @param command - * Command Text - */ - public void setCommandText(String command) { - this.cmd = new StringBuilder(); - this.cmd.append(command); - } - - /** - * Appends some text as-is to the existing command text, to ensure correct - * formatting when used as a constant consider using the - * {@link #appendLiteral(String)} or {@link #appendIri(String)} method as - * appropriate - * - * @param text - * Text to append - */ - public void append(String text) { - this.cmd.append(text); - } - - /** - * Appends a character as-is to the existing command text, to ensure correct - * formatting when used as a constant consider using one of the - * {@code appendLiteral()} methods - * - * @param c - * Character to append - */ - public void append(char c) { - this.cmd.append(c); - } - - /** - * Appends a boolean as-is to the existing command text, to ensure correct - * formatting when used as a constant consider using the - * {@link #appendLiteral(boolean)} method - * - * @param b - * Boolean to append - */ - public void append(boolean b) { - this.cmd.append(b); - } - - /** - * Appends a double as-is to the existing command text, to ensure correct - * formatting when used as a constant consider using the - * {@link #appendLiteral(double)} method - * - * @param d - * Double to append - */ - public void append(double d) { - this.cmd.append(d); - } - - /** - * Appends a float as-is to the existing command text, to ensure correct - * formatting when used as a constant consider using the - * {@link #appendLiteral(float)} method - * - * @param f - * Float to append - */ - public void append(float f) { - this.cmd.append(f); - } - - /** - * Appends an integer as-is to the existing command text, to ensure correct - * formatting when used as a constant consider using the - * {@link #appendLiteral(int)} method - * - * @param i - * Integer to append - */ - public void append(int i) { - this.cmd.append(i); - } - - /** - * Appends a long as-is to the existing command text, to ensure correct - * formatting when used as a constant consider using the - * {@link #appendLiteral(long)} method - * - * @param l - * Long to append - */ - public void append(long l) { - this.cmd.append(l); - } - - /** - * Appends an object as-is to the existing command text, to ensure correct - * formatting when used as a constant consider converting into a more - * specific type and using the appropriate {@code appendLiteral()}, - * {@code appendIri()} or {@code appendNode} methods - * - * @param obj - * Object to append - */ - public void append(Object obj) { - this.cmd.append(obj); - } - - /** - * Appends a Node to the command text as a constant using appropriate - * formatting - * - * @param n - * Node to append - */ - public void appendNode(Node n) { - SerializationContext context = new SerializationContext(this.prefixes); - context.setBaseIRI(this.baseUri); - this.cmd.append(this.stringForNode(n, context)); - } - - /** - * Appends a Node to the command text as a constant using appropriate - * formatting - * - * @param n - * Node to append - */ - public void appendNode(RDFNode n) { - this.appendNode(n.asNode()); - } - - /** - * Appends a URI to the command text as a constant using appropriate - * formatting - * - * @param uri - * URI to append - */ - public void appendIri(String uri) { - this.appendNode(NodeFactory.createURI(uri)); - } - - /** - * Appends an IRI to the command text as a constant using appropriate - * formatting - * - * @param iri - * IRI to append - */ - public void appendIri(IRI iri) { - this.appendNode(NodeFactory.createURI(iri.toString())); - } - - /** - * Appends a simple literal as a constant using appropriate formatting - * - * @param value - * Lexical Value - */ - public void appendLiteral(String value) { - this.appendNode(NodeFactoryExtra.createLiteralNode(value, null, null)); - } - - /** - * Appends a literal with a lexical value and language to the command text - * as a constant using appropriate formatting - * - * @param value - * Lexical Value - * @param lang - * Language - */ - public void appendLiteral(String value, String lang) { - this.appendNode(NodeFactoryExtra.createLiteralNode(value, lang, null)); - } - - /** - * Appends a Typed Literal to the command text as a constant using - * appropriate formatting - * - * @param value - * Lexical Value - * @param datatype - * Datatype - */ - public void appendLiteral(String value, RDFDatatype datatype) { - this.appendNode(NodeFactoryExtra.createLiteralNode(value, null, datatype.getURI())); - } - - /** - * Appends a boolean to the command text as a constant using appropriate - * formatting - * - * @param b - * Boolean to append - */ - public void appendLiteral(boolean b) { - this.appendNode(this.model.createTypedLiteral(b)); - } - - /** - * Appends an integer to the command text as a constant using appropriate - * formatting - * - * @param i - * Integer to append - */ - public void appendLiteral(int i) { - this.appendNode(NodeFactoryExtra.intToNode(i)); - } - - /** - * Appends a long to the command text as a constant using appropriate - * formatting - * - * @param l - * Long to append - */ - public void appendLiteral(long l) { - this.appendNode(NodeFactoryExtra.intToNode(l)); - } - - /** - * Appends a float to the command text as a constant using appropriate - * formatting - * - * @param f - * Float to append - */ - public void appendLiteral(float f) { - this.appendNode(this.model.createTypedLiteral(f)); - } - - /** - * Appends a double to the command text as a constant using appropriate - * formatting - * - * @param d - * Double to append - */ - public void appendLiteral(double d) { - this.appendNode(this.model.createTypedLiteral(d)); - } - - /** - * Appends a date time to the command text as a constant using appropriate - * formatting - * - * @param dt - * Date Time to append - */ - public void appendLiteral(Calendar dt) { - this.appendNode(this.model.createTypedLiteral(dt)); - } - - /** - * Gets the basic Command Text - * <p> - * <strong>Note:</strong> This will not reflect any injected parameters, to - * see the command with injected parameters invoke the {@link #toString()} - * method - * </p> - * - * @return Command Text - */ - public String getCommandText() { - return this.cmd.toString(); - } - - /** - * Sets the Base URI which will be prepended to the query/update - * - * @param base - * Base URI - */ - public void setBaseUri(String base) { - this.baseUri = base; - } - - /** - * Gets the Base URI which will be prepended to a query - * - * @return Base URI - */ - public String getBaseUri() { - return this.baseUri; - } - - /** - * Helper method which does the validation of the parameters - * - * @param n - * Node - */ - protected void validateParameterValue(Node n) { - if (n.isURI()) { - if (n.getURI().contains(">")) - throw new ARQException("Value for the parameter contains a SPARQL injection risk"); - } - } - - /** - * Sets the Parameters - * - * @param map - * Parameters - */ - public void setParams(QuerySolutionMap map) { - if (map != null) { - Iterator<String> iter = map.varNames(); - while (iter.hasNext()) { - String var = iter.next(); - this.setParam(var, map.get(var).asNode()); - } - } - } - - /** - * Sets a Positional Parameter - * <p> - * Setting a parameter to null is equivalent to calling - * {@link #clearParam(int)} for the given variable - * </p> - * - * @param index - * Positional Index - * @param n - * Node - */ - public void setParam(int index, Node n) { - if (index < 0) - throw new IndexOutOfBoundsException(); - if (n != null) { - this.validateParameterValue(n); - this.positionalParams.put(index, n); - } else { - this.positionalParams.remove(index); - } - } - - /** - * Sets a variable parameter - * <p> - * Setting a parameter to null is equivalent to calling - * {@link #clearParam(String)} for the given variable - * </p> - * - * @param var - * Variable - * @param n - * Value - * - */ - public void setParam(String var, Node n) { - if (var == null) - throw new IllegalArgumentException("var cannot be null"); - if (var.startsWith("?") || var.startsWith("$")) - var = var.substring(1); - if (n != null) { - this.validateParameterValue(n); - this.params.put(var, n); - } else { - this.params.remove(var); - } - } - - /** - * Sets a positional parameter - * <p> - * Setting a parameter to null is equivalent to calling - * {@link #clearParam(String)} for the given variable - * </p> - * - * @param index - * Positional Index - * @param n - * Node - */ - public void setParam(int index, RDFNode n) { - this.setParam(index, n.asNode()); - } - - /** - * Sets a variable parameter - * <p> - * Setting a parameter to null is equivalent to calling - * {@link #clearParam(String)} for the given variable - * </p> - * - * @param var - * Variable - * @param n - * Value - */ - public void setParam(String var, RDFNode n) { - this.setParam(var, n.asNode()); - } - - /** - * Sets a positional parameter to an IRI - * <p> - * Setting a parameter to null is equivalent to calling - * {@link #clearParam(int)} for the given index - * </p> - * - * @param index - * Positional Index - * @param iri - * IRI - */ - public void setIri(int index, String iri) { - this.setParam(index, NodeFactory.createURI(iri)); - } - - /** - * Sets a variable parameter to an IRI - * <p> - * Setting a parameter to null is equivalent to calling - * {@link #clearParam(String)} for the given variable - * </p> - * - * @param var - * Variable - * @param iri - * IRI - */ - public void setIri(String var, String iri) { - this.setParam(var, NodeFactory.createURI(iri)); - } - - /** - * Sets a positional parameter to an IRI - * <p> - * Setting a parameter to null is equivalent to calling - * {@link #clearParam(int)} for the given index - * </p> - * - * @param index - * Positional Index - * @param iri - * IRI - */ - public void setIri(int index, IRI iri) { - this.setIri(index, iri.toString()); - } - - /** - * Sets a variable parameter to an IRI - * <p> - * Setting a parameter to null is equivalent to calling - * {@link #clearParam(String)} for the given variable - * </p> - * - * @param var - * Variable - * @param iri - * IRI - */ - public void setIri(String var, IRI iri) { - this.setIri(var, iri.toString()); - } - - /** - * Sets a positional parameter to an IRI - * <p> - * Setting a parameter to null is equivalent to calling - * {@link #clearParam(int)} for the given index - * </p> - * - * @param index - * Positional Index - * @param url - * URL - */ - public void setIri(int index, URL url) { - this.setIri(index, url.toString()); - } - - /** - * Sets a variable parameter to an IRI - * <p> - * Setting a parameter to null is equivalent to calling - * {@link #clearParam(String)} for the given variable - * </p> - * - * @param var - * Variable - * @param url - * URL used as IRI - * - */ - public void setIri(String var, URL url) { - this.setIri(var, url.toString()); - } - - /** - * Sets a positional parameter to a Literal - * <p> - * Setting a parameter to null is equivalent to calling - * {@link #clearParam(int)} for the given index - * </p> - * - * @param index - * Positional Index - * @param lit - * Value - * - */ - public void setLiteral(int index, Literal lit) { - this.setParam(index, lit.asNode()); - } - - /** - * Sets a variable parameter to a Literal - * <p> - * Setting a parameter to null is equivalent to calling - * {@link #clearParam(String)} for the given variable - * </p> - * - * @param var - * Variable - * @param lit - * Value - * - */ - public void setLiteral(String var, Literal lit) { - this.setParam(var, lit.asNode()); - } - - /** - * Sets a positional parameter to a literal - * <p> - * Setting a parameter to null is equivalent to calling - * {@link #clearParam(int)} for the given index - * </p> - * - * @param index - * Positional Index - * @param value - * Lexical Value - * - */ - public void setLiteral(int index, String value) { - this.setParam(index, NodeFactoryExtra.createLiteralNode(value, null, null)); - } - - /** - * Sets a variable parameter to a literal - * <p> - * Setting a parameter to null is equivalent to calling - * {@link #clearParam(String)} for the given variable - * </p> - * - * @param var - * Variable - * @param value - * Lexical Value - * - */ - public void setLiteral(String var, String value) { - this.setParam(var, NodeFactoryExtra.createLiteralNode(value, null, null)); - } - - /** - * Sets a positional parameter to a literal with a language - * <p> - * Setting a parameter to null is equivalent to calling - * {@link #clearParam(int)} for the given index - * </p> - * - * @param index - * Positional index - * @param value - * Lexical Value - * @param lang - * Language - * - */ - public void setLiteral(int index, String value, String lang) { - this.setParam(index, NodeFactoryExtra.createLiteralNode(value, lang, null)); - } - - /** - * Sets a variable parameter to a literal with a language - * <p> - * Setting a parameter to null is equivalent to calling - * {@link #clearParam(String)} for the given variable - * </p> - * - * @param var - * Variable - * @param value - * Lexical Value - * @param lang - * Language - * - */ - public void setLiteral(String var, String value, String lang) { - this.setParam(var, NodeFactoryExtra.createLiteralNode(value, lang, null)); - } - - /** - * Sets a positional arameter to a typed literal - * <p> - * Setting a parameter to null is equivalent to calling - * {@link #clearParam(int)} for the given index - * </p> - * - * @param index - * Positional Index - * @param value - * Lexical Value - * @param datatype - * Datatype - * - */ - public void setLiteral(int index, String value, RDFDatatype datatype) { - this.setParam(index, this.model.createTypedLiteral(value, datatype)); - } - - /** - * Sets a variable parameter to a typed literal - * <p> - * Setting a parameter to null is equivalent to calling - * {@link #clearParam(String)} for the given variable - * </p> - * - * @param var - * Variable - * @param value - * Lexical Value - * @param datatype - * Datatype - * - */ - public void setLiteral(String var, String value, RDFDatatype datatype) { - this.setParam(var, this.model.createTypedLiteral(value, datatype)); - } - - /** - * Sets a positional parameter to a boolean literal - * - * @param index - * Positional Index - * @param value - * boolean - */ - public void setLiteral(int index, boolean value) { - this.setParam(index, this.model.createTypedLiteral(value)); - } - - /** - * Sets a variable parameter to a boolean literal - * - * @param var - * Variable - * @param value - * boolean - */ - public void setLiteral(String var, boolean value) { - this.setParam(var, this.model.createTypedLiteral(value)); - } - - /** - * Sets a positional parameter to an integer literal - * - * @param index - * Positional Index - * @param i - * Integer Value - */ - public void setLiteral(int index, int i) { - this.setParam(index, NodeFactoryExtra.intToNode(i)); - } - - /** - * Sets a variable parameter to an integer literal - * - * @param var - * Variable - * @param i - * Integer Value - */ - public void setLiteral(String var, int i) { - this.setParam(var, NodeFactoryExtra.intToNode(i)); - } - - /** - * Sets a positional parameter to an integer literal - * - * @param index - * Positional Index - * @param l - * Integer Value - */ - public void setLiteral(int index, long l) { - this.setParam(index, NodeFactoryExtra.intToNode(l)); - } - - /** - * Sets a variable parameter to an integer literal - * - * @param var - * Variable - * @param l - * Integer Value - */ - public void setLiteral(String var, long l) { - this.setParam(var, NodeFactoryExtra.intToNode(l)); - } - - /** - * Sets a positional parameter to a float literal - * - * @param index - * Positional Index - * @param f - * Float value - */ - public void setLiteral(int index, float f) { - this.setParam(index, NodeFactoryExtra.floatToNode(f)); - } - - /** - * Sets a variable parameter to a float literal - * - * @param var - * Variable - * @param f - * Float value - */ - public void setLiteral(String var, float f) { - this.setParam(var, NodeFactoryExtra.floatToNode(f)); - } - - /** - * Sets a positional parameter to a double literal - * - * @param index - * Positional Index - * @param d - * Double value - */ - public void setLiteral(int index, double d) { - this.setParam(index, this.model.createTypedLiteral(d)); - } - - /** - * Sets a variable parameter to a double literal - * - * @param var - * Variable - * @param d - * Double value - */ - public void setLiteral(String var, double d) { - this.setParam(var, this.model.createTypedLiteral(d)); - } - - /** - * Sets a positional parameter to a date time literal - * - * @param index - * Positional Index - * @param dt - * Date Time value - */ - public void setLiteral(int index, Calendar dt) { - this.setParam(index, this.model.createTypedLiteral(dt)); - } - - /** - * Sets a variable parameter to a date time literal - * - * @param var - * Variable - * @param dt - * Date Time value - */ - public void setLiteral(String var, Calendar dt) { - this.setParam(var, this.model.createTypedLiteral(dt)); - } - - /** - * Gets the current value for a variable parameter - * - * @param var - * Variable - * @return Current value or null if not set - */ - public Node getParam(String var) { - return this.params.get(var); - } - - /** - * Gets the current value for a positional parameter - * - * @param index - * Positional Index - * @return Current value or null if not set - */ - public Node getParam(int index) { - return this.positionalParams.get(index); - } - - /** - * Gets the variable names which are currently treated as variable - * parameters (i.e. have values set for them) - * - * @return Iterator of variable names - */ - @Deprecated - public Iterator<String> getVars() { - return this.params.keySet().iterator(); - } - - /** - * Gets the map of currently set variable parameters, this will be an - * unmodifiable map - * - * @return Map of variable names and values - */ - public Map<String, Node> getVariableParameters() { - return Collections.unmodifiableMap(this.params); - } - - /** - * Gets the map of currently set positional parameters, this will be an - * unmodifiable map - * - * @return Map of positional indexes and values - */ - public Map<Integer, Node> getPositionalParameters() { - return Collections.unmodifiableMap(this.positionalParams); - } - - // TODO: Detecting eligible variable parameters - // public Iterator<String> getEligibleVariableParameters() { - // - // } - - /** - * Gets the eligible positional parameters i.e. detected positional - * parameters that may be set in the command string as it currently stands - * - * @return Iterator of eligible positional parameters - */ - public Iterator<Integer> getEligiblePositionalParameters() { - Pattern p = Pattern.compile("(\\?)[\\s;,.]"); - List<Integer> positions = new ArrayList<>(); - int index = 0; - Matcher matcher = p.matcher(this.cmd.toString()); - while (matcher.find()) { - positions.add(index); - index++; - } - return positions.iterator(); - } - - /** - * Clears the value for a variable parameter so the given variable will not - * have a value injected - * - * @param var - * Variable - */ - public void clearParam(String var) { - this.params.remove(var); - } - - /** - * Clears the value for a positional parameter - * - * @param index - * Positional Index - */ - public void clearParam(int index) { - this.positionalParams.remove(index); - } - - /** - * Clears all values for both variable and positional parameters - */ - public void clearParams() { - this.params.clear(); - this.positionalParams.clear(); - } - - /** - * Helper method which checks whether it is safe to inject to a variable - * parameter the given value - * - * @param command - * Current command string - * @param var - * Variable - * @param n - * Value to inject - * @throws ARQException - * Thrown if not safe to inject, error message will describe why - * it is unsafe to inject - */ - protected void validateSafeToInject(String command, String var, Node n) throws ARQException { - // Looks for the known injection attack vectors and throws an error if - // any are encountered - - // A ?var surrounded by " or ' where the variable is a literal is an - // attack vector - Pattern p = Pattern.compile("\"[?$]" + var + "\"|'[?$]" + var + "'"); - - if (p.matcher(command).find() && n.isLiteral()) { - throw new ARQException( - "Command string is vunerable to injection attack, variable ?" - + var - + " appears surrounded directly by quotes and is bound to a literal which provides a SPARQL injection attack vector"); - } - - // Parse out delimiter info - DelimiterInfo delims = this.findDelimiters(command); - - // Check each occurrence of the variable for safety - p = Pattern.compile("([?$]" + var + ")([^\\w]|$)"); - Matcher matcher = p.matcher(command); - while (matcher.find()) { - MatchResult posMatch = matcher.toMatchResult(); - - if (n.isLiteral()) { - if (delims.isInsideLiteral(posMatch.start(1), posMatch.end(1))) { - throw new ARQException( - "Command string is vunerable to injection attack, variable ?" - + var - + " appears inside of a literal and is bound to a literal which provides a SPARQL injection attack vector"); - } - } - } - } - - /** - * Helper method which checks whether it is safe to inject to a positional - * parameter the given value - * - * @param command - * Current command string - * @param index - * Positional parameter index - * @param position - * Position within the command string at which the positional - * parameter occurs - * @param n - * Value to inject - * @throws ARQException - * Thrown if not safe to inject, error message will describe why - * it is unsafe to inject - */ - protected void validateSafeToInject(String command, int index, int position, Node n) throws ARQException { - // Parse out delimiter info - DelimiterInfo delims = this.findDelimiters(command); - - // Check each occurrence of the variable for safety - if (n.isLiteral()) { - if (delims.isInsideLiteral(position, position)) { - throw new ARQException( - "Command string is vunerable to injection attack, a positional paramter (index " - + index - + ") appears inside of a literal and is bound to a literal which provides a SPARQL injection attack vector"); - } - } - } - - /** - * Helper method which does light parsing on the command string to find the - * position of all relevant delimiters - * - * @param command - * Command String - * @return DelimiterInfo - */ - protected final DelimiterInfo findDelimiters(String command) { - DelimiterInfo delims = new DelimiterInfo(); - delims.parseFrom(command); - return delims; - } - - protected final String stringForNode(Node n, SerializationContext context) { - String str = FmtUtils.stringForNode(n, context); - if (n.isLiteral() && str.contains("'")) { - // Should escape ' to avoid a possible injection vulnerability - str = str.replace("'", "\\'"); - } - return str; - } - - /** - * <p> - * This method is where the actual work happens, the original command text - * is always preserved and we just generated a temporary command string by - * prepending the defined Base URI and namespace prefixes at the start of - * the command and injecting the set parameters into a copy of that base - * command string and return the resulting command. - * </p> - * <p> - * This class makes no guarantees about the validity of the returned string - * for use as a SPARQL Query or Update, for example if a variable parameter - * was injected which was mentioned in the SELECT variables list you'd have - * a syntax error when you try to parse the query. If you run into issues - * like this try using a mixture of variable and positional parameters. - * </p> - * - * @throws ARQException - * May be thrown if the code detects a SPARQL Injection - * vulnerability because of the interaction of the command - * string and the injected variables - */ - @Override - public String toString() { - String command = this.cmd.toString(); - Pattern p; - - // Go ahead and inject Variable Parameters - SerializationContext context = new SerializationContext(this.prefixes); - context.setBaseIRI(this.baseUri); - for (String var : this.params.keySet()) { - Node n = this.params.get(var); - if (n == null) { - continue; - } - this.validateSafeToInject(command, var, n); - - p = Pattern.compile("([?$]" + var + ")([^\\w]|$)"); - command = p.matcher(command).replaceAll(Matcher.quoteReplacement(this.stringForNode(n, context)) + "$2"); - } - - // Then inject Positional Parameters - // To do this we need to find the ? we will replace - p = Pattern.compile("(\\?)[\\s;,.]"); - int index = -1; - int adj = 0; - Matcher matcher = p.matcher(command); - while (matcher.find()) { - index++; - MatchResult posMatch = matcher.toMatchResult(); - - Node n = this.positionalParams.get(index); - if (n == null) - continue; - this.validateSafeToInject(command, index, posMatch.start(1) + adj, n); - - String nodeStr = this.stringForNode(n, context); - command = command.substring(0, posMatch.start() + adj) + nodeStr - + command.substring(posMatch.start() + adj + 1); - // Because we are using a matcher over the string state prior to - // starting replacements we need to - // track the offset adjustments to make - adj += nodeStr.length() - 1; - } - - // Build the final command string - StringBuilder finalCmd = new StringBuilder(); - - // Add BASE declaration - if (this.baseUri != null) { - finalCmd.append("BASE "); - finalCmd.append(FmtUtils.stringForURI(this.baseUri, null, null)); - finalCmd.append('\n'); - } - - // Then pre-pend prefixes - - for (String prefix : this.prefixes.getNsPrefixMap().keySet()) { - finalCmd.append("PREFIX "); - finalCmd.append(prefix); - finalCmd.append(": "); - finalCmd.append(FmtUtils.stringForURI(this.prefixes.getNsPrefixURI(prefix), null, null)); - finalCmd.append('\n'); - } - - finalCmd.append(command); - return finalCmd.toString(); - } - - /** - * Attempts to take the command text with parameters injected from the - * {@link #toString()} method and parse it as a {@link Query} - * - * @return Query if the command text is a valid SPARQL query - * @exception QueryException - * Thrown if the command text does not parse - */ - public Query asQuery() throws QueryException { - return QueryFactory.create(this.toString()); - } - - /** - * Attempts to take the command text with parameters injected from the - * {@link #toString()} method and parse it as a {@link UpdateRequest} - * - * @return Update if the command text is a valid SPARQL Update request - * (one/more update commands) - */ - public UpdateRequest asUpdate() { - return UpdateFactory.create(this.toString()); - } - - /** - * Makes a full copy of this parameterized string - * - * @return Copy of the string - */ - public ParameterizedSparqlString copy() { - return this.copy(true, true, true); - } - - /** - * Makes a copy of the command text, base URI and prefix mapping and - * optionally copies parameter values - * - * @param copyParams - * Whether to copy parameters - * @return Copy of the string - */ - public ParameterizedSparqlString copy(boolean copyParams) { - return this.copy(copyParams, true, true); - } - - /** - * Makes a copy of the command text and optionally copies other aspects - * - * @param copyParams - * Whether to copy parameters - * @param copyBase - * Whether to copy the Base URI - * @param copyPrefixes - * Whether to copy the prefix mappings - * @return Copy of the string - */ - public ParameterizedSparqlString copy(boolean copyParams, boolean copyBase, boolean copyPrefixes) { - ParameterizedSparqlString copy = new ParameterizedSparqlString(this.cmd.toString(), null, - (copyBase ? this.baseUri : null), (copyPrefixes ? this.prefixes : null)); - if (copyParams) { - Iterator<String> vars = this.getVars(); - while (vars.hasNext()) { - String var = vars.next(); - copy.setParam(var, this.getParam(var)); - } - for (Entry<Integer, Node> entry : this.positionalParams.entrySet()) { - copy.setParam(entry.getKey(), entry.getValue()); - } - } - return copy; - } - - @Override - public PrefixMapping setNsPrefix(String prefix, String uri) { - return this.prefixes.setNsPrefix(prefix, uri); - } - - @Override - public PrefixMapping removeNsPrefix(String prefix) { - return this.prefixes.removeNsPrefix(prefix); - } - - @Override - public PrefixMapping setNsPrefixes(PrefixMapping other) { - return this.prefixes.setNsPrefixes(other); - } - - @Override - public PrefixMapping setNsPrefixes(Map<String, String> map) { - return this.prefixes.setNsPrefixes(map); - } - - @Override - public PrefixMapping withDefaultMappings(PrefixMapping map) { - return this.prefixes.withDefaultMappings(map); - } - - @Override - public String getNsPrefixURI(String prefix) { - return this.prefixes.getNsPrefixURI(prefix); - } - - @Override - public String getNsURIPrefix(String uri) { - return this.prefixes.getNsURIPrefix(uri); - } - - @Override - public Map<String, String> getNsPrefixMap() { - return this.prefixes.getNsPrefixMap(); - } - - @Override - public String expandPrefix(String prefixed) { - return this.prefixes.expandPrefix(prefixed); - } - - @Override - public String shortForm(String uri) { - return this.prefixes.shortForm(uri); - } - - @Override - public String qnameFor(String uri) { - return this.prefixes.qnameFor(uri); - } - - @Override - public PrefixMapping lock() { - return this.prefixes.lock(); - } - - @Override - public boolean samePrefixMappingAs(PrefixMapping other) { - return this.prefixes.samePrefixMappingAs(other); - } - - /** - * Represents information about delimiters in a string - * - */ - private class DelimiterInfo { - private List<Pair<Integer, String>> starts = new ArrayList<>(); - private Map<Integer, Integer> stops = new HashMap<>(); - - /** - * Parse delimiters from a string, discards any previously parsed - * information - * - * @param command - * Command string - */ - public void parseFrom(String command) { - this.starts.clear(); - this.stops.clear(); - - char[] cs = command.toCharArray(); - for (int i = 0; i < cs.length; i++) { - switch (cs[i]) { - case '"': - // Start of a Literal - // Is it a long literal? - if (i < cs.length - 2 && cs[i + 1] == '"' && cs[i + 2] == '"') { - this.addStart(i, "\"\"\""); - for (int j = i + 3; j < cs.length - 2; j++) { - if (cs[j] == '"' && cs[j + 1] == '"' && cs[j + 2] == '"') { - this.addStop(i, j + 2); - i = j + 2; - } - } - // Was unterminated - } else { - // Normal literal, scan till we see a " which is not - // preceded by a \ - this.addStart(i, "\""); - for (int j = i + 1; j < cs.length; j++) { - if (cs[j] == '"' && cs[j - 1] != '\\') { - this.addStop(i, j); - i = j; - continue; - } - } - // Was unterminated - } - break; - case '<': - // Start of a URI - this.addStart(i, "<"); - for (int j = i + 1; j < cs.length; j++) { - if (cs[j] == '>' && cs[j - 1] != '\\') { - this.addStop(i, j); - i = j; - continue; - } - } - // Was unterminated - break; - case '\'': - // Start of alternative literal form - // Start of a Literal - // Is it a long literal? - if (i < cs.length - 2 && cs[i + 1] == '\'' && cs[i + 2] == '\'') { - this.addStart(i, "'''"); - for (int j = i + 3; j < cs.length - 2; j++) { - if (cs[j] == '\'' && cs[j + 1] == '\'' && cs[j + 2] == '\'') { - this.addStop(i, j + 2); - i = j + 2; - } - } - // Was unterminated - } else { - // Normal literal, scan till we see a ' which is not - // preceded by a \ - this.addStart(i, "'"); - for (int j = i + 1; j < cs.length; j++) { - if (cs[j] == '\'' && cs[j - 1] != '\\') { - this.addStop(i, j); - i = j; - continue; - } - } - // Was unterminated - } - break; - case '#': - // Start of a comment - // Scan to next newline - this.addStart(i, "#"); - for (int j = i + 1; j < cs.length; j++) { - if (cs[j] == '\n' || cs[j] == '\r') { - this.addStop(i, j); - i = j; - continue; - } - } - this.addStop(i, cs.length - 1); - break; - case '\n': - case '\r': - case '.': - case ',': - case ';': - case '(': - case ')': - case '{': - case '}': - case '[': - case ']': - // Treat various punctuation as delimiters - this.addStart(i, new String(new char[] { cs[i] })); - this.addStop(i, i); - break; - } - } - } - - public void addStart(int index, String delim) { - this.starts.add(new Pair<>(index, delim)); - } - - public void addStop(int start, int stop) { - this.stops.put(start, stop); - } - - public Pair<Integer, String> findBefore(int index) { - Pair<Integer, String> found = null; - for (Pair<Integer, String> pair : this.starts) { - if (pair.getLeft() < index) - found = pair; - if (pair.getLeft() >= index) - break; - } - return found; - } - - public Pair<Integer, String> findAfter(int index) { - for (Pair<Integer, String> pair : this.starts) { - if (pair.getLeft() > index) - return pair; - } - return null; - } - - public boolean isInsideLiteral(int start, int stop) { - Pair<Integer, String> pair = this.findBefore(start); - if (pair == null) - return false; - if (pair.getRight().equals("\"")) { - Integer nearestStop = this.stops.get(pair.getLeft()); - if (nearestStop == null) - return true; // Inside unterminated literal - return (nearestStop > stop); // May be inside a literal - } else { - // Not inside a literal - return false; - } - } - - public boolean isInsideAltLiteral(int start, int stop) { - Pair<Integer, String> pair = this.findBefore(start); - if (pair == null) - return false; - if (pair.getRight().equals("'")) { - Integer nearestStop = this.stops.get(pair.getLeft()); - if (nearestStop == null) - return true; // Inside unterminated literal - return (nearestStop > stop); // May be inside a literal - } else { - // Not inside a literal - return false; - } - } - - public boolean isBetweenLiterals(int start, int stop) { - Pair<Integer, String> pairBefore = this.findBefore(start); - if (pairBefore == null) - return false; - if (pairBefore.getRight().equals("\"")) { - Integer stopBefore = this.stops.get(pairBefore.getLeft()); - if (stopBefore == null) - return false; // Inside unterminated literal - - // We occur after a literal, is there a subsequent literal? - Pair<Integer, String> pairAfter = this.findAfter(stop); - return pairAfter != null && pairAfter.getRight().equals("\""); - } else { - // Previous deliminator is not that of a literal - return false; - } - } - - } -} +/** + * 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.query; + +import java.net.URL; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.regex.MatchResult; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.apache.jena.atlas.lib.Pair; +import org.apache.jena.datatypes.RDFDatatype ; +import org.apache.jena.graph.Node ; +import org.apache.jena.graph.NodeFactory ; +import org.apache.jena.iri.IRI; +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.shared.PrefixMapping ; +import org.apache.jena.shared.impl.PrefixMappingImpl ; +import org.apache.jena.sparql.ARQException ; +import org.apache.jena.sparql.serializer.SerializationContext ; +import org.apache.jena.sparql.util.FmtUtils ; +import org.apache.jena.sparql.util.NodeFactoryExtra ; +import org.apache.jena.update.UpdateFactory ; +import org.apache.jena.update.UpdateRequest ; + +/** + * <p> + * A Parameterized SPARQL String is a SPARQL query/update into which values may + * be injected. + * </p> + * <h3>Injecting Values</h3> + * <p> + * Values may be injected in several ways: + * </p> + * <ul> + * <li>By treating a variable in the SPARQL string as a parameter</li> + * <li>Using JDBC style positional parameters</li> + * <li>Appending values directly to the command text being built</li> + * </ul> + * <h4>Variable Parameters</h3> + * <p> + * Any variable in the command may have a value injected to it, injecting a + * value replaces all usages of that variable in the command i.e. substitutes + * the variable for a constant, injection is done by textual substitution. + * </p> <h4>Positional Parameters</h4> + * <p> + * You can use JDBC style positional parameters if you prefer, a JDBC style + * parameter is a single {@code ?} followed by whitespace or certain punctuation + * characters (currently {@code ; , .}). Positional parameters have a unique + * index which reflects the order in which they appear in the string. Positional + * parameters use a zero based index. + * </p> + * <h4>Buffer Usage</h3> </p> Additionally you may use this purely as a + * {@link StringBuffer} replacement for creating queries since it provides a + * large variety of convenience methods for appending things either as-is or as + * nodes (which causes appropriate formatting to be applied). </p> + * <h3>Intended Usage</h3> + * <p> + * The intended usage of this is where using a {@link QuerySolutionMap} as + * initial bindings is either inappropriate or not possible e.g. + * </p> + * <ul> + * <li>Generating query/update strings in code without lots of error prone and + * messy string concatenation</li> + * <li>Preparing a query/update for remote execution</li> + * <li>Where you do not want to simply say some variable should have a certain + * value but rather wish to insert constants into the query/update in place of + * variables</li> + * <li>Defending against SPARQL injection when creating a query/update using + * some external input, see SPARQL Injection notes for limitations.</li> + * <li>Provide a more convenient way to prepend common prefixes to your query</li> + * </ul> + * <p> + * This class is useful for preparing both queries and updates hence the generic + * name as it provides programmatic ways to replace variables in the query with + * constants and to add prefix and base declarations. A {@link Query} or + * {@link UpdateRequest} can be created using the {@link #asQuery()} and + * {@link #asUpdate()} methods assuming the command an instance represents is + * actually valid as a query/update. + * </p> + * <h3>Warnings</h3> + * <ol> + * <li>Note that this class does not in any way check that your command is + * syntactically correct until such time as you try and parse it as a + * {@link Query} or {@link UpdateRequest}.</li> + * <li>Also note that injection is done purely based on textual replacement, it + * does not understand or respect variable scope in any way. For example if your + * command text contains sub queries you should ensure that variables within the + * sub query which you don't want replaced have distinct names from those in the + * outer query you do want replaced (or vice versa)</li> + * </ol> + * <h3>SPARQL Injection Notes</h3> + * <p> + * While this class was in part designed to prevent SPARQL injection it is by no + * means foolproof because it works purely at the textual level. The current + * version of the code addresses some possible attack vectors that the + * developers have identified but we do not claim to be sufficiently devious to + * have thought of and prevented every possible attack vector. + * </p> + * <p> + * Therefore we <strong>strongly</strong> recommend that users concerned about + * SPARQL Injection attacks perform their own validation on provided parameters + * and test their use of this class themselves prior to its use in any security + * conscious deployment. We also recommend that users do not use easily + * guess-able variable names for their parameters as these can allow a chained + * injection attack though generally speaking the code should prevent these. + * </p> + */ +public class ParameterizedSparqlString implements PrefixMapping { + + private Model model = ModelFactory.createDefaultModel(); + + private StringBuilder cmd = new StringBuilder(); + private String baseUri; + private Map<String, Node> params = new HashMap<>(); + private Map<Integer, Node> positionalParams = new HashMap<>(); + private PrefixMapping prefixes; + + /** + * Creates a new parameterized string + * + * @param command + * Raw Command Text + * @param map + * Initial Parameters to inject + * @param base + * Base URI + * @param prefixes + * Prefix Mapping + */ + public ParameterizedSparqlString(String command, QuerySolutionMap map, String base, PrefixMapping prefixes) { + if (command != null) + this.cmd.append(command); + this.setParams(map); + this.baseUri = (base != null && !base.equals("") ? base : null); + this.prefixes = new PrefixMappingImpl(); + if (prefixes != null) + this.prefixes.setNsPrefixes(prefixes); + } + + /** + * Creates a new parameterized string + * + * @param command + * Raw Command Text + * @param map + * Initial Parameters to inject + * @param base + * Base URI + */ + public ParameterizedSparqlString(String command, QuerySolutionMap map, String base) { + this(command, map, base, null); + } + + /** + * Creates a new parameterized string + * + * @param command + * Raw Command Text + * @param map + * Initial Parameters to inject + * @param prefixes + * Prefix Mapping + */ + public ParameterizedSparqlString(String command, QuerySolutionMap map, PrefixMapping prefixes) { + this(command, map, null, prefixes); + } + + /** + * Creates a new parameterized string + * + * @param command + * Raw Command Text + * @param map + * Initial Parameters to inject + */ + public ParameterizedSparqlString(String command, QuerySolutionMap map) { + this(command, map, null, null); + } + + /** + * Creates a new parameterized string + * + * @param command + * Raw Command Text + * @param base + * Base URI + * @param prefixes + * Prefix Mapping + */ + public ParameterizedSparqlString(String command, String base, PrefixMapping prefixes) { + this(command, null, base, prefixes); + } + + /** + * Creates a new parameterized string + * + * @param command + * Raw Command Text + * @param prefixes + * Prefix Mapping + */ + public ParameterizedSparqlString(String command, PrefixMapping prefixes) { + this(command, null, null, prefixes); + } + + /** + * Creates a new parameterized string + * + * @param command + * Raw Command Text + * @param base + * Base URI + */ + public ParameterizedSparqlString(String command, String base) { + this(command, null, base, null); + } + + /** + * Creates a new parameterized string + * + * @param command + * Raw Command Text + */ + public ParameterizedSparqlString(String command) { + this(command, null, null, null); + } + + /** + * Creates a new parameterized string + * + * @param map + * Initial Parameters to inject + * @param prefixes + * Prefix Mapping + */ + public ParameterizedSparqlString(QuerySolutionMap map, PrefixMapping prefixes) { + this(null, map, null, prefixes); + } + + /** + * Creates a new parameterized string + * + * @param map + * Initial Parameters to inject + */ + public ParameterizedSparqlString(QuerySolutionMap map) { + this(null, map, null, null); + } + + /** + * Creates a new parameterized string + * + * @param prefixes + * Prefix Mapping + */ + public ParameterizedSparqlString(PrefixMapping prefixes) { + this(null, null, null, prefixes); + } + + /** + * Creates a new parameterized string with an empty command text + */ + public ParameterizedSparqlString() { + this("", null, null, null); + } + + /** + * Sets the command text, overwriting any existing command text. If you want + * to append to the command text use one of the {@link #append(String)}, + * {@link #appendIri(String)}, {@link #appendLiteral(String)} or + * {@link #appendNode(Node)} methods instead + * + * @param command + * Command Text + */ + public void setCommandText(String command) { + this.cmd = new StringBuilder(); + this.cmd.append(command); + } + + /** + * Appends some text as-is to the existing command text, to ensure correct + * formatting when used as a constant consider using the + * {@link #appendLiteral(String)} or {@link #appendIri(String)} method as + * appropriate + * + * @param text + * Text to append + */ + public void append(String text) { + this.cmd.append(text); + } + + /** + * Appends a character as-is to the existing command text, to ensure correct + * formatting when used as a constant consider using one of the + * {@code appendLiteral()} methods + * + * @param c + * Character to append + */ + public void append(char c) { + this.cmd.append(c); + } + + /** + * Appends a boolean as-is to the existing command text, to ensure correct + * formatting when used as a constant consider using the + * {@link #appendLiteral(boolean)} method + * + * @param b + * Boolean to append + */ + public void append(boolean b) { + this.cmd.append(b); + } + + /** + * Appends a double as-is to the existing command text, to ensure correct + * formatting when used as a constant consider using the + * {@link #appendLiteral(double)} method + * + * @param d + * Double to append + */ + public void append(double d) { + this.cmd.append(d); + } + + /** + * Appends a float as-is to the existing command text, to ensure correct + * formatting when used as a constant consider using the + * {@link #appendLiteral(float)} method + * + * @param f + * Float to append + */ + public void append(float f) { + this.cmd.append(f); + } + + /** + * Appends an integer as-is to the existing command text, to ensure correct + * formatting when used as a constant consider using the + * {@link #appendLiteral(int)} method + * + * @param i + * Integer to append + */ + public void append(int i) { + this.cmd.append(i); + } + + /** + * Appends a long as-is to the existing command text, to ensure correct + * formatting when used as a constant consider using the + * {@link #appendLiteral(long)} method + * + * @param l + * Long to append + */ + public void append(long l) { + this.cmd.append(l); + } + + /** + * Appends an object as-is to the existing command text, to ensure correct + * formatting when used as a constant consider converting into a more + * specific type and using the appropriate {@code appendLiteral()}, + * {@code appendIri()} or {@code appendNode} methods + * + * @param obj + * Object to append + */ + public void append(Object obj) { + this.cmd.append(obj); + } + + /** + * Appends a Node to the command text as a constant using appropriate + * formatting + * + * @param n + * Node to append + */ + public void appendNode(Node n) { + SerializationContext context = new SerializationContext(this.prefixes); + context.setBaseIRI(this.baseUri); + this.cmd.append(this.stringForNode(n, context)); + } + + /** + * Appends a Node to the command text as a constant using appropriate + * formatting + * + * @param n + * Node to append + */ + public void appendNode(RDFNode n) { + this.appendNode(n.asNode()); + } + + /** + * Appends a URI to the command text as a constant using appropriate + * formatting + * + * @param uri + * URI to append + */ + public void appendIri(String uri) { + this.appendNode(NodeFactory.createURI(uri)); + } + + /** + * Appends an IRI to the command text as a constant using appropriate + * formatting + * + * @param iri + * IRI to append + */ + public void appendIri(IRI iri) { + this.appendNode(NodeFactory.createURI(iri.toString())); + } + + /** + * Appends a simple literal as a constant using appropriate formatting + * + * @param value + * Lexical Value + */ + public void appendLiteral(String value) { + this.appendNode(NodeFactoryExtra.createLiteralNode(value, null, null)); + } + + /** + * Appends a literal with a lexical value and language to the command text + * as a constant using appropriate formatting + * + * @param value + * Lexical Value + * @param lang + * Language + */ + public void appendLiteral(String value, String lang) { + this.appendNode(NodeFactoryExtra.createLiteralNode(value, lang, null)); + } + + /** + * Appends a Typed Literal to the command text as a constant using + * appropriate formatting + * + * @param value + * Lexical Value + * @param datatype + * Datatype + */ + public void appendLiteral(String value, RDFDatatype datatype) { + this.appendNode(NodeFactoryExtra.createLiteralNode(value, null, datatype.getURI())); + } + + /** + * Appends a boolean to the command text as a constant using appropriate + * formatting + * + * @param b + * Boolean to append + */ + public void appendLiteral(boolean b) { + this.appendNode(this.model.createTypedLiteral(b)); + } + + /** + * Appends an integer to the command text as a constant using appropriate + * formatting + * + * @param i + * Integer to append + */ + public void appendLiteral(int i) { + this.appendNode(NodeFactoryExtra.intToNode(i)); + } + + /** + * Appends a long to the command text as a constant using appropriate + * formatting + * + * @param l + * Long to append + */ + public void appendLiteral(long l) { + this.appendNode(NodeFactoryExtra.intToNode(l)); + } + + /** + * Appends a float to the command text as a constant using appropriate + * formatting + * + * @param f + * Float to append + */ + public void appendLiteral(float f) { + this.appendNode(this.model.createTypedLiteral(f)); + } + + /** + * Appends a double to the command text as a constant using appropriate + * formatting + * + * @param d + * Double to append + */ + public void appendLiteral(double d) { + this.appendNode(this.model.createTypedLiteral(d)); + } + + /** + * Appends a date time to the command text as a constant using appropriate + * formatting + * + * @param dt + * Date Time to append + */ + public void appendLiteral(Calendar dt) { + this.appendNode(this.model.createTypedLiteral(dt)); + } + + /** + * Gets the basic Command Text + * <p> + * <strong>Note:</strong> This will not reflect any injected parameters, to + * see the command with injected parameters invoke the {@link #toString()} + * method + * </p> + * + * @return Command Text + */ + public String getCommandText() { + return this.cmd.toString(); + } + + /** + * Sets the Base URI which will be prepended to the query/update + * + * @param base + * Base URI + */ + public void setBaseUri(String base) { + this.baseUri = base; + } + + /** + * Gets the Base URI which will be prepended to a query + * + * @return Base URI + */ + public String getBaseUri() { + return this.baseUri; + } + + /** + * Helper method which does the validation of the parameters + * + * @param n + * Node + */ + protected void validateParameterValue(Node n) { + if (n.isURI()) { + if (n.getURI().contains(">")) + throw new ARQException("Value for the parameter contains a SPARQL injection risk"); + } + } + + /** + * Sets the Parameters + * + * @param map + * Parameters + */ + public void setParams(QuerySolutionMap map) { + if (map != null) { + Iterator<String> iter = map.varNames(); + while (iter.hasNext()) { + String var = iter.next(); + this.setParam(var, map.get(var).asNode()); + } + } + } + + /** + * Sets a Positional Parameter + * <p> + * Setting a parameter to null is equivalent to calling + * {@link #clearParam(int)} for the given variable + * </p> + * + * @param index + * Positional Index + * @param n + * Node + */ + public void setParam(int index, Node n) { + if (index < 0) + throw new IndexOutOfBoundsException(); + if (n != null) { + this.validateParameterValue(n); + this.positionalParams.put(index, n); + } else { + this.positionalParams.remove(index); + } + } + + /** + * Sets a variable parameter + * <p> + * Setting a parameter to null is equivalent to calling + * {@link #clearParam(String)} for the given variable + * </p> + * + * @param var + * Variable + * @param n + * Value + * + */ + public void setParam(String var, Node n) { + if (var == null) + throw new IllegalArgumentException("var cannot be null"); + if (var.startsWith("?") || var.startsWith("$")) + var = var.substring(1); + if (n != null) { + this.validateParameterValue(n); + this.params.put(var, n); + } else { + this.params.remove(var); + } + } + + /** + * Sets a positional parameter + * <p> + * Setting a parameter to null is equivalent to calling + * {@link #clearParam(String)} for the given variable + * </p> + * + * @param index + * Positional Index + * @param n + * Node + */ + public void setParam(int index, RDFNode n) { + this.setParam(index, n.asNode()); + } + + /** + * Sets a variable parameter + * <p> + * Setting a parameter to null is equivalent to calling + * {@link #clearParam(String)} for the given variable + * </p> + * + * @param var + * Variable + * @param n + * Value + */ + public void setParam(String var, RDFNode n) { + this.setParam(var, n.asNode()); + } + + /** + * Sets a positional parameter to an IRI + * <p> + * Setting a parameter to null is equivalent to calling + * {@link #clearParam(int)} for the given index + * </p> + * + * @param index + * Positional Index + * @param iri + * IRI + */ + public void setIri(int index, String iri) { + this.setParam(index, NodeFactory.createURI(iri)); + } + + /** + * Sets a variable parameter to an IRI + * <p> + * Setting a parameter to null is equivalent to calling + * {@link #clearParam(String)} for the given variable + * </p> + * + * @param var + * Variable + * @param iri + * IRI + */ + public void setIri(String var, String iri) { + this.setParam(var, NodeFactory.createURI(iri)); + } + + /** + * Sets a positional parameter to an IRI + * <p> + * Setting a parameter to null is equivalent to calling + * {@link #clearParam(int)} for the given index + * </p> + * + * @param index + * Positional Index + * @param iri + * IRI + */ + public void setIri(int index, IRI iri) { + this.setIri(index, iri.toString()); + } + + /** + * Sets a variable parameter to an IRI + * <p> + * Setting a parameter to null is equivalent to calling + * {@link #clearParam(String)} for the given variable + * </p> + * + * @param var + * Variable + * @param iri + * IRI + */ + public void setIri(String var, IRI iri) { + this.setIri(var, iri.toString()); + } + + /** + * Sets a positional parameter to an IRI + * <p> + * Setting a parameter to null is equivalent to calling + * {@link #clearParam(int)} for the given index + * </p> + * + * @param index + * Positional Index + * @param url + * URL + */ + public void setIri(int index, URL url) { + this.setIri(index, url.toString()); + } + + /** + * Sets a variable parameter to an IRI + * <p> + * Setting a parameter to null is equivalent to calling + * {@link #clearParam(String)} for the given variable + * </p> + * + * @param var + * Variable + * @param url + * URL used as IRI + * + */ + public void setIri(String var, URL url) { + this.setIri(var, url.toString()); + } + + /** + * Sets a positional parameter to a Literal + * <p> + * Setting a parameter to null is equivalent to calling + * {@link #clearParam(int)} for the given index + * </p> + * + * @param index + * Positional Index + * @param lit + * Value + * + */ + public void setLiteral(int index, Literal lit) { + this.setParam(index, lit.asNode()); + } + + /** + * Sets a variable parameter to a Literal + * <p> + * Setting a parameter to null is equivalent to calling + * {@link #clearParam(String)} for the given variable + * </p> + * + * @param var + * Variable + * @param lit + * Value + * + */ + public void setLiteral(String var, Literal lit) { + this.setParam(var, lit.asNode()); + } + + /** + * Sets a positional parameter to a literal + * <p> + * Setting a parameter to null is equivalent to calling + * {@link #clearParam(int)} for the given index + * </p> + * + * @param index + * Positional Index + * @param value + * Lexical Value + * + */ + public void setLiteral(int index, String value) { + this.setParam(index, NodeFactoryExtra.createLiteralNode(value, null, null)); + } + + /** + * Sets a variable parameter to a literal + * <p> + * Setting a parameter to null is equivalent to calling + * {@link #clearParam(String)} for the given variable + * </p> + * + * @param var + * Variable + * @param value + * Lexical Value + * + */ + public void setLiteral(String var, String value) { + this.setParam(var, NodeFactoryExtra.createLiteralNode(value, null, null)); + } + + /** + * Sets a positional parameter to a literal with a language + * <p> + * Setting a parameter to null is equivalent to calling + * {@link #clearParam(int)} for the given index + * </p> + * + * @param index + * Positional index + * @param value + * Lexical Value + * @param lang + * Language + * + */ + public void setLiteral(int index, String value, String lang) { + this.setParam(index, NodeFactoryExtra.createLiteralNode(value, lang, null)); + } + + /** + * Sets a variable parameter to a literal with a language + * <p> + * Setting a parameter to null is equivalent to calling + * {@link #clearParam(String)} for the given variable + * </p> + * + * @param var + * Variable + * @param value + * Lexical Value + * @param lang + * Language + * + */ + public void setLiteral(String var, String value, String lang) { + this.setParam(var, NodeFactoryExtra.createLiteralNode(value, lang, null)); + } + + /** + * Sets a positional arameter to a typed literal + * <p> + * Setting a parameter to null is equivalent to calling + * {@link #clearParam(int)} for the given index + * </p> + * + * @param index + * Positional Index + * @param value + * Lexical Value + * @param datatype + * Datatype + * + */ + public void setLiteral(int index, String value, RDFDatatype datatype) { + this.setParam(index, this.model.createTypedLiteral(value, datatype)); + } + + /** + * Sets a variable parameter to a typed literal + * <p> + * Setting a parameter to null is equivalent to calling + * {@link #clearParam(String)} for the given variable + * </p> + * + * @param var + * Variable + * @param value + * Lexical Value + * @param datatype + * Datatype + * + */ + public void setLiteral(String var, String value, RDFDatatype datatype) { + this.setParam(var, this.model.createTypedLiteral(value, datatype)); + } + + /** + * Sets a positional parameter to a boolean literal + * + * @param index + * Positional Index + * @param value + * boolean + */ + public void setLiteral(int index, boolean value) { + this.setParam(index, this.model.createTypedLiteral(value)); + } + + /** + * Sets a variable parameter to a boolean literal + * + * @param var + * Variable + * @param value + * boolean + */ + public void setLiteral(String var, boolean value) { + this.setParam(var, this.model.createTypedLiteral(value)); + } + + /** + * Sets a positional parameter to an integer literal + * + * @param index + * Positional Index + * @param i + * Integer Value + */ + public void setLiteral(int index, int i) { + this.setParam(index, NodeFactoryExtra.intToNode(i)); + } + + /** + * Sets a variable parameter to an integer literal + * + * @param var + * Variable + * @param i + * Integer Value + */ + public void setLiteral(String var, int i) { + this.setParam(var, NodeFactoryExtra.intToNode(i)); + } + + /** + * Sets a positional parameter to an integer literal + * + * @param index + * Positional Index + * @param l + * Integer Value + */ + public void setLiteral(int index, long l) { + this.setParam(index, NodeFactoryExtra.intToNode(l)); + } + + /** + * Sets a variable parameter to an integer literal + * + * @param var + * Variable + * @param l + * Integer Value + */ + public void setLiteral(String var, long l) { + this.setParam(var, NodeFactoryExtra.intToNode(l)); + } + + /** + * Sets a positional parameter to a float literal + * + * @param index + * Positional Index + * @param f + * Float value + */ + public void setLiteral(int index, float f) { + this.setParam(index, NodeFactoryExtra.floatToNode(f)); + } + + /** + * Sets a variable parameter to a float literal + * + * @param var + * Variable + * @param f + * Float value + */ + public void setLiteral(String var, float f) { + this.setParam(var, NodeFactoryExtra.floatToNode(f)); + } + + /** + * Sets a positional parameter to a double literal + * + * @param index + * Positional Index + * @param d + * Double value + */ + public void setLiteral(int index, double d) { + this.setParam(index, this.model.createTypedLiteral(d)); + } + + /** + * Sets a variable parameter to a double literal + * + * @param var + * Variable + * @param d + * Double value + */ + public void setLiteral(String var, double d) { + this.setParam(var, this.model.createTypedLiteral(d)); + } + + /** + * Sets a positional parameter to a date time literal + * + * @param index + * Positional Index + * @param dt + * Date Time value + */ + public void setLiteral(int index, Calendar dt) { + this.setParam(index, this.model.createTypedLiteral(dt)); + } + + /** + * Sets a variable parameter to a date time literal + * + * @param var + * Variable + * @param dt + * Date Time value + */ + public void setLiteral(String var, Calendar dt) { + this.setParam(var, this.model.createTypedLiteral(dt)); + } + + /** + * Gets the current value for a variable parameter + * + * @param var + * Variable + * @return Current value or null if not set + */ + public Node getParam(String var) { + return this.params.get(var); + } + + /** + * Gets the current value for a positional parameter + * + * @param index + * Positional Index + * @return Current value or null if not set + */ + public Node getParam(int index) { + return this.positionalParams.get(index); + } + + /** + * Gets the variable names which are currently treated as variable + * parameters (i.e. have values set for them) + * + * @return Iterator of variable names + */ + @Deprecate
<TRUNCATED>
