Skip to content

Commit 2652dcb

Browse files
committed
GH-1112 sh:closed within sh:not
1 parent 1b80a4a commit 2652dcb

59 files changed

Lines changed: 1263 additions & 3 deletions

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/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/constraintcomponents/ClosedConstraintComponent.java

Lines changed: 112 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -100,10 +100,14 @@ public ClosedConstraintComponent(ClosedConstraintComponent closedConstraintCompo
100100

101101
@Override
102102
public void toModel(Resource subject, IRI predicate, Model model, Set<Resource> cycleDetection) {
103-
if (!ignoredProperties.isEmpty() && !model.contains(getId(), SHACL.IGNORED_PROPERTIES, null)) {
103+
104+
if (!ignoredProperties.isEmpty()) {
104105
model.add(subject, SHACL.IGNORED_PROPERTIES, ignoredPropertiesHead);
105-
ShaclAstLists.listToRdf(ignoredProperties, ignoredPropertiesHead, model);
106+
if (!model.contains(ignoredPropertiesHead, null, null)) {
107+
ShaclAstLists.listToRdf(ignoredProperties, ignoredPropertiesHead, model);
108+
}
106109
}
110+
107111
model.add(subject, SHACL.CLOSED, literal(true));
108112
}
109113

@@ -233,7 +237,12 @@ public PlanNode generateTransactionalValidationPlan(ConnectionsGroup connections
233237
PlanNode targetNodePlanNode;
234238

235239
if (overrideTargetNode != null) {
236-
targetNodePlanNode = overrideTargetNode.getPlanNode();
240+
targetNodePlanNode = getTargetChain()
241+
.getEffectiveTarget(scope, connectionsGroup.getRdfsSubClassOfReasoner(),
242+
stableRandomVariableProvider)
243+
.extend(overrideTargetNode.getPlanNode(), connectionsGroup, validationSettings.getDataGraph(),
244+
scope, EffectiveTarget.Extend.right,
245+
false, null);
237246
} else {
238247
PlanNode addedTargets = effectiveTarget.getPlanNode(connectionsGroup, validationSettings.getDataGraph(),
239248
scope, false, null);
@@ -313,6 +322,106 @@ public PlanNode generateTransactionalValidationPlan(ConnectionsGroup connections
313322
@Override
314323
public PlanNode getAllTargetsPlan(ConnectionsGroup connectionsGroup, Resource[] dataGraph, Scope scope,
315324
StatementMatcher.StableRandomVariableProvider stableRandomVariableProvider) {
325+
326+
EffectiveTarget effectiveTarget = getTargetChain().getEffectiveTarget(scope,
327+
connectionsGroup.getRdfsSubClassOfReasoner(), stableRandomVariableProvider);
328+
329+
switch (scope) {
330+
case none:
331+
throw new IllegalStateException();
332+
case nodeShape:
333+
334+
PlanNode targets = effectiveTarget.getPlanNode(connectionsGroup, dataGraph,
335+
scope, true, null);
336+
337+
// get all subjects of all triples where the predicate is not in the allAllowedPredicates set
338+
PlanNode unorderedSelectAdded = new UnorderedSelect(connectionsGroup.getAddedStatements(), null, null,
339+
null, dataGraph,
340+
UnorderedSelect.Mapper.SubjectScopedMapper.getFunction(scope),
341+
(statement -> !allAllowedPredicates.contains(statement.getPredicate())));
342+
343+
// get all subjects of all triples where the predicate is not in the allAllowedPredicates set
344+
PlanNode unorderedSelectRemoved = new UnorderedSelect(connectionsGroup.getRemovedStatements(), null, null,
345+
null, dataGraph,
346+
UnorderedSelect.Mapper.SubjectScopedMapper.getFunction(scope),
347+
(statement -> !allAllowedPredicates.contains(statement.getPredicate())));
348+
349+
// then remove any that are in the targets node
350+
unorderedSelectAdded = new TrimToTarget(new NotValuesIn(unorderedSelectAdded, targets));
351+
unorderedSelectRemoved = new TrimToTarget(new NotValuesIn(unorderedSelectRemoved, targets));
352+
353+
// union and remove duplicates
354+
PlanNode unique = Unique.getInstance(UnionNode.getInstance(unorderedSelectAdded, unorderedSelectRemoved),
355+
false);
356+
357+
// then check that the rest are actually targets
358+
PlanNode targetFilter = effectiveTarget.getTargetFilter(connectionsGroup,
359+
dataGraph,
360+
unique);
361+
362+
// this should now be targets that are not valid
363+
PlanNode extend = effectiveTarget.extend(targetFilter, connectionsGroup,
364+
dataGraph,
365+
scope, EffectiveTarget.Extend.left, false, null);
366+
367+
return UnionNode.getInstance(extend, effectiveTarget.getPlanNode(connectionsGroup,
368+
dataGraph, scope, true, null));
369+
370+
case propertyShape:
371+
Path path = getTargetChain().getPath().get();
372+
373+
BufferedSplitter addedTargetsBufferedSplitter = new BufferedSplitter(
374+
effectiveTarget.getPlanNode(connectionsGroup, dataGraph, scope, false,
375+
null));
376+
PlanNode addedTargets = addedTargetsBufferedSplitter.getPlanNode();
377+
PlanNode addedByPath = path.getAllAdded(connectionsGroup, dataGraph, null);
378+
379+
addedByPath = effectiveTarget.getTargetFilter(connectionsGroup,
380+
dataGraph, Unique.getInstance(new TrimToTarget(addedByPath), false));
381+
382+
addedByPath = new ReduceTargets(addedByPath, addedTargetsBufferedSplitter.getPlanNode());
383+
384+
addedByPath = effectiveTarget.extend(addedByPath, connectionsGroup, dataGraph,
385+
scope,
386+
EffectiveTarget.Extend.left, false,
387+
null);
388+
389+
PlanNode addedByValue = new UnorderedSelect(connectionsGroup.getAddedStatements(), null, null,
390+
null, dataGraph,
391+
UnorderedSelect.Mapper.SubjectScopedMapper.getFunction(scope), (statement -> {
392+
return !allAllowedPredicates.contains(statement.getPredicate());
393+
}));
394+
395+
PlanNode removedByValue = new UnorderedSelect(connectionsGroup.getRemovedStatements(), null, null,
396+
null, dataGraph,
397+
UnorderedSelect.Mapper.SubjectScopedMapper.getFunction(scope), (statement -> {
398+
return !allAllowedPredicates.contains(statement.getPredicate());
399+
}));
400+
401+
addedByValue = UnionNode.getInstance(addedByValue, removedByValue);
402+
403+
addedByValue = getTargetChain()
404+
.getEffectiveTarget(Scope.nodeShape,
405+
connectionsGroup.getRdfsSubClassOfReasoner(), stableRandomVariableProvider)
406+
.extend(addedByValue, connectionsGroup, dataGraph, Scope.nodeShape,
407+
EffectiveTarget.Extend.left,
408+
false, null);
409+
410+
addedByValue = getTargetChain()
411+
.getEffectiveTarget(Scope.nodeShape,
412+
connectionsGroup.getRdfsSubClassOfReasoner(), stableRandomVariableProvider)
413+
.getTargetFilter(connectionsGroup, dataGraph, addedByValue);
414+
415+
addedTargets = UnionNode.getInstance(addedTargets,
416+
new TrimToTarget(new ShiftToPropertyShape(addedByValue)));
417+
418+
addedTargets = UnionNode.getInstance(addedByPath, addedTargets);
419+
addedTargets = Unique.getInstance(addedTargets, false);
420+
421+
return addedTargets;
422+
423+
}
424+
316425
throw new UnsupportedOperationException();
317426
}
318427

core/sail/shacl/src/test/java/org/eclipse/rdf4j/sail/shacl/AbstractShaclTest.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -588,6 +588,12 @@ void referenceImplementationTestCaseValidation(TestCase testCase) {
588588
return;
589589
}
590590

591+
// the TopBraid SHACL API doesn't agree with other implementations on how sh:closed should work in a property
592+
// shape
593+
if (testCase.testCasePath.startsWith("test-cases/closed/notPropertyShape/")) {
594+
return;
595+
}
596+
591597
printTestCase(testCase);
592598

593599
Dataset shaclDataset = DatasetFactory.create();
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
PREFIX ex: <http://example.com/ns#>
2+
PREFIX owl: <http://www.w3.org/2002/07/owl#>
3+
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
4+
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
5+
PREFIX sh: <http://www.w3.org/ns/shacl#>
6+
PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
7+
8+
INSERT DATA {
9+
ex:validPerson1 a ex:Person ;
10+
ex:name "John" ;
11+
ex:age 30 ;
12+
ex:thisPropertyIsIgnored "Ignored by sh:closed" ;
13+
.
14+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
@prefix ex: <http://example.com/ns#> .
2+
@prefix foaf: <http://xmlns.com/foaf/0.1/> .
3+
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
4+
@prefix sh: <http://www.w3.org/ns/shacl#> .
5+
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
6+
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
7+
@prefix rsx: <http://rdf4j.org/shacl-extensions#> .
8+
@prefix rdf4j: <http://rdf4j.org/schema/rdf4j#> .
9+
10+
[] a sh:ValidationReport;
11+
rdf4j:truncated false;
12+
sh:conforms false;
13+
sh:result [ a sh:ValidationResult;
14+
rsx:shapesGraph rdf4j:SHACLShapeGraph;
15+
sh:focusNode ex:validPerson1;
16+
sh:resultSeverity sh:Violation;
17+
sh:sourceConstraintComponent sh:NotConstraintComponent;
18+
sh:sourceShape ex:Person;
19+
sh:value ex:validPerson1
20+
] .
21+
22+
ex:Person a sh:NodeShape;
23+
sh:not [ a sh:NodeShape;
24+
sh:closed true;
25+
sh:ignoredProperties (ex:thisPropertyIsIgnored ex:thisPropertyIsAlsoIgnored);
26+
sh:property [ a sh:PropertyShape;
27+
sh:path ex:name
28+
], [ a sh:PropertyShape;
29+
sh:path ex:age
30+
], [ a sh:PropertyShape;
31+
sh:path rdf:type
32+
]
33+
];
34+
sh:targetClass ex:Person .
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
PREFIX ex: <http://example.com/ns#>
2+
PREFIX owl: <http://www.w3.org/2002/07/owl#>
3+
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
4+
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
5+
PREFIX sh: <http://www.w3.org/ns/shacl#>
6+
PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
7+
8+
INSERT DATA {
9+
ex:validPerson1
10+
ex:name "John" ;
11+
ex:age 30 .
12+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
PREFIX ex: <http://example.com/ns#>
2+
PREFIX owl: <http://www.w3.org/2002/07/owl#>
3+
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
4+
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
5+
PREFIX sh: <http://www.w3.org/ns/shacl#>
6+
PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
7+
8+
INSERT DATA {
9+
ex:validPerson1 a ex:Person.
10+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
@prefix ex: <http://example.com/ns#> .
2+
@prefix foaf: <http://xmlns.com/foaf/0.1/> .
3+
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
4+
@prefix sh: <http://www.w3.org/ns/shacl#> .
5+
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
6+
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
7+
@prefix rsx: <http://rdf4j.org/shacl-extensions#> .
8+
@prefix rdf4j: <http://rdf4j.org/schema/rdf4j#> .
9+
10+
[] a sh:ValidationReport;
11+
rdf4j:truncated false;
12+
sh:conforms false;
13+
sh:result [ a sh:ValidationResult;
14+
rsx:shapesGraph rdf4j:SHACLShapeGraph;
15+
sh:focusNode ex:validPerson1;
16+
sh:resultSeverity sh:Violation;
17+
sh:sourceConstraintComponent sh:NotConstraintComponent;
18+
sh:sourceShape ex:Person;
19+
sh:value ex:validPerson1
20+
] .
21+
22+
ex:Person a sh:NodeShape;
23+
sh:not [ a sh:NodeShape;
24+
sh:closed true;
25+
sh:ignoredProperties (ex:thisPropertyIsIgnored ex:thisPropertyIsAlsoIgnored);
26+
sh:property [ a sh:PropertyShape;
27+
sh:path ex:name
28+
], [ a sh:PropertyShape;
29+
sh:path ex:age
30+
], [ a sh:PropertyShape;
31+
sh:path rdf:type
32+
]
33+
];
34+
sh:targetClass ex:Person .
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
PREFIX ex: <http://example.com/ns#>
2+
PREFIX owl: <http://www.w3.org/2002/07/owl#>
3+
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
4+
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
5+
PREFIX sh: <http://www.w3.org/ns/shacl#>
6+
PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
7+
8+
INSERT DATA {
9+
ex:validPerson1 a ex:Person ;
10+
ex:name "John" ;
11+
ex:age 30 ;
12+
ex:age2 30;
13+
.
14+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
PREFIX ex: <http://example.com/ns#>
2+
PREFIX owl: <http://www.w3.org/2002/07/owl#>
3+
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
4+
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
5+
PREFIX sh: <http://www.w3.org/ns/shacl#>
6+
PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
7+
8+
DELETE DATA {
9+
ex:validPerson1 ex:age2 30 .
10+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
@prefix ex: <http://example.com/ns#> .
2+
@prefix foaf: <http://xmlns.com/foaf/0.1/> .
3+
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
4+
@prefix sh: <http://www.w3.org/ns/shacl#> .
5+
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
6+
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
7+
@prefix rsx: <http://rdf4j.org/shacl-extensions#> .
8+
@prefix rdf4j: <http://rdf4j.org/schema/rdf4j#> .
9+
10+
[] a sh:ValidationReport;
11+
rdf4j:truncated false;
12+
sh:conforms false;
13+
sh:result [ a sh:ValidationResult;
14+
rsx:shapesGraph rdf4j:SHACLShapeGraph;
15+
sh:focusNode ex:validPerson1;
16+
sh:resultSeverity sh:Violation;
17+
sh:sourceConstraintComponent sh:NotConstraintComponent;
18+
sh:sourceShape ex:Person;
19+
sh:value ex:validPerson1
20+
] .
21+
22+
ex:Person a sh:NodeShape;
23+
sh:not [ a sh:NodeShape;
24+
sh:closed true;
25+
sh:ignoredProperties (ex:thisPropertyIsIgnored ex:thisPropertyIsAlsoIgnored);
26+
sh:property [ a sh:PropertyShape;
27+
sh:path ex:name
28+
], [ a sh:PropertyShape;
29+
sh:path ex:age
30+
], [ a sh:PropertyShape;
31+
sh:path rdf:type
32+
]
33+
];
34+
sh:targetClass ex:Person .

0 commit comments

Comments
 (0)