Skip to content

Commit eb9d21c

Browse files
committed
GH-4864 support sh:name, sh:description, sh:defaultValue, sh:order, sh:group
1 parent a6ec965 commit eb9d21c

55 files changed

Lines changed: 1440 additions & 12 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/ShaclSail.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -320,13 +320,19 @@ 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,
330336
DASH.hasValueIn,
331337
RSX.targetShape,
332338
RSX.dataGraph,

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

Lines changed: 33 additions & 4 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());

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/constraintcomponents/DisjointConstraintComponent.java

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,31 @@
1212
package org.eclipse.rdf4j.sail.shacl.ast.constraintcomponents;
1313

1414
import java.util.List;
15+
import java.util.Optional;
1516
import java.util.Set;
1617

1718
import org.eclipse.rdf4j.model.IRI;
1819
import org.eclipse.rdf4j.model.Literal;
1920
import org.eclipse.rdf4j.model.Model;
2021
import org.eclipse.rdf4j.model.Resource;
22+
import org.eclipse.rdf4j.model.Value;
2123
import org.eclipse.rdf4j.model.vocabulary.SHACL;
2224
import org.eclipse.rdf4j.sail.shacl.SourceConstraintComponent;
25+
import org.eclipse.rdf4j.sail.shacl.ValidationSettings;
26+
import org.eclipse.rdf4j.sail.shacl.ast.SparqlFragment;
27+
import org.eclipse.rdf4j.sail.shacl.ast.StatementMatcher;
28+
import org.eclipse.rdf4j.sail.shacl.ast.ValidationApproach;
29+
import org.eclipse.rdf4j.sail.shacl.ast.paths.Path;
30+
import org.eclipse.rdf4j.sail.shacl.ast.planNodes.CheckDisjointValuesBasedOnPathAndPredicate;
31+
import org.eclipse.rdf4j.sail.shacl.ast.planNodes.PlanNode;
32+
import org.eclipse.rdf4j.sail.shacl.ast.planNodes.PlanNodeProvider;
33+
import org.eclipse.rdf4j.sail.shacl.ast.planNodes.UnionNode;
34+
import org.eclipse.rdf4j.sail.shacl.ast.planNodes.Unique;
35+
import org.eclipse.rdf4j.sail.shacl.ast.planNodes.UnorderedSelect;
36+
import org.eclipse.rdf4j.sail.shacl.ast.planNodes.ValidationTuple;
37+
import org.eclipse.rdf4j.sail.shacl.ast.targets.EffectiveTarget;
38+
import org.eclipse.rdf4j.sail.shacl.ast.targets.TargetChain;
39+
import org.eclipse.rdf4j.sail.shacl.wrapper.data.ConnectionsGroup;
2340

2441
public class DisjointConstraintComponent extends AbstractConstraintComponent {
2542

@@ -39,6 +56,89 @@ public SourceConstraintComponent getConstraintComponent() {
3956
return SourceConstraintComponent.DisjointConstraintComponent;
4057
}
4158

59+
@Override
60+
public PlanNode generateTransactionalValidationPlan(ConnectionsGroup connectionsGroup,
61+
ValidationSettings validationSettings, PlanNodeProvider overrideTargetNode, Scope scope) {
62+
StatementMatcher.StableRandomVariableProvider stableRandomVariableProvider = new StatementMatcher.StableRandomVariableProvider();
63+
64+
TargetChain targetChain = getTargetChain();
65+
66+
EffectiveTarget effectiveTarget = targetChain.getEffectiveTarget(scope,
67+
connectionsGroup.getRdfsSubClassOfReasoner(), stableRandomVariableProvider);
68+
Optional<Path> path = targetChain.getPath();
69+
70+
PlanNode allTargets;
71+
72+
if (overrideTargetNode != null) {
73+
allTargets = effectiveTarget.extend(overrideTargetNode.getPlanNode(), connectionsGroup,
74+
validationSettings.getDataGraph(), scope, EffectiveTarget.Extend.right, false, null);
75+
} else {
76+
77+
PlanNode targets = effectiveTarget.getPlanNode(connectionsGroup, validationSettings.getDataGraph(),
78+
scope, true, null);
79+
80+
PlanNode allTargetsBasedOnPredicate = getAllTargetsBasedOnPredicate(connectionsGroup, validationSettings,
81+
effectiveTarget);
82+
83+
allTargets = Unique.getInstance(UnionNode.getInstance(allTargetsBasedOnPredicate, targets), false);
84+
}
85+
86+
StatementMatcher.Variable<Resource> subject = new StatementMatcher.Variable<>("a");
87+
StatementMatcher.Variable<Value> object = new StatementMatcher.Variable<>("c");
88+
89+
SparqlFragment targetQueryFragment = null;
90+
if (path.isPresent()) {
91+
targetQueryFragment = path.get()
92+
.getTargetQueryFragment(subject, object, connectionsGroup.getRdfsSubClassOfReasoner(),
93+
stableRandomVariableProvider, Set.of());
94+
}
95+
96+
return new CheckDisjointValuesBasedOnPathAndPredicate(connectionsGroup.getBaseConnection(),
97+
validationSettings.getDataGraph(), allTargets, predicate, subject, object, targetQueryFragment);
98+
}
99+
100+
private PlanNode getAllTargetsBasedOnPredicate(ConnectionsGroup connectionsGroup,
101+
ValidationSettings validationSettings, EffectiveTarget effectiveTarget) {
102+
PlanNode addedByPredicate = new UnorderedSelect(connectionsGroup.getAddedStatements(), null, predicate, null,
103+
validationSettings.getDataGraph(), (s, d) -> {
104+
return new ValidationTuple(s.getSubject(), Scope.propertyShape, false, d);
105+
});
106+
107+
PlanNode removedByPredicate = new UnorderedSelect(connectionsGroup.getRemovedStatements(), null, predicate,
108+
null, validationSettings.getDataGraph(), (s, d) -> {
109+
return new ValidationTuple(s.getSubject(), Scope.propertyShape, false, d);
110+
});
111+
112+
PlanNode targetFilter1 = effectiveTarget.getTargetFilter(connectionsGroup, validationSettings.getDataGraph(),
113+
addedByPredicate);
114+
PlanNode targetFilter2 = effectiveTarget.getTargetFilter(connectionsGroup, validationSettings.getDataGraph(),
115+
removedByPredicate);
116+
117+
PlanNode allTargetsBasedOnPredicate = Unique.getInstance(UnionNode.getInstance(targetFilter1, targetFilter2),
118+
false);
119+
return allTargetsBasedOnPredicate;
120+
}
121+
122+
@Override
123+
public ValidationApproach getPreferredValidationApproach(ConnectionsGroup connectionsGroup) {
124+
return ValidationApproach.Transactional;
125+
}
126+
127+
@Override
128+
public ValidationApproach getOptimalBulkValidationApproach() {
129+
return ValidationApproach.Transactional;
130+
}
131+
132+
@Override
133+
public boolean requiresEvaluation(ConnectionsGroup connectionsGroup, Scope scope, Resource[] dataGraph,
134+
StatementMatcher.StableRandomVariableProvider stableRandomVariableProvider) {
135+
136+
// todo both consider the target chain with added and removed values (path), and also added and removed values
137+
// for the predicate path
138+
139+
return true;
140+
}
141+
42142
@Override
43143
public ConstraintComponent deepClone() {
44144
return new DisjointConstraintComponent(predicate);

0 commit comments

Comments
 (0)