Skip to content

Commit 6466382

Browse files
authored
GH-4758 support for union of data graphs as well as union of shape graphs (#4760)
2 parents fddce60 + ec631df commit 6466382

28 files changed

Lines changed: 477 additions & 44 deletions

File tree

core/model-vocabulary/src/main/java/org/eclipse/rdf4j/model/vocabulary/RSX.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,14 @@ public class RSX {
3939

4040
/** <var>http://rdf4j.org/shacl-extensions#targetShape</var> */
4141
public final static IRI targetShape = create("targetShape");
42+
4243
public final static IRI dataGraph = create("dataGraph");
4344
public final static IRI shapesGraph = create("shapesGraph");
45+
4446
public final static IRI valueConformsToXsdDatatypeFunction = create("valueConformsToXsdDatatypeFunction");
4547

48+
public final static IRI DataAndShapesGraphLink = create("DataAndShapesGraphLink");
49+
4650
private static IRI create(String localName) {
4751
return Vocabularies.createIRI(RSX.NAMESPACE, localName);
4852
}

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

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
import org.eclipse.rdf4j.model.vocabulary.DASH;
4040
import org.eclipse.rdf4j.model.vocabulary.RDF4J;
4141
import org.eclipse.rdf4j.model.vocabulary.RSX;
42+
import org.eclipse.rdf4j.model.vocabulary.SESAME;
4243
import org.eclipse.rdf4j.model.vocabulary.SHACL;
4344
import org.eclipse.rdf4j.repository.Repository;
4445
import org.eclipse.rdf4j.repository.RepositoryConnection;
@@ -327,7 +328,9 @@ public static List<IRI> getSupportedShaclPredicates() {
327328
SHACL.NAMESPACE_PROP,
328329
SHACL.SEVERITY_PROP,
329330
DASH.hasValueIn,
330-
RSX.targetShape
331+
RSX.targetShape,
332+
RSX.dataGraph,
333+
RSX.shapesGraph
331334
);
332335
}
333336

@@ -374,6 +377,9 @@ public void init() throws SailException {
374377
if (g.equals(RDF4J.NIL)) {
375378
return null;
376379
}
380+
if (g.equals(SESAME.NIL)) {
381+
return null;
382+
}
377383
return g;
378384
})
379385
.toArray(IRI[]::new);

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
import org.eclipse.rdf4j.model.Statement;
3737
import org.eclipse.rdf4j.model.Value;
3838
import org.eclipse.rdf4j.model.vocabulary.RDF4J;
39+
import org.eclipse.rdf4j.model.vocabulary.SESAME;
3940
import org.eclipse.rdf4j.repository.sail.SailRepositoryConnection;
4041
import org.eclipse.rdf4j.sail.NotifyingSailConnection;
4142
import org.eclipse.rdf4j.sail.Sail;
@@ -189,6 +190,9 @@ public void begin(IsolationLevel level) throws SailException {
189190
if (g.equals(RDF4J.NIL)) {
190191
return null;
191192
}
193+
if (g.equals(SESAME.NIL)) {
194+
return null;
195+
}
192196
return g;
193197
}).toArray(IRI[]::new);
194198

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

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

14+
import java.util.Arrays;
1415
import java.util.List;
1516
import java.util.stream.Collectors;
1617
import java.util.stream.Stream;
@@ -66,6 +67,14 @@ public static ValidationReport validate(Sail dataRepo, Sail shapesRepo) {
6667

6768
shapes = Shape.Factory.getShapes(parsed).stream().distinct().collect(Collectors.toList());
6869

70+
if (logger.isDebugEnabled()) {
71+
for (ContextWithShape shape : shapes) {
72+
logger.debug("Using data graph(s) {} and shape graph(s) {} with shape {}",
73+
Arrays.toString(shape.getDataGraph()), Arrays.toString(shape.getShapeGraph()),
74+
shape.getShape());
75+
}
76+
}
77+
6978
}
7079
shapesConnection.commit();
7180
}

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,15 @@
1212

1313
import java.util.ArrayList;
1414
import java.util.Arrays;
15+
import java.util.Comparator;
1516
import java.util.HashSet;
1617
import java.util.List;
1718
import java.util.Set;
1819

1920
import org.eclipse.rdf4j.model.Model;
2021
import org.eclipse.rdf4j.model.Resource;
2122
import org.eclipse.rdf4j.model.Statement;
23+
import org.eclipse.rdf4j.model.Value;
2224
import org.eclipse.rdf4j.model.impl.DynamicModel;
2325
import org.eclipse.rdf4j.model.impl.DynamicModelFactory;
2426

@@ -31,7 +33,8 @@ public class ContextWithShape {
3133
public ContextWithShape(Resource[] dataGraph, Resource[] shapeGraph, Shape shape) {
3234
this.shapeGraph = shapeGraph;
3335
this.dataGraph = dataGraph;
34-
Arrays.sort(this.dataGraph);
36+
Arrays.sort(this.dataGraph, Comparator.comparing(v -> v != null ? v.stringValue() : "null"));
37+
Arrays.sort(this.shapeGraph, Comparator.comparing(v -> v != null ? v.stringValue() : "null"));
3538
this.shape = shape;
3639
}
3740

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2023 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+
import org.eclipse.rdf4j.common.exception.RDF4JException;
15+
import org.eclipse.rdf4j.model.Resource;
16+
17+
/**
18+
* An exception indicating that something went wrong when parsing the SHACL statements, but without a specific shape
19+
* being the cause.
20+
*/
21+
public class ShaclParsingException extends RDF4JException {
22+
23+
public ShaclParsingException(String msg) {
24+
super(msg);
25+
}
26+
}

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

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
import java.util.Arrays;
1515
import java.util.Objects;
16+
import java.util.function.BiFunction;
1617
import java.util.function.Function;
1718

1819
import org.apache.commons.text.StringEscapeUtils;
@@ -41,13 +42,13 @@ public class UnorderedSelect implements PlanNode {
4142
private final IRI predicate;
4243
private final Value object;
4344
private final Resource[] dataGraph;
44-
private final Function<Statement, ValidationTuple> mapper;
45+
private final BiFunction<Statement, Resource[], ValidationTuple> mapper;
4546

4647
private boolean printed = false;
4748
private ValidationExecutionLogger validationExecutionLogger;
4849

4950
public UnorderedSelect(SailConnection connection, Resource subject, IRI predicate, Value object,
50-
Resource[] dataGraph, Function<Statement, ValidationTuple> mapper) {
51+
Resource[] dataGraph, BiFunction<Statement, Resource[], ValidationTuple> mapper) {
5152
this.connection = connection;
5253
assert this.connection != null;
5354
this.subject = subject;
@@ -83,7 +84,7 @@ protected boolean localHasNext() {
8384

8485
@Override
8586
protected ValidationTuple loggingNext() {
86-
return mapper.apply(statements.next());
87+
return mapper.apply(statements.next(), dataGraph);
8788
}
8889

8990
};
@@ -188,7 +189,7 @@ public int hashCode() {
188189
}
189190

190191
public static class Mapper {
191-
public static class SubjectScopedMapper implements Function<Statement, ValidationTuple> {
192+
public static class SubjectScopedMapper implements BiFunction<Statement, Resource[], ValidationTuple> {
192193

193194
private final ConstraintComponent.Scope scope;
194195

@@ -202,8 +203,8 @@ private SubjectScopedMapper(ConstraintComponent.Scope scope) {
202203
static SubjectScopedMapper noneInstance = new SubjectScopedMapper(ConstraintComponent.Scope.none);
203204

204205
@Override
205-
public ValidationTuple apply(Statement s) {
206-
return new ValidationTuple(s.getSubject(), scope, false, s.getContext());
206+
public ValidationTuple apply(Statement s, Resource[] dataGraph) {
207+
return new ValidationTuple(s.getSubject(), scope, false, dataGraph);
207208
}
208209

209210
@Override
@@ -232,7 +233,7 @@ public static SubjectScopedMapper getFunction(ConstraintComponent.Scope scope) {
232233

233234
}
234235

235-
public static class ObjectScopedMapper implements Function<Statement, ValidationTuple> {
236+
public static class ObjectScopedMapper implements BiFunction<Statement, Resource[], ValidationTuple> {
236237

237238
private final ConstraintComponent.Scope scope;
238239

@@ -246,8 +247,8 @@ private ObjectScopedMapper(ConstraintComponent.Scope scope) {
246247
static ObjectScopedMapper noneInstance = new ObjectScopedMapper(ConstraintComponent.Scope.none);
247248

248249
@Override
249-
public ValidationTuple apply(Statement s) {
250-
return new ValidationTuple(s.getObject(), scope, false, s.getContext());
250+
public ValidationTuple apply(Statement s, Resource[] dataGraph) {
251+
return new ValidationTuple(s.getObject(), scope, false, dataGraph);
251252
}
252253

253254
@Override
@@ -276,17 +277,18 @@ public static ObjectScopedMapper getFunction(ConstraintComponent.Scope scope) {
276277

277278
}
278279

279-
public static class SubjectObjectPropertyShapeMapper implements Function<Statement, ValidationTuple> {
280+
public static class SubjectObjectPropertyShapeMapper
281+
implements BiFunction<Statement, Resource[], ValidationTuple> {
280282

281283
private SubjectObjectPropertyShapeMapper() {
282284
}
283285

284286
static SubjectObjectPropertyShapeMapper instance = new SubjectObjectPropertyShapeMapper();
285287

286288
@Override
287-
public ValidationTuple apply(Statement s) {
289+
public ValidationTuple apply(Statement s, Resource[] dataGraph) {
288290
return new ValidationTuple(s.getSubject(), s.getObject(), ConstraintComponent.Scope.propertyShape,
289-
true, s.getContext());
291+
true, dataGraph);
290292
}
291293

292294
@Override

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

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -87,10 +87,6 @@ public ValidationTuple(Value[] chain, ConstraintComponent.Scope scope, boolean h
8787
this.contexts = contexts;
8888
}
8989

90-
public ValidationTuple(Value a, Value c, ConstraintComponent.Scope scope, boolean hasValue, Resource context) {
91-
this(a, c, scope, hasValue, context == null ? NULL_CONTEXT : new Resource[] { context });
92-
}
93-
9490
public ValidationTuple(Value a, Value c, ConstraintComponent.Scope scope, boolean hasValue, Resource[] contexts) {
9591
chain = new Value[] { a, c };
9692

@@ -101,10 +97,6 @@ public ValidationTuple(Value a, Value c, ConstraintComponent.Scope scope, boolea
10197
this.contexts = contexts;
10298
}
10399

104-
public ValidationTuple(Value subject, ConstraintComponent.Scope scope, boolean hasValue, Resource context) {
105-
this(subject, scope, hasValue, new Resource[] { context });
106-
}
107-
108100
public ValidationTuple(Value subject, ConstraintComponent.Scope scope, boolean hasValue, Resource[] contexts) {
109101
chain = new Value[] { subject };
110102
this.scope = scope;

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,9 @@ public class ValuesBackedNode implements PlanNode {
3939
boolean printed = false;
4040
private ValidationExecutionLogger validationExecutionLogger;
4141

42-
public ValuesBackedNode(SortedSet<Value> values, ConstraintComponent.Scope scope, Resource[] contexts) {
42+
public ValuesBackedNode(SortedSet<Value> values, ConstraintComponent.Scope scope, Resource[] dataGraph) {
4343
this.tuples = values.stream()
44-
.map(c -> new ValidationTuple(c, scope, false, contexts))
44+
.map(c -> new ValidationTuple(c, scope, false, dataGraph))
4545
.collect(Collectors.toList());
4646
this.values = values;
4747
this.scope = scope;

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -512,18 +512,18 @@ public boolean hasStatements() {
512512
static class ActiveTargetTupleMapper implements Function<ValidationTuple, ValidationTuple> {
513513
private final ConstraintComponent.Scope scope;
514514
private final boolean includePropertyShapeValues;
515-
private final Resource[] contexts;
515+
private final Resource[] dataGraph;
516516

517517
public ActiveTargetTupleMapper(ConstraintComponent.Scope scope, boolean includePropertyShapeValues,
518-
Resource[] contexts) {
518+
Resource[] dataGraph) {
519519
this.scope = scope;
520520
this.includePropertyShapeValues = includePropertyShapeValues;
521-
this.contexts = contexts;
521+
this.dataGraph = dataGraph;
522522
}
523523

524524
@Override
525525
public ValidationTuple apply(ValidationTuple validationTuple) {
526-
return new ValidationTuple(validationTuple.getActiveTarget(), scope, includePropertyShapeValues, contexts);
526+
return new ValidationTuple(validationTuple.getActiveTarget(), scope, includePropertyShapeValues, dataGraph);
527527
}
528528

529529
@Override

0 commit comments

Comments
 (0)