diff --git a/shacl12-core/index.html b/shacl12-core/index.html index 666223db..db2cb2ad 100644 --- a/shacl12-core/index.html +++ b/shacl12-core/index.html @@ -1113,7 +1113,7 @@

Shapes and Constraints

Constraint parameters, for example:
-
sh:closed : xsd:boolean
+
sh:closed : xsd:boolean or sh:ByTypes
sh:or : rdf:List
sh:not : sh:Shape
sh:property : sh:PropertyShape
@@ -6037,7 +6037,8 @@

sh:closed, sh:ignoredProperties

sh:closed Set to true to close the shape. - The values of sh:closed in a shape are literals with datatype xsd:boolean. + The values of sh:closed in a shape are literals with datatype xsd:boolean + or the IRI sh:ByTypes. @@ -6054,14 +6055,41 @@

sh:closed, sh:ignoredProperties

Let $closed be a parameter value for sh:closed. Let $ignoredProperties be a value for sh:ignoredProperties. - If $closed is true then - there is a validation result for each triple that has a value node as its - subject and a predicate that is not explicitly enumerated as a value of sh:path - in any of the property shapes declared via sh:property at the current shape. +

+ If $closed is true or sh:ByTypes and P + is the set of properties defined below, + then there is a validation result for each triple that has a value node as its + subject and a predicate that is not in P. If $ignoredProperties has a value then the properties enumerated as members of this SHACL list are also permitted for the value node. The validation result MUST have the predicate of the triple as its sh:resultPath, and the object of the triple as its sh:value. +

+ If $closed is true, then P is the set of IRI properties + that can be reached from the current shape via the SPARQL path sh:property/sh:path. +

+ If $closed is sh:ByTypes, then P is the set of IRI properties + that can be reached from the value node via the following algorithm, plus rdf:type: +
+ +function collectProperties(S)
+  add all IRI properties that can be reached from S via the SPARQL path
+      sh:property/sh:path
+  if S is a SHACL instance of rdfs:Class in the shapes graph {
+    for each triple in the shapes graph matching (S rdfs:subClassOf ?o)
+      collectProperties(?o)
+    for each triple in the shapes graph matching (?s sh:targetClass S)
+      collectProperties(?s)
+  }
+  if S is a SHACL instance of sh:NodeShape in the shapes graph
+    for each triple in the shapes graph matching (S sh:node ?o)
+      collectProperties(?o)
+
+for each rdf:type T of the value node in the data graph
+  collectProperties(T)
+
+ Note that implementations need to avoid infinite loops in the algorithm above by preventing + it from visiting the same `S` twice.

The remainder of this section is informative.

@@ -6149,6 +6177,15 @@

sh:closed, sh:ignoredProperties

+

+ The use case for sh:closed sh:ByTypes includes properties that are declared + in superclasses of the types of the current value node (via rdfs:subClassOf), + as well as other shapes that are linked to those types via sh:targetClass and + the shapes that can be reached from one node shape to the other via sh:node. + Examples for sh:ByTypes can be found in the test case library: + closed-003.ttl, + closed-004.ttl. +

@@ -6866,10 +6903,10 @@

Changes between SHACL 1.0 Core and SHACL 1.2 Core

diff --git a/shacl12-test-suite/tests/core/node/closed-003.ttl b/shacl12-test-suite/tests/core/node/closed-003.ttl new file mode 100644 index 00000000..9fc1e084 --- /dev/null +++ b/shacl12-test-suite/tests/core/node/closed-003.ttl @@ -0,0 +1,66 @@ +@prefix dash: . +@prefix ex: . +@prefix mf: . +@prefix owl: . +@prefix rdf: . +@prefix rdfs: . +@prefix sh: . +@prefix sht: . +@prefix xsd: . + +ex:RootClass + a rdfs:Class, sh:NodeShape ; + sh:property [ sh:path ex:rootClassProperty1 ] ; + sh:closed sh:ByTypes ; +. +ex:SubClass + a rdfs:Class, sh:NodeShape ; + rdfs:subClassOf ex:RootClass ; + sh:property [ sh:path ex:subClassProperty1 ] ; + sh:property [ sh:path ex:subClassProperty2 ] ; + sh:closed sh:ByTypes ; +. + +# Invalid because it uses ex:subClassProperty1 but it's not an instance of SubClass +ex:InvalidInstance1 + a ex:RootClass ; + ex:subClassProperty1 1 ; +. +ex:ValidInstance1 + a ex:RootClass ; + ex:rootClassProperty1 1 ; +. +ex:ValidInstance2 + a ex:SubClass ; + ex:rootClassProperty1 1 ; + ex:subClassProperty1 3 ; + ex:subClassProperty2 4 ; +. +<> + rdf:type mf:Manifest ; + mf:entries ( + + ) ; +. + + rdf:type sht:Validate ; + rdfs:label "Test of sh:closed at node shape 003" ; + mf:action [ + sht:dataGraph <> ; + sht:shapesGraph <> ; + ] ; + mf:result [ + rdf:type sh:ValidationReport ; + sh:conforms "false"^^xsd:boolean ; + sh:result [ + rdf:type sh:ValidationResult ; + sh:focusNode ex:InvalidInstance1 ; + sh:resultPath ex:subClassProperty1 ; + sh:resultSeverity sh:Violation ; + sh:sourceConstraintComponent sh:ClosedConstraintComponent ; + sh:sourceShape ex:RootClass ; + sh:value 1 ; + ] ; + ] ; + mf:status sht:approved ; +. diff --git a/shacl12-test-suite/tests/core/node/closed-004.ttl b/shacl12-test-suite/tests/core/node/closed-004.ttl new file mode 100644 index 00000000..43d6da49 --- /dev/null +++ b/shacl12-test-suite/tests/core/node/closed-004.ttl @@ -0,0 +1,84 @@ +@prefix dash: . +@prefix ex: . +@prefix mf: . +@prefix owl: . +@prefix rdf: . +@prefix rdfs: . +@prefix sh: . +@prefix sht: . +@prefix xsd: . + +ex:RootClass + a rdfs:Class, sh:NodeShape ; + sh:property [ sh:path ex:rootClassProperty1 ] ; + sh:closed sh:ByTypes ; +. +ex:SubClass + a rdfs:Class, sh:NodeShape ; + rdfs:subClassOf ex:RootClass ; + sh:property [ sh:path ex:subClassProperty1 ] ; + sh:property [ sh:path ex:subClassProperty2 ] ; +. +ex:RootShape + a sh:NodeShape ; + sh:targetClass ex:RootClass ; + sh:property [ sh:path ex:rootShapeProperty1 ] ; +. +ex:SubShape + a sh:NodeShape ; + sh:targetClass ex:SubClass ; + sh:node ex:SuperShape ; + sh:property [ sh:path ex:subShapeProperty1 ] ; +. +ex:SuperShape + a sh:NodeShape ; + sh:property [ sh:path ex:superShapeProperty1 ] ; +. + +# Invalid because it uses ex:subClassProperty1 but it's not an instance of SubClass +ex:InvalidInstance1 + a ex:RootClass ; + ex:subClassProperty1 1 ; +. +ex:ValidInstance1 + a ex:RootClass ; + ex:rootClassProperty1 1 ; + ex:rootShapeProperty1 2 ; +. +ex:ValidInstance2 + a ex:SubClass ; + ex:rootClassProperty1 1 ; + ex:rootShapeProperty1 2 ; + ex:subClassProperty1 3 ; + ex:subClassProperty2 4 ; + ex:subShapeProperty1 5 ; + ex:superShapeProperty1 6 ; +. +<> + rdf:type mf:Manifest ; + mf:entries ( + + ) ; +. + + rdf:type sht:Validate ; + rdfs:label "Test of sh:closed at node shape 004" ; + mf:action [ + sht:dataGraph <> ; + sht:shapesGraph <> ; + ] ; + mf:result [ + rdf:type sh:ValidationReport ; + sh:conforms "false"^^xsd:boolean ; + sh:result [ + rdf:type sh:ValidationResult ; + sh:focusNode ex:InvalidInstance1 ; + sh:resultPath ex:subClassProperty1 ; + sh:resultSeverity sh:Violation ; + sh:sourceConstraintComponent sh:ClosedConstraintComponent ; + sh:sourceShape ex:RootClass ; + sh:value 1 ; + ] ; + ] ; + mf:status sht:approved ; +. diff --git a/shacl12-test-suite/tests/core/node/manifest.ttl b/shacl12-test-suite/tests/core/node/manifest.ttl index 7cf8dc3d..bccbfa95 100644 --- a/shacl12-test-suite/tests/core/node/manifest.ttl +++ b/shacl12-test-suite/tests/core/node/manifest.ttl @@ -12,6 +12,8 @@ mf:include ; mf:include ; mf:include ; + mf:include ; + mf:include ; mf:include ; mf:include ; mf:include ; diff --git a/shacl12-vocabularies/shacl.ttl b/shacl12-vocabularies/shacl.ttl index 58c2f834..67ee87b3 100644 --- a/shacl12-vocabularies/shacl.ttl +++ b/shacl12-vocabularies/shacl.ttl @@ -533,7 +533,6 @@ sh:ClosedConstraintComponent sh:ClosedConstraintComponent-closed a sh:Parameter ; sh:path sh:closed ; - sh:datatype xsd:boolean ; rdfs:isDefinedBy sh: . sh:ClosedConstraintComponent-ignoredProperties