This is an automated email from the ASF dual-hosted git repository. afs pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/jena.git
commit bee9dccde41347886531aff489d732259ff3a786 Author: Andy Seaborne <[email protected]> AuthorDate: Fri Apr 24 08:17:56 2026 +0100 GH-3888: Minimal impl of sh:memberShape --- .../{ConstraintTerm.java => ConstraintList.java} | 22 ++++---- .../shacl/engine/constraint/ConstraintTerm.java | 4 +- .../engine/constraint/DatatypeConstraint.java | 3 +- .../jena/shacl/engine/constraint/InConstraint.java | 2 +- .../shacl/engine/constraint/JLogConstraint.java | 2 +- .../engine/constraint/JViolationConstraint.java | 2 +- .../{JLogConstraint.java => ListMaxLength.java} | 52 ++++++++--------- .../{InConstraint.java => ListMemberShape.java} | 66 ++++++++++++++-------- .../{JLogConstraint.java => ListMinLength.java} | 52 ++++++++--------- .../{InConstraint.java => ListUniqueMembers.java} | 55 ++++++++---------- .../engine/constraint/NodeKindConstraint.java | 22 +++++--- .../shacl/engine/constraint/PatternConstraint.java | 2 +- .../jena/shacl/engine/constraint/ShXone.java | 3 +- .../shacl/engine/constraint/StrLanguageIn.java | 2 +- .../engine/constraint/StrMaxLengthConstraint.java | 2 +- .../engine/constraint/StrMinLengthConstraint.java | 2 +- .../engine/constraint/ValueRangeConstraint.java | 3 +- .../jena/shacl/parser/ConstraintVisitor.java | 5 ++ .../org/apache/jena/shacl/parser/Constraints.java | 9 +++ .../org/apache/jena/shacl/vocabulary/SHACL.java | 60 ++++++++++++++------ 20 files changed, 212 insertions(+), 158 deletions(-) diff --git a/jena-shacl/src/main/java/org/apache/jena/shacl/engine/constraint/ConstraintTerm.java b/jena-shacl/src/main/java/org/apache/jena/shacl/engine/constraint/ConstraintList.java similarity index 74% copy from jena-shacl/src/main/java/org/apache/jena/shacl/engine/constraint/ConstraintTerm.java copy to jena-shacl/src/main/java/org/apache/jena/shacl/engine/constraint/ConstraintList.java index 284830cf88..8493c8f339 100644 --- a/jena-shacl/src/main/java/org/apache/jena/shacl/engine/constraint/ConstraintTerm.java +++ b/jena-shacl/src/main/java/org/apache/jena/shacl/engine/constraint/ConstraintList.java @@ -33,37 +33,35 @@ import org.apache.jena.shacl.validation.event.ConstraintEvaluatedOnFocusNodeEven import org.apache.jena.shacl.validation.event.ConstraintEvaluatedOnSinglePathNodeEvent; import org.apache.jena.sparql.path.Path; -/* Constraint that does not need access to the data other than the nodes supplied. e.g. sh:datatype. */ -public abstract class ConstraintTerm implements Constraint { +public abstract class ConstraintList implements Constraint { - public ConstraintTerm() {} + protected ConstraintList() {} @Override final public void validatePropertyShape(ValidationContext vCxt, Graph data, Shape shape, Node focusNode, Path path, Set<Node> valueNodes) { - valueNodes.forEach(x->applyConstraintTerm(vCxt, shape, focusNode, path, x)); + valueNodes.forEach(x->applyConstraintList(vCxt, shape, focusNode, path, data, x)); } @Override final public void validateNodeShape(ValidationContext vCxt, Graph data, Shape shape, Node focusNode) { - applyConstraintTerm(vCxt, shape, focusNode, null, focusNode); + applyConstraintList(vCxt, shape, focusNode, null, data, focusNode); } - private void applyConstraintTerm(ValidationContext vCxt, Shape shape, Node focusNode, Path path, Node term) { - ReportItem item = validate(vCxt, term); - boolean passed = item == null; + private void applyConstraintList(ValidationContext vCxt, Shape shape, Node focusNode, Path path, Graph data, Node listHead) { + ReportItem item = validateList(vCxt, data, listHead); + boolean passed = (item == null); if (path == null) { vCxt.notifyValidationListener(() -> new ConstraintEvaluatedOnFocusNodeEvent(vCxt, shape, focusNode, this, passed)); } else { vCxt.notifyValidationListener(() -> new ConstraintEvaluatedOnSinglePathNodeEvent(vCxt, shape, focusNode, this, path, - term, passed)); + listHead, passed)); } - if ( passed ) { + if ( passed ) return; - } vCxt.reportEntry(item, shape, focusNode, path, this); } - public abstract ReportItem validate(ValidationContext vCxt, Node n) ; + protected abstract ReportItem validateList(ValidationContext vCxt, Graph data, Node n) ; } diff --git a/jena-shacl/src/main/java/org/apache/jena/shacl/engine/constraint/ConstraintTerm.java b/jena-shacl/src/main/java/org/apache/jena/shacl/engine/constraint/ConstraintTerm.java index 284830cf88..0264c6fdb6 100644 --- a/jena-shacl/src/main/java/org/apache/jena/shacl/engine/constraint/ConstraintTerm.java +++ b/jena-shacl/src/main/java/org/apache/jena/shacl/engine/constraint/ConstraintTerm.java @@ -52,7 +52,7 @@ public abstract class ConstraintTerm implements Constraint { private void applyConstraintTerm(ValidationContext vCxt, Shape shape, Node focusNode, Path path, Node term) { ReportItem item = validate(vCxt, term); - boolean passed = item == null; + boolean passed = (item == null); if (path == null) { vCxt.notifyValidationListener(() -> new ConstraintEvaluatedOnFocusNodeEvent(vCxt, shape, focusNode, this, passed)); } else { @@ -65,5 +65,5 @@ public abstract class ConstraintTerm implements Constraint { vCxt.reportEntry(item, shape, focusNode, path, this); } - public abstract ReportItem validate(ValidationContext vCxt, Node n) ; + protected abstract ReportItem validate(ValidationContext vCxt, Node n) ; } diff --git a/jena-shacl/src/main/java/org/apache/jena/shacl/engine/constraint/DatatypeConstraint.java b/jena-shacl/src/main/java/org/apache/jena/shacl/engine/constraint/DatatypeConstraint.java index 35d329fe05..b47ad94d57 100644 --- a/jena-shacl/src/main/java/org/apache/jena/shacl/engine/constraint/DatatypeConstraint.java +++ b/jena-shacl/src/main/java/org/apache/jena/shacl/engine/constraint/DatatypeConstraint.java @@ -75,7 +75,7 @@ public class DatatypeConstraint extends ConstraintTerm { } @Override - public ReportItem validate(ValidationContext vCxt, Node n) { + protected ReportItem validate(ValidationContext vCxt, Node n) { if ( n.isLiteral() && dtURI.equals(n.getLiteralDatatypeURI()) ) { // Must be valid for the type if ( ! rdfDatatype.isValid(n.getLiteralLexicalForm()) ) { @@ -131,6 +131,7 @@ public class DatatypeConstraint extends ConstraintTerm { @Override public String toString() { + // DRY (with ListMmeberShape, others? String x; if ( datatype.isURI() ) { if ( dtURI.startsWith(XSD.getURI()) ) diff --git a/jena-shacl/src/main/java/org/apache/jena/shacl/engine/constraint/InConstraint.java b/jena-shacl/src/main/java/org/apache/jena/shacl/engine/constraint/InConstraint.java index 0d9bd00d86..82f1300c5c 100644 --- a/jena-shacl/src/main/java/org/apache/jena/shacl/engine/constraint/InConstraint.java +++ b/jena-shacl/src/main/java/org/apache/jena/shacl/engine/constraint/InConstraint.java @@ -56,7 +56,7 @@ public class InConstraint extends ConstraintTerm { } @Override - public ReportItem validate(ValidationContext vCxt, Node n) { + protected ReportItem validate(ValidationContext vCxt, Node n) { if ( values.contains(n) ) return null; String errMsg = toString()+" : RDF term "+displayStr(n)+" not in expected values"; diff --git a/jena-shacl/src/main/java/org/apache/jena/shacl/engine/constraint/JLogConstraint.java b/jena-shacl/src/main/java/org/apache/jena/shacl/engine/constraint/JLogConstraint.java index 4a4bd6b697..4fdd687c75 100644 --- a/jena-shacl/src/main/java/org/apache/jena/shacl/engine/constraint/JLogConstraint.java +++ b/jena-shacl/src/main/java/org/apache/jena/shacl/engine/constraint/JLogConstraint.java @@ -50,7 +50,7 @@ public class JLogConstraint extends ConstraintTerm { } @Override - public ReportItem validate(ValidationContext vCxt, Node n) { + protected ReportItem validate(ValidationContext vCxt, Node n) { String msg = String.format("%s[%s]", message, ShLib.displayStr(n)); ShaclSystem.shaclSystemLogger.warn(msg); return null; diff --git a/jena-shacl/src/main/java/org/apache/jena/shacl/engine/constraint/JViolationConstraint.java b/jena-shacl/src/main/java/org/apache/jena/shacl/engine/constraint/JViolationConstraint.java index ac8430b65d..5945e106e5 100644 --- a/jena-shacl/src/main/java/org/apache/jena/shacl/engine/constraint/JViolationConstraint.java +++ b/jena-shacl/src/main/java/org/apache/jena/shacl/engine/constraint/JViolationConstraint.java @@ -49,7 +49,7 @@ public class JViolationConstraint extends ConstraintTerm { } @Override - public ReportItem validate(ValidationContext vCxt, Node n) { + protected ReportItem validate(ValidationContext vCxt, Node n) { if ( ! generateViolation ) return null; return new ReportItem("Violation"); diff --git a/jena-shacl/src/main/java/org/apache/jena/shacl/engine/constraint/JLogConstraint.java b/jena-shacl/src/main/java/org/apache/jena/shacl/engine/constraint/ListMaxLength.java similarity index 63% copy from jena-shacl/src/main/java/org/apache/jena/shacl/engine/constraint/JLogConstraint.java copy to jena-shacl/src/main/java/org/apache/jena/shacl/engine/constraint/ListMaxLength.java index 4a4bd6b697..36253ed3f3 100644 --- a/jena-shacl/src/main/java/org/apache/jena/shacl/engine/constraint/JLogConstraint.java +++ b/jena-shacl/src/main/java/org/apache/jena/shacl/engine/constraint/ListMaxLength.java @@ -21,54 +21,55 @@ package org.apache.jena.shacl.engine.constraint; -import java.util.Objects; - +import org.apache.jena.atlas.io.IndentedWriter; +import org.apache.jena.atlas.lib.NotImplemented; +import org.apache.jena.graph.Graph; import org.apache.jena.graph.Node; +import org.apache.jena.riot.out.NodeFormatter; import org.apache.jena.shacl.engine.ValidationContext; -import org.apache.jena.shacl.lib.ShLib; import org.apache.jena.shacl.parser.ConstraintVisitor; -import org.apache.jena.shacl.sys.ShaclSystem; import org.apache.jena.shacl.validation.ReportItem; -import org.apache.jena.shacl.vocabulary.SHJ; +import org.apache.jena.shacl.vocabulary.SHACL; + +/** sh:memberShape */ -/** A constraint that logs when touched but does not causes a violation */ -public class JLogConstraint extends ConstraintTerm { +public class ListMaxLength extends ConstraintList { - private final String message; + public ListMaxLength(Node node) {} - public JLogConstraint(String message) { - this.message = message; + @Override + public void visit(ConstraintVisitor visitor){ + visitor.visit(this); } - public String getMessage() { - return message; + @Override + public void printCompact(IndentedWriter out, NodeFormatter nodeFmt) { + throw new NotImplemented(); } @Override - public Node getComponent() { - return SHJ.LogConstraintComponent; + protected ReportItem validateList(ValidationContext vCxt, Graph data, Node headNode) { + throw new NotImplemented(); } @Override - public ReportItem validate(ValidationContext vCxt, Node n) { - String msg = String.format("%s[%s]", message, ShLib.displayStr(n)); - ShaclSystem.shaclSystemLogger.warn(msg); - return null; + public Node getComponent() { + return SHACL.ListMaxLengthConstraintComponent; } @Override - public void visit(ConstraintVisitor visitor){ - visitor.visit(this); + public void print(IndentedWriter out, NodeFormatter nodeFmt) { + out.print(toString()); } @Override public String toString() { - return "Log["+message+"]"; + return "ListMemberShape[]"; } @Override public int hashCode() { - return Objects.hash(message); + throw new NotImplemented(); } @Override @@ -77,10 +78,9 @@ public class JLogConstraint extends ConstraintTerm { return true; if ( obj == null ) return false; - if ( getClass() != obj.getClass() ) + if ( !(obj instanceof ListMaxLength other) ) return false; - JLogConstraint other = (JLogConstraint)obj; - return Objects.equals(message, other.message); + throw new NotImplemented(); } -} +} diff --git a/jena-shacl/src/main/java/org/apache/jena/shacl/engine/constraint/InConstraint.java b/jena-shacl/src/main/java/org/apache/jena/shacl/engine/constraint/ListMemberShape.java similarity index 57% copy from jena-shacl/src/main/java/org/apache/jena/shacl/engine/constraint/InConstraint.java copy to jena-shacl/src/main/java/org/apache/jena/shacl/engine/constraint/ListMemberShape.java index 0d9bd00d86..133edec8e9 100644 --- a/jena-shacl/src/main/java/org/apache/jena/shacl/engine/constraint/InConstraint.java +++ b/jena-shacl/src/main/java/org/apache/jena/shacl/engine/constraint/ListMemberShape.java @@ -21,66 +21,84 @@ package org.apache.jena.shacl.engine.constraint; -import static org.apache.jena.shacl.compact.writer.CompactOut.compactArrayNodes; +import static org.apache.jena.shacl.compact.writer.CompactOut.compact; import static org.apache.jena.shacl.lib.ShLib.displayStr; -import java.util.ArrayList; -import java.util.Collections; import java.util.List; import java.util.Objects; import org.apache.jena.atlas.io.IndentedWriter; +import org.apache.jena.graph.Graph; import org.apache.jena.graph.Node; import org.apache.jena.riot.out.NodeFormatter; import org.apache.jena.shacl.engine.ValidationContext; import org.apache.jena.shacl.parser.ConstraintVisitor; +import org.apache.jena.shacl.parser.Shape; import org.apache.jena.shacl.validation.ReportItem; +import org.apache.jena.shacl.validation.ValidationProc; import org.apache.jena.shacl.vocabulary.SHACL; +import org.apache.jena.sparql.util.graph.GNode; +import org.apache.jena.sparql.util.graph.GraphList; -/** sh:in */ -public class InConstraint extends ConstraintTerm { +/** sh:memberShape */ - private final List<Node> values = new ArrayList<>(); +public class ListMemberShape extends ConstraintList { - public InConstraint(List<Node> list) { - values.addAll(list); + protected final Node shape; + + public ListMemberShape(Node node) { + this.shape = node; } - public List<Node> getValues() { - return Collections.unmodifiableList(values); + @Override + public void visit(ConstraintVisitor visitor){ + visitor.visit(this); } @Override - public Node getComponent() { - return SHACL.InConstraintComponent; + public void printCompact(IndentedWriter out, NodeFormatter nodeFmt) { + compact(out, nodeFmt, "memberShape", shape); } @Override - public ReportItem validate(ValidationContext vCxt, Node n) { - if ( values.contains(n) ) + protected ReportItem validateList(ValidationContext vCxt, Graph data, Node headNode) { + Shape memberShape = vCxt.getShapes().getShape(shape); + if ( memberShape == null ) { + // XXX + //vCxt.reportEntry(, shape, focusNode, path, valueNode, constraint); + // No shape. Error? return null; - String errMsg = toString()+" : RDF term "+displayStr(n)+" not in expected values"; - return new ReportItem(errMsg, n); + } + // XXX Check for valid lists. + GNode gNode = GNode.create(data, headNode); + List<Node> members = GraphList.members(gNode); + + members.forEach(x->{ + ValidationProc.execValidateShape(vCxt, data, memberShape, x); + }); + // XXX Isolate validation and wrap violations? + return null; } @Override - public void visit(ConstraintVisitor visitor){ - visitor.visit(this); + public Node getComponent() { + return SHACL.MemberShapeConstraintComponent; } @Override - public void printCompact(IndentedWriter out, NodeFormatter nodeFmt) { - compactArrayNodes(out, nodeFmt, "in", values); + public void print(IndentedWriter out, NodeFormatter nodeFmt) { + out.print(toString()); } @Override public String toString() { - return "InConstraint"+values; + // XXX Prefixes + return "ListMemberShape["+displayStr(shape)+"]"; } @Override public int hashCode() { - return Objects.hash(values); + return Objects.hash(shape); } @Override @@ -89,8 +107,8 @@ public class InConstraint extends ConstraintTerm { return true; if ( obj == null ) return false; - if ( !(obj instanceof InConstraint other) ) + if ( !(obj instanceof ListMemberShape other) ) return false; - return Objects.equals(values, other.values); + return shape.sameTermAs(other.shape); } } diff --git a/jena-shacl/src/main/java/org/apache/jena/shacl/engine/constraint/JLogConstraint.java b/jena-shacl/src/main/java/org/apache/jena/shacl/engine/constraint/ListMinLength.java similarity index 63% copy from jena-shacl/src/main/java/org/apache/jena/shacl/engine/constraint/JLogConstraint.java copy to jena-shacl/src/main/java/org/apache/jena/shacl/engine/constraint/ListMinLength.java index 4a4bd6b697..6fff10ba56 100644 --- a/jena-shacl/src/main/java/org/apache/jena/shacl/engine/constraint/JLogConstraint.java +++ b/jena-shacl/src/main/java/org/apache/jena/shacl/engine/constraint/ListMinLength.java @@ -21,54 +21,55 @@ package org.apache.jena.shacl.engine.constraint; -import java.util.Objects; - +import org.apache.jena.atlas.io.IndentedWriter; +import org.apache.jena.atlas.lib.NotImplemented; +import org.apache.jena.graph.Graph; import org.apache.jena.graph.Node; +import org.apache.jena.riot.out.NodeFormatter; import org.apache.jena.shacl.engine.ValidationContext; -import org.apache.jena.shacl.lib.ShLib; import org.apache.jena.shacl.parser.ConstraintVisitor; -import org.apache.jena.shacl.sys.ShaclSystem; import org.apache.jena.shacl.validation.ReportItem; -import org.apache.jena.shacl.vocabulary.SHJ; +import org.apache.jena.shacl.vocabulary.SHACL; + +/** sh:memberShape */ -/** A constraint that logs when touched but does not causes a violation */ -public class JLogConstraint extends ConstraintTerm { +public class ListMinLength extends ConstraintList { - private final String message; + public ListMinLength(Node node) { } - public JLogConstraint(String message) { - this.message = message; + @Override + public void visit(ConstraintVisitor visitor){ + visitor.visit(this); } - public String getMessage() { - return message; + @Override + public void printCompact(IndentedWriter out, NodeFormatter nodeFmt) { + throw new NotImplemented(); } @Override - public Node getComponent() { - return SHJ.LogConstraintComponent; + protected ReportItem validateList(ValidationContext vCxt, Graph data, Node headNode) { + throw new NotImplemented(); } @Override - public ReportItem validate(ValidationContext vCxt, Node n) { - String msg = String.format("%s[%s]", message, ShLib.displayStr(n)); - ShaclSystem.shaclSystemLogger.warn(msg); - return null; + public Node getComponent() { + return SHACL.ListMinLengthConstraintComponent; } @Override - public void visit(ConstraintVisitor visitor){ - visitor.visit(this); + public void print(IndentedWriter out, NodeFormatter nodeFmt) { + out.print(toString()); } @Override public String toString() { - return "Log["+message+"]"; + return "ListMemberShape[]"; } @Override public int hashCode() { - return Objects.hash(message); + throw new NotImplemented(); } @Override @@ -77,10 +78,9 @@ public class JLogConstraint extends ConstraintTerm { return true; if ( obj == null ) return false; - if ( getClass() != obj.getClass() ) + if ( !(obj instanceof ListMinLength other) ) return false; - JLogConstraint other = (JLogConstraint)obj; - return Objects.equals(message, other.message); + throw new NotImplemented(); } -} +} diff --git a/jena-shacl/src/main/java/org/apache/jena/shacl/engine/constraint/InConstraint.java b/jena-shacl/src/main/java/org/apache/jena/shacl/engine/constraint/ListUniqueMembers.java similarity index 62% copy from jena-shacl/src/main/java/org/apache/jena/shacl/engine/constraint/InConstraint.java copy to jena-shacl/src/main/java/org/apache/jena/shacl/engine/constraint/ListUniqueMembers.java index 0d9bd00d86..4c7893015a 100644 --- a/jena-shacl/src/main/java/org/apache/jena/shacl/engine/constraint/InConstraint.java +++ b/jena-shacl/src/main/java/org/apache/jena/shacl/engine/constraint/ListUniqueMembers.java @@ -21,15 +21,9 @@ package org.apache.jena.shacl.engine.constraint; -import static org.apache.jena.shacl.compact.writer.CompactOut.compactArrayNodes; -import static org.apache.jena.shacl.lib.ShLib.displayStr; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Objects; - import org.apache.jena.atlas.io.IndentedWriter; +import org.apache.jena.atlas.lib.NotImplemented; +import org.apache.jena.graph.Graph; import org.apache.jena.graph.Node; import org.apache.jena.riot.out.NodeFormatter; import org.apache.jena.shacl.engine.ValidationContext; @@ -37,50 +31,48 @@ import org.apache.jena.shacl.parser.ConstraintVisitor; import org.apache.jena.shacl.validation.ReportItem; import org.apache.jena.shacl.vocabulary.SHACL; -/** sh:in */ -public class InConstraint extends ConstraintTerm { +/** sh:memberShape */ - private final List<Node> values = new ArrayList<>(); +public class ListUniqueMembers extends ConstraintList { - public InConstraint(List<Node> list) { - values.addAll(list); - } + public ListUniqueMembers(Node node) {} - public List<Node> getValues() { - return Collections.unmodifiableList(values); + @Override + public void visit(ConstraintVisitor visitor){ + visitor.visit(this); } @Override - public Node getComponent() { - return SHACL.InConstraintComponent; + public void printCompact(IndentedWriter out, NodeFormatter nodeFmt) { + //compact(out, nodeFmt, "nodeKind", getKind()); + // Property context only. +// String s = getKind().getLocalName(); +// out.print(s); } @Override - public ReportItem validate(ValidationContext vCxt, Node n) { - if ( values.contains(n) ) - return null; - String errMsg = toString()+" : RDF term "+displayStr(n)+" not in expected values"; - return new ReportItem(errMsg, n); + protected ReportItem validateList(ValidationContext vCxt, Graph data, Node headNode) { + throw new NotImplemented(); } @Override - public void visit(ConstraintVisitor visitor){ - visitor.visit(this); + public Node getComponent() { + return SHACL.UniqueMembersConstraintComponent; } @Override - public void printCompact(IndentedWriter out, NodeFormatter nodeFmt) { - compactArrayNodes(out, nodeFmt, "in", values); + public void print(IndentedWriter out, NodeFormatter nodeFmt) { + out.print(toString()); } @Override public String toString() { - return "InConstraint"+values; + return "ListMemberShape[]"; } @Override public int hashCode() { - return Objects.hash(values); + throw new NotImplemented(); } @Override @@ -89,8 +81,9 @@ public class InConstraint extends ConstraintTerm { return true; if ( obj == null ) return false; - if ( !(obj instanceof InConstraint other) ) + if ( !(obj instanceof ListUniqueMembers other) ) return false; - return Objects.equals(values, other.values); + throw new NotImplemented(); } + } diff --git a/jena-shacl/src/main/java/org/apache/jena/shacl/engine/constraint/NodeKindConstraint.java b/jena-shacl/src/main/java/org/apache/jena/shacl/engine/constraint/NodeKindConstraint.java index ee955f022b..6404af002e 100644 --- a/jena-shacl/src/main/java/org/apache/jena/shacl/engine/constraint/NodeKindConstraint.java +++ b/jena-shacl/src/main/java/org/apache/jena/shacl/engine/constraint/NodeKindConstraint.java @@ -39,25 +39,28 @@ import org.apache.jena.shacl.vocabulary.SHACL; public class NodeKindConstraint extends ConstraintTerm { //sh:NodeKind: sh:BlankNode, sh:IRI, sh:Literal sh:BlankNodeOrIRI, sh:BlankNodeOrLiteral and sh:IRIOrLiteral. + // SHACL 1.2 -- sh:TripleTerm private final Node kind; private final boolean canBeIRI; private final boolean canBeBlankNode; private final boolean canBeLiteral; + private final boolean canBeTripleTerm; public NodeKindConstraint(Node kind) { Objects.requireNonNull(kind); if ( ! kind.isURI() ) throw new IllegalArgumentException("NodeKindConstraint; not an IRI for the kind kind"); this.kind = kind; - this.canBeIRI = kind.equals(SHACL.IRI) || kind.equals(SHACL.BlankNodeOrIRI) || kind.equals(SHACL.IRIOrLiteral); - this.canBeBlankNode = kind.equals(SHACL.BlankNode) || kind.equals(SHACL.BlankNodeOrIRI) || kind.equals(SHACL.BlankNodeOrLiteral); - this.canBeLiteral = kind.equals(SHACL.Literal) || kind.equals(SHACL.BlankNodeOrLiteral) || kind.equals(SHACL.IRIOrLiteral); + this.canBeIRI = kind.equals(SHACL.IRI) || kind.equals(SHACL.BlankNodeOrIRI) || kind.equals(SHACL.IRIOrLiteral); + this.canBeBlankNode = kind.equals(SHACL.BlankNode) || kind.equals(SHACL.BlankNodeOrIRI) || kind.equals(SHACL.BlankNodeOrLiteral); + this.canBeLiteral = kind.equals(SHACL.Literal) || kind.equals(SHACL.BlankNodeOrLiteral) || kind.equals(SHACL.IRIOrLiteral); + this.canBeTripleTerm = kind.equals(SHACL.TripleTerm); - if ( ! canBeIRI && ! canBeBlankNode && ! canBeLiteral ) + if ( ! canBeIRI && ! canBeBlankNode && ! canBeLiteral && ! canBeTripleTerm ) throw new IllegalArgumentException( "NodeKind["+kind.getLocalName()+"] : "+ - "not one of sh:BlankNode, sh:IRI, sh:Literal sh:BlankNodeOrIRI, sh:BlankNodeOrLiteral and sh:IRIOrLiteral"); + "not one of sh:BlankNode, sh:IRI, sh:Literal sh:BlankNodeOrIRI, sh:BlankNodeOrLiteral, sh:IRIOrLiteral, or sh:TripleTerm"); } public Node getKind() { return kind; } @@ -88,10 +91,11 @@ public class NodeKindConstraint extends ConstraintTerm { } @Override - public ReportItem validate(ValidationContext vCxt, Node n) { - if ( canBeIRI && n.isURI() ) return null; - if ( canBeBlankNode && n.isBlank() ) return null; - if ( canBeLiteral && n.isLiteral() ) return null; + protected ReportItem validate(ValidationContext vCxt, Node n) { + if ( canBeIRI && n.isURI() ) return null; + if ( canBeBlankNode && n.isBlank() ) return null; + if ( canBeLiteral && n.isLiteral() ) return null; + if ( canBeTripleTerm && n.isTripleTerm() ) return null; String msg = toString()+" : Expected "+kind.getLocalName()+" for "+displayStr(n); return new ReportItem(msg, n); } diff --git a/jena-shacl/src/main/java/org/apache/jena/shacl/engine/constraint/PatternConstraint.java b/jena-shacl/src/main/java/org/apache/jena/shacl/engine/constraint/PatternConstraint.java index 0e1b551bef..02088e026c 100644 --- a/jena-shacl/src/main/java/org/apache/jena/shacl/engine/constraint/PatternConstraint.java +++ b/jena-shacl/src/main/java/org/apache/jena/shacl/engine/constraint/PatternConstraint.java @@ -66,7 +66,7 @@ public class PatternConstraint extends ConstraintTerm { } @Override - public ReportItem validate(ValidationContext vCxt, Node n) { + protected ReportItem validate(ValidationContext vCxt, Node n) { if ( n.isBlank() ) { String msg = toString()+": Blank node: "+ShLib.displayStr(n); return new ReportItem(msg, n); diff --git a/jena-shacl/src/main/java/org/apache/jena/shacl/engine/constraint/ShXone.java b/jena-shacl/src/main/java/org/apache/jena/shacl/engine/constraint/ShXone.java index 727e5824c0..a4c1670904 100644 --- a/jena-shacl/src/main/java/org/apache/jena/shacl/engine/constraint/ShXone.java +++ b/jena-shacl/src/main/java/org/apache/jena/shacl/engine/constraint/ShXone.java @@ -51,8 +51,9 @@ public class ShXone extends ConstraintOpN { int c = 0; for ( Shape sh : others ) { ValidationContext vCxt2 = ValidationContext.create(vCxt); + vCxt2.setVerbose(false); ValidationProc.execValidateShape(vCxt2, data, sh, node); - boolean innerConforms = vCxt2.generateReport().conforms(); + boolean innerConforms = ! vCxt2.hasViolation(); if ( innerConforms ) { c++; // Choice: count all vs break as soon as error detected diff --git a/jena-shacl/src/main/java/org/apache/jena/shacl/engine/constraint/StrLanguageIn.java b/jena-shacl/src/main/java/org/apache/jena/shacl/engine/constraint/StrLanguageIn.java index c4d7167f9d..a7c139274d 100644 --- a/jena-shacl/src/main/java/org/apache/jena/shacl/engine/constraint/StrLanguageIn.java +++ b/jena-shacl/src/main/java/org/apache/jena/shacl/engine/constraint/StrLanguageIn.java @@ -47,7 +47,7 @@ public class StrLanguageIn extends ConstraintTerm { } @Override - public ReportItem validate(ValidationContext vCxt, Node n) { + protected ReportItem validate(ValidationContext vCxt, Node n) { if ( ! n.isLiteral() ) return new ReportItem(toString()+": Not a literal",n); String langTag = n.getLiteralLanguage(); diff --git a/jena-shacl/src/main/java/org/apache/jena/shacl/engine/constraint/StrMaxLengthConstraint.java b/jena-shacl/src/main/java/org/apache/jena/shacl/engine/constraint/StrMaxLengthConstraint.java index 8c450ba8e0..4504448c81 100644 --- a/jena-shacl/src/main/java/org/apache/jena/shacl/engine/constraint/StrMaxLengthConstraint.java +++ b/jena-shacl/src/main/java/org/apache/jena/shacl/engine/constraint/StrMaxLengthConstraint.java @@ -49,7 +49,7 @@ public class StrMaxLengthConstraint extends ConstraintTerm { } @Override - public ReportItem validate(ValidationContext vCxt, Node n) { + protected ReportItem validate(ValidationContext vCxt, Node n) { if ( n.isBlank() ) { String msg = toString()+": Blank node: "+ShLib.displayStr(n); return new ReportItem(msg, n); diff --git a/jena-shacl/src/main/java/org/apache/jena/shacl/engine/constraint/StrMinLengthConstraint.java b/jena-shacl/src/main/java/org/apache/jena/shacl/engine/constraint/StrMinLengthConstraint.java index c043ec9a48..ee77623dc3 100644 --- a/jena-shacl/src/main/java/org/apache/jena/shacl/engine/constraint/StrMinLengthConstraint.java +++ b/jena-shacl/src/main/java/org/apache/jena/shacl/engine/constraint/StrMinLengthConstraint.java @@ -49,7 +49,7 @@ public class StrMinLengthConstraint extends ConstraintTerm { } @Override - public ReportItem validate(ValidationContext vCxt, Node n) { + protected ReportItem validate(ValidationContext vCxt, Node n) { if ( n.isBlank() ) { String msg = toString()+": Blank node: "+ShLib.displayStr(n); return new ReportItem(msg, n); diff --git a/jena-shacl/src/main/java/org/apache/jena/shacl/engine/constraint/ValueRangeConstraint.java b/jena-shacl/src/main/java/org/apache/jena/shacl/engine/constraint/ValueRangeConstraint.java index da605c88a1..813d248181 100644 --- a/jena-shacl/src/main/java/org/apache/jena/shacl/engine/constraint/ValueRangeConstraint.java +++ b/jena-shacl/src/main/java/org/apache/jena/shacl/engine/constraint/ValueRangeConstraint.java @@ -51,7 +51,8 @@ public abstract class ValueRangeConstraint extends ConstraintTerm { } @Override - final public ReportItem validate(ValidationContext vCxt, Node n) { + protected + final ReportItem validate(ValidationContext vCxt, Node n) { NodeValue nv = NodeValue.makeNode(n); ValueSpace vs = NodeValue.classifyValueOp(nodeValue, nv); try { diff --git a/jena-shacl/src/main/java/org/apache/jena/shacl/parser/ConstraintVisitor.java b/jena-shacl/src/main/java/org/apache/jena/shacl/parser/ConstraintVisitor.java index 401879d29e..9a44b18a8d 100644 --- a/jena-shacl/src/main/java/org/apache/jena/shacl/parser/ConstraintVisitor.java +++ b/jena-shacl/src/main/java/org/apache/jena/shacl/parser/ConstraintVisitor.java @@ -55,6 +55,11 @@ public interface ConstraintVisitor { void visit(ConstraintComponentSPARQL constraint); void visit(SparqlConstraint constraint); + void visit(ListMemberShape constraint); + void visit(ListMinLength constraint); + void visit(ListMaxLength constraint); + void visit(ListUniqueMembers constraint); + // Other Constraints void visit(JViolationConstraint constraint); void visit(JLogConstraint constraint); diff --git a/jena-shacl/src/main/java/org/apache/jena/shacl/parser/Constraints.java b/jena-shacl/src/main/java/org/apache/jena/shacl/parser/Constraints.java index 24708af8d9..6e54e27d08 100644 --- a/jena-shacl/src/main/java/org/apache/jena/shacl/parser/Constraints.java +++ b/jena-shacl/src/main/java/org/apache/jena/shacl/parser/Constraints.java @@ -108,6 +108,11 @@ public class Constraints { dispatch.put( SHACL.in, (g, s, p, o) -> new InConstraint(list(g,o)) ); dispatch.put( SHACL.closed, (g, s, p, o) -> new ClosedConstraint(g,s,booleanValue(o)) ); + dispatch.put( SHACL.memberShape, (g, s, p, o) -> new ListMemberShape(o)); + dispatch.put( SHACL.minListLength, (g, s, p, o) -> new ListMinLength(o)); + dispatch.put( SHACL.maxListLength, (g, s, p, o) -> new ListMaxLength(o)); + dispatch.put( SHACL.uniqueMembers, (g, s, p, o) -> new ListUniqueMembers(o)); + // Below //dispatch.put( SHACL.not, (g, s, p, o) -> notImplemented(p) ); //dispatch.put( SHACL.and, (g, s, p, o) -> notImplemented(p) ); @@ -202,6 +207,10 @@ public class Constraints { Shape other = ShapesParser.parseShapeStep(traversed, parsed, g, o); if ( other instanceof PropertyShape ) throw new ShaclParseException("Object of sh:node must be a node shape, not a property shape"); + if ( other == null ) { + System.err.print("sh:node - no such shape"); + return null; + } return new ShNode(other); } diff --git a/jena-shacl/src/main/java/org/apache/jena/shacl/vocabulary/SHACL.java b/jena-shacl/src/main/java/org/apache/jena/shacl/vocabulary/SHACL.java index 2975fb28d5..1dd769ed7d 100644 --- a/jena-shacl/src/main/java/org/apache/jena/shacl/vocabulary/SHACL.java +++ b/jena-shacl/src/main/java/org/apache/jena/shacl/vocabulary/SHACL.java @@ -29,6 +29,7 @@ import org.apache.jena.graph.NodeFactory; // Vocabulary for http://www.w3.org/ns/shacl# // schemagen then converted to Node level. // then add SHACL-AF +// Then SHACL 1.2 added public class SHACL { private static Node createResource(String uri) { return NodeFactory.createURI(uri); } @@ -231,6 +232,39 @@ public class SHACL { /** <p>Specifies the node kind (e.g. IRI or literal) each value node.</p> */ public static final Node nodeKind = createProperty( "http://www.w3.org/ns/shacl#nodeKind" ); + /** SHACL 1.2: sh:memberShape */ + public static final Node memberShape = createProperty( "http://www.w3.org/ns/shacl#memberShape" ); + + /** SHACL 1.2: minListLength */ + public static final Node minListLength = createProperty( "http://www.w3.org/ns/shacl#minListLength" ); + + /** SHACL 1.2: maxListLength */ + public static final Node maxListLength = createProperty( "http://www.w3.org/ns/shacl#maxListLength" ); + + /** SHACL 1.2: uniqueMembers */ + public static final Node uniqueMembers = createProperty( "http://www.w3.org/ns/shacl#uniqueMembers" ); + + /** <p>Node kind -- literal.</p> */ + public static final Node Literal = createResource( "http://www.w3.org/ns/shacl#Literal" ); + + /** <p>Node kind -- IRI.</p> */ + public static final Node IRI = createResource( "http://www.w3.org/ns/shacl#IRI" ); + + /** <p>Node kind -- blank node.</p> */ + public static final Node BlankNode = createResource( "http://www.w3.org/ns/shacl#BlankNode" ); + + /** <p>Node kind -- triple term</p> */ + public static final Node TripleTerm = createResource( "http://www.w3.org/ns/shacl#TripleTerm" ); + + /** <p>Node kind -- IRI or literal.</p> */ + public static final Node IRIOrLiteral = createResource( "http://www.w3.org/ns/shacl#IRIOrLiteral" ); + + /** <p>Node kind -- blank node or IRI.</p> */ + public static final Node BlankNodeOrIRI = createResource( "http://www.w3.org/ns/shacl#BlankNodeOrIRI" ); + + /** <p>Node kind -- blank nodes or literals.</p> */ + public static final Node BlankNodeOrLiteral = createResource( "http://www.w3.org/ns/shacl#BlankNodeOrLiteral" ); + /** <p>The validator(s) used to evaluate a constraint in the context of a node shape.</p> */ public static final Node nodeValidator = createProperty( "http://www.w3.org/ns/shacl#nodeValidator" ); @@ -430,15 +464,6 @@ public class SHACL { public static final Node AndConstraintComponent_and = createResource( "http://www.w3.org/ns/shacl#AndConstraintComponent-and" ); - /** <p>The node kind of all blank nodes.</p> */ - public static final Node BlankNode = createResource( "http://www.w3.org/ns/shacl#BlankNode" ); - - /** <p>The node kind of all blank nodes or IRIs.</p> */ - public static final Node BlankNodeOrIRI = createResource( "http://www.w3.org/ns/shacl#BlankNodeOrIRI" ); - - /** <p>The node kind of all blank nodes or literals.</p> */ - public static final Node BlankNodeOrLiteral = createResource( "http://www.w3.org/ns/shacl#BlankNodeOrLiteral" ); - /** <p>A constraint component that can be used to verify that each value node is * an instance of a given type.</p> */ @@ -499,12 +524,6 @@ public class SHACL { public static final Node HasValueConstraintComponent_hasValue = createResource( "http://www.w3.org/ns/shacl#HasValueConstraintComponent-hasValue" ); - /** <p>The node kind of all IRIs.</p> */ - public static final Node IRI = createResource( "http://www.w3.org/ns/shacl#IRI" ); - - /** <p>The node kind of all IRIs or literals.</p> */ - public static final Node IRIOrLiteral = createResource( "http://www.w3.org/ns/shacl#IRIOrLiteral" ); - /** <p>A constraint component that can be used to exclusively enumerate the permitted * value nodes.</p> */ @@ -575,9 +594,6 @@ public class SHACL { public static final Node LessThanOrEqualsConstraintComponent_lessThanOrEquals = createResource( "http://www.w3.org/ns/shacl#LessThanOrEqualsConstraintComponent-lessThanOrEquals" ); - /** <p>The node kind of all literals.</p> */ - public static final Node Literal = createResource( "http://www.w3.org/ns/shacl#Literal" ); - /** <p>A constraint component that can be used to restrict the maximum number of * value nodes.</p> */ @@ -653,6 +669,14 @@ public class SHACL { public static final Node NodeKindConstraintComponent_nodeKind = createResource( "http://www.w3.org/ns/shacl#NodeKindConstraintComponent-nodeKind" ); + public static final Node MemberShapeConstraintComponent = createResource( "http://www.w3.org/ns/shacl#MemberShapeConstraintComponent" ); + + public static final Node ListMinLengthConstraintComponent = createResource( "http://www.w3.org/ns/shacl#ListMinLengthConstraintComponent" ); + + public static final Node ListMaxLengthConstraintComponent = createResource( "http://www.w3.org/ns/shacl#ListMaxLengthConstraintComponent" ); + + public static final Node UniqueMembersConstraintComponent = createResource( "http://www.w3.org/ns/shacl#UniqueMembersConstraintComponent" ); + /** <p>A node shape is a shape that specifies constraint that need to be met with * respect to focus nodes.</p> */
