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 abccdc47a7 GH-4108: SHACL pre-binding
abccdc47a7 is described below
commit abccdc47a78ff503713d7ab380711d7568592663
Author: Andy Seaborne <[email protected]>
AuthorDate: Wed Jul 29 14:11:37 2026 +0100
GH-4108: SHACL pre-binding
---
.../constraint/ShaclPrebindingException.java} | 17 +++++---------
.../shacl/engine/constraint/SparqlValidation.java | 27 ++++++++++++++++++----
.../org/apache/jena/shacl/vocabulary/SHACL.java | 7 +++---
.../{manifest.ttl-orig => manifest-1.0.ttl} | 0
.../test/files/std/sparql/pre-binding/manifest.ttl | 21 +++++++++++------
...nding-006.ttl => pre-binding-006-withdrawn.ttl} | 0
...04.ttl => unsupported-sparql-004-withdrawn.ttl} | 0
.../test/java/org/apache/jena/shacl/TC_SHACL.java | 5 ++--
.../org/apache/jena/shacl/testing/ShaclTest.java | 14 ++++++++---
.../org/apache/jena/shacl/testing/ShaclTests.java | 2 +-
.../{TestJenaShacl.java => Scripts_JenaShacl.java} | 4 ++--
.../jena/shacl/tests/jena_shacl/TS_JenaShacl.java | 2 +-
...stShaclCoreWG.java => Scripts_ShaclCoreWG.java} | 4 ++--
...aclSparqlWG.java => Scripts_ShaclSparqlWG.java} | 8 +++++--
.../apache/jena/shacl/tests/std/TS_StdSHACL.java | 4 ++--
15 files changed, 74 insertions(+), 41 deletions(-)
diff --git
a/jena-shacl/src/test/java/org/apache/jena/shacl/tests/jena_shacl/TS_JenaShacl.java
b/jena-shacl/src/main/java/org/apache/jena/shacl/engine/constraint/ShaclPrebindingException.java
similarity index 75%
copy from
jena-shacl/src/test/java/org/apache/jena/shacl/tests/jena_shacl/TS_JenaShacl.java
copy to
jena-shacl/src/main/java/org/apache/jena/shacl/engine/constraint/ShaclPrebindingException.java
index fe82875390..8e7a94e2c6 100644
---
a/jena-shacl/src/test/java/org/apache/jena/shacl/tests/jena_shacl/TS_JenaShacl.java
+++
b/jena-shacl/src/main/java/org/apache/jena/shacl/engine/constraint/ShaclPrebindingException.java
@@ -19,17 +19,12 @@
* SPDX-License-Identifier: Apache-2.0
*/
-package org.apache.jena.shacl.tests.jena_shacl;
+package org.apache.jena.shacl.engine.constraint;
-import org.junit.platform.suite.api.SelectClasses;
-import org.junit.platform.suite.api.Suite;
-
-@Suite
-@SelectClasses({
- // jena tests
- TestJenaShaclByCode.class,
- TestJenaShacl.class
-} )
-public class TS_JenaShacl {
+import org.apache.jena.shacl.ShaclException;
+public class ShaclPrebindingException extends ShaclException {
+ public ShaclPrebindingException(String msg) {
+ super(msg);
+ }
}
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 395d8d29a5..195c47b720 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
@@ -50,8 +50,7 @@ import org.apache.jena.sparql.engine.binding.Binding;
import org.apache.jena.sparql.path.P_Link;
import org.apache.jena.sparql.path.Path;
import org.apache.jena.sparql.path.PathFactory;
-import org.apache.jena.sparql.syntax.Element;
-import org.apache.jena.sparql.syntax.ElementPathBlock;
+import org.apache.jena.sparql.syntax.*;
import org.apache.jena.sparql.syntax.syntaxtransform.ElementTransformCopyBase;
import org.apache.jena.sparql.syntax.syntaxtransform.QueryTransformOps;
import org.apache.jena.sparql.util.ModelUtils;
@@ -111,6 +110,16 @@ import org.apache.jena.sparql.util.ModelUtils;
if ( path != null && !(path instanceof P_Link ) )
query = QueryTransformOps.transform(query, new
ElementTransformPath(SparqlConstraint.varPath, path));
+ // Check for disallowed syntax forms:
+ // MINUS, SERVICE
+ // "SPARQL queries MUST not contain a MINUS clause"
+ // "SPARQL queries MUST not contain a VALUES clause that mentions any
potentially pre-bound variable"
+ // "SPARQL queries MUST not use the syntax form AS ?var for any
potentially pre-bound variable"
+ // "Furthermore, SPARQL queries SHOULD not contain a federated query
(SERVICE)."
+
+ // VALUES and AS cause QueryScopeException happen during durign
execution.
+
+ checkQuerySyntaxPreBinding(query);
if ( USE_QueryTransformOps ) {
// Done with QueryTransformOps.transform
@@ -120,12 +129,11 @@ import org.apache.jena.sparql.util.ModelUtils;
Query query2 = QueryTransformOps.replaceVars(query, substitutions);
qExec = QueryExecutionFactory.create(query2, model);
} else {
+ // Use QueryExecution substitute (not initialBinding)
// Done with pre-binding.
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();
-
// ---- Dataset needed for the shapes graph
Resource shapesGraphResource = model.createResource("foo");
qsm.add("currentShape",
ModelUtils.convertGraphNodeToRDFNode(shape.getShapeNode(), model));
@@ -139,6 +147,8 @@ import org.apache.jena.sparql.util.ModelUtils;
}
// ASK validator.
+ // XXX Wrap in query solution, with $this and ?value.
+
if ( qExec.getQuery().isAskType() ) {
boolean askResult = qExec.execAsk();
if ( ! askResult ) {
@@ -191,6 +201,15 @@ import org.apache.jena.sparql.util.ModelUtils;
return false;
}
+ private static ElementVisitor eltPrebindingCheckVisitor = new
ElementVisitorBase() {
+ @Override public void visit(ElementMinus el) { throw new
ShaclPrebindingException("MINUS found in query for pre-binding"); }
+ @Override public void visit(ElementService el) { throw new
ShaclPrebindingException("SERBVICE found in query for pre-binding"); }
+ };
+
+ private static void checkQuerySyntaxPreBinding(Query query) {
+ ElementWalker.walk(query.getQueryPattern(), eltPrebindingCheckVisitor);
+ }
+
/** Result message: SELECT substitute */
private static String substitute(String violationTemplate, Binding row) {
String x = violationTemplate;
diff --git
a/jena-shacl/src/main/java/org/apache/jena/shacl/vocabulary/SHACL.java
b/jena-shacl/src/main/java/org/apache/jena/shacl/vocabulary/SHACL.java
index 1dd769ed7d..9552052ce5 100644
--- a/jena-shacl/src/main/java/org/apache/jena/shacl/vocabulary/SHACL.java
+++ b/jena-shacl/src/main/java/org/apache/jena/shacl/vocabulary/SHACL.java
@@ -65,9 +65,6 @@ public class SHACL {
*/
public static final Node annotationVarName = createProperty(
"http://www.w3.org/ns/shacl#annotationVarName" );
- /** <p>The SPARQL ASK query to execute.</p> */
- public static final Node ask = createProperty(
"http://www.w3.org/ns/shacl#ask" );
-
/** <p>The type that all value nodes must have.</p> */
public static final Node class_ = createProperty(
"http://www.w3.org/ns/shacl#class" );
@@ -366,6 +363,10 @@ public class SHACL {
/** <p>The SPARQL SELECT query to execute.</p> */
public static final Node select = createProperty(
"http://www.w3.org/ns/shacl#select" );
+ /** <p>The SPARQL SELECT query to execute.</p> */
+ public static final Node ask = createProperty(
"http://www.w3.org/ns/shacl#ask" );
+
+
/** <p>Defines the severity that validation results produced by a shape
must have.
* Defaults to sh:Violation.</p>
*/
diff --git a/jena-shacl/src/test/files/std/sparql/pre-binding/manifest.ttl-orig
b/jena-shacl/src/test/files/std/sparql/pre-binding/manifest-1.0.ttl
similarity index 100%
rename from jena-shacl/src/test/files/std/sparql/pre-binding/manifest.ttl-orig
rename to jena-shacl/src/test/files/std/sparql/pre-binding/manifest-1.0.ttl
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 2c8a6457eb..4e931f07d9 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
@@ -10,22 +10,29 @@
mf:include <pre-binding-003.ttl> ;
mf:include <pre-binding-004.ttl> ;
mf:include <pre-binding-005.ttl> ;
- # Prebinding $this inside nested SELECT
+
+ ## Test withdrawn in at SHACL 1.2
## mf:include <pre-binding-006.ttl> ;
+
mf:include <pre-binding-007.ttl> ;
-
mf:include <shapesGraph-001.ttl> ;
# MINUS
-## mf:include <unsupported-sparql-001.ttl> ;
+ mf:include <unsupported-sparql-001.ttl> ;
+
# VALUES
-## mf:include <unsupported-sparql-002.ttl> ;
+ mf:include <unsupported-sparql-002.ttl> ;
+
# SERVICE
-## mf:include <unsupported-sparql-003.ttl> ;
+ mf:include <unsupported-sparql-003.ttl> ;
+
# SELECT
+ ## Test withdrawn in at SHACL 1.2
## mf:include <unsupported-sparql-004.ttl> ;
+
# BIND assign to $this
-## mf:include <unsupported-sparql-005.ttl> ;
+ mf:include <unsupported-sparql-005.ttl> ;
+
# ASK validator reassign ?value
-## mf:include <unsupported-sparql-006.ttl> ;
+ mf:include <unsupported-sparql-006.ttl> ;
.
diff --git
a/jena-shacl/src/test/files/std/sparql/pre-binding/pre-binding-006.ttl
b/jena-shacl/src/test/files/std/sparql/pre-binding/pre-binding-006-withdrawn.ttl
similarity index 100%
rename from jena-shacl/src/test/files/std/sparql/pre-binding/pre-binding-006.ttl
rename to
jena-shacl/src/test/files/std/sparql/pre-binding/pre-binding-006-withdrawn.ttl
diff --git
a/jena-shacl/src/test/files/std/sparql/pre-binding/unsupported-sparql-004.ttl
b/jena-shacl/src/test/files/std/sparql/pre-binding/unsupported-sparql-004-withdrawn.ttl
similarity index 100%
rename from
jena-shacl/src/test/files/std/sparql/pre-binding/unsupported-sparql-004.ttl
rename to
jena-shacl/src/test/files/std/sparql/pre-binding/unsupported-sparql-004-withdrawn.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 a35658ff4c..7d2cdc865b 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
@@ -34,13 +34,12 @@ import org.apache.jena.shacl.tests.std.TS_StdSHACL;
@Suite
@SelectClasses({
TestValidationReport.class
+ , TestImports.class
+ , TestValidationListener.class
, TS_StdSHACL.class
, TS_JenaShacl.class
, TS_Compact.class
-
- , TestImports.class
- , TestValidationListener.class
} )
public class TC_SHACL { }
diff --git
a/jena-shacl/src/test/java/org/apache/jena/shacl/testing/ShaclTest.java
b/jena-shacl/src/test/java/org/apache/jena/shacl/testing/ShaclTest.java
index 5755c523c7..eba6da170a 100644
--- a/jena-shacl/src/test/java/org/apache/jena/shacl/testing/ShaclTest.java
+++ b/jena-shacl/src/test/java/org/apache/jena/shacl/testing/ShaclTest.java
@@ -31,8 +31,10 @@ import org.apache.jena.riot.RDFDataMgr;
import org.apache.jena.shacl.ShaclValidator;
import org.apache.jena.shacl.Shapes;
import org.apache.jena.shacl.ValidationReport;
+import org.apache.jena.shacl.engine.constraint.ShaclPrebindingException;
import org.apache.jena.shacl.lib.ShLib;
import org.apache.jena.shacl.validation.VR;
+import org.apache.jena.sparql.syntax.syntaxtransform.QueryScopeException;
public class ShaclTest {
@@ -50,10 +52,16 @@ public class ShaclTest {
try {
ValidationReport testReport =
validate(ShaclValidator.get(), shapesGraph, dataGraph);
if ( testReport.conforms() )
- fail("Expect a test failure: "+test.origin());
- } catch (RuntimeException ex) {
- // Ignore.
+ fail("Expected a test failure: "+test.origin());
+ else
+ // !conforms expected
+ return;
+ } catch ( QueryScopeException | ShaclPrebindingException ex) {
+ // "Success"
+ return;
}
+ // "Success"
+ return;
}
// Fails on unimplemented.
diff --git
a/jena-shacl/src/test/java/org/apache/jena/shacl/testing/ShaclTests.java
b/jena-shacl/src/test/java/org/apache/jena/shacl/testing/ShaclTests.java
index 25f19cd617..248ff903d8 100644
--- a/jena-shacl/src/test/java/org/apache/jena/shacl/testing/ShaclTests.java
+++ b/jena-shacl/src/test/java/org/apache/jena/shacl/testing/ShaclTests.java
@@ -69,7 +69,7 @@ public class ShaclTests {
private static String decideName(ShaclTestItem stc) {
String fn = FileOps.basename(stc.origin());
- return stc.name()+"("+fn+")";
+ return stc.name()+" ("+fn+")";
}
private static void manifest(String manifestFile, List<String> manifests,
List<ShaclTestItem> testCases) {
diff --git
a/jena-shacl/src/test/java/org/apache/jena/shacl/tests/jena_shacl/TestJenaShacl.java
b/jena-shacl/src/test/java/org/apache/jena/shacl/tests/jena_shacl/Scripts_JenaShacl.java
similarity index 95%
rename from
jena-shacl/src/test/java/org/apache/jena/shacl/tests/jena_shacl/TestJenaShacl.java
rename to
jena-shacl/src/test/java/org/apache/jena/shacl/tests/jena_shacl/Scripts_JenaShacl.java
index b2d6ba5a57..62f521b195 100644
---
a/jena-shacl/src/test/java/org/apache/jena/shacl/tests/jena_shacl/TestJenaShacl.java
+++
b/jena-shacl/src/test/java/org/apache/jena/shacl/tests/jena_shacl/Scripts_JenaShacl.java
@@ -36,7 +36,7 @@ import org.apache.jena.shacl.testing.ShaclTests;
@ParameterizedClass(name="{index}: {0}")
@MethodSource("provideArgs")
-public class TestJenaShacl {
+public class Scripts_JenaShacl {
private static Stream<Arguments> provideArgs() {
String manifestFile = "src/test/files/local/manifest.ttl";
@@ -48,7 +48,7 @@ public class TestJenaShacl {
private ShaclTestItem test;
- public TestJenaShacl(String name, ShaclTestItem test) {
+ public Scripts_JenaShacl(String name, ShaclTestItem test) {
this.test = test;
}
diff --git
a/jena-shacl/src/test/java/org/apache/jena/shacl/tests/jena_shacl/TS_JenaShacl.java
b/jena-shacl/src/test/java/org/apache/jena/shacl/tests/jena_shacl/TS_JenaShacl.java
index fe82875390..0a8ecdf70e 100644
---
a/jena-shacl/src/test/java/org/apache/jena/shacl/tests/jena_shacl/TS_JenaShacl.java
+++
b/jena-shacl/src/test/java/org/apache/jena/shacl/tests/jena_shacl/TS_JenaShacl.java
@@ -28,7 +28,7 @@ import org.junit.platform.suite.api.Suite;
@SelectClasses({
// jena tests
TestJenaShaclByCode.class,
- TestJenaShacl.class
+ Scripts_JenaShacl.class
} )
public class TS_JenaShacl {
diff --git
a/jena-shacl/src/test/java/org/apache/jena/shacl/tests/std/TestShaclCoreWG.java
b/jena-shacl/src/test/java/org/apache/jena/shacl/tests/std/Scripts_ShaclCoreWG.java
similarity index 95%
rename from
jena-shacl/src/test/java/org/apache/jena/shacl/tests/std/TestShaclCoreWG.java
rename to
jena-shacl/src/test/java/org/apache/jena/shacl/tests/std/Scripts_ShaclCoreWG.java
index f22c75750d..15fac23f56 100644
---
a/jena-shacl/src/test/java/org/apache/jena/shacl/tests/std/TestShaclCoreWG.java
+++
b/jena-shacl/src/test/java/org/apache/jena/shacl/tests/std/Scripts_ShaclCoreWG.java
@@ -37,7 +37,7 @@ import org.apache.jena.shacl.testing.ShaclTests;
@ParameterizedClass(name="{0}")
@MethodSource("provideArgs")
-public class TestShaclCoreWG {
+public class Scripts_ShaclCoreWG {
private static Stream<Arguments> provideArgs() {
String manifestFile = "src/test/files/std/core/manifest.ttl";
@@ -49,7 +49,7 @@ public class TestShaclCoreWG {
private ShaclTestItem test;
- public TestShaclCoreWG(String name, ShaclTestItem test) {
+ public Scripts_ShaclCoreWG(String name, ShaclTestItem test) {
ShaclSystem.setMode(ShaclSystem.Mode.TEST);
this.test = test;
}
diff --git
a/jena-shacl/src/test/java/org/apache/jena/shacl/tests/std/TestShaclSparqlWG.java
b/jena-shacl/src/test/java/org/apache/jena/shacl/tests/std/Scripts_ShaclSparqlWG.java
similarity index 90%
rename from
jena-shacl/src/test/java/org/apache/jena/shacl/tests/std/TestShaclSparqlWG.java
rename to
jena-shacl/src/test/java/org/apache/jena/shacl/tests/std/Scripts_ShaclSparqlWG.java
index 8ea4c51787..6350f63dcb 100644
---
a/jena-shacl/src/test/java/org/apache/jena/shacl/tests/std/TestShaclSparqlWG.java
+++
b/jena-shacl/src/test/java/org/apache/jena/shacl/tests/std/Scripts_ShaclSparqlWG.java
@@ -36,7 +36,7 @@ import org.apache.jena.shacl.testing.ShaclTests;
@ParameterizedClass(name="{0}")
@MethodSource("provideArgs")
-public class TestShaclSparqlWG {
+public class Scripts_ShaclSparqlWG {
private static Stream<Arguments> provideArgs() {
// // The W3C WG tests for SPARQL-based Constraints is made up of:
@@ -44,7 +44,11 @@ public class TestShaclSparqlWG {
// //String manifest =
"src/test/files/std/sparql/pre-binding/manifest.ttl";
// //String manifest = "src/test/files/std/sparql/node/manifest.ttl";
// //String manifest = "src/test/files/std/sparql/component/manifest.ttl";
+
+ // pre-binding/manifest.ttl is different to the SHACL 1.0 published
tests.
+ // Some tests withdrawn in SHACL 1.2
String manifestFile = "src/test/files/std/sparql/manifest.ttl";
+
List<String> omitManifests = List.of();
List<Pair<String, ShaclTestItem>> z =
ShaclTests.manifestNamed(manifestFile, omitManifests);
List<Arguments> x = z.stream().map(p->Arguments.of(p.getLeft(),
p.getRight())).toList();
@@ -53,7 +57,7 @@ public class TestShaclSparqlWG {
private ShaclTestItem test;
- public TestShaclSparqlWG(String name, ShaclTestItem test) {
+ public Scripts_ShaclSparqlWG(String name, ShaclTestItem test) {
this.test = test;
}
diff --git
a/jena-shacl/src/test/java/org/apache/jena/shacl/tests/std/TS_StdSHACL.java
b/jena-shacl/src/test/java/org/apache/jena/shacl/tests/std/TS_StdSHACL.java
index 268376d6ae..df8caad78e 100644
--- a/jena-shacl/src/test/java/org/apache/jena/shacl/tests/std/TS_StdSHACL.java
+++ b/jena-shacl/src/test/java/org/apache/jena/shacl/tests/std/TS_StdSHACL.java
@@ -27,8 +27,8 @@ import org.junit.platform.suite.api.Suite;
@Suite
@SelectClasses({
// The W3C Working Group tests.
- TestShaclCoreWG.class ,
- TestShaclSparqlWG.class
+ Scripts_ShaclCoreWG.class ,
+ Scripts_ShaclSparqlWG.class
} )
public class TS_StdSHACL {