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 8fef93101bbbd942b8ce8ac03257345bc225d065
Author: Andy Seaborne <[email protected]>
AuthorDate: Sun Dec 31 15:42:05 2023 +0000

    GH-2157: Support ?currentShape and ?shapesGraph
---
 .../shacl/engine/constraint/SparqlValidation.java  | 31 ++++++---
 jena-shacl/src/test/files/local/manifest.ttl       |  4 ++
 .../files/local/other/sparql-vars-001-data.ttl     | 10 +++
 .../files/local/other/sparql-vars-001-results.ttl  | 18 +++++
 .../files/local/other/sparql-vars-001-shape.ttl    | 35 ++++++++++
 .../test/files/std/sparql/pre-binding/manifest.ttl |  3 +-
 .../test/java/org/apache/jena/shacl/TC_SHACL.java  |  2 +
 .../tests/jena_shacl/JenaShaclTestsByCode.java     | 77 ++++++++++++++++++++++
 8 files changed, 169 insertions(+), 11 deletions(-)

diff --git 
a/jena-shacl/src/main/java/org/apache/jena/shacl/engine/constraint/SparqlValidation.java
 
b/jena-shacl/src/main/java/org/apache/jena/shacl/engine/constraint/SparqlValidation.java
index d40d0f092b..aa9e36b6bb 100644
--- 
a/jena-shacl/src/main/java/org/apache/jena/shacl/engine/constraint/SparqlValidation.java
+++ 
b/jena-shacl/src/main/java/org/apache/jena/shacl/engine/constraint/SparqlValidation.java
@@ -33,6 +33,7 @@ import org.apache.jena.query.*;
 import org.apache.jena.rdf.model.Model;
 import org.apache.jena.rdf.model.ModelFactory;
 import org.apache.jena.rdf.model.RDFNode;
+import org.apache.jena.rdf.model.Resource;
 import org.apache.jena.riot.out.NodeFmtLib;
 import org.apache.jena.shacl.engine.Parameter;
 import org.apache.jena.shacl.engine.ShaclPaths;
@@ -41,9 +42,7 @@ import org.apache.jena.shacl.lib.ShLib;
 import org.apache.jena.shacl.parser.Constraint;
 import org.apache.jena.shacl.parser.Shape;
 import 
org.apache.jena.shacl.validation.event.ConstraintEvaluatedOnSinglePathNodeEvent;
-import org.apache.jena.sparql.core.PathBlock;
-import org.apache.jena.sparql.core.TriplePath;
-import org.apache.jena.sparql.core.Var;
+import org.apache.jena.sparql.core.*;
 import org.apache.jena.sparql.engine.binding.Binding;
 import org.apache.jena.sparql.path.P_Link;
 import org.apache.jena.sparql.path.Path;
@@ -109,6 +108,7 @@ import org.apache.jena.sparql.util.ModelUtils;
         if ( path != null && !(path instanceof P_Link ) )
             query = QueryTransformOps.transform(query, new 
ElementTransformPath(SparqlConstraint.varPath, path));
 
+
         if ( USE_QueryTransformOps ) {
             // Done with QueryTransformOps.transform
             Map<Var, Node> substitutions = 
parameterMapToSyntaxSubstitutions(parameterMap, focusNode, path);
@@ -121,7 +121,18 @@ import org.apache.jena.sparql.util.ModelUtils;
             QuerySolutionMap qsm = parameterMapToPreBinding(parameterMap, 
focusNode, path, model);
             if ( query.isAskType() )
                 qsm.add("value", 
ModelUtils.convertGraphNodeToRDFNode(valueNode, model));
-            qExec = 
QueryExecution.create().query(query).model(model).initialBinding(qsm).build();
+            //qExec = 
QueryExecution.create().query(query).model(model).initialBinding(qsm).build();
+
+            // ---- Dataset needed for the shapes graph
+            Resource shapesGraphResource = model.createResource("foo");
+            qsm.add("currentShape", 
ModelUtils.convertGraphNodeToRDFNode(shape.getShapeNode(), model));
+            qsm.add("shapesGraph", shapesGraphResource);
+
+            // No copying of graphs.  Set the default graph on creation.
+            DatasetGraph dsg = 
DatasetGraphFactory.createGeneral(model.getGraph()); // Dataset by links.
+            dsg.addGraph(shapesGraphResource.asNode(), shape.getShapeGraph());
+            Dataset ds = DatasetFactory.wrap(dsg);
+            qExec = 
QueryExecution.create().query(query).dataset(ds).initialBinding(qsm).build();
         }
 
         // ASK validator.
@@ -133,14 +144,16 @@ import org.apache.jena.sparql.util.ModelUtils;
                     : substitute(violationTemplate, parameterMap, focusNode, 
path, valueNode);
                 vCxt.reportEntry(msg, shape, focusNode, path, valueNode, 
reportConstraint);
             }
-            vCxt.notifyValidationListener(() -> new 
ConstraintEvaluatedOnSinglePathNodeEvent(vCxt, shape, focusNode, 
reportConstraint, path, valueNode,
-                            b));
+            vCxt.notifyValidationListener(() ->
+                new ConstraintEvaluatedOnSinglePathNodeEvent(vCxt, shape, 
focusNode, reportConstraint, path, valueNode,b));
             return b;
         }
 
+        // SELECT validator.
         ResultSet rs = qExec.execSelect();
         if ( ! rs.hasNext() ) {
-            vCxt.notifyValidationListener(() -> new 
ConstraintEvaluatedOnSinglePathNodeEvent(vCxt, shape, focusNode, 
reportConstraint, path, valueNode, true));
+            vCxt.notifyValidationListener(() ->
+                new ConstraintEvaluatedOnSinglePathNodeEvent(vCxt, shape, 
focusNode, reportConstraint, path, valueNode, true));
             return true;
         }
 
@@ -168,8 +181,8 @@ import org.apache.jena.sparql.util.ModelUtils;
             }
             final Path finalRPath = rPath;
             final Node finalValue = value;
-            vCxt.notifyValidationListener(() -> new 
ConstraintEvaluatedOnSinglePathNodeEvent(vCxt, shape, focusNode, 
reportConstraint, finalRPath, finalValue,
-                            false));
+            vCxt.notifyValidationListener(() ->
+                new ConstraintEvaluatedOnSinglePathNodeEvent(vCxt, shape, 
focusNode, reportConstraint, finalRPath, finalValue, false));
             vCxt.reportEntry(msg, shape, focusNode, rPath, value, 
reportConstraint);
         }
         return false;
diff --git a/jena-shacl/src/test/files/local/manifest.ttl 
b/jena-shacl/src/test/files/local/manifest.ttl
index 76ae277694..0b0d688d90 100644
--- a/jena-shacl/src/test/files/local/manifest.ttl
+++ b/jena-shacl/src/test/files/local/manifest.ttl
@@ -7,6 +7,10 @@ PREFIX sh: <http://www.w3.org/ns/shacl#>
 PREFIX sht: <http://www.w3.org/ns/shacl-test#>
 PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
 
+## Not all the test are controlled by manifest because they
+## test features not easily captured in a single test file.
+## e.g. split shapes and data
+
 <>
   rdf:type mf:Manifest ;
   mf:include <additional/lang-simple-1.ttl> ;
diff --git a/jena-shacl/src/test/files/local/other/sparql-vars-001-data.ttl 
b/jena-shacl/src/test/files/local/other/sparql-vars-001-data.ttl
new file mode 100644
index 0000000000..7ee05ce1ca
--- /dev/null
+++ b/jena-shacl/src/test/files/local/other/sparql-vars-001-data.ttl
@@ -0,0 +1,10 @@
+PREFIX : <http://example/>
+
+:testDataSubject1 :p1 1 .
+   
+:testDataSubject2 :p1 2 .
+
+:testDataSubject3 :p2 1 .
+
+:testDataSubject3 :p2 2 .
+
diff --git a/jena-shacl/src/test/files/local/other/sparql-vars-001-results.ttl 
b/jena-shacl/src/test/files/local/other/sparql-vars-001-results.ttl
new file mode 100644
index 0000000000..a2c8e63cd2
--- /dev/null
+++ b/jena-shacl/src/test/files/local/other/sparql-vars-001-results.ttl
@@ -0,0 +1,18 @@
+PREFIX :     <http://example/>
+PREFIX rdf:  <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
+PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
+PREFIX sh:   <http://www.w3.org/ns/shacl#>
+PREFIX xsd:  <http://www.w3.org/2001/XMLSchema#>
+
+[ rdf:type     sh:ValidationReport;
+  sh:conforms  false;
+  sh:result    [ rdf:type                      sh:ValidationResult;
+                 sh:focusNode                  :testDataSubject1;
+                 sh:resultMessage              "Property-value pair 
(<http://example/p1> 1) found";
+                 sh:resultPath                 :p1;
+                 sh:resultSeverity             sh:Violation;
+                 sh:sourceConstraintComponent  sh:SPARQLConstraintComponent;
+                 sh:sourceShape                
<file:///home/afs/ASF/jena5/jena-shacl/src/test/files/local/other/sparql-vars-001-shape.ttl#PropertyValueShape>;
+                 sh:value                      1
+               ]
+] .
diff --git a/jena-shacl/src/test/files/local/other/sparql-vars-001-shape.ttl 
b/jena-shacl/src/test/files/local/other/sparql-vars-001-shape.ttl
new file mode 100644
index 0000000000..45420509db
--- /dev/null
+++ b/jena-shacl/src/test/files/local/other/sparql-vars-001-shape.ttl
@@ -0,0 +1,35 @@
+# Test ?currentShape and ?shapesGraph
+
+PREFIX :      <http://example/>
+PREFIX rdf:       <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
+PREFIX sh:        <http://www.w3.org/ns/shacl#>
+## PREFIX xsd:     <http://www.w3.org/2001/XMLSchema#>
+
+
+<#prefixes>
+  sh:declare [ sh:prefix "ex";     sh:namespace "http://example/"; ] ;
+  .
+
+<#PropertyValueShape>
+  rdf:type sh:NodeShape ;
+
+  sh:targetNode :testDataSubject1 ;
+  sh:targetNode :testDataSubject2 ;
+  sh:targetNode :testDataSubject3 ;
+  sh:targetNode :testDataSubject4 ;
+
+  sh:labelTemplate "Test for absence of a property-value pair, using SPARQL" ;
+  sh:prefixes <#prefixes> ;
+  # Target must not have this pair.
+  :propertyValue ( :p1 1 ) ;
+  sh:sparql [ 
+      sh:message "Property-value pair ({?path} {?value}) found" ;
+      sh:prefixes <#prefixes> ;
+      sh:select """
+               SELECT ?this (?property AS ?path) ?value {
+            GRAPH ?shapesGraph { ?currentShape ex:propertyValue ( ?property 
?value ) }
+                       ?this ?property ?value .
+               }
+""" ;
+    ] ;
+.
diff --git a/jena-shacl/src/test/files/std/sparql/pre-binding/manifest.ttl 
b/jena-shacl/src/test/files/std/sparql/pre-binding/manifest.ttl
index 8276ee2898..2c8a6457eb 100644
--- a/jena-shacl/src/test/files/std/sparql/pre-binding/manifest.ttl
+++ b/jena-shacl/src/test/files/std/sparql/pre-binding/manifest.ttl
@@ -14,8 +14,7 @@
 ##     mf:include <pre-binding-006.ttl> ;
        mf:include <pre-binding-007.ttl> ;
     
-    # $shapesGraph not supported.
-##     mf:include <shapesGraph-001.ttl> ;
+       mf:include <shapesGraph-001.ttl> ;
 
     # MINUS
 ##     mf:include <unsupported-sparql-001.ttl> ;
diff --git a/jena-shacl/src/test/java/org/apache/jena/shacl/TC_SHACL.java 
b/jena-shacl/src/test/java/org/apache/jena/shacl/TC_SHACL.java
index 15f99f0b9a..879a87b40c 100644
--- a/jena-shacl/src/test/java/org/apache/jena/shacl/TC_SHACL.java
+++ b/jena-shacl/src/test/java/org/apache/jena/shacl/TC_SHACL.java
@@ -22,6 +22,7 @@ import org.apache.jena.shacl.compact.TS_Compact;
 import org.apache.jena.shacl.tests.TestImports;
 import org.apache.jena.shacl.tests.TestValidationReport;
 import org.apache.jena.shacl.tests.ValidationListenerTests;
+import org.apache.jena.shacl.tests.jena_shacl.JenaShaclTestsByCode;
 import org.apache.jena.shacl.tests.jena_shacl.TS_JenaShacl;
 import org.apache.jena.shacl.tests.std.TS_StdSHACL;
 import org.junit.runner.RunWith;
@@ -35,6 +36,7 @@ import org.junit.runners.Suite;
     , TS_Compact.class
     , TestImports.class
     , ValidationListenerTests.class
+    , JenaShaclTestsByCode.class
 } )
 
 public class TC_SHACL { }
diff --git 
a/jena-shacl/src/test/java/org/apache/jena/shacl/tests/jena_shacl/JenaShaclTestsByCode.java
 
b/jena-shacl/src/test/java/org/apache/jena/shacl/tests/jena_shacl/JenaShaclTestsByCode.java
new file mode 100644
index 0000000000..7114359063
--- /dev/null
+++ 
b/jena-shacl/src/test/java/org/apache/jena/shacl/tests/jena_shacl/JenaShaclTestsByCode.java
@@ -0,0 +1,77 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.shacl.tests.jena_shacl;
+
+import static org.junit.Assert.assertTrue;
+
+import java.io.PrintStream;
+
+import org.apache.jena.graph.Graph;
+import org.apache.jena.riot.Lang;
+import org.apache.jena.riot.RDFParser;
+import org.apache.jena.riot.RDFWriter;
+import org.apache.jena.shacl.ShaclValidator;
+import org.apache.jena.sparql.util.IsoMatcher;
+import org.junit.Test;
+
+public class JenaShaclTestsByCode {
+    static final String DIR = "src/test/files/local/other/";
+    @Test public void sparql_vars_001() {
+        execTest("sparql-vars-001",
+                 DIR+"sparql-vars-001-data.ttl",
+                 DIR+"sparql-vars-001-shape.ttl",
+                 DIR+"sparql-vars-001-results.ttl");
+
+    }
+
+    private void execTest(String label, String datafile, String shapesfile, 
String expectedResults) {
+        Graph data = load(datafile) ;
+        Graph shapes = load(shapesfile) ;
+        Graph expected = load(expectedResults);
+
+        Graph actual = ShaclValidator.get().validate(shapes, data).getGraph();
+
+        boolean checkResults = IsoMatcher.isomorphic(expected, actual);
+        if ( ! checkResults ) {
+            PrintStream out = System.out;
+            out.println("==== Failure: "+label);
+            if ( false ) {
+                // Very verbose : enable for development/debugging!
+                out.println("== Data:");
+                write(out, data);
+                out.println("== Shapes:");
+                write(out, shapes);
+            }
+            out.println("== Expected:");
+            write(out, expected);
+            out.println("== Actual:");
+            write(out, actual);
+        }
+        assertTrue(checkResults);
+    }
+
+    private Graph load(String file) {
+        return RDFParser.source(file).toGraph();
+    }
+
+    private void write(PrintStream out, Graph graph) {
+        RDFWriter.source(graph).lang(Lang.TTL).output(out);
+    }
+
+}

Reply via email to