Skip to content

Commit a92ae7b

Browse files
authored
GH-4770 SHACL Property Pair Constraint Components (#4771)
2 parents a6ec965 + 72813eb commit a92ae7b

158 files changed

Lines changed: 3055 additions & 112 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/model-vocabulary/src/main/java/org/eclipse/rdf4j/model/vocabulary/RSX.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ public class RSX {
4646
public final static IRI valueConformsToXsdDatatypeFunction = create("valueConformsToXsdDatatypeFunction");
4747

4848
public final static IRI DataAndShapesGraphLink = create("DataAndShapesGraphLink");
49+
public final static IRI actualPairwisePath = create("actualPairwisePath");
4950

5051
private static IRI create(String localName) {
5152
return Vocabularies.createIRI(RSX.NAMESPACE, localName);

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -320,13 +320,21 @@ public static List<IRI> getSupportedShaclPredicates() {
320320
SHACL.QUALIFIED_VALUE_SHAPE,
321321
SHACL.SHAPES_GRAPH,
322322
SHACL.MESSAGE,
323+
SHACL.NAME,
324+
SHACL.DESCRIPTION,
325+
SHACL.DEFAULT_VALUE,
326+
SHACL.ORDER,
327+
SHACL.GROUP,
323328
SHACL.DECLARE,
324329
SHACL.SPARQL,
325330
SHACL.SELECT,
326331
SHACL.PREFIXES,
327332
SHACL.PREFIX_PROP,
328333
SHACL.NAMESPACE_PROP,
329334
SHACL.SEVERITY_PROP,
335+
SHACL.EQUALS,
336+
SHACL.LESS_THAN,
337+
SHACL.LESS_THAN_OR_EQUALS,
330338
DASH.hasValueIn,
331339
RSX.targetShape,
332340
RSX.dataGraph,

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

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

242+
@Override
243+
public boolean overrideValidationReport() {
244+
return false;
245+
}
246+
242247
@Override
243248
public SparqlFragment buildSparqlValidNodes_rsx_targetShape(Variable<Value> subject,
244249
Variable<Value> object,

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

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import java.util.stream.Collectors;
1717

1818
import org.eclipse.rdf4j.model.IRI;
19+
import org.eclipse.rdf4j.model.Literal;
1920
import org.eclipse.rdf4j.model.Model;
2021
import org.eclipse.rdf4j.model.Resource;
2122
import org.eclipse.rdf4j.model.Value;
@@ -45,10 +46,11 @@
4546
public class PropertyShape extends Shape {
4647
private static final Logger logger = LoggerFactory.getLogger(PropertyShape.class);
4748

48-
List<String> name;
49-
List<String> description;
50-
Object defaultValue;
51-
Object group;
49+
List<Literal> name;
50+
List<Literal> description;
51+
Value defaultValue;
52+
Value group;
53+
Value order;
5254

5355
Path path;
5456

@@ -62,6 +64,7 @@ public PropertyShape(PropertyShape propertyShape) {
6264
this.defaultValue = propertyShape.defaultValue;
6365
this.group = propertyShape.group;
6466
this.path = propertyShape.path;
67+
this.order = propertyShape.order;
6568
}
6669

6770
public static PropertyShape getInstance(ShaclProperties properties, ShapeSource shapeSource,
@@ -93,6 +96,12 @@ public void populate(ShaclProperties properties, ShapeSource connection, ParseSe
9396
throw new IllegalStateException(properties.getId() + " is a sh:PropertyShape without a sh:path!");
9497
}
9598

99+
this.name = properties.getName();
100+
this.description = properties.getDescription();
101+
this.defaultValue = properties.getDefaultValue();
102+
this.order = properties.getOrder();
103+
this.group = properties.getGroup();
104+
96105
constraintComponents = getConstraintComponents(properties, connection, parseSettings, cache);
97106
}
98107

@@ -107,6 +116,26 @@ public void toModel(Resource subject, IRI predicate, Model model, Set<Resource>
107116
super.toModel(subject, predicate, model, cycleDetection);
108117
model.add(getId(), RDF.TYPE, SHACL.PROPERTY_SHAPE);
109118

119+
for (Literal literal : name) {
120+
model.add(getId(), SHACL.NAME, literal);
121+
}
122+
123+
for (Literal literal : description) {
124+
model.add(getId(), SHACL.DESCRIPTION, literal);
125+
}
126+
127+
if (defaultValue != null) {
128+
model.add(getId(), SHACL.DEFAULT_VALUE, defaultValue);
129+
}
130+
131+
if (order != null) {
132+
model.add(getId(), SHACL.ORDER, order);
133+
}
134+
135+
if (group != null) {
136+
model.add(getId(), SHACL.GROUP, group);
137+
}
138+
110139
if (subject != null) {
111140
if (predicate == null) {
112141
model.add(subject, SHACL.PROPERTY, getId());
@@ -202,7 +231,7 @@ public PlanNode generateTransactionalValidationPlan(ConnectionsGroup connections
202231
.generateTransactionalValidationPlan(connectionsGroup, validationSettings, overrideTargetNode,
203232
Scope.propertyShape);
204233

205-
if (!(constraintComponent instanceof PropertyShape)) {
234+
if (!(constraintComponent instanceof PropertyShape) && !constraintComponent.overrideValidationReport()) {
206235
validationPlanNode = new ValidationReportNode(validationPlanNode, t -> {
207236
return new ValidationResult(t.getActiveTarget(), t.getValue(), this,
208237
constraintComponent, getSeverity(), t.getScope(), t.getContexts(),
@@ -286,6 +315,11 @@ public ConstraintComponent deepClone() {
286315
return nodeShape;
287316
}
288317

318+
@Override
319+
public boolean overrideValidationReport() {
320+
return false;
321+
}
322+
289323
@Override
290324
public SparqlFragment buildSparqlValidNodes_rsx_targetShape(Variable<Value> subject,
291325
Variable<Value> object,

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

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,13 @@ public class ShaclProperties {
9696
private final List<Literal> message = new ArrayList<>();
9797
private IRI severity;
9898

99+
private final List<Literal> name = new ArrayList<>();
100+
private final List<Literal> description = new ArrayList<>();
101+
102+
private Value defaultValue;
103+
private Value order;
104+
private Value group;
105+
99106
private final List<Resource> sparql = new ArrayList<>();
100107

101108
public ShaclProperties(Resource id, ShapeSource connection) {
@@ -173,6 +180,21 @@ public ShaclProperties(Resource id, ShapeSource connection) {
173180
throw getExceptionForCastIssue(id, predicate, Literal.class, object);
174181
}
175182
break;
183+
case "http://www.w3.org/ns/shacl#name":
184+
try {
185+
name.add((Literal) object);
186+
} catch (ClassCastException e) {
187+
throw getExceptionForCastIssue(id, predicate, Literal.class, object);
188+
}
189+
break;
190+
case "http://www.w3.org/ns/shacl#description":
191+
try {
192+
description.add((Literal) object);
193+
} catch (ClassCastException e) {
194+
throw getExceptionForCastIssue(id, predicate, Literal.class, object);
195+
}
196+
break;
197+
176198
case "http://www.w3.org/ns/shacl#severity":
177199
if (severity != null) {
178200
throw getExceptionForAlreadyPopulated(id, predicate, severity, object);
@@ -183,6 +205,24 @@ public ShaclProperties(Resource id, ShapeSource connection) {
183205
throw getExceptionForCastIssue(id, predicate, IRI.class, object);
184206
}
185207
break;
208+
case "http://www.w3.org/ns/shacl#defaultValue":
209+
if (defaultValue != null) {
210+
throw getExceptionForAlreadyPopulated(id, predicate, defaultValue, object);
211+
}
212+
defaultValue = object;
213+
break;
214+
case "http://www.w3.org/ns/shacl#group":
215+
if (group != null) {
216+
throw getExceptionForAlreadyPopulated(id, predicate, group, object);
217+
}
218+
group = object;
219+
break;
220+
case "http://www.w3.org/ns/shacl#order":
221+
if (order != null) {
222+
throw getExceptionForAlreadyPopulated(id, predicate, order, object);
223+
}
224+
order = object;
225+
break;
186226
case "http://www.w3.org/ns/shacl#languageIn":
187227
if (languageIn != null) {
188228
throw getExceptionForAlreadyPopulated(id, predicate, languageIn, object);
@@ -592,8 +632,9 @@ private static ShaclShapeParsingException getExceptionForCastIssue(Resource id,
592632
}
593633

594634
private static String getClassName(Value object) {
595-
if (object == null)
635+
if (object == null) {
596636
return "null";
637+
}
597638
String actualClassName;
598639
if (object.isIRI()) {
599640
actualClassName = "IRI";
@@ -726,6 +767,26 @@ public IRI getSeverity() {
726767
return severity;
727768
}
728769

770+
public List<Literal> getName() {
771+
return name;
772+
}
773+
774+
public List<Literal> getDescription() {
775+
return description;
776+
}
777+
778+
public Value getDefaultValue() {
779+
return defaultValue;
780+
}
781+
782+
public Value getOrder() {
783+
return order;
784+
}
785+
786+
public Value getGroup() {
787+
return group;
788+
}
789+
729790
public List<Resource> getProperty() {
730791
return property;
731792
}

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -309,23 +309,23 @@ List<ConstraintComponent> getConstraintComponents(ShaclProperties properties, Sh
309309
}
310310

311311
for (IRI iri : properties.getEquals()) {
312-
var equalsConstraintComponent = new EqualsConstraintComponent(iri);
312+
var equalsConstraintComponent = new EqualsConstraintComponent(iri, this);
313313
constraintComponent.add(equalsConstraintComponent);
314314
}
315315

316316
for (IRI iri : properties.getDisjoint()) {
317-
var disjointConstraintComponent = new DisjointConstraintComponent(iri);
317+
var disjointConstraintComponent = new DisjointConstraintComponent(iri, this);
318318
constraintComponent.add(disjointConstraintComponent);
319319
}
320320

321321
for (IRI iri : properties.getLessThan()) {
322-
var lessThanConstraintComponent = new LessThanConstraintComponent(iri);
322+
var lessThanConstraintComponent = new LessThanConstraintComponent(iri, this);
323323
constraintComponent.add(lessThanConstraintComponent);
324324
}
325325

326326
for (IRI iri : properties.getLessThanOrEquals()) {
327327
var lessThanOrEqualsConstraintComponent = new LessThanOrEqualsConstraintComponent(
328-
iri);
328+
iri, this);
329329
constraintComponent.add(lessThanOrEqualsConstraintComponent);
330330
}
331331

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

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,16 @@
2222
import org.eclipse.rdf4j.sail.shacl.ast.StatementMatcher.Variable;
2323
import org.eclipse.rdf4j.sail.shacl.ast.ValidationApproach;
2424
import org.eclipse.rdf4j.sail.shacl.ast.ValidationQuery;
25+
import org.eclipse.rdf4j.sail.shacl.ast.paths.Path;
26+
import org.eclipse.rdf4j.sail.shacl.ast.planNodes.BufferedSplitter;
2527
import org.eclipse.rdf4j.sail.shacl.ast.planNodes.EmptyNode;
2628
import org.eclipse.rdf4j.sail.shacl.ast.planNodes.PlanNode;
2729
import org.eclipse.rdf4j.sail.shacl.ast.planNodes.PlanNodeProvider;
30+
import org.eclipse.rdf4j.sail.shacl.ast.planNodes.ReduceTargets;
31+
import org.eclipse.rdf4j.sail.shacl.ast.planNodes.TrimToTarget;
32+
import org.eclipse.rdf4j.sail.shacl.ast.planNodes.UnionNode;
33+
import org.eclipse.rdf4j.sail.shacl.ast.planNodes.Unique;
34+
import org.eclipse.rdf4j.sail.shacl.ast.targets.EffectiveTarget;
2835
import org.eclipse.rdf4j.sail.shacl.ast.targets.TargetChain;
2936
import org.eclipse.rdf4j.sail.shacl.wrapper.data.ConnectionsGroup;
3037
import org.eclipse.rdf4j.sail.shacl.wrapper.data.RdfsSubClassOfReasoner;
@@ -144,4 +151,31 @@ public String stringRepresentationOfValue(Value value) {
144151
throw new IllegalStateException(value.getClass().getSimpleName());
145152
}
146153

154+
static PlanNode getAllTargetsIncludingThoseAddedByPath(ConnectionsGroup connectionsGroup,
155+
ValidationSettings validationSettings, Scope scope, EffectiveTarget effectiveTarget, Path path,
156+
boolean includeTargetsAffectedByRemoval) {
157+
PlanNode allTargets;
158+
BufferedSplitter addedTargets = new BufferedSplitter(
159+
effectiveTarget.getPlanNode(connectionsGroup, validationSettings.getDataGraph(),
160+
scope, includeTargetsAffectedByRemoval, null));
161+
162+
PlanNode addedByPath = path.getAllAdded(connectionsGroup, validationSettings.getDataGraph(), null);
163+
164+
addedByPath = Unique.getInstance(new TrimToTarget(addedByPath), false);
165+
166+
addedByPath = new ReduceTargets(addedByPath, addedTargets.getPlanNode());
167+
168+
addedByPath = effectiveTarget.extend(addedByPath, connectionsGroup, validationSettings.getDataGraph(),
169+
scope, EffectiveTarget.Extend.left,
170+
false,
171+
null);
172+
173+
allTargets = UnionNode.getInstance(addedTargets.getPlanNode(), addedByPath);
174+
return allTargets;
175+
}
176+
177+
@Override
178+
public boolean overrideValidationReport() {
179+
return false;
180+
}
147181
}

0 commit comments

Comments
 (0)