Skip to content

Commit 26be6a5

Browse files
authored
GH-1112 sh:closed (#4944)
2 parents 3afda22 + 1dc2438 commit 26be6a5

180 files changed

Lines changed: 3498 additions & 215 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/ValidationQuery.java

Lines changed: 64 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,24 +16,27 @@
1616
import java.util.Collections;
1717
import java.util.HashSet;
1818
import java.util.List;
19+
import java.util.Optional;
1920
import java.util.Set;
21+
import java.util.function.Function;
2022

2123
import org.eclipse.rdf4j.model.Namespace;
2224
import org.eclipse.rdf4j.model.Resource;
2325
import org.eclipse.rdf4j.model.Value;
26+
import org.eclipse.rdf4j.query.BindingSet;
2427
import org.eclipse.rdf4j.sail.SailConnection;
2528
import org.eclipse.rdf4j.sail.shacl.ast.StatementMatcher.Variable;
2629
import org.eclipse.rdf4j.sail.shacl.ast.constraintcomponents.ConstraintComponent;
2730
import org.eclipse.rdf4j.sail.shacl.ast.planNodes.EmptyNode;
2831
import org.eclipse.rdf4j.sail.shacl.ast.planNodes.PlanNode;
2932
import org.eclipse.rdf4j.sail.shacl.ast.planNodes.Select;
30-
import org.eclipse.rdf4j.sail.shacl.ast.planNodes.ValidationReportNode;
3133
import org.eclipse.rdf4j.sail.shacl.ast.planNodes.ValidationTuple;
3234
import org.eclipse.rdf4j.sail.shacl.results.ValidationResult;
3335

3436
public class ValidationQuery {
3537

3638
private final Set<Namespace> namespaces = new HashSet<>();
39+
private ValidationResultGenerator validationResultGenerator;
3740
private String query;
3841
private ConstraintComponent.Scope scope;
3942
private ConstraintComponent.Scope scope_validationReport;
@@ -54,6 +57,7 @@ public class ValidationQuery {
5457

5558
private Severity severity;
5659
private Shape shape;
60+
private List<Variable<?>> extraVariables = List.of();
5761

5862
public ValidationQuery(Collection<Namespace> namespaces, String query, List<Variable<Value>> targets,
5963
Variable<Value> value,
@@ -90,6 +94,8 @@ public ValidationQuery(Collection<Namespace> namespaces, String query, List<Vari
9094
this.constraintComponent = constraintComponent;
9195
this.severity = severity;
9296
this.shape = shape;
97+
this.validationResultGenerator = new ValidationResultGenerator();
98+
9399
}
94100

95101
public ValidationQuery(Set<Namespace> namespaces, String query, ConstraintComponent.Scope scope,
@@ -101,6 +107,7 @@ public ValidationQuery(Set<Namespace> namespaces, String query, ConstraintCompon
101107
this.variables = Collections.unmodifiableList(variables);
102108
this.targetIndex = targetIndex;
103109
this.valueIndex = valueIndex;
110+
this.validationResultGenerator = new ValidationResultGenerator();
104111
}
105112

106113
/**
@@ -169,41 +176,78 @@ public PlanNode getValidationPlan(SailConnection baseConnection, Resource[] data
169176

170177
Select select = new Select(baseConnection, fullQueryString, bindings -> {
171178

179+
var validationResultFunction = validationResultGenerator.getValidationTupleValidationResultFunction(this,
180+
shapesGraphs, bindings);
181+
182+
ValidationTuple validationTuple;
183+
172184
if (scope_validationReport == ConstraintComponent.Scope.propertyShape) {
173185
if (propertyShapeWithValue_validationReport) {
174-
return new ValidationTuple(bindings.getValue(getTargetVariable(true)),
186+
validationTuple = new ValidationTuple(bindings.getValue(getTargetVariable(true)),
175187
bindings.getValue(getValueVariable(true)),
176188
scope_validationReport, true, dataGraph);
177189
} else {
178-
return new ValidationTuple(bindings.getValue(getTargetVariable(true)),
190+
validationTuple = new ValidationTuple(bindings.getValue(getTargetVariable(true)),
179191
scope_validationReport, false, dataGraph);
180192
}
181193

182194
} else {
183-
return new ValidationTuple(bindings.getValue(getTargetVariable(true)),
195+
validationTuple = new ValidationTuple(bindings.getValue(getTargetVariable(true)),
184196
scope_validationReport, true, dataGraph);
185197
}
186198

199+
return validationTuple.addValidationResult(validationResultFunction);
200+
187201
}, dataGraph);
188202

189-
return new ValidationReportNode(select, t -> {
190-
return new ValidationResult(t.getActiveTarget(), t.getValue(), shape,
191-
constraintComponent_validationReport, severity, t.getScope(), t.getContexts(), shapesGraphs);
192-
});
203+
return select;
204+
205+
}
206+
207+
public static class ValidationResultGenerator {
208+
209+
public Function<ValidationTuple, ValidationResult> getValidationTupleValidationResultFunction(
210+
ValidationQuery validationQuery, Resource[] shapesGraphs, BindingSet bindings) {
211+
return t -> new ValidationResult(t.getActiveTarget(), t.getValue(), validationQuery.shape,
212+
validationQuery.constraintComponent_validationReport, validationQuery.severity, t.getScope(),
213+
t.getContexts(), shapesGraphs);
214+
}
215+
216+
}
193217

218+
public void setValidationResultGenerator(List<Variable<?>> extraVariables,
219+
ValidationResultGenerator validationResultGenerator) {
220+
this.validationResultGenerator = validationResultGenerator;
221+
this.extraVariables = extraVariables;
194222
}
195223

196224
private String getFullQueryString() {
225+
String extraVariablesString;
226+
if (!extraVariables.isEmpty()) {
227+
Optional<String> reduce = extraVariables.stream()
228+
.map(Variable::asSparqlVariable)
229+
.reduce((a, b) -> a + " " + b);
230+
if (reduce.isPresent()) {
231+
extraVariablesString = reduce.get() + " ";
232+
} else {
233+
extraVariablesString = "";
234+
}
235+
} else
236+
extraVariablesString = "";
237+
197238
if (scope_validationReport == ConstraintComponent.Scope.propertyShape
198239
&& propertyShapeWithValue_validationReport) {
240+
199241
return ShaclPrefixParser.toSparqlPrefixes(namespaces) + "\nSELECT DISTINCT " +
200242
"?" + getTargetVariable(true) + " " +
201243
"?" + getValueVariable(true) + " " +
244+
extraVariablesString +
202245
"WHERE {\n" + query + "\n}";
203246

204247
} else {
205248
return ShaclPrefixParser.toSparqlPrefixes(namespaces) + "\nSELECT DISTINCT " +
206249
"?" + getTargetVariable(true) + " " +
250+
extraVariablesString +
207251
"WHERE {\n" + query + "\n}";
208252
}
209253
}
@@ -267,6 +311,18 @@ public void makeCurrentStateValidationReport() {
267311
propertyShapeWithValue_validationReport = propertyShapeWithValue;
268312
}
269313

314+
public Shape getShape() {
315+
return shape;
316+
}
317+
318+
public Severity getSeverity() {
319+
return severity;
320+
}
321+
322+
public ConstraintComponent getConstraintComponent_validationReport() {
323+
return constraintComponent_validationReport;
324+
}
325+
270326
// used for sh:deactivated
271327
public static class Deactivated extends ValidationQuery {
272328

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
}

0 commit comments

Comments
 (0)