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;
+    }
 }

Reply via email to