This is an automated email from the ASF dual-hosted git repository. andy pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/jena.git
commit c799f21bb8c1a31cfbb53fc3aa6f823d7d4b4461 Author: Andy Seaborne <[email protected]> AuthorDate: Thu Mar 19 22:14:20 2026 +0000 GH-3809: Round-trip equality for CONSTRUCTWHERE --- .../apache/jena/sparql/lang/SPARQLParserBase.java | 42 +++------------ .../org/apache/jena/sparql/modify/TemplateLib.java | 63 +++++++++++++++++++--- 2 files changed, 61 insertions(+), 44 deletions(-) diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/lang/SPARQLParserBase.java b/jena-arq/src/main/java/org/apache/jena/sparql/lang/SPARQLParserBase.java index 9e7e2b788e..200546d9ce 100644 --- a/jena-arq/src/main/java/org/apache/jena/sparql/lang/SPARQLParserBase.java +++ b/jena-arq/src/main/java/org/apache/jena/sparql/lang/SPARQLParserBase.java @@ -24,15 +24,13 @@ package org.apache.jena.sparql.lang; import java.util.*; import org.apache.jena.graph.Node; -import org.apache.jena.graph.NodeFactory; -import org.apache.jena.graph.Triple; import org.apache.jena.query.Query; import org.apache.jena.query.QueryParseException; -import org.apache.jena.sparql.ARQConstants; import org.apache.jena.sparql.ARQInternalErrorException; import org.apache.jena.sparql.core.*; import org.apache.jena.sparql.engine.binding.Binding; import org.apache.jena.sparql.engine.binding.BindingBuilder; +import org.apache.jena.sparql.modify.TemplateLib; import org.apache.jena.sparql.modify.UpdateSink; import org.apache.jena.sparql.modify.request.*; import org.apache.jena.sparql.syntax.*; @@ -51,21 +49,6 @@ public class SPARQLParserBase extends QueryParserBase { } public Query getQuery() { return query; } - - // The ARQ parser is both query and update languages. - -// // ---- SPARQL/Update (Submission) -// private UpdateRequest requestSubmission = null; -// -// protected UpdateRequest getUpdateRequestSubmission() { return requestSubmission; } -// public void setUpdateRequest(UpdateRequest request) -// { -// setPrologue(request); -// this.requestSubmission = request; -// // And create a query because we may have nested selects. -// this.query = new Query (); -// } - private UpdateSink sink = null; // Places to push settings across points where we reset. @@ -244,8 +227,7 @@ public class SPARQLParserBase extends QueryParserBase { rowBuilder = Binding.builder(); } - protected void finishValuesClause(int line, int col) - { + protected void finishValuesClause(int line, int col) { getQuery().setValuesDataBlock(variables, values); } @@ -287,28 +269,16 @@ public class SPARQLParserBase extends QueryParserBase { protected void finishDataBlockValueRow(int line, int col) { //if ( variables.size() != currentValueRow().size() ) - if ( currentColumn+1 != variables.size() ) - { - String msg = String.format("Mismatch: %d variables but %d values",variables.size(), currentColumn+1); + if ( currentColumn + 1 != variables.size() ) { + String msg = String.format("Mismatch: %d variables but %d values", variables.size(), currentColumn + 1); msg = QueryParseException.formatMessage(msg, line, col); - throw new QueryParseException(msg, line , col); + throw new QueryParseException(msg, line, col); } values.add(rowBuilder.build()); } protected ElementGroup templateToQueryPattern(Template template){ - ElementGroup elg = new ElementGroup(); - Map<Node, BasicPattern> graphs = template.getGraphPattern(); - for(Node n: graphs.keySet()){ - Element el = new ElementPathBlock(graphs.get(n)); - if(! Quad.defaultGraphNodeGenerated.equals(n) ){ - ElementGroup e = new ElementGroup(); - e.addElement(el); - el = new ElementNamedGraph(n, e); - } - elg.addElement(el); - } - return elg; + return TemplateLib.templateToQueryPattern(template); } private void pushLabelState() { diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/modify/TemplateLib.java b/jena-arq/src/main/java/org/apache/jena/sparql/modify/TemplateLib.java index a57d60a71b..8d55129024 100644 --- a/jena-arq/src/main/java/org/apache/jena/sparql/modify/TemplateLib.java +++ b/jena-arq/src/main/java/org/apache/jena/sparql/modify/TemplateLib.java @@ -29,19 +29,18 @@ import org.apache.jena.atlas.iterator.Iter; import org.apache.jena.graph.Node; import org.apache.jena.graph.NodeFactory; import org.apache.jena.graph.Triple; +import org.apache.jena.sparql.core.BasicPattern; import org.apache.jena.sparql.core.Quad; import org.apache.jena.sparql.core.Substitute; import org.apache.jena.sparql.core.Var; import org.apache.jena.sparql.engine.binding.Binding; +import org.apache.jena.sparql.lang.LabelToNodeMap; +import org.apache.jena.sparql.syntax.*; import org.apache.jena.sparql.util.NodeUtils; public class TemplateLib { // See also Substitute -- combine? - // Or is this specifc enough to CONSTRUCT/Update template processing? - - // TODO We could eliminate some of the duplication in this class by writing - // generic methods and adding a shared super-interface to Triple and Quad - + // Or is this specific enough to CONSTRUCT/Update template processing? /** * Take a template, as a list of quad patterns, a default graph, and an * iterator of bindings, and produce an iterator of quads that results from @@ -65,7 +64,7 @@ public class TemplateLib { return quads ; Stream<Quad> remappedStream = quads.stream().map(q-> !q.isDefaultGraph() ? q : Quad.create(dftGraph, q.getSubject(), q.getPredicate(), q.getObject()) - ) ; + ); return remappedStream.toList(); } @@ -148,7 +147,6 @@ public class TemplateLib { Quad q = quad; if ( s1 != s || p1 != p || o1 != o || g1 != g ) q = Quad.create(g1, s1, p1, o1); - Quad q2 = Substitute.substitute(q, b); return q2; } @@ -175,7 +173,6 @@ public class TemplateLib { Triple t = triple; if ( s1 != s || p1 != p || o1 != o ) t = Triple.create(s1, p1, o1); - Triple t2 = Substitute.substitute(t, b); return t2; } @@ -186,4 +183,54 @@ public class TemplateLib { bNodeMap.put(n, NodeFactory.createBlankNode()); return bNodeMap.get(n); } + + // ---- Template to query pattern + // Used by CONSTRUCTWHERE to convert a template into a query pattern of the same shape. + + public static ElementGroup templateToQueryPattern(Template template){ + ElementGroup elg = new ElementGroup(); + Map<Node, BasicPattern> graphs = template.getGraphPattern(); + for(Node gn: graphs.keySet()){ + BasicPattern bgp = graphs.get(gn); + BasicPattern bgp2 = templateToQuery(bgp); + Element el = new ElementPathBlock(bgp2); + if(! Quad.defaultGraphNodeGenerated.equals(gn) ){ + ElementGroup e = new ElementGroup(); + e.addElement(el); + el = new ElementNamedGraph(gn, e); + } + elg.addElement(el); + } + return elg; + } + + private static BasicPattern templateToQuery(BasicPattern bgp) { + BasicPattern bgp2 = new BasicPattern(); + // Create anon vars. + LabelToNodeMap mapper = LabelToNodeMap.createVarMap(); + Map<Node, Node> blankNodeReplacements = new HashMap<>(); + for ( Triple t : bgp.getList() ) { + Node s = t.getSubject(); + Node s2 = templateNodeToQueryPatternNode(s, mapper); + Node p = t.getPredicate(); + Node p2 = templateNodeToQueryPatternNode(p, mapper); + Node o = t.getObject(); + Node o2 = templateNodeToQueryPatternNode(o, mapper); + if ( s == s2 && p == p2 && o == o2 ) { + bgp2.add(t); + continue; + } + Triple t2 = Triple.create(s2, p2, o2); + bgp2.add(t2); + } + return bgp2; + } + + // Rename blank nodes as anon variables for a query pattern. + private static Node templateNodeToQueryPatternNode(Node n, LabelToNodeMap map) { + if ( ! n.isBlank() ) + return n; + Node n2 = map.asNode(n.getBlankNodeLabel()); + return n2; + } }
