This is an automated email from the ASF dual-hosted git repository.

afs pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/jena.git


The following commit(s) were added to refs/heads/main by this push:
     new 524a00073c GH-4058: Test RDF validity inside triple terms
524a00073c is described below

commit 524a00073ce3659824cf7813bf53312428fe2ca3
Author: Andy Seaborne <[email protected]>
AuthorDate: Sat Jul 11 17:15:59 2026 +0100

    GH-4058: Test RDF validity inside triple terms
---
 .../org/apache/jena/sparql/util/NodeUtils.java     | 43 ++++++++++++++++++++--
 .../java/org/apache/jena/sparql/api/TestAPI.java   | 34 ++++++++++++++++-
 2 files changed, 73 insertions(+), 4 deletions(-)

diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/util/NodeUtils.java 
b/jena-arq/src/main/java/org/apache/jena/sparql/util/NodeUtils.java
index 9719f89597..74f5b9c40c 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/util/NodeUtils.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/util/NodeUtils.java
@@ -211,6 +211,21 @@ public class NodeUtils
      */
     public static boolean isDirLangString(Node n) { return 
Util.isDirLangString(n); }
 
+    /**
+     * Determines whether a triple is valid as a RDF statement.
+     * <p>
+     * This function reflects the fact that the {@link Triple} API is flexible 
in
+     * allowing any Node type in any position (including non-RDF node types 
like
+     * Variable) and as such not all Triples can be safely converted into 
Statements
+     * </p>
+     * @param triple Triple
+     * @return true if a valid as a statement
+     */
+    public static boolean isValidAsRDF(Triple triple) {
+        if ( triple == null )
+            return false;
+        return isValidAsRDF(triple.getSubject(), triple.getPredicate(), 
triple.getObject());
+    }
 
     /**
      * Determines whether a triple (as s/p/o) is valid as a RDF statement.
@@ -222,7 +237,7 @@ public class NodeUtils
      * @param s Subject
      * @param p Predicate
      * @param o Object
-     * @return True if a valid as a statement
+     * @return true if a valid as a statement
      */
     public static boolean isValidAsRDF(Node s, Node p, Node o) {
         if ( s == null || ( ! s.isBlank() && ! s.isURI() ) )
@@ -231,20 +246,42 @@ public class NodeUtils
             return false;
         if ( o == null || ( ! o.isBlank() && ! o.isURI() && ! o.isLiteral() && 
!o.isTripleTerm() ) )
             return false;
+
+        if ( o.isTripleTerm() ) {
+            Triple tt = o.getTriple();
+            return isValidAsRDF(tt.getSubject(), tt.getPredicate(), 
tt.getObject());
+        }
         return true;
     }
 
+    /**
+     * Determines whether a quad is valid as a RDF statement.
+     * <p>
+     * This function reflects the fact that the {@link Triple} API is flexible 
in
+     * allowing any Node type in any position (including non-RDF node types 
like
+     * Variable) and as such not all quads can be safely converted into 
Statements
+     * </p>
+     * @param quad Quad
+     * @return true if a valid as a graph name and statement
+     */
+    public static boolean isValidAsRDF(Quad quad) {
+        if ( quad == null )
+            return false;
+        return isValidAsRDF(quad.getGraph(), quad.getSubject(), 
quad.getPredicate(), quad.getObject());
+    }
+
     /**
      * Determines whether a quad (as g/s/p/o) is valid as a RDF statement.
      * <p>
      * This function reflects the fact that the {@link Triple} API is flexible 
in
      * allowing any Node type in any position (including non-RDF node types 
like
-     * Variable) and as such not all Triples can be safely converted into 
Statements
+     * Variable) and as such not all quads can be safely converted into 
Statements
      * </p>
+     * @param g Graph
      * @param s Subject
      * @param p Predicate
      * @param o Object
-     * @return True if a valid as a statement
+     * @return true if a valid as a graph name and statement
      */
     public static boolean isValidAsRDF(Node g, Node s, Node p, Node o) {
         if ( g == null || ( ! g.isURI() && ! g.isBlank() ) )
diff --git a/jena-arq/src/test/java/org/apache/jena/sparql/api/TestAPI.java 
b/jena-arq/src/test/java/org/apache/jena/sparql/api/TestAPI.java
index 33bfdfa5c9..35c6947eed 100644
--- a/jena-arq/src/test/java/org/apache/jena/sparql/api/TestAPI.java
+++ b/jena-arq/src/test/java/org/apache/jena/sparql/api/TestAPI.java
@@ -71,7 +71,7 @@ public class TestAPI
     }
 
     @Test
-    public void test_API1() {
+    public void testSelect1() {
         String qs = "SELECT * {?s ?p ?o}";
         try (QueryExecution qExec = QueryExecution.model(m).query(qs).build()) 
{
             ResultSet rs = qExec.execSelect();
@@ -84,6 +84,38 @@ public class TestAPI
         }
     }
 
+    @Test
+    public void testConstruct1() {
+
+        String qs = "CONSTRUCT {?s ?p ?o} WHERE { ?s ?p ?o }";
+        try (QueryExecution qExec = QueryExecution.model(m).query(qs).build()) 
{
+            Model m2 = qExec.execConstruct();
+            assertTrue(!m2.isEmpty(), () -> "No results");
+            assertTrue(m.isIsomorphicWith(m2), ()-> "Not isonorphic");
+        }
+    }
+
+    @Test
+    public void testConstructBad1() {
+        String qs =  """
+                PREFIX :     <http://example/>
+                PREFIX rdf:  <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
+                CONSTRUCT {
+                    << 456 :p :o >> .
+                    ?x :p :o .
+                    :s ?x :o .
+                    :tt1 :p <<( ?x :c2 :c3 )>> .
+                    :tt2 :p <<( :c1 ?x :c3 )>> .
+                    :tt3 :p <<( :c1 :c2 <<( ?x :d2 :d3 )>> )>> .
+                 } WHERE { BIND(123 AS ?x) }
+                """;
+        // Bad triples in result. They should be filtered out.
+        try (QueryExecution qExec = QueryExecution.model(m).query(qs).build()) 
{
+            Model m2 = qExec.execConstruct();
+            assertTrue(m2.isEmpty(), () -> "Expected empty graph");
+        }
+    }
+
     @Test
     public void testSubstitutionConstruct1() {
         QuerySolutionMap init = new QuerySolutionMap();

Reply via email to