Skip to content

Commit 1b80a4a

Browse files
committed
GH-1112 initial implementation for sh:closed
1 parent 3afda22 commit 1b80a4a

122 files changed

Lines changed: 2064 additions & 206 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

core/sail/shacl/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@
6666
<dependency>
6767
<groupId>org.topbraid</groupId>
6868
<artifactId>shacl</artifactId>
69-
<version>1.3.2</version>
69+
<version>1.4.3</version>
7070
<scope>test</scope>
7171
</dependency>
7272
<dependency>

core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ShaclSail.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,8 @@ public static List<IRI> getSupportedShaclPredicates() {
335335
SHACL.EQUALS,
336336
SHACL.LESS_THAN,
337337
SHACL.LESS_THAN_OR_EQUALS,
338+
SHACL.CLOSED,
339+
SHACL.IGNORED_PROPERTIES,
338340
DASH.hasValueIn,
339341
RSX.targetShape,
340342
RSX.dataGraph,
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2024 Eclipse RDF4J contributors.
3+
*
4+
* All rights reserved. This program and the accompanying materials
5+
* are made available under the terms of the Eclipse Distribution License v1.0
6+
* which accompanies this distribution, and is available at
7+
* http://www.eclipse.org/org/documents/edl-v10.php.
8+
*
9+
* SPDX-License-Identifier: BSD-3-Clause
10+
******************************************************************************/
11+
12+
package org.eclipse.rdf4j.sail.shacl.ast;
13+
14+
public interface CanProduceValidationReport {
15+
16+
void setProducesValidationReport(boolean producesValidationReport);
17+
18+
boolean producesValidationReport();
19+
20+
}

core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/NodeShape.java

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -239,11 +239,6 @@ public ConstraintComponent deepClone() {
239239
return nodeShape;
240240
}
241241

242-
@Override
243-
public boolean overrideValidationReport() {
244-
return false;
245-
}
246-
247242
@Override
248243
public SparqlFragment buildSparqlValidNodes_rsx_targetShape(Variable<Value> subject,
249244
Variable<Value> object,

core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/PropertyShape.java

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -231,7 +231,7 @@ public PlanNode generateTransactionalValidationPlan(ConnectionsGroup connections
231231
.generateTransactionalValidationPlan(connectionsGroup, validationSettings, overrideTargetNode,
232232
Scope.propertyShape);
233233

234-
if (!(constraintComponent instanceof PropertyShape) && !constraintComponent.overrideValidationReport()) {
234+
if (produceValidationReports) {
235235
validationPlanNode = new ValidationReportNode(validationPlanNode, t -> {
236236
return new ValidationResult(t.getActiveTarget(), t.getValue(), this,
237237
constraintComponent, getSeverity(), t.getScope(), t.getContexts(),
@@ -315,11 +315,6 @@ public ConstraintComponent deepClone() {
315315
return nodeShape;
316316
}
317317

318-
@Override
319-
public boolean overrideValidationReport() {
320-
return false;
321-
}
322-
323318
@Override
324319
public SparqlFragment buildSparqlValidNodes_rsx_targetShape(Variable<Value> subject,
325320
Variable<Value> object,

core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/Shape.java

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@
7272
import org.eclipse.rdf4j.sail.shacl.ast.constraintcomponents.QualifiedMinCountConstraintComponent;
7373
import org.eclipse.rdf4j.sail.shacl.ast.constraintcomponents.SparqlConstraintComponent;
7474
import org.eclipse.rdf4j.sail.shacl.ast.constraintcomponents.UniqueLangConstraintComponent;
75+
import org.eclipse.rdf4j.sail.shacl.ast.constraintcomponents.VoidConstraintComponent;
7576
import org.eclipse.rdf4j.sail.shacl.ast.constraintcomponents.XoneConstraintComponent;
7677
import org.eclipse.rdf4j.sail.shacl.ast.planNodes.EmptyNode;
7778
import org.eclipse.rdf4j.sail.shacl.ast.planNodes.PlanNode;
@@ -295,7 +296,7 @@ List<ConstraintComponent> getConstraintComponents(ShaclProperties properties, Sh
295296

296297
if (properties.isClosed()) {
297298
constraintComponent.add(new ClosedConstraintComponent(shapeSource, properties.getProperty(),
298-
properties.getIgnoredProperties()));
299+
properties.getIgnoredProperties(), this));
299300
}
300301

301302
for (IRI iri : properties.getClazz()) {
@@ -379,6 +380,10 @@ List<ConstraintComponent> getConstraintComponents(ShaclProperties properties, Sh
379380
constraintComponent.add(component);
380381
}
381382

383+
if (constraintComponent.isEmpty()) {
384+
constraintComponent.add(new VoidConstraintComponent());
385+
}
386+
382387
return constraintComponent;
383388
}
384389

@@ -608,11 +613,17 @@ private static void calculateIfProducesValidationResult(List<Shape> split) {
608613
}
609614
}
610615

611-
propertyShape.produceValidationReports = true;
616+
if (propertyShape.constraintComponents.get(0) instanceof CanProduceValidationReport) {
617+
((CanProduceValidationReport) propertyShape.constraintComponents.get(0))
618+
.setProducesValidationReport(true);
619+
} else {
620+
propertyShape.produceValidationReports = true;
621+
}
612622

613623
} else if (shape instanceof NodeShape) {
614-
if (shape.constraintComponents.get(0) instanceof SparqlConstraintComponent) {
615-
((SparqlConstraintComponent) shape.constraintComponents.get(0)).produceValidationReports = true;
624+
if (shape.constraintComponents.get(0) instanceof CanProduceValidationReport) {
625+
((CanProduceValidationReport) shape.constraintComponents.get(0))
626+
.setProducesValidationReport(true);
616627
} else {
617628
shape.produceValidationReports = true;
618629
}

core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/StatementMatcher.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,8 +322,22 @@ public int hashCode() {
322322
return Objects.hash(subject, predicate, object);
323323
}
324324

325+
private final static String VALUES_TARGET_0 = "VALUES ( ?target_0000000000 ){}\n";
326+
private final static String VALUES_TARGET_0_AND_1 = "VALUES ( ?target_0000000000 ?target_0000000001 ){}\n";
327+
325328
public String getSparqlValuesDecl(Set<String> varNamesRestriction, boolean addInheritedVarNames,
326329
Set<String> varNamesInQueryFragment) {
330+
331+
// EffectiveTarget interns all the first 1000 variable names, so we can use == to compare them
332+
if (subject.name == "target_0000000000" && predicate.name == null && object.name == null
333+
&& varNamesRestriction.contains(subject.name) && varNamesInQueryFragment.contains(subject.name)) {
334+
return VALUES_TARGET_0;
335+
} else if (subject.name == "target_0000000000" && predicate.name == null && object.name == "target_0000000001"
336+
&& varNamesRestriction.contains(subject.name) && varNamesInQueryFragment.contains(subject.name)
337+
&& varNamesRestriction.contains(object.name) && varNamesInQueryFragment.contains(object.name)) {
338+
return VALUES_TARGET_0_AND_1;
339+
}
340+
327341
StringBuilder sb = new StringBuilder("VALUES ( ");
328342
if (subject.name != null && varNamesRestriction.contains(subject.name) ||
329343
subject.baseName != null && varNamesRestriction.contains(subject.baseName)) {

core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/constraintcomponents/AbstractConstraintComponent.java

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -174,8 +174,4 @@ static PlanNode getAllTargetsIncludingThoseAddedByPath(ConnectionsGroup connecti
174174
return allTargets;
175175
}
176176

177-
@Override
178-
public boolean overrideValidationReport() {
179-
return false;
180-
}
181177
}

core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/constraintcomponents/AbstractPairwiseConstraintComponent.java

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -11,20 +11,18 @@
1111

1212
package org.eclipse.rdf4j.sail.shacl.ast.constraintcomponents;
1313

14-
import java.util.List;
1514
import java.util.Optional;
1615
import java.util.Set;
1716

1817
import org.eclipse.rdf4j.model.IRI;
19-
import org.eclipse.rdf4j.model.Literal;
2018
import org.eclipse.rdf4j.model.Model;
2119
import org.eclipse.rdf4j.model.Resource;
2220
import org.eclipse.rdf4j.model.Value;
2321
import org.eclipse.rdf4j.sail.shacl.ValidationSettings;
22+
import org.eclipse.rdf4j.sail.shacl.ast.CanProduceValidationReport;
2423
import org.eclipse.rdf4j.sail.shacl.ast.Shape;
2524
import org.eclipse.rdf4j.sail.shacl.ast.SparqlFragment;
2625
import org.eclipse.rdf4j.sail.shacl.ast.StatementMatcher;
27-
import org.eclipse.rdf4j.sail.shacl.ast.ValidationApproach;
2826
import org.eclipse.rdf4j.sail.shacl.ast.paths.Path;
2927
import org.eclipse.rdf4j.sail.shacl.ast.planNodes.EmptyNode;
3028
import org.eclipse.rdf4j.sail.shacl.ast.planNodes.PlanNode;
@@ -39,10 +37,12 @@
3937
import org.eclipse.rdf4j.sail.shacl.ast.targets.TargetChain;
4038
import org.eclipse.rdf4j.sail.shacl.wrapper.data.ConnectionsGroup;
4139

42-
abstract class AbstractPairwiseConstraintComponent extends AbstractConstraintComponent {
40+
abstract class AbstractPairwiseConstraintComponent extends AbstractConstraintComponent
41+
implements CanProduceValidationReport {
4342

4443
final Shape shape;
4544
final IRI predicate;
45+
boolean producesValidationReport;
4646

4747
public AbstractPairwiseConstraintComponent(IRI predicate, Shape shape) {
4848
this.predicate = predicate;
@@ -139,12 +139,12 @@ private PlanNode getAllTargetsBasedOnPredicate(ConnectionsGroup connectionsGroup
139139
PlanNode addedByPredicate = new UnorderedSelect(connectionsGroup.getAddedStatements(), null, predicate, null,
140140
validationSettings.getDataGraph(), (s, d) -> {
141141
return new ValidationTuple(s.getSubject(), Scope.propertyShape, false, d);
142-
});
142+
}, null);
143143

144144
PlanNode removedByPredicate = new UnorderedSelect(connectionsGroup.getRemovedStatements(), null, predicate,
145145
null, validationSettings.getDataGraph(), (s, d) -> {
146146
return new ValidationTuple(s.getSubject(), Scope.propertyShape, false, d);
147-
});
147+
}, null);
148148

149149
PlanNode targetFilter1 = effectiveTarget.getTargetFilter(connectionsGroup, validationSettings.getDataGraph(),
150150
addedByPredicate);
@@ -168,8 +168,8 @@ public PlanNode getAllTargetsPlan(ConnectionsGroup connectionsGroup, Resource[]
168168
// removed statements that match predicate could affect sh:or
169169
if (connectionsGroup.getStats().hasRemoved()) {
170170
PlanNode deletedPredicates = new UnorderedSelect(connectionsGroup.getRemovedStatements(), null,
171-
predicate,
172-
null, dataGraph, UnorderedSelect.Mapper.SubjectScopedMapper.getFunction(Scope.propertyShape));
171+
predicate, null, dataGraph,
172+
UnorderedSelect.Mapper.SubjectScopedMapper.getFunction(Scope.propertyShape), null);
173173
deletedPredicates = getTargetChain()
174174
.getEffectiveTarget(Scope.propertyShape, connectionsGroup.getRdfsSubClassOfReasoner(),
175175
stableRandomVariableProvider)
@@ -187,7 +187,8 @@ public PlanNode getAllTargetsPlan(ConnectionsGroup connectionsGroup, Resource[]
187187
// added statements that match predicate could affect sh:not
188188
if (connectionsGroup.getStats().hasAdded()) {
189189
PlanNode addedPredicates = new UnorderedSelect(connectionsGroup.getAddedStatements(), null, predicate,
190-
null, dataGraph, UnorderedSelect.Mapper.SubjectScopedMapper.getFunction(Scope.propertyShape));
190+
null, dataGraph, UnorderedSelect.Mapper.SubjectScopedMapper.getFunction(Scope.propertyShape),
191+
null);
191192
addedPredicates = getTargetChain()
192193
.getEffectiveTarget(Scope.propertyShape, connectionsGroup.getRdfsSubClassOfReasoner(),
193194
stableRandomVariableProvider)
@@ -212,7 +213,7 @@ public PlanNode getAllTargetsPlan(ConnectionsGroup connectionsGroup, Resource[]
212213
if (connectionsGroup.getStats().hasRemoved()) {
213214
PlanNode deletedPredicates = new UnorderedSelect(connectionsGroup.getRemovedStatements(), null,
214215
predicate, null,
215-
dataGraph, UnorderedSelect.Mapper.SubjectScopedMapper.getFunction(Scope.nodeShape));
216+
dataGraph, UnorderedSelect.Mapper.SubjectScopedMapper.getFunction(Scope.nodeShape), null);
216217
deletedPredicates = getTargetChain()
217218
.getEffectiveTarget(Scope.nodeShape, connectionsGroup.getRdfsSubClassOfReasoner(),
218219
stableRandomVariableProvider)
@@ -231,7 +232,7 @@ public PlanNode getAllTargetsPlan(ConnectionsGroup connectionsGroup, Resource[]
231232
if (connectionsGroup.getStats().hasAdded()) {
232233
PlanNode addedPredicates = new UnorderedSelect(connectionsGroup.getAddedStatements(), null, predicate,
233234
null,
234-
dataGraph, UnorderedSelect.Mapper.SubjectScopedMapper.getFunction(Scope.nodeShape));
235+
dataGraph, UnorderedSelect.Mapper.SubjectScopedMapper.getFunction(Scope.nodeShape), null);
235236
addedPredicates = getTargetChain()
236237
.getEffectiveTarget(Scope.nodeShape, connectionsGroup.getRdfsSubClassOfReasoner(),
237238
stableRandomVariableProvider)
@@ -280,7 +281,12 @@ public int hashCode() {
280281
}
281282

282283
@Override
283-
public boolean overrideValidationReport() {
284-
return true;
284+
public void setProducesValidationReport(boolean producesValidationReport) {
285+
this.producesValidationReport = producesValidationReport;
286+
}
287+
288+
@Override
289+
public boolean producesValidationReport() {
290+
return producesValidationReport;
285291
}
286292
}

core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/constraintcomponents/ClassConstraintComponent.java

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ public PlanNode generateTransactionalValidationPlan(ConnectionsGroup connections
110110
if (connectionsGroup.getStats().hasRemoved()) {
111111
PlanNode deletedTypes = new UnorderedSelect(connectionsGroup.getRemovedStatements(), null, RDF.TYPE,
112112
clazz, validationSettings.getDataGraph(),
113-
UnorderedSelect.Mapper.SubjectScopedMapper.getFunction(Scope.nodeShape));
113+
UnorderedSelect.Mapper.SubjectScopedMapper.getFunction(Scope.nodeShape), null);
114114

115115
deletedTypes = getTargetChain()
116116
.getEffectiveTarget(Scope.nodeShape,
@@ -176,7 +176,7 @@ public PlanNode generateTransactionalValidationPlan(ConnectionsGroup connections
176176
if (connectionsGroup.getStats().hasRemoved()) {
177177
PlanNode deletedTypes = new UnorderedSelect(connectionsGroup.getRemovedStatements(), null, RDF.TYPE,
178178
clazz, validationSettings.getDataGraph(),
179-
UnorderedSelect.Mapper.SubjectScopedMapper.getFunction(scope));
179+
UnorderedSelect.Mapper.SubjectScopedMapper.getFunction(scope), null);
180180
deletedTypes = getTargetChain()
181181
.getEffectiveTarget(scope, connectionsGroup.getRdfsSubClassOfReasoner(),
182182
stableRandomVariableProvider)
@@ -216,7 +216,8 @@ public PlanNode getAllTargetsPlan(ConnectionsGroup connectionsGroup, Resource[]
216216
// removed type statements that match clazz could affect sh:or
217217
if (connectionsGroup.getStats().hasRemoved()) {
218218
PlanNode deletedTypes = new UnorderedSelect(connectionsGroup.getRemovedStatements(), null, RDF.TYPE,
219-
clazz, dataGraph, UnorderedSelect.Mapper.SubjectScopedMapper.getFunction(Scope.nodeShape));
219+
clazz, dataGraph, UnorderedSelect.Mapper.SubjectScopedMapper.getFunction(Scope.nodeShape),
220+
null);
220221
deletedTypes = getTargetChain()
221222
.getEffectiveTarget(Scope.nodeShape, connectionsGroup.getRdfsSubClassOfReasoner(),
222223
stableRandomVariableProvider)
@@ -233,7 +234,8 @@ public PlanNode getAllTargetsPlan(ConnectionsGroup connectionsGroup, Resource[]
233234
// added type statements that match clazz could affect sh:not
234235
if (connectionsGroup.getStats().hasAdded()) {
235236
PlanNode addedTypes = new UnorderedSelect(connectionsGroup.getAddedStatements(), null, RDF.TYPE,
236-
clazz, dataGraph, UnorderedSelect.Mapper.SubjectScopedMapper.getFunction(Scope.nodeShape));
237+
clazz, dataGraph, UnorderedSelect.Mapper.SubjectScopedMapper.getFunction(Scope.nodeShape),
238+
null);
237239
addedTypes = getTargetChain()
238240
.getEffectiveTarget(Scope.nodeShape, connectionsGroup.getRdfsSubClassOfReasoner(),
239241
stableRandomVariableProvider)
@@ -254,7 +256,7 @@ public PlanNode getAllTargetsPlan(ConnectionsGroup connectionsGroup, Resource[]
254256
// removed type statements that match clazz could affect sh:or
255257
if (connectionsGroup.getStats().hasRemoved()) {
256258
PlanNode deletedTypes = new UnorderedSelect(connectionsGroup.getRemovedStatements(), null, RDF.TYPE, clazz,
257-
dataGraph, UnorderedSelect.Mapper.SubjectScopedMapper.getFunction(Scope.nodeShape));
259+
dataGraph, UnorderedSelect.Mapper.SubjectScopedMapper.getFunction(Scope.nodeShape), null);
258260
deletedTypes = getTargetChain()
259261
.getEffectiveTarget(Scope.nodeShape, connectionsGroup.getRdfsSubClassOfReasoner(),
260262
stableRandomVariableProvider)
@@ -271,7 +273,7 @@ public PlanNode getAllTargetsPlan(ConnectionsGroup connectionsGroup, Resource[]
271273
// added type statements that match clazz could affect sh:not
272274
if (connectionsGroup.getStats().hasAdded()) {
273275
PlanNode addedTypes = new UnorderedSelect(connectionsGroup.getAddedStatements(), null, RDF.TYPE, clazz,
274-
dataGraph, UnorderedSelect.Mapper.SubjectScopedMapper.getFunction(Scope.nodeShape));
276+
dataGraph, UnorderedSelect.Mapper.SubjectScopedMapper.getFunction(Scope.nodeShape), null);
275277
addedTypes = getTargetChain()
276278
.getEffectiveTarget(Scope.nodeShape, connectionsGroup.getRdfsSubClassOfReasoner(),
277279
stableRandomVariableProvider)

0 commit comments

Comments
 (0)