Skip to content

Issue 172: Support for sh:closed sh:ByTypes #399

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 7 commits into
base: gh-pages
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 47 additions & 10 deletions shacl12-core/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -1113,7 +1113,7 @@ <h2>Shapes and Constraints</h2>
<div class="diagram-class-properties">
<div class="diagram-class-properties-start">
<div><b><a href="#constraints">Constraint parameters</a></b>, for example:</div>
<div><a href="#ClosedConstraintComponent">sh:closed</a> : xsd:boolean</div>
<div><a href="#ClosedConstraintComponent">sh:closed</a> : xsd:boolean or sh:ByTypes</div>
<div><a href="#OrConstraintComponent">sh:or</a> : rdf:List</div>
<div><a href="#NotConstraintComponent">sh:not</a> : sh:Shape</div>
<div><a href="#PropertyConstraintComponent">sh:property</a> : sh:PropertyShape</div>
Expand Down Expand Up @@ -6037,7 +6037,8 @@ <h3>sh:closed, sh:ignoredProperties</h3>
<td><code>sh:closed</code></td>
<td>
Set to <code>true</code> to close the shape.
<span data-syntax-rule="closed-datatype">The values of <code>sh:closed</code> in a shape are literals with datatype <code>xsd:boolean</code>.</span>
<span data-syntax-rule="closed-datatype">The values of <code>sh:closed</code> in a shape are literals with datatype <code>xsd:boolean</code>
or the <a>IRI</a> <code>sh:ByTypes</code>.</span>
</td>
</tr>
<tr>
Expand All @@ -6054,14 +6055,41 @@ <h3>sh:closed, sh:ignoredProperties</h3>
<div class="def-text-body" data-validator="Closed">
Let <code>$closed</code> be a <a>parameter value</a> for <code>sh:closed</code>.
Let <code>$ignoredProperties</code> be a <a>value</a> for <code>sh:ignoredProperties</code>.
If <code>$closed</code> is <code>true</code> then
there is a <a>validation result</a> for each <a>triple</a> that has a <a>value node</a> as its
<a>subject</a> and a <a>predicate</a> that is not explicitly enumerated as a <a>value</a> of <code>sh:path</code>
in any of the <a>property shapes</a> declared via <code>sh:property</code> at the current shape.
<br/><br/>
If <code>$closed</code> is <code>true</code> or <code>sh:ByTypes</code> and <code>P</code>
is the set of properties defined below,
then there is a <a>validation result</a> for each <a>triple</a> that has a <a>value node</a> as its
<a>subject</a> and a <a>predicate</a> that is not in <code>P</code>.
If <code>$ignoredProperties</code> has a value then the properties enumerated as <a>members</a> of this <a>SHACL list</a>
are also permitted for the <a>value node</a>.
The <a>validation result</a> MUST have the <a>predicate</a> of the triple as its <code>sh:resultPath</code>,
and the <a>object</a> of the triple as its <code>sh:value</code>.
<br/><br/>
If <code>$closed</code> is <code>true</code>, then <code>P</code> is the set of <a>IRI</a> properties
that can be reached from the current shape via the SPARQL path <code>sh:property/sh:path</code>.
<br/><br/>
If <code>$closed</code> is <code>sh:ByTypes</code>, then <code>P</code> is the set of <a>IRI</a> properties
that can be reached from the value node via the following algorithm, plus <code>rdf:type</code>:
<div style="margin: 20px">
<code>
function collectProperties(S)<br />
&nbsp;&nbsp;add all IRI properties that can be reached from S via the SPARQL path<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sh:property/sh:path<br />
&nbsp;&nbsp;if S is a SHACL instance of rdfs:Class in the shapes graph {<br />
&nbsp;&nbsp;&nbsp;&nbsp;for each triple in the shapes graph matching (S rdfs:subClassOf ?o)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;collectProperties(?o)<br />
&nbsp;&nbsp;&nbsp;&nbsp;for each triple in the shapes graph matching (?s sh:targetClass S)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;collectProperties(?s)<br />
&nbsp;&nbsp;}<br/>
&nbsp;&nbsp;if S is a SHACL instance of sh:NodeShape in the shapes graph<br />
&nbsp;&nbsp;&nbsp;&nbsp;for each triple in the shapes graph matching (S sh:node ?o)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;collectProperties(?o)<br />
<br />
for each rdf:type T of the value node in the data graph<br />
&nbsp;&nbsp;collectProperties(T)<br />
Comment on lines +6074 to +6089
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
<code>
function collectProperties(S)<br />
&nbsp;&nbsp;add all IRI properties that can be reached from S via the SPARQL path<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sh:property/sh:path<br />
&nbsp;&nbsp;if S is a SHACL instance of rdfs:Class in the shapes graph {<br />
&nbsp;&nbsp;&nbsp;&nbsp;for each triple in the shapes graph matching (S rdfs:subClassOf ?o)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;collectProperties(?o)<br />
&nbsp;&nbsp;&nbsp;&nbsp;for each triple in the shapes graph matching (?s sh:targetClass S)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;collectProperties(?s)<br />
&nbsp;&nbsp;}<br/>
&nbsp;&nbsp;if S is a SHACL instance of sh:NodeShape in the shapes graph<br />
&nbsp;&nbsp;&nbsp;&nbsp;for each triple in the shapes graph matching (S sh:node ?o)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;collectProperties(?o)<br />
<br />
for each rdf:type T of the value node in the data graph<br />
&nbsp;&nbsp;collectProperties(T)<br />
<code>
<pre>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, then {
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, then
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)</pre>

Copy link
Contributor Author

@HolgerKnublauch HolgerKnublauch Jun 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The pre was adding syntax highlighting in unsuitable places in my testing. Have you tried to render your change?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried out the suggested changes, and indeed some unwanted highlighting was added.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do my editing in GitHub's web interface, so I can't see my changes rendered until they're merged into a PR and PR-Preview is invoked.

I guess there's some CSS used on other <pre> blocks to prevent the unwanted syntax highlighting. Changing from the manual formatting to the <pre> isn't vital; if I find the CSS incantation I'll try to apply it later.

</code></div>
Note that implementations need to avoid infinite loops in the algorithm above by preventing
it from visiting the same `S` twice.
</div>
</div>
<p><em>The remainder of this section is informative.</em></p>
Expand Down Expand Up @@ -6149,6 +6177,15 @@ <h3>sh:closed, sh:ignoredProperties</h3>
</div>
</div>
</aside>
<p>
The use case for <code>sh:closed sh:ByTypes</code> includes properties that are declared
in superclasses of the types of the current value node (via <code>rdfs:subClassOf</code>),
as well as other shapes that are linked to those types via <code>sh:targetClass</code> and
the shapes that can be reached from one node shape to the other via <code>sh:node</code>.
Examples for <code>sh:ByTypes</code> can be found in the test case library:
<a href="../shacl12-test-suite/tests/core/node/closed-003.ttl">closed-003.ttl</a>,
<a href="../shacl12-test-suite/tests/core/node/closed-004.ttl">closed-004.ttl</a>.
</p>
</section>

<section id="HasValueConstraintComponent">
Expand Down Expand Up @@ -6866,10 +6903,10 @@ <h2>Changes between SHACL 1.0 Core and SHACL 1.2 Core</h2>
<ul>
<li>Introduced <a>node expressions</a> as an extension point to dynamically compute lists of nodes. Generalized <code>sh:targetNode</code>, <code>sh:deactivated</code> and <code>sh:defaultValue</code>, and introduced <code>sh:values</code> to support node expressions.</li>
<li>Added the new constraint component <a href="#SingleLineConstraintComponent"><code>sh:singleLine</code></a>, see <a href="https://github.com/w3c/data-shapes/issues/177">Issue 177</a></li>
<li>Added the new class <a href="#ShapeClass"><code>sh:ShapeClass</code></a> for implicit class targets, see <a href="https://github.com/w3c/data-shapes/issues/212">Issue 212</a></li>
<li>Moved SPARQL-based validators from Core to an Appendix of SHACL-SPARQL, see <a href="https://github.com/w3c/data-shapes/issues/271">Issue 271</a></li>
<li>Added the new constraint component <a href="#ExpressionConstraintComponent"><code>sh:expression</code></a>, see <a href="https://github.com/w3c/data-shapes/issues/357">Issue 357</a></li>
<li>Values for <a href="#deactivated"><code>sh:deactivated</code></a>, <a href="#message"><code>sh:message</code></a> and <a href="#severity"><code>sh:severity</code></a> can now be specified using RDF 1.2 reification, see <a href="https://github.com/w3c/data-shapes/issues/173">Issue 173</a></li>
<li>Added the new class <a href="#ShapeClass"><code>sh:ShapeClass</code></a> for implicit class targets; see <a href="https://github.com/w3c/data-shapes/issues/212">Issue 212</a></li>
<li>Moved SPARQL-based validators from Core to an Appendix of SHACL-SPARQL; see <a href="https://github.com/w3c/data-shapes/issues/271">Issue 271</a></li>
<li>Added the new constraint component <a href="#ExpressionConstraintComponent"><code>sh:expression</code></a>; see <a href="https://github.com/w3c/data-shapes/issues/357">Issue 357</a></li>
<li>Added the new value <code>sh:ByTypes</code> for <a href="#ClosedConstraintComponent"><code>sh:closed</code></a>; see <a href="https://github.com/w3c/data-shapes/issues/172">Issue 172</a></li>
</ul>
</section>
</body>
Expand Down
66 changes: 66 additions & 0 deletions shacl12-test-suite/tests/core/node/closed-003.ttl
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
@prefix dash: <http://datashapes.org/dash#> .
@prefix ex: <http://example.com/ns#> .
@prefix mf: <http://www.w3.org/2001/sw/DataAccess/tests/test-manifest#> .
@prefix owl: <http://www.w3.org/2002/07/owl#> .
@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 sht: <http://www.w3.org/ns/shacl-test#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .

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 (
<closed-003>
) ;
.
<closed-003>
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 ;
.
84 changes: 84 additions & 0 deletions shacl12-test-suite/tests/core/node/closed-004.ttl
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
@prefix dash: <http://datashapes.org/dash#> .
@prefix ex: <http://example.com/ns#> .
@prefix mf: <http://www.w3.org/2001/sw/DataAccess/tests/test-manifest#> .
@prefix owl: <http://www.w3.org/2002/07/owl#> .
@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 sht: <http://www.w3.org/ns/shacl-test#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .

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 (
<closed-004>
) ;
.
<closed-004>
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 ;
.
2 changes: 2 additions & 0 deletions shacl12-test-suite/tests/core/node/manifest.ttl
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
mf:include <class-003.ttl> ;
mf:include <closed-001.ttl> ;
mf:include <closed-002.ttl> ;
mf:include <closed-003.ttl> ;
mf:include <closed-004.ttl> ;
mf:include <datatype-001.ttl> ;
mf:include <datatype-002.ttl> ;
mf:include <disjoint-001.ttl> ;
Expand Down
1 change: 0 additions & 1 deletion shacl12-vocabularies/shacl.ttl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down