From 27695b93b7454b3948cc623cff788a18942a66e2 Mon Sep 17 00:00:00 2001 From: Greg Albiston Date: Fri, 27 Jul 2018 15:05:52 +0100 Subject: [PATCH 01/18] ARQ:Query:ParameterizedSparqlString - Subclass ValueReplacement and methods added to allow substitution of varNames with collections of RDFNodes. This is used in SPARQL queries to provide inline data with the VALUES keyword. - Tests included for use cases. --- .../jena/query/ParameterizedSparqlString.java | 245 ++++++++++++++++++ .../query/TestParameterizedSparqlString.java | 134 ++++++++++ 2 files changed, 379 insertions(+) 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 0e3e1501586..fb92909b12f 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 @@ -20,7 +20,9 @@ import java.net.URL; import java.util.ArrayList; +import java.util.Arrays; import java.util.Calendar; +import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; @@ -141,6 +143,7 @@ public class ParameterizedSparqlString implements PrefixMapping { private Map params = new HashMap<>(); private Map positionalParams = new HashMap<>(); private PrefixMapping prefixes; + private Map valuesReplacements = new HashMap<>(); /** * Creates a new parameterized string @@ -1328,6 +1331,9 @@ public String toString() { command = p.matcher(command).replaceAll(Matcher.quoteReplacement(this.stringForNode(n, context)) + "$2"); } + // Inject Values Parameters + command = applyValues(command); + // Then inject Positional Parameters // To do this we need to find the ? we will replace p = Pattern.compile("(\\?)[\\s;,.]"); @@ -1734,4 +1740,243 @@ public String toString() { } } + + /** + * Assign a varName with a multiple items and whether to include + * parenthesis. + * + * @param varName + * @param items + * @param isParenthesisNeeded + */ + public void setValues(String varName, Collection items, boolean isParenthesisNeeded) { + this.valuesReplacements.put(varName, new ValueReplacement(varName, items, isParenthesisNeeded)); + } + + /** + * Assign a varName with a multiple items.
+ * Can be used to assign multiple values to a single variable or single + * value to multiple variables (if using a List) in the SPARQL query.
+ * See setGroupedValues to assign multiple values to multiple variables. + * + * @param varName + * @param items + */ + public void setValues(String varName, Collection items) { + setValues(varName, items, false); + } + + /** + * Assign a varName with a single item and whether to include parenthesis. + * + * @param varName + * @param item + * @param isParenthesisNeeded + */ + public void setValues(String varName, RDFNode item, boolean isParenthesisNeeded) { + setValues(varName, Arrays.asList(item), isParenthesisNeeded); + } + + /** + * Assign a varName with a single item. + * + * @param varName + * @param item + */ + public void setValues(String varName, RDFNode item) { + setValues(varName, Arrays.asList(item), false); + } + + /** + * Sets a map of varNames and their items. + * + * @param valuesItems + */ + public void setValues(Map> valuesItems) { + for (String varName : valuesItems.keySet()) { + Collection items = valuesItems.get(varName); + setValues(varName, items); + } + } + + /** + * All varNames in the map will use the same approach to parenthesis. + * + * @param valuesItems + * @param isParenthesisNeeded + */ + public void setValues(Map> valuesItems, Boolean isParenthesisNeeded) { + for (String varName : valuesItems.keySet()) { + Collection items = valuesItems.get(varName); + setValues(varName, items, isParenthesisNeeded); + } + } + + /** + * Combine a map of varNames and items with whether to include parenthesis. + * Missing varNames in the parenthesis map will default to false. + * + * @param valuesItems + * @param valuesParenthesis + */ + public void setValues(Map> valuesItems, Map valuesParenthesis) { + + for (String varName : valuesItems.keySet()) { + Collection items = valuesItems.get(varName); + Boolean isParenthesisNeeded; + if (valuesParenthesis.containsKey(varName)) { + isParenthesisNeeded = valuesParenthesis.get(varName); + } else { + isParenthesisNeeded = false; + } + + setValues(varName, items, isParenthesisNeeded); + } + } + + /** + * Allocate multiple lists of variables to a single varName.
+ * Using "vars" with list(list(prop_A, obj_A), list(prop_B, obj_B)) on query + * "VALUES (?p ?o) {?vars}" would produce "VALUES (?p ?o) {(prop_A obj_A) + * (prop_B obj_B)}". + * + * @param varName + * @param items + */ + public void setGroupedValues(String varName, Collection> items) { + this.valuesReplacements.put(varName, new ValueReplacement(varName, items)); + } + + private String applyValues(String command) { + + for (ValueReplacement valueReplacement : valuesReplacements.values()) { + command = valueReplacement.apply(command); + } + return command; + } + + /** + * Performs replacement of VALUES in query string. + * + */ + private class ValueReplacement { + + private final String varName; + private final Collection items; + private final Collection> groupedItems; + private final Boolean isParenthesisNeeded; + private final Boolean isGrouped; + + public ValueReplacement(String varName, Collection items, Boolean isParenthesisNeeded) { + this.varName = varName; + this.items = items; + this.groupedItems = new ArrayList<>(); + this.isParenthesisNeeded = isParenthesisNeeded; + this.isGrouped = false; + } + + public ValueReplacement(String varName, Collection> groupedItems) { + this.varName = varName; + this.items = new ArrayList<>(); + this.groupedItems = groupedItems; + this.isParenthesisNeeded = true; + this.isGrouped = true; + } + + public String apply(String command) { + + if (items.isEmpty() && groupedItems.isEmpty()) { + return command; + } + + String target = createTarget(varName); + + StringBuilder replacement; + + if (isGrouped) { + replacement = groupedApply(); + } else { + replacement = ungroupedApply(); + } + + return command.replace(target, replacement); + } + + private StringBuilder groupedApply() { + StringBuilder replacement = new StringBuilder(""); + + for (List group : groupedItems) { + replacement.append("("); + + for (RDFNode item : group) { + String insert = createInsert(item); + replacement.append(insert); + replacement.append(" "); + } + + replacement.deleteCharAt(replacement.length() - 1); + replacement.append(") "); + } + + replacement.deleteCharAt(replacement.length() - 1); + return replacement; + } + + private StringBuilder ungroupedApply() { + StringBuilder replacement = new StringBuilder(""); + + for (RDFNode item : items) { + if (isParenthesisNeeded) { + replacement.append("("); + } + String insert = createInsert(item); + replacement.append(insert); + if (isParenthesisNeeded) { + replacement.append(")"); + } + replacement.append(" "); + } + + replacement.deleteCharAt(replacement.length() - 1); + + return replacement; + } + + /** + * Tidy up varName if doesn't start with a ? or $. + * + * @param varName + * @return + */ + private String createTarget(String varName) { + String target; + + if (varName.startsWith("?") || varName.startsWith("$")) { + target = varName; + } else { + target = "?" + varName; + } + return target; + } + + /** + * Insert the SPARQL representation of the RDF node. + * + * @param item + * @return + */ + private String createInsert(RDFNode item) { + String insert; + if (item.isLiteral()) { + Literal lit = item.asLiteral(); + insert = "\"" + lit.getLexicalForm() + "\"^^" + lit.getDatatypeURI(); + } else if (item.isResource()) { + insert = "<" + item.asResource().getURI() + ">"; + } else { + insert = item.asResource().getId().getLabelString(); + } + return insert; + } + } + } diff --git a/jena-arq/src/test/java/org/apache/jena/query/TestParameterizedSparqlString.java b/jena-arq/src/test/java/org/apache/jena/query/TestParameterizedSparqlString.java index e3d20261d86..8d7594280ee 100644 --- a/jena-arq/src/test/java/org/apache/jena/query/TestParameterizedSparqlString.java +++ b/jena-arq/src/test/java/org/apache/jena/query/TestParameterizedSparqlString.java @@ -18,9 +18,11 @@ package org.apache.jena.query; +import java.util.ArrayList; import java.util.Calendar ; import java.util.HashMap; import java.util.Iterator ; +import java.util.List; import java.util.TimeZone ; import org.apache.jena.datatypes.TypeMapper ; @@ -1925,4 +1927,136 @@ public void test_param_string_bug_04() { pss.toString(); } + + @Test + public void test_set_values_item() { + // Tests a single value being added. + String str = "SELECT * WHERE { VALUES ?o {?objs} ?s ?p ?o }"; + ParameterizedSparqlString pss = new ParameterizedSparqlString(str); + pss.setValues("objs", ResourceFactory.createPlainLiteral("test")); + + String exp = "SELECT * WHERE { VALUES ?o {\"test\"^^http://www.w3.org/2001/XMLSchema#string} ?s ?p ?o }"; + String res = pss.toString(); + //System.out.println("Exp: " + exp); + //System.out.println("Res: " + res); + Assert.assertEquals(exp, res); + } + + @Test + public void test_set_values_item_parenthesis() { + // Tests a single value with parenthesis. + String str = "SELECT * WHERE { VALUES ?o {?objs} ?s ?p ?o }"; + ParameterizedSparqlString pss = new ParameterizedSparqlString(str); + pss.setValues("objs", ResourceFactory.createPlainLiteral("test"), true); + + String exp = "SELECT * WHERE { VALUES ?o {(\"test\"^^http://www.w3.org/2001/XMLSchema#string)} ?s ?p ?o }"; + String res = pss.toString(); + //System.out.println("Exp: " + exp); + //System.out.println("Res: " + res); + Assert.assertEquals(exp, res); + } + + @Test + public void test_set_values_items() { + // Tests two values for same variable. + String str = "SELECT * WHERE { VALUES ?o {?objs} ?s ?p ?o }"; + ParameterizedSparqlString pss = new ParameterizedSparqlString(str); + List objs = new ArrayList<>(); + objs.add(ResourceFactory.createPlainLiteral("obj_A")); + objs.add(ResourceFactory.createPlainLiteral("obj_B")); + pss.setValues("objs", objs); + + String exp = "SELECT * WHERE { VALUES ?o {\"obj_A\"^^http://www.w3.org/2001/XMLSchema#string \"obj_B\"^^http://www.w3.org/2001/XMLSchema#string} ?s ?p ?o }"; + String res = pss.toString(); + //System.out.println("Exp: " + exp); + //System.out.println("Res: " + res); + Assert.assertEquals(exp, res); + } + + @Test + public void test_set_values_items_parenthesis() { + // Tests two values for same variable. + String str = "SELECT * WHERE { VALUES (?o) {?objs} ?s ?p ?o }"; + ParameterizedSparqlString pss = new ParameterizedSparqlString(str); + List objs = new ArrayList<>(); + objs.add(ResourceFactory.createPlainLiteral("obj_A")); + objs.add(ResourceFactory.createPlainLiteral("obj_B")); + pss.setValues("objs", objs, true); + + String exp = "SELECT * WHERE { VALUES (?o) {(\"obj_A\"^^http://www.w3.org/2001/XMLSchema#string) (\"obj_B\"^^http://www.w3.org/2001/XMLSchema#string)} ?s ?p ?o }"; + String res = pss.toString(); + //System.out.println("Exp: " + exp); + //System.out.println("Res: " + res); + Assert.assertEquals(exp, res); + } + + @Test + public void test_set_values_multi_var() { + // Tests two variables. + String str = "SELECT * WHERE { VALUES ?p {?props} VALUES ?o {?objs} ?s ?p ?o }"; + ParameterizedSparqlString pss = new ParameterizedSparqlString(str); + List objs = new ArrayList<>(); + objs.add(ResourceFactory.createPlainLiteral("obj_A")); + objs.add(ResourceFactory.createPlainLiteral("obj_B")); + pss.setValues("objs", objs); + + List props = new ArrayList<>(); + props.add(ResourceFactory.createProperty("http://example.org/prop_A")); + props.add(ResourceFactory.createProperty("http://example.org/prop_B")); + pss.setValues("props", props); + + String exp = "SELECT * WHERE { VALUES ?p { } VALUES ?o {\"obj_A\"^^http://www.w3.org/2001/XMLSchema#string \"obj_B\"^^http://www.w3.org/2001/XMLSchema#string} ?s ?p ?o }"; + String res = pss.toString(); + //System.out.println("Exp: " + exp); + //System.out.println("Res: " + res); + Assert.assertEquals(exp, res); + } + + @Test + public void test_set_values_multi_var_parenthesis() { + // Tests two variables with parenthesis for one. + String str = "SELECT * WHERE { VALUES (?p) {?props} VALUES ?o {?objs} ?s ?p ?o }"; + ParameterizedSparqlString pss = new ParameterizedSparqlString(str); + List objs = new ArrayList<>(); + objs.add(ResourceFactory.createPlainLiteral("obj_A")); + objs.add(ResourceFactory.createPlainLiteral("obj_B")); + pss.setValues("objs", objs); + + List props = new ArrayList<>(); + props.add(ResourceFactory.createProperty("http://example.org/prop_A")); + props.add(ResourceFactory.createProperty("http://example.org/prop_B")); + pss.setValues("props", props, true); + + String exp = "SELECT * WHERE { VALUES (?p) {() ()} VALUES ?o {\"obj_A\"^^http://www.w3.org/2001/XMLSchema#string \"obj_B\"^^http://www.w3.org/2001/XMLSchema#string} ?s ?p ?o }"; + String res = pss.toString(); + //System.out.println("Exp: " + exp); + //System.out.println("Res: " + res); + Assert.assertEquals(exp, res); + } + + @Test + public void test_set_values_grouped_var() { + // Tests two variables with parenthesis for one. + String str = "SELECT * WHERE { VALUES (?p ?o) {?vars} ?s ?p ?o }"; + ParameterizedSparqlString pss = new ParameterizedSparqlString(str); + + List> vars = new ArrayList<>(); + List objsA = new ArrayList<>(); + objsA.add(ResourceFactory.createProperty("http://example.org/prop_A")); + objsA.add(ResourceFactory.createPlainLiteral("obj_A")); + vars.add(objsA); + + List objsB = new ArrayList<>(); + objsB.add(ResourceFactory.createProperty("http://example.org/prop_B")); + objsB.add(ResourceFactory.createPlainLiteral("obj_B")); + vars.add(objsB); + + pss.setGroupedValues("vars", vars); + + String exp = "SELECT * WHERE { VALUES (?p ?o) {( \"obj_A\"^^http://www.w3.org/2001/XMLSchema#string) ( \"obj_B\"^^http://www.w3.org/2001/XMLSchema#string)} ?s ?p ?o }"; + String res = pss.toString(); + //System.out.println("Exp: " + exp); + //System.out.println("Res: " + res); + Assert.assertEquals(exp, res); + } } From edbd0a2989df3efc1e622c2242bfcf86c7655ee8 Mon Sep 17 00:00:00 2001 From: Greg Albiston Date: Fri, 27 Jul 2018 17:27:43 +0100 Subject: [PATCH 02/18] ARQ:Query:ParameterizedSparqlString - applied Collection.forEach functional operators following style feedback. --- .../apache/jena/query/ParameterizedSparqlString.java | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) 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 fb92909b12f..03960e0d046 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 @@ -32,7 +32,6 @@ 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 ; @@ -1793,10 +1792,7 @@ public void setValues(String varName, RDFNode item) { * @param valuesItems */ public void setValues(Map> valuesItems) { - for (String varName : valuesItems.keySet()) { - Collection items = valuesItems.get(varName); - setValues(varName, items); - } + valuesItems.forEach(this::setValues); } /** @@ -1806,10 +1802,7 @@ public void setValues(Map> valuesItems) { * @param isParenthesisNeeded */ public void setValues(Map> valuesItems, Boolean isParenthesisNeeded) { - for (String varName : valuesItems.keySet()) { - Collection items = valuesItems.get(varName); - setValues(varName, items, isParenthesisNeeded); - } + valuesItems.forEach((varName, items) -> setValues(varName, items, isParenthesisNeeded)); } /** From b06b67bd2ba5e9afd6f254998b8143e42a69ffc6 Mon Sep 17 00:00:00 2001 From: Greg Albiston Date: Mon, 30 Jul 2018 14:00:03 +0100 Subject: [PATCH 03/18] ARQ:Query:ParameterizedSparqlString - replaced "createInsert" method with "FmtUtils.stringForNode" to convert RDFNodes to string representation. --- .../jena/query/ParameterizedSparqlString.java | 22 ++----------------- .../query/TestParameterizedSparqlString.java | 17 +++++++------- 2 files changed, 10 insertions(+), 29 deletions(-) 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 03960e0d046..2a71ea9e2bc 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 @@ -1902,7 +1902,7 @@ private StringBuilder groupedApply() { replacement.append("("); for (RDFNode item : group) { - String insert = createInsert(item); + String insert = FmtUtils.stringForNode(item.asNode(), (PrefixMapping) null); replacement.append(insert); replacement.append(" "); } @@ -1922,7 +1922,7 @@ private StringBuilder ungroupedApply() { if (isParenthesisNeeded) { replacement.append("("); } - String insert = createInsert(item); + String insert = FmtUtils.stringForNode(item.asNode(), (PrefixMapping) null); replacement.append(insert); if (isParenthesisNeeded) { replacement.append(")"); @@ -1952,24 +1952,6 @@ private String createTarget(String varName) { return target; } - /** - * Insert the SPARQL representation of the RDF node. - * - * @param item - * @return - */ - private String createInsert(RDFNode item) { - String insert; - if (item.isLiteral()) { - Literal lit = item.asLiteral(); - insert = "\"" + lit.getLexicalForm() + "\"^^" + lit.getDatatypeURI(); - } else if (item.isResource()) { - insert = "<" + item.asResource().getURI() + ">"; - } else { - insert = item.asResource().getId().getLabelString(); - } - return insert; - } } } diff --git a/jena-arq/src/test/java/org/apache/jena/query/TestParameterizedSparqlString.java b/jena-arq/src/test/java/org/apache/jena/query/TestParameterizedSparqlString.java index 8d7594280ee..fd9b79da4c6 100644 --- a/jena-arq/src/test/java/org/apache/jena/query/TestParameterizedSparqlString.java +++ b/jena-arq/src/test/java/org/apache/jena/query/TestParameterizedSparqlString.java @@ -24,8 +24,7 @@ import java.util.Iterator ; import java.util.List; import java.util.TimeZone ; - -import org.apache.jena.datatypes.TypeMapper ; +import org.apache.jena.datatypes.TypeMapper; import org.apache.jena.graph.Node ; import org.apache.jena.graph.NodeFactory ; import org.apache.jena.rdf.model.* ; @@ -1935,7 +1934,7 @@ public void test_set_values_item() { ParameterizedSparqlString pss = new ParameterizedSparqlString(str); pss.setValues("objs", ResourceFactory.createPlainLiteral("test")); - String exp = "SELECT * WHERE { VALUES ?o {\"test\"^^http://www.w3.org/2001/XMLSchema#string} ?s ?p ?o }"; + String exp = "SELECT * WHERE { VALUES ?o {\"test\"} ?s ?p ?o }"; String res = pss.toString(); //System.out.println("Exp: " + exp); //System.out.println("Res: " + res); @@ -1949,7 +1948,7 @@ public void test_set_values_item_parenthesis() { ParameterizedSparqlString pss = new ParameterizedSparqlString(str); pss.setValues("objs", ResourceFactory.createPlainLiteral("test"), true); - String exp = "SELECT * WHERE { VALUES ?o {(\"test\"^^http://www.w3.org/2001/XMLSchema#string)} ?s ?p ?o }"; + String exp = "SELECT * WHERE { VALUES ?o {(\"test\")} ?s ?p ?o }"; String res = pss.toString(); //System.out.println("Exp: " + exp); //System.out.println("Res: " + res); @@ -1966,7 +1965,7 @@ public void test_set_values_items() { objs.add(ResourceFactory.createPlainLiteral("obj_B")); pss.setValues("objs", objs); - String exp = "SELECT * WHERE { VALUES ?o {\"obj_A\"^^http://www.w3.org/2001/XMLSchema#string \"obj_B\"^^http://www.w3.org/2001/XMLSchema#string} ?s ?p ?o }"; + String exp = "SELECT * WHERE { VALUES ?o {\"obj_A\" \"obj_B\"} ?s ?p ?o }"; String res = pss.toString(); //System.out.println("Exp: " + exp); //System.out.println("Res: " + res); @@ -1983,7 +1982,7 @@ public void test_set_values_items_parenthesis() { objs.add(ResourceFactory.createPlainLiteral("obj_B")); pss.setValues("objs", objs, true); - String exp = "SELECT * WHERE { VALUES (?o) {(\"obj_A\"^^http://www.w3.org/2001/XMLSchema#string) (\"obj_B\"^^http://www.w3.org/2001/XMLSchema#string)} ?s ?p ?o }"; + String exp = "SELECT * WHERE { VALUES (?o) {(\"obj_A\") (\"obj_B\")} ?s ?p ?o }"; String res = pss.toString(); //System.out.println("Exp: " + exp); //System.out.println("Res: " + res); @@ -2005,7 +2004,7 @@ public void test_set_values_multi_var() { props.add(ResourceFactory.createProperty("http://example.org/prop_B")); pss.setValues("props", props); - String exp = "SELECT * WHERE { VALUES ?p { } VALUES ?o {\"obj_A\"^^http://www.w3.org/2001/XMLSchema#string \"obj_B\"^^http://www.w3.org/2001/XMLSchema#string} ?s ?p ?o }"; + String exp = "SELECT * WHERE { VALUES ?p { } VALUES ?o {\"obj_A\" \"obj_B\"} ?s ?p ?o }"; String res = pss.toString(); //System.out.println("Exp: " + exp); //System.out.println("Res: " + res); @@ -2027,7 +2026,7 @@ public void test_set_values_multi_var_parenthesis() { props.add(ResourceFactory.createProperty("http://example.org/prop_B")); pss.setValues("props", props, true); - String exp = "SELECT * WHERE { VALUES (?p) {() ()} VALUES ?o {\"obj_A\"^^http://www.w3.org/2001/XMLSchema#string \"obj_B\"^^http://www.w3.org/2001/XMLSchema#string} ?s ?p ?o }"; + String exp = "SELECT * WHERE { VALUES (?p) {() ()} VALUES ?o {\"obj_A\" \"obj_B\"} ?s ?p ?o }"; String res = pss.toString(); //System.out.println("Exp: " + exp); //System.out.println("Res: " + res); @@ -2053,7 +2052,7 @@ public void test_set_values_grouped_var() { pss.setGroupedValues("vars", vars); - String exp = "SELECT * WHERE { VALUES (?p ?o) {( \"obj_A\"^^http://www.w3.org/2001/XMLSchema#string) ( \"obj_B\"^^http://www.w3.org/2001/XMLSchema#string)} ?s ?p ?o }"; + String exp = "SELECT * WHERE { VALUES (?p ?o) {( \"obj_A\") ( \"obj_B\")} ?s ?p ?o }"; String res = pss.toString(); //System.out.println("Exp: " + exp); //System.out.println("Res: " + res); From 93c678001479d059989fcc66dbf55125c8f19c6c Mon Sep 17 00:00:00 2001 From: Greg Albiston Date: Mon, 30 Jul 2018 14:45:20 +0100 Subject: [PATCH 04/18] ARQ:Query:ParameterizedSparqlString - "validateParameterValue" now performed on all values' items to ensure no injection through ">" in URI. Test added based on existing tests. --- .../apache/jena/query/ParameterizedSparqlString.java | 2 ++ .../jena/query/TestParameterizedSparqlString.java | 12 ++++++++++++ 2 files changed, 14 insertions(+) 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 2a71ea9e2bc..ab3bfcc4512 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 @@ -1749,6 +1749,7 @@ public String toString() { * @param isParenthesisNeeded */ public void setValues(String varName, Collection items, boolean isParenthesisNeeded) { + items.forEach(item -> validateParameterValue(item.asNode())); this.valuesReplacements.put(varName, new ValueReplacement(varName, items, isParenthesisNeeded)); } @@ -1837,6 +1838,7 @@ public void setValues(Map> valuesItems, Ma * @param items */ public void setGroupedValues(String varName, Collection> items) { + items.forEach(collection -> collection.forEach(item -> validateParameterValue(item.asNode()))); this.valuesReplacements.put(varName, new ValueReplacement(varName, items)); } diff --git a/jena-arq/src/test/java/org/apache/jena/query/TestParameterizedSparqlString.java b/jena-arq/src/test/java/org/apache/jena/query/TestParameterizedSparqlString.java index fd9b79da4c6..69c50bfe352 100644 --- a/jena-arq/src/test/java/org/apache/jena/query/TestParameterizedSparqlString.java +++ b/jena-arq/src/test/java/org/apache/jena/query/TestParameterizedSparqlString.java @@ -2058,4 +2058,16 @@ public void test_set_values_grouped_var() { //System.out.println("Res: " + res); Assert.assertEquals(exp, res); } + + @Test(expected = ARQException.class) + public void test_set_values_uri_injection() { + // This injection is prevented by forbidding the > character in URIs + String str = "PREFIX : \nSELECT * WHERE { VALUES ?obj {?objVar}

?obj . }"; + ParameterizedSparqlString pss = new ParameterizedSparqlString(str); + pss.setValues(str, ResourceFactory.createResource("")); + + pss.asQuery(); + Assert.fail("Attempt to do SPARQL injection should result in an exception"); + } + } From a5f18c3d468a703954de464887b699e4ca95822a Mon Sep 17 00:00:00 2001 From: Greg Albiston Date: Mon, 30 Jul 2018 15:48:42 +0100 Subject: [PATCH 05/18] ARQ:Query:ParameterizedSparqlString - "validateValuesSafeToInject" method now performed when applying the values. The variables which will be used in the substitution are identified then each variable is checked against the relevant item. --- .../jena/query/ParameterizedSparqlString.java | 57 ++++++++++++++++++- .../query/TestParameterizedSparqlString.java | 39 +++++++++++++ 2 files changed, 93 insertions(+), 3 deletions(-) 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 ab3bfcc4512..cba1e6d7bd9 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 @@ -1850,6 +1850,24 @@ private String applyValues(String command) { return command; } + private static final String VALUES_KEYWORD = "values"; + + protected static String[] extractTargetVars(String command, String varName) { + String[] targetVars; + + int varIndex = command.indexOf(varName); + if (varIndex > -1) { + String subCmd = command.substring(0, varIndex).toLowerCase(); //Truncate the command at the varName. Lowercase to search both types of values. + int valuesIndex = subCmd.lastIndexOf(VALUES_KEYWORD); + int bracesIndex = subCmd.lastIndexOf("{"); + String vars = command.substring(valuesIndex + VALUES_KEYWORD.length(), bracesIndex); + targetVars = vars.replaceAll("[(?)]", "").trim().split(" "); + } else { + targetVars = new String[]{}; + } + return targetVars; + } + /** * Performs replacement of VALUES in query string. * @@ -1884,10 +1902,11 @@ public String apply(String command) { return command; } - String target = createTarget(varName); + validateValuesSafeToInject(command); - StringBuilder replacement; + String target = createTarget(); + StringBuilder replacement; if (isGrouped) { replacement = groupedApply(); } else { @@ -1943,7 +1962,7 @@ private StringBuilder ungroupedApply() { * @param varName * @return */ - private String createTarget(String varName) { + private String createTarget() { String target; if (varName.startsWith("?") || varName.startsWith("$")) { @@ -1954,6 +1973,38 @@ private String createTarget(String varName) { return target; } + protected void validateValuesSafeToInject(String command) { + + String[] targetVars = extractTargetVars(command, varName); + + for (int i = 0; i < targetVars.length; i++) { + String targetVar = targetVars[i]; + if (isGrouped) { + //Iterate through each group according to the position of var and item. + for (List group : groupedItems) { + RDFNode item = group.get(i); + validateSafeToInject(command, targetVar, item.asNode()); + } + } else { + if (targetVars.length > 1) { + if (items instanceof List) { + //Multiple vars with items in an ordered list. Each var is checked against the item. + List listItems = (List) items; + RDFNode item = listItems.get(i); + validateSafeToInject(command, targetVar, item.asNode()); + } else { + //Multiple vars with items not in an ordered list. This is parsing error. + throw new ARQException("Multiple VALUES variables (" + String.join(", ", targetVars) + ") being used without an ordered list of items: " + items.toString()); + } + } else { + //Single var with one or more items so all are checked. + for (RDFNode item : items) { + validateSafeToInject(command, targetVar, item.asNode()); + } + } + } + } + } } } diff --git a/jena-arq/src/test/java/org/apache/jena/query/TestParameterizedSparqlString.java b/jena-arq/src/test/java/org/apache/jena/query/TestParameterizedSparqlString.java index 69c50bfe352..90e53853698 100644 --- a/jena-arq/src/test/java/org/apache/jena/query/TestParameterizedSparqlString.java +++ b/jena-arq/src/test/java/org/apache/jena/query/TestParameterizedSparqlString.java @@ -2070,4 +2070,43 @@ public void test_set_values_uri_injection() { Assert.fail("Attempt to do SPARQL injection should result in an exception"); } + @Test + public void test_extract_target_vars() { + // Identifies the vars in the VALUES clause according to the substituting varName. + String cmd = "SELECT * WHERE { VALUES ?o {?objs} ?s ?p ?o }"; + String varName = "objs"; + String[] res = ParameterizedSparqlString.extractTargetVars(cmd, varName); + String[] exp = new String[]{"o"}; + + //System.out.println("Exp: " + exp); + //System.out.println("Res: " + res); + Assert.assertArrayEquals(exp, res); + } + + @Test + public void test_extract_two_target_vars() { + // Identifies the vars in the VALUES clause according to the substituting varName. + String cmd = "SELECT * WHERE { VALUES(?p ?o){?vars} ?s ?p ?o }"; + String varName = "vars"; + String[] res = ParameterizedSparqlString.extractTargetVars(cmd, varName); + String[] exp = new String[]{"p", "o"}; + + //System.out.println("Exp: " + exp); + //System.out.println("Res: " + res); + Assert.assertArrayEquals(exp, res); + } + + @Test + public void test_extract_multiple_target_vars() { + // Identifies the vars in the VALUES clause according to the substituting varName. + String cmd = "SELECT * WHERE { VALUES ?p {?props} VALUES ?o {?objs} ?s ?p ?o }"; + String varName = "objs"; + String[] res = ParameterizedSparqlString.extractTargetVars(cmd, varName); + String[] exp = new String[]{"o"}; + + //System.out.println("Exp: " + exp); + //System.out.println("Res: " + res); + Assert.assertArrayEquals(exp, res); + } + } From ecaf8d8bf08be57146c4f52b03117f4dd39f45a2 Mon Sep 17 00:00:00 2001 From: Greg Albiston Date: Mon, 30 Jul 2018 16:13:12 +0100 Subject: [PATCH 06/18] ARQ:Query:ParameterizedSparqlString - need for parenthesis in ungrouped values now determined based on the query rather than as a parameter. --- .../jena/query/ParameterizedSparqlString.java | 114 ++++++------------ .../query/TestParameterizedSparqlString.java | 18 +-- 2 files changed, 41 insertions(+), 91 deletions(-) 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 cba1e6d7bd9..3acac635ff9 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 @@ -1740,19 +1740,6 @@ public String toString() { } - /** - * Assign a varName with a multiple items and whether to include - * parenthesis. - * - * @param varName - * @param items - * @param isParenthesisNeeded - */ - public void setValues(String varName, Collection items, boolean isParenthesisNeeded) { - items.forEach(item -> validateParameterValue(item.asNode())); - this.valuesReplacements.put(varName, new ValueReplacement(varName, items, isParenthesisNeeded)); - } - /** * Assign a varName with a multiple items.
* Can be used to assign multiple values to a single variable or single @@ -1763,69 +1750,31 @@ public void setValues(String varName, Collection items, boole * @param items */ public void setValues(String varName, Collection items) { - setValues(varName, items, false); - } - - /** - * Assign a varName with a single item and whether to include parenthesis. - * - * @param varName - * @param item - * @param isParenthesisNeeded - */ - public void setValues(String varName, RDFNode item, boolean isParenthesisNeeded) { - setValues(varName, Arrays.asList(item), isParenthesisNeeded); + items.forEach(item -> validateParameterValue(item.asNode())); + this.valuesReplacements.put(varName, new ValueReplacement(varName, items)); } /** - * Assign a varName with a single item. + * Assign a varName with a single item.
* * @param varName * @param item */ public void setValues(String varName, RDFNode item) { - setValues(varName, Arrays.asList(item), false); - } - - /** - * Sets a map of varNames and their items. - * - * @param valuesItems - */ - public void setValues(Map> valuesItems) { - valuesItems.forEach(this::setValues); + setValues(varName, Arrays.asList(item)); } /** - * All varNames in the map will use the same approach to parenthesis. - * - * @param valuesItems - * @param isParenthesisNeeded - */ - public void setValues(Map> valuesItems, Boolean isParenthesisNeeded) { - valuesItems.forEach((varName, items) -> setValues(varName, items, isParenthesisNeeded)); - } - - /** - * Combine a map of varNames and items with whether to include parenthesis. - * Missing varNames in the parenthesis map will default to false. + * ** + * Sets a map of varNames and their items.
+ * Can be used to assign multiple values to a single variable or single + * value to multiple variables (if using a List) in the SPARQL query.
+ * See setGroupedValues to assign multiple values to multiple variables. * - * @param valuesItems - * @param valuesParenthesis + * @param itemsMap */ - public void setValues(Map> valuesItems, Map valuesParenthesis) { - - for (String varName : valuesItems.keySet()) { - Collection items = valuesItems.get(varName); - Boolean isParenthesisNeeded; - if (valuesParenthesis.containsKey(varName)) { - isParenthesisNeeded = valuesParenthesis.get(varName); - } else { - isParenthesisNeeded = false; - } - - setValues(varName, items, isParenthesisNeeded); - } + public void setValues(Map> itemsMap) { + itemsMap.forEach(this::setValues); } /** @@ -1835,11 +1784,11 @@ public void setValues(Map> valuesItems, Ma * (prop_B obj_B)}". * * @param varName - * @param items + * @param groupedItems */ - public void setGroupedValues(String varName, Collection> items) { - items.forEach(collection -> collection.forEach(item -> validateParameterValue(item.asNode()))); - this.valuesReplacements.put(varName, new ValueReplacement(varName, items)); + public void setGroupedValues(String varName, Collection> groupedItems) { + groupedItems.forEach(collection -> collection.forEach(item -> validateParameterValue(item.asNode()))); + this.valuesReplacements.put(varName, new ValueReplacement(varName, groupedItems, true)); } private String applyValues(String command) { @@ -1868,6 +1817,22 @@ protected static String[] extractTargetVars(String command, String varName) { return targetVars; } + protected static boolean checkParenthesis(String command, String varName) { + boolean isNeeded; + + int varIndex = command.indexOf(varName); + if (varIndex > -1) { + String subCmd = command.substring(0, varIndex).toLowerCase(); //Truncate the command at the varName. Lowercase to search both types of values. + int valuesIndex = subCmd.lastIndexOf(VALUES_KEYWORD); + int bracesIndex = subCmd.lastIndexOf("{"); + String vars = command.substring(valuesIndex + VALUES_KEYWORD.length(), bracesIndex); + isNeeded = vars.contains("("); + } else { + isNeeded = false; + } + return isNeeded; + } + /** * Performs replacement of VALUES in query string. * @@ -1877,23 +1842,20 @@ private class ValueReplacement { private final String varName; private final Collection items; private final Collection> groupedItems; - private final Boolean isParenthesisNeeded; private final Boolean isGrouped; - public ValueReplacement(String varName, Collection items, Boolean isParenthesisNeeded) { + public ValueReplacement(String varName, Collection items) { this.varName = varName; this.items = items; this.groupedItems = new ArrayList<>(); - this.isParenthesisNeeded = isParenthesisNeeded; this.isGrouped = false; } - public ValueReplacement(String varName, Collection> groupedItems) { + public ValueReplacement(String varName, Collection> groupedItems, Boolean isGrouped) { this.varName = varName; this.items = new ArrayList<>(); this.groupedItems = groupedItems; - this.isParenthesisNeeded = true; - this.isGrouped = true; + this.isGrouped = isGrouped; } public String apply(String command) { @@ -1910,7 +1872,8 @@ public String apply(String command) { if (isGrouped) { replacement = groupedApply(); } else { - replacement = ungroupedApply(); + + replacement = ungroupedApply(command); } return command.replace(target, replacement); @@ -1936,7 +1899,8 @@ private StringBuilder groupedApply() { return replacement; } - private StringBuilder ungroupedApply() { + private StringBuilder ungroupedApply(String command) { + boolean isParenthesisNeeded = checkParenthesis(command, varName); StringBuilder replacement = new StringBuilder(""); for (RDFNode item : items) { diff --git a/jena-arq/src/test/java/org/apache/jena/query/TestParameterizedSparqlString.java b/jena-arq/src/test/java/org/apache/jena/query/TestParameterizedSparqlString.java index 90e53853698..adf110835f1 100644 --- a/jena-arq/src/test/java/org/apache/jena/query/TestParameterizedSparqlString.java +++ b/jena-arq/src/test/java/org/apache/jena/query/TestParameterizedSparqlString.java @@ -1941,20 +1941,6 @@ public void test_set_values_item() { Assert.assertEquals(exp, res); } - @Test - public void test_set_values_item_parenthesis() { - // Tests a single value with parenthesis. - String str = "SELECT * WHERE { VALUES ?o {?objs} ?s ?p ?o }"; - ParameterizedSparqlString pss = new ParameterizedSparqlString(str); - pss.setValues("objs", ResourceFactory.createPlainLiteral("test"), true); - - String exp = "SELECT * WHERE { VALUES ?o {(\"test\")} ?s ?p ?o }"; - String res = pss.toString(); - //System.out.println("Exp: " + exp); - //System.out.println("Res: " + res); - Assert.assertEquals(exp, res); - } - @Test public void test_set_values_items() { // Tests two values for same variable. @@ -1980,7 +1966,7 @@ public void test_set_values_items_parenthesis() { List objs = new ArrayList<>(); objs.add(ResourceFactory.createPlainLiteral("obj_A")); objs.add(ResourceFactory.createPlainLiteral("obj_B")); - pss.setValues("objs", objs, true); + pss.setValues("objs", objs); String exp = "SELECT * WHERE { VALUES (?o) {(\"obj_A\") (\"obj_B\")} ?s ?p ?o }"; String res = pss.toString(); @@ -2024,7 +2010,7 @@ public void test_set_values_multi_var_parenthesis() { List props = new ArrayList<>(); props.add(ResourceFactory.createProperty("http://example.org/prop_A")); props.add(ResourceFactory.createProperty("http://example.org/prop_B")); - pss.setValues("props", props, true); + pss.setValues("props", props); String exp = "SELECT * WHERE { VALUES (?p) {() ()} VALUES ?o {\"obj_A\" \"obj_B\"} ?s ?p ?o }"; String res = pss.toString(); From f367e23c3413ef4a10df4b775f07d3139a900bc1 Mon Sep 17 00:00:00 2001 From: Greg Albiston Date: Mon, 30 Jul 2018 16:17:49 +0100 Subject: [PATCH 07/18] ARQ:Query:ParameterizedSparqlString - more examples provided in JavaDocs. --- .../jena/query/ParameterizedSparqlString.java | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) 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 3acac635ff9..669cc6cc2ee 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 @@ -1741,10 +1741,13 @@ public String toString() { } /** - * Assign a varName with a multiple items.
+ * Assign a VALUES varName with a multiple items.
* Can be used to assign multiple values to a single variable or single * value to multiple variables (if using a List) in the SPARQL query.
- * See setGroupedValues to assign multiple values to multiple variables. + * See setGroupedValues to assign multiple values to multiple variables.
+ * Using "var" with list(prop_A, obj_A) on query "VALUES ?p ?o {?var}" would + * produce "VALUES ?p ?o {prop_A obj_A}". + * * * @param varName * @param items @@ -1755,7 +1758,9 @@ public void setValues(String varName, Collection items) { } /** - * Assign a varName with a single item.
+ * Assign a VALUES varName with a single item.
+ * Using "var" with Literal obj_A on query "VALUES ?o {?var}" would produce + * "VALUES ?o {obj_A}". * * @param varName * @param item @@ -1766,7 +1771,7 @@ public void setValues(String varName, RDFNode item) { /** * ** - * Sets a map of varNames and their items.
+ * Sets a map of VALUES varNames and their items.
* Can be used to assign multiple values to a single variable or single * value to multiple variables (if using a List) in the SPARQL query.
* See setGroupedValues to assign multiple values to multiple variables. @@ -1778,7 +1783,7 @@ public void setValues(Map> itemsMap) { } /** - * Allocate multiple lists of variables to a single varName.
+ * Allocate multiple lists of variables to a single VALUES varName.
* Using "vars" with list(list(prop_A, obj_A), list(prop_B, obj_B)) on query * "VALUES (?p ?o) {?vars}" would produce "VALUES (?p ?o) {(prop_A obj_A) * (prop_B obj_B)}". From 8b1548c4164048567940d488b146bcc8cd9a23cb Mon Sep 17 00:00:00 2001 From: Greg Albiston Date: Mon, 30 Jul 2018 16:26:15 +0100 Subject: [PATCH 08/18] ARQ:Query:ParameterizedSparqlString - simplified checking the need for parenthesis. --- .../org/apache/jena/query/ParameterizedSparqlString.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) 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 669cc6cc2ee..9a3fae80e20 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 @@ -1829,9 +1829,8 @@ protected static boolean checkParenthesis(String command, String varName) { if (varIndex > -1) { String subCmd = command.substring(0, varIndex).toLowerCase(); //Truncate the command at the varName. Lowercase to search both types of values. int valuesIndex = subCmd.lastIndexOf(VALUES_KEYWORD); - int bracesIndex = subCmd.lastIndexOf("{"); - String vars = command.substring(valuesIndex + VALUES_KEYWORD.length(), bracesIndex); - isNeeded = vars.contains("("); + int parenthesisIndex = subCmd.indexOf("(", valuesIndex + VALUES_KEYWORD.length()); + isNeeded = parenthesisIndex > -1; } else { isNeeded = false; } From 9662ff6e7dc57633cfbc78632a596f56f2db3268 Mon Sep 17 00:00:00 2001 From: Greg Albiston Date: Mon, 30 Jul 2018 16:49:37 +0100 Subject: [PATCH 09/18] ARQ:Query:ParameterizedSparqlString - parenthesis now always applied when there is more than one target variables. Only check the query for parenthesis in the single target variable case. --- .../jena/query/ParameterizedSparqlString.java | 48 +++++++++++-------- .../query/TestParameterizedSparqlString.java | 17 +++++++ 2 files changed, 46 insertions(+), 19 deletions(-) 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 9a3fae80e20..8ecc6444c12 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 @@ -1745,8 +1745,8 @@ public String toString() { * Can be used to assign multiple values to a single variable or single * value to multiple variables (if using a List) in the SPARQL query.
* See setGroupedValues to assign multiple values to multiple variables.
- * Using "var" with list(prop_A, obj_A) on query "VALUES ?p ?o {?var}" would - * produce "VALUES ?p ?o {prop_A obj_A}". + * Using "var" with list(prop_A, obj_A) on query "VALUES (?p ?o) {?var}" + * would produce "VALUES (?p ?o) {(prop_A obj_A)}". * * * @param varName @@ -1868,7 +1868,8 @@ public String apply(String command) { return command; } - validateValuesSafeToInject(command); + String[] targetVars = extractTargetVars(command, varName); + validateValuesSafeToInject(command, targetVars); String target = createTarget(); @@ -1877,7 +1878,7 @@ public String apply(String command) { replacement = groupedApply(); } else { - replacement = ungroupedApply(command); + replacement = ungroupedApply(command, targetVars.length); } return command.replace(target, replacement); @@ -1903,24 +1904,35 @@ private StringBuilder groupedApply() { return replacement; } - private StringBuilder ungroupedApply(String command) { - boolean isParenthesisNeeded = checkParenthesis(command, varName); + private StringBuilder ungroupedApply(String command, int targetVarCount) { + StringBuilder replacement = new StringBuilder(""); - for (RDFNode item : items) { - if (isParenthesisNeeded) { - replacement.append("("); + if (targetVarCount == 1) { + boolean isParenthesisNeeded = checkParenthesis(command, varName); + for (RDFNode item : items) { + if (isParenthesisNeeded) { + replacement.append("("); + } + String insert = FmtUtils.stringForNode(item.asNode(), (PrefixMapping) null); + replacement.append(insert); + if (isParenthesisNeeded) { + replacement.append(")"); + } + replacement.append(" "); } - String insert = FmtUtils.stringForNode(item.asNode(), (PrefixMapping) null); - replacement.append(insert); - if (isParenthesisNeeded) { - replacement.append(")"); + replacement.deleteCharAt(replacement.length() - 1); + } else { + replacement.append("("); + for (RDFNode item : items) { + String insert = FmtUtils.stringForNode(item.asNode(), (PrefixMapping) null); + replacement.append(insert); + replacement.append(" "); } - replacement.append(" "); + replacement.deleteCharAt(replacement.length() - 1); + replacement.append(")"); } - replacement.deleteCharAt(replacement.length() - 1); - return replacement; } @@ -1941,9 +1953,7 @@ private String createTarget() { return target; } - protected void validateValuesSafeToInject(String command) { - - String[] targetVars = extractTargetVars(command, varName); + protected void validateValuesSafeToInject(String command, String[] targetVars) { for (int i = 0; i < targetVars.length; i++) { String targetVar = targetVars[i]; diff --git a/jena-arq/src/test/java/org/apache/jena/query/TestParameterizedSparqlString.java b/jena-arq/src/test/java/org/apache/jena/query/TestParameterizedSparqlString.java index adf110835f1..1dc14eee408 100644 --- a/jena-arq/src/test/java/org/apache/jena/query/TestParameterizedSparqlString.java +++ b/jena-arq/src/test/java/org/apache/jena/query/TestParameterizedSparqlString.java @@ -1975,6 +1975,23 @@ public void test_set_values_items_parenthesis() { Assert.assertEquals(exp, res); } + @Test + public void test_set_values_multiple_variables_parenthesis() { + // Tests two values for same variable. + String str = "SELECT * WHERE { VALUES (?p ?o) {?vars} ?s ?p ?o }"; + ParameterizedSparqlString pss = new ParameterizedSparqlString(str); + List vars = new ArrayList<>(); + vars.add(ResourceFactory.createProperty("http://example.org/prop_A")); + vars.add(ResourceFactory.createPlainLiteral("obj_A")); + pss.setValues("vars", vars); + + String exp = "SELECT * WHERE { VALUES (?p ?o) {( \"obj_A\")} ?s ?p ?o }"; + String res = pss.toString(); + System.out.println("Exp: " + exp); + System.out.println("Res: " + res); + Assert.assertEquals(exp, res); + } + @Test public void test_set_values_multi_var() { // Tests two variables. From b708cff4b81878423e7cd9f99dcefd81c077a262 Mon Sep 17 00:00:00 2001 From: Greg Albiston Date: Thu, 2 Aug 2018 13:16:06 +0100 Subject: [PATCH 10/18] ARQ:Query:ParameterizedSparqlString - Parenthesis always applied. Term "group" replaced with "row". --- .../jena/query/ParameterizedSparqlString.java | 76 +++++++------------ .../query/TestParameterizedSparqlString.java | 57 +++----------- 2 files changed, 36 insertions(+), 97 deletions(-) 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 8ecc6444c12..e45f3d6e1b3 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 @@ -1744,7 +1744,7 @@ public String toString() { * Assign a VALUES varName with a multiple items.
* Can be used to assign multiple values to a single variable or single * value to multiple variables (if using a List) in the SPARQL query.
- * See setGroupedValues to assign multiple values to multiple variables.
+ * See setRowValues to assign multiple values to multiple variables.
* Using "var" with list(prop_A, obj_A) on query "VALUES (?p ?o) {?var}" * would produce "VALUES (?p ?o) {(prop_A obj_A)}". * @@ -1774,7 +1774,7 @@ public void setValues(String varName, RDFNode item) { * Sets a map of VALUES varNames and their items.
* Can be used to assign multiple values to a single variable or single * value to multiple variables (if using a List) in the SPARQL query.
- * See setGroupedValues to assign multiple values to multiple variables. + * See setRowValues to assign multiple values to multiple variables. * * @param itemsMap */ @@ -1789,11 +1789,11 @@ public void setValues(Map> itemsMap) { * (prop_B obj_B)}". * * @param varName - * @param groupedItems + * @param rowItems */ - public void setGroupedValues(String varName, Collection> groupedItems) { - groupedItems.forEach(collection -> collection.forEach(item -> validateParameterValue(item.asNode()))); - this.valuesReplacements.put(varName, new ValueReplacement(varName, groupedItems, true)); + public void setRowValues(String varName, Collection> rowItems) { + rowItems.forEach(collection -> collection.forEach(item -> validateParameterValue(item.asNode()))); + this.valuesReplacements.put(varName, new ValueReplacement(varName, rowItems, true)); } private String applyValues(String command) { @@ -1822,21 +1822,6 @@ protected static String[] extractTargetVars(String command, String varName) { return targetVars; } - protected static boolean checkParenthesis(String command, String varName) { - boolean isNeeded; - - int varIndex = command.indexOf(varName); - if (varIndex > -1) { - String subCmd = command.substring(0, varIndex).toLowerCase(); //Truncate the command at the varName. Lowercase to search both types of values. - int valuesIndex = subCmd.lastIndexOf(VALUES_KEYWORD); - int parenthesisIndex = subCmd.indexOf("(", valuesIndex + VALUES_KEYWORD.length()); - isNeeded = parenthesisIndex > -1; - } else { - isNeeded = false; - } - return isNeeded; - } - /** * Performs replacement of VALUES in query string. * @@ -1845,26 +1830,26 @@ private class ValueReplacement { private final String varName; private final Collection items; - private final Collection> groupedItems; - private final Boolean isGrouped; + private final Collection> rowItems; + private final Boolean isRows; public ValueReplacement(String varName, Collection items) { this.varName = varName; this.items = items; - this.groupedItems = new ArrayList<>(); - this.isGrouped = false; + this.rowItems = new ArrayList<>(); + this.isRows = false; } - public ValueReplacement(String varName, Collection> groupedItems, Boolean isGrouped) { + public ValueReplacement(String varName, Collection> rowItems, Boolean isRows) { this.varName = varName; this.items = new ArrayList<>(); - this.groupedItems = groupedItems; - this.isGrouped = isGrouped; + this.rowItems = rowItems; + this.isRows = isRows; } public String apply(String command) { - if (items.isEmpty() && groupedItems.isEmpty()) { + if (items.isEmpty() && rowItems.isEmpty()) { return command; } @@ -1874,23 +1859,22 @@ public String apply(String command) { String target = createTarget(); StringBuilder replacement; - if (isGrouped) { - replacement = groupedApply(); + if (isRows) { + replacement = rowsApply(); } else { - - replacement = ungroupedApply(command, targetVars.length); + replacement = singleApply(targetVars.length); } return command.replace(target, replacement); } - private StringBuilder groupedApply() { + private StringBuilder rowsApply() { StringBuilder replacement = new StringBuilder(""); - for (List group : groupedItems) { + for (List row : rowItems) { replacement.append("("); - for (RDFNode item : group) { + for (RDFNode item : row) { String insert = FmtUtils.stringForNode(item.asNode(), (PrefixMapping) null); replacement.append(insert); replacement.append(" "); @@ -1904,22 +1888,16 @@ private StringBuilder groupedApply() { return replacement; } - private StringBuilder ungroupedApply(String command, int targetVarCount) { + private StringBuilder singleApply(int targetVarCount) { StringBuilder replacement = new StringBuilder(""); if (targetVarCount == 1) { - boolean isParenthesisNeeded = checkParenthesis(command, varName); for (RDFNode item : items) { - if (isParenthesisNeeded) { - replacement.append("("); - } + replacement.append("("); String insert = FmtUtils.stringForNode(item.asNode(), (PrefixMapping) null); replacement.append(insert); - if (isParenthesisNeeded) { - replacement.append(")"); - } - replacement.append(" "); + replacement.append(") "); } replacement.deleteCharAt(replacement.length() - 1); } else { @@ -1957,10 +1935,10 @@ protected void validateValuesSafeToInject(String command, String[] targetVars) { for (int i = 0; i < targetVars.length; i++) { String targetVar = targetVars[i]; - if (isGrouped) { - //Iterate through each group according to the position of var and item. - for (List group : groupedItems) { - RDFNode item = group.get(i); + if (isRows) { + //Iterate through each row according to the position of var and item. + for (List row : rowItems) { + RDFNode item = row.get(i); validateSafeToInject(command, targetVar, item.asNode()); } } else { diff --git a/jena-arq/src/test/java/org/apache/jena/query/TestParameterizedSparqlString.java b/jena-arq/src/test/java/org/apache/jena/query/TestParameterizedSparqlString.java index 1dc14eee408..1cf331234cf 100644 --- a/jena-arq/src/test/java/org/apache/jena/query/TestParameterizedSparqlString.java +++ b/jena-arq/src/test/java/org/apache/jena/query/TestParameterizedSparqlString.java @@ -1929,29 +1929,12 @@ public void test_param_string_bug_04() { @Test public void test_set_values_item() { - // Tests a single value being added. + // Tests a single value being added - always adding parenthesis. String str = "SELECT * WHERE { VALUES ?o {?objs} ?s ?p ?o }"; ParameterizedSparqlString pss = new ParameterizedSparqlString(str); pss.setValues("objs", ResourceFactory.createPlainLiteral("test")); - String exp = "SELECT * WHERE { VALUES ?o {\"test\"} ?s ?p ?o }"; - String res = pss.toString(); - //System.out.println("Exp: " + exp); - //System.out.println("Res: " + res); - Assert.assertEquals(exp, res); - } - - @Test - public void test_set_values_items() { - // Tests two values for same variable. - String str = "SELECT * WHERE { VALUES ?o {?objs} ?s ?p ?o }"; - ParameterizedSparqlString pss = new ParameterizedSparqlString(str); - List objs = new ArrayList<>(); - objs.add(ResourceFactory.createPlainLiteral("obj_A")); - objs.add(ResourceFactory.createPlainLiteral("obj_B")); - pss.setValues("objs", objs); - - String exp = "SELECT * WHERE { VALUES ?o {\"obj_A\" \"obj_B\"} ?s ?p ?o }"; + String exp = "SELECT * WHERE { VALUES ?o {(\"test\")} ?s ?p ?o }"; String res = pss.toString(); //System.out.println("Exp: " + exp); //System.out.println("Res: " + res); @@ -1987,37 +1970,15 @@ public void test_set_values_multiple_variables_parenthesis() { String exp = "SELECT * WHERE { VALUES (?p ?o) {( \"obj_A\")} ?s ?p ?o }"; String res = pss.toString(); - System.out.println("Exp: " + exp); - System.out.println("Res: " + res); - Assert.assertEquals(exp, res); - } - - @Test - public void test_set_values_multi_var() { - // Tests two variables. - String str = "SELECT * WHERE { VALUES ?p {?props} VALUES ?o {?objs} ?s ?p ?o }"; - ParameterizedSparqlString pss = new ParameterizedSparqlString(str); - List objs = new ArrayList<>(); - objs.add(ResourceFactory.createPlainLiteral("obj_A")); - objs.add(ResourceFactory.createPlainLiteral("obj_B")); - pss.setValues("objs", objs); - - List props = new ArrayList<>(); - props.add(ResourceFactory.createProperty("http://example.org/prop_A")); - props.add(ResourceFactory.createProperty("http://example.org/prop_B")); - pss.setValues("props", props); - - String exp = "SELECT * WHERE { VALUES ?p { } VALUES ?o {\"obj_A\" \"obj_B\"} ?s ?p ?o }"; - String res = pss.toString(); //System.out.println("Exp: " + exp); //System.out.println("Res: " + res); Assert.assertEquals(exp, res); } @Test - public void test_set_values_multi_var_parenthesis() { - // Tests two variables with parenthesis for one. - String str = "SELECT * WHERE { VALUES (?p) {?props} VALUES ?o {?objs} ?s ?p ?o }"; + public void test_set_values_multi_var() { + // Tests two variables - always adding parenthesis. + String str = "SELECT * WHERE { VALUES ?p {?props} VALUES ?o {?objs} ?s ?p ?o }"; ParameterizedSparqlString pss = new ParameterizedSparqlString(str); List objs = new ArrayList<>(); objs.add(ResourceFactory.createPlainLiteral("obj_A")); @@ -2029,10 +1990,10 @@ public void test_set_values_multi_var_parenthesis() { props.add(ResourceFactory.createProperty("http://example.org/prop_B")); pss.setValues("props", props); - String exp = "SELECT * WHERE { VALUES (?p) {() ()} VALUES ?o {\"obj_A\" \"obj_B\"} ?s ?p ?o }"; + String exp = "SELECT * WHERE { VALUES ?p {() ()} VALUES ?o {(\"obj_A\") (\"obj_B\")} ?s ?p ?o }"; String res = pss.toString(); - //System.out.println("Exp: " + exp); - //System.out.println("Res: " + res); + System.out.println("Exp: " + exp); + System.out.println("Res: " + res); Assert.assertEquals(exp, res); } @@ -2053,7 +2014,7 @@ public void test_set_values_grouped_var() { objsB.add(ResourceFactory.createPlainLiteral("obj_B")); vars.add(objsB); - pss.setGroupedValues("vars", vars); + pss.setRowValues("vars", vars); String exp = "SELECT * WHERE { VALUES (?p ?o) {( \"obj_A\") ( \"obj_B\")} ?s ?p ?o }"; String res = pss.toString(); From 856e06c2d12cdf194db4661d9e91bddb7c522e49 Mon Sep 17 00:00:00 2001 From: Greg Albiston Date: Thu, 2 Aug 2018 13:49:21 +0100 Subject: [PATCH 11/18] ARQ:Query:ParameterizedSparqlString - Distinction removed between "items" and "rowItems". Alternative checks (replacing values and validating injection) are made when only a single variable found. Otherwise treat multiple variables with single row or many rows the same. - Now ensure all "row" Collections are Lists when values are being set for later handling. --- .../jena/query/ParameterizedSparqlString.java | 113 +++++++----------- 1 file changed, 43 insertions(+), 70 deletions(-) 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 e45f3d6e1b3..c3b38574fce 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 @@ -32,6 +32,7 @@ import java.util.regex.MatchResult; import java.util.regex.Matcher; import java.util.regex.Pattern; +import java.util.stream.Collectors; import org.apache.jena.atlas.lib.Pair; import org.apache.jena.datatypes.RDFDatatype ; import org.apache.jena.graph.Node ; @@ -1754,7 +1755,15 @@ public String toString() { */ public void setValues(String varName, Collection items) { items.forEach(item -> validateParameterValue(item.asNode())); - this.valuesReplacements.put(varName, new ValueReplacement(varName, items)); + + //Ensure that a list is used for the items. + Collection> rowItems = new ArrayList<>(); + if (items instanceof List) { + rowItems.add((List) items); + } else { + rowItems.add(new ArrayList<>(items)); + } + this.valuesReplacements.put(varName, new ValueReplacement(varName, rowItems)); } /** @@ -1793,7 +1802,7 @@ public void setValues(Map> itemsMap) { */ public void setRowValues(String varName, Collection> rowItems) { rowItems.forEach(collection -> collection.forEach(item -> validateParameterValue(item.asNode()))); - this.valuesReplacements.put(varName, new ValueReplacement(varName, rowItems, true)); + this.valuesReplacements.put(varName, new ValueReplacement(varName, rowItems)); } private String applyValues(String command) { @@ -1829,27 +1838,16 @@ protected static String[] extractTargetVars(String command, String varName) { private class ValueReplacement { private final String varName; - private final Collection items; private final Collection> rowItems; - private final Boolean isRows; - public ValueReplacement(String varName, Collection items) { + public ValueReplacement(String varName, Collection> rowItems) { this.varName = varName; - this.items = items; - this.rowItems = new ArrayList<>(); - this.isRows = false; - } - - public ValueReplacement(String varName, Collection> rowItems, Boolean isRows) { - this.varName = varName; - this.items = new ArrayList<>(); this.rowItems = rowItems; - this.isRows = isRows; } public String apply(String command) { - if (items.isEmpty() && rowItems.isEmpty()) { + if (rowItems.isEmpty()) { return command; } @@ -1857,59 +1855,38 @@ public String apply(String command) { validateValuesSafeToInject(command, targetVars); String target = createTarget(); - - StringBuilder replacement; - if (isRows) { - replacement = rowsApply(); - } else { - replacement = singleApply(targetVars.length); - } + StringBuilder replacement = buildReplacement(targetVars.length); return command.replace(target, replacement); } - private StringBuilder rowsApply() { - StringBuilder replacement = new StringBuilder(""); - - for (List row : rowItems) { - replacement.append("("); - for (RDFNode item : row) { - String insert = FmtUtils.stringForNode(item.asNode(), (PrefixMapping) null); - replacement.append(insert); - replacement.append(" "); - } - - replacement.deleteCharAt(replacement.length() - 1); - replacement.append(") "); - } - - replacement.deleteCharAt(replacement.length() - 1); - return replacement; - } - - private StringBuilder singleApply(int targetVarCount) { + private StringBuilder buildReplacement(int targetVarCount) { StringBuilder replacement = new StringBuilder(""); if (targetVarCount == 1) { - for (RDFNode item : items) { + for (List row : rowItems) { + for (RDFNode item : row) { replacement.append("("); String insert = FmtUtils.stringForNode(item.asNode(), (PrefixMapping) null); replacement.append(insert); replacement.append(") "); + } } - replacement.deleteCharAt(replacement.length() - 1); } else { - replacement.append("("); - for (RDFNode item : items) { - String insert = FmtUtils.stringForNode(item.asNode(), (PrefixMapping) null); - replacement.append(insert); - replacement.append(" "); + for (List row : rowItems) { + replacement.append("("); + for (RDFNode item : row) { + String insert = FmtUtils.stringForNode(item.asNode(), (PrefixMapping) null); + replacement.append(insert); + replacement.append(" "); + } + replacement.deleteCharAt(replacement.length() - 1); + replacement.append(") "); } - replacement.deleteCharAt(replacement.length() - 1); - replacement.append(")"); } + replacement.deleteCharAt(replacement.length() - 1); return replacement; } @@ -1933,29 +1910,25 @@ private String createTarget() { protected void validateValuesSafeToInject(String command, String[] targetVars) { - for (int i = 0; i < targetVars.length; i++) { - String targetVar = targetVars[i]; - if (isRows) { - //Iterate through each row according to the position of var and item. - for (List row : rowItems) { - RDFNode item = row.get(i); + if (targetVars.length == 1) { + //Single var with one or more items so all checked against the same var. + String targetVar = targetVars[0]; + for (List row : rowItems) { + for (RDFNode item : row) { validateSafeToInject(command, targetVar, item.asNode()); } - } else { - if (targetVars.length > 1) { - if (items instanceof List) { - //Multiple vars with items in an ordered list. Each var is checked against the item. - List listItems = (List) items; - RDFNode item = listItems.get(i); + } + } else { + //Multiple var with one or more rows. + for (int i = 0; i < targetVars.length; i++) { + String targetVar = targetVars[i]; + for (List row : rowItems) { + if (targetVars.length == row.size()) { + RDFNode item = row.get(i); validateSafeToInject(command, targetVar, item.asNode()); } else { - //Multiple vars with items not in an ordered list. This is parsing error. - throw new ARQException("Multiple VALUES variables (" + String.join(", ", targetVars) + ") being used without an ordered list of items: " + items.toString()); - } - } else { - //Single var with one or more items so all are checked. - for (RDFNode item : items) { - validateSafeToInject(command, targetVar, item.asNode()); + String rowString = row.stream().map(RDFNode::toString).collect(Collectors.joining(",")); + throw new ARQException("Number of VALUES variables (" + String.join(", ", targetVars) + ") does not equal replacement row (" + rowString + ")."); } } } From ec242ef1ffc5cf081f5e8652dd72c4b8002f7b32 Mon Sep 17 00:00:00 2001 From: Greg Albiston Date: Thu, 2 Aug 2018 14:24:00 +0100 Subject: [PATCH 12/18] ARQ:Query:ParameterizedSparqlString - "extractTargetVars" method now checks for missing VALUES keyword and braces. Additional test cases added. --- .../jena/query/ParameterizedSparqlString.java | 18 ++-- .../query/TestParameterizedSparqlString.java | 94 +++++++++++++++++-- 2 files changed, 98 insertions(+), 14 deletions(-) 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 c3b38574fce..b65106e6ff3 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 @@ -1816,17 +1816,18 @@ private String applyValues(String command) { private static final String VALUES_KEYWORD = "values"; protected static String[] extractTargetVars(String command, String varName) { - String[] targetVars; + String[] targetVars = new String[]{}; int varIndex = command.indexOf(varName); if (varIndex > -1) { String subCmd = command.substring(0, varIndex).toLowerCase(); //Truncate the command at the varName. Lowercase to search both types of values. int valuesIndex = subCmd.lastIndexOf(VALUES_KEYWORD); - int bracesIndex = subCmd.lastIndexOf("{"); - String vars = command.substring(valuesIndex + VALUES_KEYWORD.length(), bracesIndex); - targetVars = vars.replaceAll("[(?)]", "").trim().split(" "); - } else { - targetVars = new String[]{}; + int openBracesIndex = subCmd.lastIndexOf("{"); + int closeBracesIndex = subCmd.lastIndexOf("}"); + if (valuesIndex > -1 && valuesIndex < openBracesIndex && closeBracesIndex < valuesIndex) { //Ensure that VALUES keyword is found, open braces index is located after the VALUES and any close braces is located before the VALUES. + String vars = command.substring(valuesIndex + VALUES_KEYWORD.length(), openBracesIndex); + targetVars = vars.replaceAll("[(?)]", "").trim().split(" "); + } } return targetVars; } @@ -1852,6 +1853,11 @@ public String apply(String command) { } String[] targetVars = extractTargetVars(command, varName); + if (targetVars.length == 0) { + //VALUES keyword has not been found or there is another issue so do not modify the command. + return command; + } + validateValuesSafeToInject(command, targetVars); String target = createTarget(); diff --git a/jena-arq/src/test/java/org/apache/jena/query/TestParameterizedSparqlString.java b/jena-arq/src/test/java/org/apache/jena/query/TestParameterizedSparqlString.java index 1cf331234cf..6f174fbfe0b 100644 --- a/jena-arq/src/test/java/org/apache/jena/query/TestParameterizedSparqlString.java +++ b/jena-arq/src/test/java/org/apache/jena/query/TestParameterizedSparqlString.java @@ -1992,8 +1992,8 @@ public void test_set_values_multi_var() { String exp = "SELECT * WHERE { VALUES ?p {() ()} VALUES ?o {(\"obj_A\") (\"obj_B\")} ?s ?p ?o }"; String res = pss.toString(); - System.out.println("Exp: " + exp); - System.out.println("Res: " + res); + //System.out.println("Exp: " + exp); + //System.out.println("Res: " + res); Assert.assertEquals(exp, res); } @@ -2042,8 +2042,8 @@ public void test_extract_target_vars() { String[] res = ParameterizedSparqlString.extractTargetVars(cmd, varName); String[] exp = new String[]{"o"}; - //System.out.println("Exp: " + exp); - //System.out.println("Res: " + res); + //System.out.println("Exp: " + String.join(",", exp)); + //System.out.println("Res: " + String.join(",", res)); Assert.assertArrayEquals(exp, res); } @@ -2055,8 +2055,8 @@ public void test_extract_two_target_vars() { String[] res = ParameterizedSparqlString.extractTargetVars(cmd, varName); String[] exp = new String[]{"p", "o"}; - //System.out.println("Exp: " + exp); - //System.out.println("Res: " + res); + ///System.out.println("Exp: " + String.join(",", exp)); + //System.out.println("Res: " + String.join(",", res)); Assert.assertArrayEquals(exp, res); } @@ -2068,8 +2068,86 @@ public void test_extract_multiple_target_vars() { String[] res = ParameterizedSparqlString.extractTargetVars(cmd, varName); String[] exp = new String[]{"o"}; - //System.out.println("Exp: " + exp); - //System.out.println("Res: " + res); + //System.out.println("Exp: " + String.join(",", exp)); + //System.out.println("Res: " + String.join(",", res)); + Assert.assertArrayEquals(exp, res); + } + + @Test + public void test_extract_target_vars_missing_target() { + // Missing target variable name so should return empty array. + String cmd = "SELECT * WHERE { VALUES ?o {} ?s ?p ?o }"; + String varName = "objs"; + String[] res = ParameterizedSparqlString.extractTargetVars(cmd, varName); + String[] exp = new String[]{}; + + //System.out.println("Exp: " + String.join(",", exp)); + //System.out.println("Res: " + String.join(",", res)); + Assert.assertArrayEquals(exp, res); + } + + @Test + public void test_extract_target_vars_missing_brace() { + // Missing brace so should return empty array. + String cmd = "SELECT * WHERE { VALUES ?o ?objs} ?s ?p ?o }"; + String varName = "objs"; + String[] res = ParameterizedSparqlString.extractTargetVars(cmd, varName); + String[] exp = new String[]{}; + + //System.out.println("Exp: " + String.join(",", exp)); + //System.out.println("Res: " + String.join(",", res)); + Assert.assertArrayEquals(exp, res); + } + + @Test + public void test_extract_multiple_target_vars_missing_brace() { + // Missing brace so should return empty array. + String cmd = "SELECT * WHERE { VALUES ?p {?props} VALUES ?o ?objs} ?s ?p ?o }"; + String varName = "objs"; + String[] res = ParameterizedSparqlString.extractTargetVars(cmd, varName); + String[] exp = new String[]{}; + + //System.out.println("Exp: " + String.join(",", exp)); + //System.out.println("Res: " + String.join(",", res)); + Assert.assertArrayEquals(exp, res); + } + + @Test + public void test_extract_target_vars_missing_values() { + // Missing VALUES keyword so should return empty array. + String cmd = "SELECT * WHERE { ?o {?objs} ?s ?p ?o }"; + String varName = "objs"; + String[] res = ParameterizedSparqlString.extractTargetVars(cmd, varName); + String[] exp = new String[]{}; + + //System.out.println("Exp: " + String.join(",", exp)); + //System.out.println("Res: " + String.join(",", res)); + Assert.assertArrayEquals(exp, res); + } + + @Test + public void test_extract_multiple_target_vars_missing_values() { + // Missing VALUES keyword so should return empty array. + String cmd = "SELECT * WHERE { VALUES ?p {?props} ?o {?objs} ?s ?p ?o }"; + String varName = "objs"; + String[] res = ParameterizedSparqlString.extractTargetVars(cmd, varName); + String[] exp = new String[]{}; + + //System.out.println("Exp: " + String.join(",", exp)); + //System.out.println("Res: " + String.join(",", res)); + Assert.assertArrayEquals(exp, res); + } + + @Test + public void test_extract_multiple_target_vars_no_braces() { + // Missing braces and VALUES keyword so should return empty array. + String cmd = "SELECT * WHERE { VALUES ?p ?props ?o ?objs ?s ?p ?o }"; + String varName = "objs"; + String[] res = ParameterizedSparqlString.extractTargetVars(cmd, varName); + String[] exp = new String[]{}; + + //System.out.println("Exp: " + String.join(",", exp)); + //System.out.println("Res: " + String.join(",", res)); Assert.assertArrayEquals(exp, res); } From 5eb24eb0675e5cc5dd7c6d92cca7f3eeb6b76dba Mon Sep 17 00:00:00 2001 From: Greg Albiston Date: Thu, 2 Aug 2018 14:42:07 +0100 Subject: [PATCH 13/18] ARQ:Query:ParameterizedSparqlString - targetting varNames now takes into account "$" variable syntax. --- .../jena/query/ParameterizedSparqlString.java | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) 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 b65106e6ff3..b1b54460071 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 @@ -1826,7 +1826,7 @@ protected static String[] extractTargetVars(String command, String varName) { int closeBracesIndex = subCmd.lastIndexOf("}"); if (valuesIndex > -1 && valuesIndex < openBracesIndex && closeBracesIndex < valuesIndex) { //Ensure that VALUES keyword is found, open braces index is located after the VALUES and any close braces is located before the VALUES. String vars = command.substring(valuesIndex + VALUES_KEYWORD.length(), openBracesIndex); - targetVars = vars.replaceAll("[(?)]", "").trim().split(" "); + targetVars = vars.replaceAll("[(?$)]", "").trim().split(" "); } } return targetVars; @@ -1861,13 +1861,12 @@ public String apply(String command) { validateValuesSafeToInject(command, targetVars); String target = createTarget(); - StringBuilder replacement = buildReplacement(targetVars.length); + String replacement = buildReplacement(targetVars.length); - return command.replace(target, replacement); + return command.replaceAll(target, replacement); } - - private StringBuilder buildReplacement(int targetVarCount) { + private String buildReplacement(int targetVarCount) { StringBuilder replacement = new StringBuilder(""); @@ -1894,7 +1893,7 @@ private StringBuilder buildReplacement(int targetVarCount) { } replacement.deleteCharAt(replacement.length() - 1); - return replacement; + return replacement.toString(); } /** @@ -1909,7 +1908,7 @@ private String createTarget() { if (varName.startsWith("?") || varName.startsWith("$")) { target = varName; } else { - target = "?" + varName; + target = "[?$]" + varName; } return target; } From 16fb7ce9a3a08abb53aa791b938166e9f612ab0c Mon Sep 17 00:00:00 2001 From: Greg Albiston Date: Thu, 2 Aug 2018 14:48:35 +0100 Subject: [PATCH 14/18] ARQ:Query:ParameterizedSparqlString - Tests for malformed queries which leave the query unchanged. Following current class design where syntax correctness is checked and reported by Query or UpdateRequest. --- .../query/TestParameterizedSparqlString.java | 56 +++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/jena-arq/src/test/java/org/apache/jena/query/TestParameterizedSparqlString.java b/jena-arq/src/test/java/org/apache/jena/query/TestParameterizedSparqlString.java index 6f174fbfe0b..4a050c6c0bb 100644 --- a/jena-arq/src/test/java/org/apache/jena/query/TestParameterizedSparqlString.java +++ b/jena-arq/src/test/java/org/apache/jena/query/TestParameterizedSparqlString.java @@ -1941,6 +1941,62 @@ public void test_set_values_item() { Assert.assertEquals(exp, res); } + @Test + public void test_set_values_item2() { + // Tests a single value being added using '$' variable syntax - always adding parenthesis. + String str = "SELECT * WHERE { VALUES $o {$objs} $s $p $o }"; + ParameterizedSparqlString pss = new ParameterizedSparqlString(str); + pss.setValues("objs", ResourceFactory.createPlainLiteral("test")); + + String exp = "SELECT * WHERE { VALUES $o {(\"test\")} $s $p $o }"; + String res = pss.toString(); + //System.out.println("Exp: " + exp); + //System.out.println("Res: " + res); + Assert.assertEquals(exp, res); + } + + @Test + public void test_set_values_item_missing_values() { + // VALUES keyword missing so query is unchanged. + String str = "SELECT * WHERE { ?o {?objs} ?s ?p ?o }"; + ParameterizedSparqlString pss = new ParameterizedSparqlString(str); + pss.setValues("objs", ResourceFactory.createPlainLiteral("test")); + + String exp = "SELECT * WHERE { ?o {?objs} ?s ?p ?o }"; + String res = pss.toString(); + //System.out.println("Exp: " + exp); + //System.out.println("Res: " + res); + Assert.assertEquals(exp, res); + } + + @Test + public void test_set_values_item_missing_braces() { + // Braces missing so query is unchanged. + String str = "SELECT * WHERE { VALUES ?o ?objs ?s ?p ?o }"; + ParameterizedSparqlString pss = new ParameterizedSparqlString(str); + pss.setValues("objs", ResourceFactory.createPlainLiteral("test")); + + String exp = "SELECT * WHERE { VALUES ?o ?objs ?s ?p ?o }"; + String res = pss.toString(); + //System.out.println("Exp: " + exp); + //System.out.println("Res: " + res); + Assert.assertEquals(exp, res); + } + + @Test + public void test_set_values_item_missing_varName() { + // varName missing ('props' instead of 'objs') so query is unchanged. + String str = "SELECT * WHERE { VALUES ?o {?objs} ?s ?p ?o }"; + ParameterizedSparqlString pss = new ParameterizedSparqlString(str); + pss.setValues("props", ResourceFactory.createPlainLiteral("test")); + + String exp = "SELECT * WHERE { VALUES ?o {?objs} ?s ?p ?o }"; + String res = pss.toString(); + //System.out.println("Exp: " + exp); + //System.out.println("Res: " + res); + Assert.assertEquals(exp, res); + } + @Test public void test_set_values_items_parenthesis() { // Tests two values for same variable. From 9c72875ef6e77367877b4c8b168e520de059c77b Mon Sep 17 00:00:00 2001 From: Greg Albiston Date: Thu, 2 Aug 2018 14:54:05 +0100 Subject: [PATCH 15/18] ARQ:Query:ParameterizedSparqlString - valuesReplacement now cleared by "clearParam(String var)" and "clearParams" in line with the other stored parameters. --- .../org/apache/jena/query/ParameterizedSparqlString.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) 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 b1b54460071..5305654a976 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 @@ -1159,14 +1159,15 @@ public Iterator getEligiblePositionalParameters() { } /** - * Clears the value for a variable parameter so the given variable will not - * have a value injected + * Clears the value for a variable or values parameter so the given variable + * will not * have a value injected * * @param var * Variable */ public void clearParam(String var) { this.params.remove(var); + this.valuesReplacements.remove(var); } /** @@ -1180,10 +1181,11 @@ public void clearParam(int index) { } /** - * Clears all values for both variable and positional parameters + * Clears all values for variable, values and positional parameters */ public void clearParams() { this.params.clear(); + this.valuesReplacements.clear(); this.positionalParams.clear(); } From d1ee09b4d4e844bcced79e9ef2ab04cd26018997 Mon Sep 17 00:00:00 2001 From: Greg Albiston Date: Thu, 2 Aug 2018 15:01:13 +0100 Subject: [PATCH 16/18] ARQ:Query:ParameterizedSparqlString - replacement values now use the same mechanism to apply context prefixes as the remainder of class to provide consistency. --- .../jena/query/ParameterizedSparqlString.java | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) 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 5305654a976..b8b8978ecb0 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 @@ -1334,7 +1334,7 @@ public String toString() { } // Inject Values Parameters - command = applyValues(command); + command = applyValues(command, context); // Then inject Positional Parameters // To do this we need to find the ? we will replace @@ -1807,10 +1807,10 @@ public void setRowValues(String varName, Collection> row this.valuesReplacements.put(varName, new ValueReplacement(varName, rowItems)); } - private String applyValues(String command) { + private String applyValues(String command, SerializationContext context) { for (ValueReplacement valueReplacement : valuesReplacements.values()) { - command = valueReplacement.apply(command); + command = valueReplacement.apply(command, context); } return command; } @@ -1848,7 +1848,7 @@ public ValueReplacement(String varName, Collection> rowI this.rowItems = rowItems; } - public String apply(String command) { + public String apply(String command, SerializationContext context) { if (rowItems.isEmpty()) { return command; @@ -1863,29 +1863,29 @@ public String apply(String command) { validateValuesSafeToInject(command, targetVars); String target = createTarget(); - String replacement = buildReplacement(targetVars.length); + String replacement = buildReplacement(targetVars.length, context); return command.replaceAll(target, replacement); } - private String buildReplacement(int targetVarCount) { + private String buildReplacement(int targetVarCount, SerializationContext context) { StringBuilder replacement = new StringBuilder(""); if (targetVarCount == 1) { for (List row : rowItems) { for (RDFNode item : row) { - replacement.append("("); - String insert = FmtUtils.stringForNode(item.asNode(), (PrefixMapping) null); - replacement.append(insert); - replacement.append(") "); + replacement.append("("); + String insert = stringForNode(item.asNode(), context); + replacement.append(insert); + replacement.append(") "); } } } else { for (List row : rowItems) { replacement.append("("); for (RDFNode item : row) { - String insert = FmtUtils.stringForNode(item.asNode(), (PrefixMapping) null); + String insert = stringForNode(item.asNode(), context); replacement.append(insert); replacement.append(" "); } From c865069a56f595df50f665c8155c67a600e4c91d Mon Sep 17 00:00:00 2001 From: Greg Albiston Date: Thu, 2 Aug 2018 15:09:09 +0100 Subject: [PATCH 17/18] ARQ:Query:ParameterizedSparqlString - replaced "varName" with "valueName" to distinguish from the "vars" that will be substituted when query is executed. --- .../jena/query/ParameterizedSparqlString.java | 66 +++++++++---------- .../query/TestParameterizedSparqlString.java | 48 +++++++------- 2 files changed, 57 insertions(+), 57 deletions(-) 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 b8b8978ecb0..acf50e8ed6b 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 @@ -1744,18 +1744,18 @@ public String toString() { } /** - * Assign a VALUES varName with a multiple items.
+ * Assign a VALUES valueName with a multiple items.
* Can be used to assign multiple values to a single variable or single * value to multiple variables (if using a List) in the SPARQL query.
* See setRowValues to assign multiple values to multiple variables.
- * Using "var" with list(prop_A, obj_A) on query "VALUES (?p ?o) {?var}" - * would produce "VALUES (?p ?o) {(prop_A obj_A)}". + * Using "valueName" with list(prop_A, obj_A) on query "VALUES (?p ?o) + * {?valueName}" * would produce "VALUES (?p ?o) {(prop_A obj_A)}". * * - * @param varName + * @param valueName * @param items */ - public void setValues(String varName, Collection items) { + public void setValues(String valueName, Collection items) { items.forEach(item -> validateParameterValue(item.asNode())); //Ensure that a list is used for the items. @@ -1765,24 +1765,24 @@ public void setValues(String varName, Collection items) { } else { rowItems.add(new ArrayList<>(items)); } - this.valuesReplacements.put(varName, new ValueReplacement(varName, rowItems)); + this.valuesReplacements.put(valueName, new ValueReplacement(valueName, rowItems)); } /** - * Assign a VALUES varName with a single item.
- * Using "var" with Literal obj_A on query "VALUES ?o {?var}" would produce - * "VALUES ?o {obj_A}". + * Assign a VALUES valueName with a single item.
+ * Using "valueName" with Literal obj_A on query "VALUES ?o {?valueName}" + * would produce * "VALUES ?o {obj_A}". * - * @param varName + * @param valueName * @param item */ - public void setValues(String varName, RDFNode item) { - setValues(varName, Arrays.asList(item)); + public void setValues(String valueName, RDFNode item) { + setValues(valueName, Arrays.asList(item)); } /** * ** - * Sets a map of VALUES varNames and their items.
+ * Sets a map of VALUES valueNames and their items.
* Can be used to assign multiple values to a single variable or single * value to multiple variables (if using a List) in the SPARQL query.
* See setRowValues to assign multiple values to multiple variables. @@ -1794,17 +1794,17 @@ public void setValues(Map> itemsMap) { } /** - * Allocate multiple lists of variables to a single VALUES varName.
- * Using "vars" with list(list(prop_A, obj_A), list(prop_B, obj_B)) on query - * "VALUES (?p ?o) {?vars}" would produce "VALUES (?p ?o) {(prop_A obj_A) - * (prop_B obj_B)}". + * Allocate multiple lists of variables to a single VALUES valueName.
+ * Using "valuesName" with list(list(prop_A, obj_A), list(prop_B, obj_B)) on + * query "VALUES (?p ?o) {?valuesName}" would produce "VALUES (?p ?o) + * {(prop_A obj_A) * (prop_B obj_B)}". * - * @param varName + * @param valueName * @param rowItems */ - public void setRowValues(String varName, Collection> rowItems) { + public void setRowValues(String valueName, Collection> rowItems) { rowItems.forEach(collection -> collection.forEach(item -> validateParameterValue(item.asNode()))); - this.valuesReplacements.put(varName, new ValueReplacement(varName, rowItems)); + this.valuesReplacements.put(valueName, new ValueReplacement(valueName, rowItems)); } private String applyValues(String command, SerializationContext context) { @@ -1817,12 +1817,12 @@ private String applyValues(String command, SerializationContext context) { private static final String VALUES_KEYWORD = "values"; - protected static String[] extractTargetVars(String command, String varName) { + protected static String[] extractTargetVars(String command, String valueName) { String[] targetVars = new String[]{}; - int varIndex = command.indexOf(varName); - if (varIndex > -1) { - String subCmd = command.substring(0, varIndex).toLowerCase(); //Truncate the command at the varName. Lowercase to search both types of values. + int valueIndex = command.indexOf(valueName); + if (valueIndex > -1) { + String subCmd = command.substring(0, valueIndex).toLowerCase(); //Truncate the command at the valueName. Lowercase to search both cases of VALUES keyword. int valuesIndex = subCmd.lastIndexOf(VALUES_KEYWORD); int openBracesIndex = subCmd.lastIndexOf("{"); int closeBracesIndex = subCmd.lastIndexOf("}"); @@ -1840,11 +1840,11 @@ protected static String[] extractTargetVars(String command, String varName) { */ private class ValueReplacement { - private final String varName; + private final String valueName; private final Collection> rowItems; - public ValueReplacement(String varName, Collection> rowItems) { - this.varName = varName; + public ValueReplacement(String valueName, Collection> rowItems) { + this.valueName = valueName; this.rowItems = rowItems; } @@ -1854,7 +1854,7 @@ public String apply(String command, SerializationContext context) { return command; } - String[] targetVars = extractTargetVars(command, varName); + String[] targetVars = extractTargetVars(command, valueName); if (targetVars.length == 0) { //VALUES keyword has not been found or there is another issue so do not modify the command. return command; @@ -1899,18 +1899,18 @@ private String buildReplacement(int targetVarCount, SerializationContext context } /** - * Tidy up varName if doesn't start with a ? or $. + * Tidy up valueName if doesn't start with a ? or $. * - * @param varName + * @param valueName * @return */ private String createTarget() { String target; - if (varName.startsWith("?") || varName.startsWith("$")) { - target = varName; + if (valueName.startsWith("?") || valueName.startsWith("$")) { + target = valueName; } else { - target = "[?$]" + varName; + target = "[?$]" + valueName; } return target; } diff --git a/jena-arq/src/test/java/org/apache/jena/query/TestParameterizedSparqlString.java b/jena-arq/src/test/java/org/apache/jena/query/TestParameterizedSparqlString.java index 4a050c6c0bb..589a005b9a5 100644 --- a/jena-arq/src/test/java/org/apache/jena/query/TestParameterizedSparqlString.java +++ b/jena-arq/src/test/java/org/apache/jena/query/TestParameterizedSparqlString.java @@ -1984,8 +1984,8 @@ public void test_set_values_item_missing_braces() { } @Test - public void test_set_values_item_missing_varName() { - // varName missing ('props' instead of 'objs') so query is unchanged. + public void test_set_values_item_missing_valueName() { + // valueName missing ('props' instead of 'objs') so query is unchanged. String str = "SELECT * WHERE { VALUES ?o {?objs} ?s ?p ?o }"; ParameterizedSparqlString pss = new ParameterizedSparqlString(str); pss.setValues("props", ResourceFactory.createPlainLiteral("test")); @@ -2092,10 +2092,10 @@ public void test_set_values_uri_injection() { @Test public void test_extract_target_vars() { - // Identifies the vars in the VALUES clause according to the substituting varName. + // Identifies the vars in the VALUES clause according to the substituting valueName. String cmd = "SELECT * WHERE { VALUES ?o {?objs} ?s ?p ?o }"; - String varName = "objs"; - String[] res = ParameterizedSparqlString.extractTargetVars(cmd, varName); + String valueName = "objs"; + String[] res = ParameterizedSparqlString.extractTargetVars(cmd, valueName); String[] exp = new String[]{"o"}; //System.out.println("Exp: " + String.join(",", exp)); @@ -2105,10 +2105,10 @@ public void test_extract_target_vars() { @Test public void test_extract_two_target_vars() { - // Identifies the vars in the VALUES clause according to the substituting varName. - String cmd = "SELECT * WHERE { VALUES(?p ?o){?vars} ?s ?p ?o }"; - String varName = "vars"; - String[] res = ParameterizedSparqlString.extractTargetVars(cmd, varName); + // Identifies the vars in the VALUES clause according to the substituting valueName. + String cmd = "SELECT * WHERE { VALUES(?p ?o){?valuesName} ?s ?p ?o }"; + String valueName = "valuesName"; + String[] res = ParameterizedSparqlString.extractTargetVars(cmd, valueName); String[] exp = new String[]{"p", "o"}; ///System.out.println("Exp: " + String.join(",", exp)); @@ -2118,10 +2118,10 @@ public void test_extract_two_target_vars() { @Test public void test_extract_multiple_target_vars() { - // Identifies the vars in the VALUES clause according to the substituting varName. + // Identifies the vars in the VALUES clause according to the substituting valueName. String cmd = "SELECT * WHERE { VALUES ?p {?props} VALUES ?o {?objs} ?s ?p ?o }"; - String varName = "objs"; - String[] res = ParameterizedSparqlString.extractTargetVars(cmd, varName); + String valueName = "objs"; + String[] res = ParameterizedSparqlString.extractTargetVars(cmd, valueName); String[] exp = new String[]{"o"}; //System.out.println("Exp: " + String.join(",", exp)); @@ -2133,8 +2133,8 @@ public void test_extract_multiple_target_vars() { public void test_extract_target_vars_missing_target() { // Missing target variable name so should return empty array. String cmd = "SELECT * WHERE { VALUES ?o {} ?s ?p ?o }"; - String varName = "objs"; - String[] res = ParameterizedSparqlString.extractTargetVars(cmd, varName); + String valueName = "objs"; + String[] res = ParameterizedSparqlString.extractTargetVars(cmd, valueName); String[] exp = new String[]{}; //System.out.println("Exp: " + String.join(",", exp)); @@ -2146,8 +2146,8 @@ public void test_extract_target_vars_missing_target() { public void test_extract_target_vars_missing_brace() { // Missing brace so should return empty array. String cmd = "SELECT * WHERE { VALUES ?o ?objs} ?s ?p ?o }"; - String varName = "objs"; - String[] res = ParameterizedSparqlString.extractTargetVars(cmd, varName); + String valueName = "objs"; + String[] res = ParameterizedSparqlString.extractTargetVars(cmd, valueName); String[] exp = new String[]{}; //System.out.println("Exp: " + String.join(",", exp)); @@ -2159,8 +2159,8 @@ public void test_extract_target_vars_missing_brace() { public void test_extract_multiple_target_vars_missing_brace() { // Missing brace so should return empty array. String cmd = "SELECT * WHERE { VALUES ?p {?props} VALUES ?o ?objs} ?s ?p ?o }"; - String varName = "objs"; - String[] res = ParameterizedSparqlString.extractTargetVars(cmd, varName); + String valueName = "objs"; + String[] res = ParameterizedSparqlString.extractTargetVars(cmd, valueName); String[] exp = new String[]{}; //System.out.println("Exp: " + String.join(",", exp)); @@ -2172,8 +2172,8 @@ public void test_extract_multiple_target_vars_missing_brace() { public void test_extract_target_vars_missing_values() { // Missing VALUES keyword so should return empty array. String cmd = "SELECT * WHERE { ?o {?objs} ?s ?p ?o }"; - String varName = "objs"; - String[] res = ParameterizedSparqlString.extractTargetVars(cmd, varName); + String valueName = "objs"; + String[] res = ParameterizedSparqlString.extractTargetVars(cmd, valueName); String[] exp = new String[]{}; //System.out.println("Exp: " + String.join(",", exp)); @@ -2185,8 +2185,8 @@ public void test_extract_target_vars_missing_values() { public void test_extract_multiple_target_vars_missing_values() { // Missing VALUES keyword so should return empty array. String cmd = "SELECT * WHERE { VALUES ?p {?props} ?o {?objs} ?s ?p ?o }"; - String varName = "objs"; - String[] res = ParameterizedSparqlString.extractTargetVars(cmd, varName); + String valueName = "objs"; + String[] res = ParameterizedSparqlString.extractTargetVars(cmd, valueName); String[] exp = new String[]{}; //System.out.println("Exp: " + String.join(",", exp)); @@ -2198,8 +2198,8 @@ public void test_extract_multiple_target_vars_missing_values() { public void test_extract_multiple_target_vars_no_braces() { // Missing braces and VALUES keyword so should return empty array. String cmd = "SELECT * WHERE { VALUES ?p ?props ?o ?objs ?s ?p ?o }"; - String varName = "objs"; - String[] res = ParameterizedSparqlString.extractTargetVars(cmd, varName); + String valueName = "objs"; + String[] res = ParameterizedSparqlString.extractTargetVars(cmd, valueName); String[] exp = new String[]{}; //System.out.println("Exp: " + String.join(",", exp)); From b405304988fad9f872cb99fcc7fbd4e7ae259e41 Mon Sep 17 00:00:00 2001 From: Greg Albiston Date: Thu, 2 Aug 2018 15:39:43 +0100 Subject: [PATCH 18/18] ARQ:Query:TestParameterizedSparqlString - tests for incorrect number of values for variables. --- .../query/TestParameterizedSparqlString.java | 32 +++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/jena-arq/src/test/java/org/apache/jena/query/TestParameterizedSparqlString.java b/jena-arq/src/test/java/org/apache/jena/query/TestParameterizedSparqlString.java index 589a005b9a5..7a32ded46a5 100644 --- a/jena-arq/src/test/java/org/apache/jena/query/TestParameterizedSparqlString.java +++ b/jena-arq/src/test/java/org/apache/jena/query/TestParameterizedSparqlString.java @@ -2015,7 +2015,7 @@ public void test_set_values_items_parenthesis() { } @Test - public void test_set_values_multiple_variables_parenthesis() { + public void test_set_values_multiple_variables() { // Tests two values for same variable. String str = "SELECT * WHERE { VALUES (?p ?o) {?vars} ?s ?p ?o }"; ParameterizedSparqlString pss = new ParameterizedSparqlString(str); @@ -2031,6 +2031,34 @@ public void test_set_values_multiple_variables_parenthesis() { Assert.assertEquals(exp, res); } + @Test(expected = ARQException.class) + public void test_set_values_multiple_variables_too_few() { + // Test of one value for two variables. + String str = "SELECT * WHERE { VALUES (?p ?o) {?vars} ?s ?p ?o }"; + ParameterizedSparqlString pss = new ParameterizedSparqlString(str); + List vars = new ArrayList<>(); + vars.add(ResourceFactory.createProperty("http://example.org/prop_A")); + pss.setValues("vars", vars); + + pss.toString(); + Assert.fail("Attempt to insert incorrect number of values."); + } + + @Test(expected = ARQException.class) + public void test_set_values_multiple_variables_too_many() { + // Test of three values for two variables. + String str = "SELECT * WHERE { VALUES (?p ?o) {?vars} ?s ?p ?o }"; + ParameterizedSparqlString pss = new ParameterizedSparqlString(str); + List vars = new ArrayList<>(); + vars.add(ResourceFactory.createProperty("http://example.org/prop_A")); + vars.add(ResourceFactory.createPlainLiteral("obj_A")); + vars.add(ResourceFactory.createPlainLiteral("obj_A")); + pss.setValues("vars", vars); + + pss.toString(); + Assert.fail("Attempt to insert incorrect number of values."); + } + @Test public void test_set_values_multi_var() { // Tests two variables - always adding parenthesis. @@ -2086,7 +2114,7 @@ public void test_set_values_uri_injection() { ParameterizedSparqlString pss = new ParameterizedSparqlString(str); pss.setValues(str, ResourceFactory.createResource("")); - pss.asQuery(); + pss.toString(); Assert.fail("Attempt to do SPARQL injection should result in an exception"); }