[
https://issues.apache.org/jira/browse/JENA-1578?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=16560884#comment-16560884
]
ASF GitHub Bot commented on JENA-1578:
--------------------------------------
Github user afs commented on a diff in the pull request:
https://github.com/apache/jena/pull/449#discussion_r205953412
--- Diff:
jena-arq/src/main/java/org/apache/jena/query/ParameterizedSparqlString.java ---
@@ -1734,4 +1739,237 @@ 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<? extends RDFNode>
items, boolean isParenthesisNeeded) {
+ this.valuesReplacements.put(varName, new ValueReplacement(varName,
items, isParenthesisNeeded));
+ }
+
+ /**
+ * Assign a varName with a multiple items.<br>
+ * 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.<br>
+ * See setGroupedValues to assign multiple values to multiple
variables.
+ *
+ * @param varName
+ * @param items
+ */
+ public void setValues(String varName, Collection<? extends RDFNode>
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<String, Collection<? extends RDFNode>>
valuesItems) {
+ valuesItems.forEach(this::setValues);
+ }
+
+ /**
+ * All varNames in the map will use the same approach to parenthesis.
+ *
+ * @param valuesItems
+ * @param isParenthesisNeeded
+ */
+ public void setValues(Map<String, Collection<? extends RDFNode>>
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.
+ *
+ * @param valuesItems
+ * @param valuesParenthesis
+ */
+ public void setValues(Map<String, Collection<? extends RDFNode>>
valuesItems, Map<String, Boolean> valuesParenthesis) {
+
+ for (String varName : valuesItems.keySet()) {
+ Collection<? extends RDFNode> 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.<br>
+ * 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<List<? extends
RDFNode>> 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<? extends RDFNode> items;
+ private final Collection<List<? extends RDFNode>> groupedItems;
+ private final Boolean isParenthesisNeeded;
+ private final Boolean isGrouped;
+
+ public ValueReplacement(String varName, Collection<? extends
RDFNode> items, Boolean isParenthesisNeeded) {
+ this.varName = varName;
+ this.items = items;
+ this.groupedItems = new ArrayList<>();
+ this.isParenthesisNeeded = isParenthesisNeeded;
+ this.isGrouped = false;
+ }
+
+ public ValueReplacement(String varName, Collection<List<? extends
RDFNode>> 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<? extends RDFNode> 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()) {
--- End diff --
Literals with language tags need treating specially. Lang tag literals have
datatype rdf:langString, which should not be printed, and the @lang must be
printed.
Literal lexical forms need escaping for any ", newline and tabs in the
string.
Other forms could be made to be more natural : strings without
"^^xsd:string", integers as `123` etc.
The good news is that there is code to help at the node level so the
literal and URI cases can be replaced by
`FmtUtils.stringForNode(rdfNode.asNode(), (PrefixMapping)null)` - must use the
"(PrefixMapping)null" form otherwise a helpful (for display) prefix mapping is
used. This is the code used by the SPARQLserializer.
This includes the delimiters e.g `<...>` or string `"..."`.
The URI may need care in case "`...> finish URI early + something to
follow`" is injected.
> SPARQL VALUES for ParameterizedSparqlString
> -------------------------------------------
>
> Key: JENA-1578
> URL: https://issues.apache.org/jira/browse/JENA-1578
> Project: Apache Jena
> Issue Type: New Feature
> Components: ARQ
> Affects Versions: Jena 3.8.0
> Reporter: Greg Albiston
> Priority: Minor
>
> ParameterizedSparqlString provides an API for substituting variables within
> SPARQL queries with bound values. It does not support the SPARQL VALUES
> keyword which allows multiple values to be specified. The VALUES syntax
> supports multiple values for a single variable, sets of values for multiple
> variables and multiple sets of values for multiple values.
> Inquiry on 24/07/18 the mailing list about this feature. Patch is forthcoming.
--
This message was sent by Atlassian JIRA
(v7.6.3#76005)