diff --git a/jena-arq/src/test/java/org/apache/jena/arq/junit/manifest/TestMakers.java b/jena-arq/src/test/java/org/apache/jena/arq/junit/manifest/TestMakers.java
index 3d4ee22ec45..b37c8f75997 100644
--- a/jena-arq/src/test/java/org/apache/jena/arq/junit/manifest/TestMakers.java
+++ b/jena-arq/src/test/java/org/apache/jena/arq/junit/manifest/TestMakers.java
@@ -21,6 +21,7 @@
package org.apache.jena.arq.junit.manifest;
+import java.util.ArrayList;
import java.util.List;
import org.apache.jena.arq.junit.SurpressedTest;
@@ -52,7 +53,9 @@ private static TestMakers systemSetup() {
* Add a test maker to the system-wide test makers list
*/
public static void install(TestMaker testMaker) {
- systemSetup.add(testMaker);
+ List next = new ArrayList<>(systemSetup.installed);
+ next.add(testMaker);
+ set(next);
}
public static void reset() {
@@ -77,10 +80,6 @@ private TestMakers(List testMakers) {
installed = testMakers;
}
- public void add(TestMaker testMaker) {
- installed.add(testMaker);
- }
-
public void clear() {
installed.clear();
}
diff --git a/jena-cmds/src/main/java/shacl/shacl_validate.java b/jena-cmds/src/main/java/shacl/shacl_validate.java
index 96df4d54e51..bdd2d5728ec 100644
--- a/jena-cmds/src/main/java/shacl/shacl_validate.java
+++ b/jena-cmds/src/main/java/shacl/shacl_validate.java
@@ -21,6 +21,8 @@
package shacl;
+import java.util.List;
+
import org.apache.jena.atlas.logging.LogCtl;
import org.apache.jena.cmd.ArgDecl;
import org.apache.jena.cmd.CmdException;
@@ -36,11 +38,14 @@
import org.apache.jena.shacl.ValidationReport;
import org.apache.jena.shacl.engine.ValidationContext;
import org.apache.jena.shacl.lib.ShLib;
+import org.apache.jena.sparql.graph.GraphFactory;
import org.apache.jena.sys.JenaSystem;
/** SHACL validation.
- *
- * Usage: shacl validate [--text] --shapes SHAPES --data DATA
+ *
+ * Usage: shacl validate [--text] [-v|--verbose] [--target=] --shapes SHAPES --data DATA
+ * Usage: shacl validate [--text] [-v|--verbose] [--target=] FILE
+ *
*/
public class shacl_validate extends CmdMain {
@@ -55,8 +60,10 @@ public class shacl_validate extends CmdMain {
private ArgDecl argShapes = new ArgDecl(true, "--shapes", "--shapesfile", "--shapefile", "-s");
private ArgDecl argTargetNode = new ArgDecl(true, "--target", "--node", "-n", "-t");
- private String datafile = null;
- private String shapesfile = null;
+ // Allow multiple files for each - combine into one graph each for data and for shapes.
+ private List datafiles = null;
+ private List shapesfiles = null;
+ private String shapesURL = null;
private String targetNode = null; // Parse later.
private boolean textOutput = false;
@@ -75,44 +82,43 @@ public shacl_validate(String[] argv) {
@Override
protected String getSummary() {
- return getCommandName()+" [--target URI] --shapes shapesFile --data dataFile";
+ return getCommandName()+" [--target URI] [--shapes shapesFile --data dataFile] [FILE ...]";
}
@Override
protected void processModulesAndArgs() {
super.processModulesAndArgs();
- datafile = super.getValue(argData);
- shapesfile = super.getValue(argShapes);
+ // --data can be empty in which case shapes and data are in the shapes files.
+ datafiles = super.getValues(argData);
+ shapesfiles = super.getValues(argShapes);
- // No -- arguments, use act on single file of shapes and data.
- if ( datafile == null && shapesfile == null ) {
- if ( positionals.size() == 1 ) {
- datafile = positionals.get(0);
- shapesfile = positionals.get(0);
- }
+ // If there are no arguments, act the commandline positional arguments as shapes and data.
+ if ( datafiles.isEmpty() && shapesfiles.isEmpty() ) {
+ if ( positionals.isEmpty() )
+ throw new CmdException("No input");
+ shapesfiles = positionals;
}
-
- if ( datafile == null )
- throw new CmdException("Usage: "+getSummary());
- if ( shapesfile == null )
- shapesfile = datafile;
+ if ( shapesfiles == null || shapesfiles.isEmpty() )
+ throw new CmdException("No shapes files");
textOutput = super.hasArg(argOutputText);
- if ( contains(argTargetNode) ) {
+ if ( contains(argTargetNode) )
targetNode = getValue(argTargetNode);
- }
+
+ // For imports.
+ shapesURL = shapesfiles.getFirst();
}
@Override
protected void exec() {
- Graph shapesGraph = load(shapesfile, "shapes file");
+ Graph shapesGraph = load(shapesfiles);
Graph dataGraph;
- if ( datafile.equals(shapesfile) )
+ if ( datafiles.isEmpty() )
dataGraph = shapesGraph;
else
- dataGraph = load(datafile, "data file");
+ dataGraph = load(datafiles);
Node node = null;
if ( targetNode != null ) {
@@ -120,7 +126,7 @@ protected void exec() {
node = NodeFactory.createURI(x);
}
- shapesGraph = Imports.withImports(shapesfile, shapesGraph);
+ shapesGraph = Imports.withImports(shapesURL, shapesGraph);
if ( isVerbose() )
ValidationContext.VERBOSE = true;
@@ -138,14 +144,17 @@ protected void exec() {
RDFDataMgr.write(System.out, report.getGraph(), Lang.TTL);
}
- private Graph load(String filename, String scope) {
- try {
- Graph graph = RDFDataMgr.loadGraph(filename);
- return graph;
- } catch (RiotException ex) {
- System.err.println("Loading "+scope);
- throw ex;
- }
+ private Graph load(List files) {
+ Graph graph = GraphFactory.createDefaultGraph();
+ files.forEach(fn-> {
+ try {
+ RDFDataMgr.read(graph, fn);
+ } catch (RiotException ex) {
+ System.err.println("Error loading "+fn);
+ throw ex;
+ }
+ });
+ return graph;
}
@Override
diff --git a/jena-shacl/src/main/java/org/apache/jena/shacl/engine/constraint/ConstraintList.java b/jena-shacl/src/main/java/org/apache/jena/shacl/engine/constraint/ConstraintList.java
new file mode 100644
index 00000000000..8493c8f339b
--- /dev/null
+++ b/jena-shacl/src/main/java/org/apache/jena/shacl/engine/constraint/ConstraintList.java
@@ -0,0 +1,67 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package org.apache.jena.shacl.engine.constraint;
+
+import java.util.Set;
+
+import org.apache.jena.graph.Graph;
+import org.apache.jena.graph.Node;
+import org.apache.jena.shacl.engine.ValidationContext;
+import org.apache.jena.shacl.parser.Constraint;
+import org.apache.jena.shacl.parser.Shape;
+import org.apache.jena.shacl.validation.ReportItem;
+import org.apache.jena.shacl.validation.event.ConstraintEvaluatedOnFocusNodeEvent;
+import org.apache.jena.shacl.validation.event.ConstraintEvaluatedOnSinglePathNodeEvent;
+import org.apache.jena.sparql.path.Path;
+
+public abstract class ConstraintList implements Constraint {
+
+ protected ConstraintList() {}
+
+ @Override
+ final
+ public void validatePropertyShape(ValidationContext vCxt, Graph data, Shape shape, Node focusNode, Path path, Set valueNodes) {
+ valueNodes.forEach(x->applyConstraintList(vCxt, shape, focusNode, path, data, x));
+ }
+
+ @Override
+ final
+ public void validateNodeShape(ValidationContext vCxt, Graph data, Shape shape, Node focusNode) {
+ applyConstraintList(vCxt, shape, focusNode, null, data, focusNode);
+ }
+
+ 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,
+ listHead, passed));
+ }
+ if ( passed )
+ return;
+ vCxt.reportEntry(item, shape, focusNode, path, this);
+ }
+
+ 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 284830cf88f..0264c6fdb6f 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 void validateNodeShape(ValidationContext vCxt, Graph data, Shape shape, N
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 @@ private void applyConstraintTerm(ValidationContext vCxt, Shape shape, Node focus
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 35d329fe051..b47ad94d576 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 RDFDatatype getRDFDatatype() {
}
@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 void printCompact(IndentedWriter out, NodeFormatter nodeFmt) {
@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 0d9bd00d86c..82f1300c5cf 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 Node getComponent() {
}
@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 4a4bd6b6975..4fdd687c75c 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 Node getComponent() {
}
@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 ac8430b65d4..5945e106e5c 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 Node getComponent() {
}
@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/ListMaxLength.java b/jena-shacl/src/main/java/org/apache/jena/shacl/engine/constraint/ListMaxLength.java
new file mode 100644
index 00000000000..36253ed3f3f
--- /dev/null
+++ b/jena-shacl/src/main/java/org/apache/jena/shacl/engine/constraint/ListMaxLength.java
@@ -0,0 +1,86 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package org.apache.jena.shacl.engine.constraint;
+
+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.parser.ConstraintVisitor;
+import org.apache.jena.shacl.validation.ReportItem;
+import org.apache.jena.shacl.vocabulary.SHACL;
+
+/** sh:memberShape */
+
+public class ListMaxLength extends ConstraintList {
+
+ public ListMaxLength(Node node) {}
+
+ @Override
+ public void visit(ConstraintVisitor visitor){
+ visitor.visit(this);
+ }
+
+ @Override
+ public void printCompact(IndentedWriter out, NodeFormatter nodeFmt) {
+ throw new NotImplemented();
+ }
+
+ @Override
+ protected ReportItem validateList(ValidationContext vCxt, Graph data, Node headNode) {
+ throw new NotImplemented();
+ }
+
+ @Override
+ public Node getComponent() {
+ return SHACL.ListMaxLengthConstraintComponent;
+ }
+
+ @Override
+ public void print(IndentedWriter out, NodeFormatter nodeFmt) {
+ out.print(toString());
+ }
+
+ @Override
+ public String toString() {
+ return "ListMemberShape[]";
+ }
+
+ @Override
+ public int hashCode() {
+ throw new NotImplemented();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if ( this == obj )
+ return true;
+ if ( obj == null )
+ return false;
+ if ( !(obj instanceof ListMaxLength other) )
+ return false;
+ throw new NotImplemented();
+ }
+
+}
diff --git a/jena-shacl/src/main/java/org/apache/jena/shacl/engine/constraint/ListMemberShape.java b/jena-shacl/src/main/java/org/apache/jena/shacl/engine/constraint/ListMemberShape.java
new file mode 100644
index 00000000000..133edec8e9c
--- /dev/null
+++ b/jena-shacl/src/main/java/org/apache/jena/shacl/engine/constraint/ListMemberShape.java
@@ -0,0 +1,114 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package org.apache.jena.shacl.engine.constraint;
+
+import static org.apache.jena.shacl.compact.writer.CompactOut.compact;
+import static org.apache.jena.shacl.lib.ShLib.displayStr;
+
+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:memberShape */
+
+public class ListMemberShape extends ConstraintList {
+
+ protected final Node shape;
+
+ public ListMemberShape(Node node) {
+ this.shape = node;
+ }
+
+ @Override
+ public void visit(ConstraintVisitor visitor){
+ visitor.visit(this);
+ }
+
+ @Override
+ public void printCompact(IndentedWriter out, NodeFormatter nodeFmt) {
+ compact(out, nodeFmt, "memberShape", shape);
+ }
+
+ @Override
+ 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;
+ }
+ // XXX Check for valid lists.
+ GNode gNode = GNode.create(data, headNode);
+ List members = GraphList.members(gNode);
+
+ members.forEach(x->{
+ ValidationProc.execValidateShape(vCxt, data, memberShape, x);
+ });
+ // XXX Isolate validation and wrap violations?
+ return null;
+ }
+
+ @Override
+ public Node getComponent() {
+ return SHACL.MemberShapeConstraintComponent;
+ }
+
+ @Override
+ public void print(IndentedWriter out, NodeFormatter nodeFmt) {
+ out.print(toString());
+ }
+
+ @Override
+ public String toString() {
+ // XXX Prefixes
+ return "ListMemberShape["+displayStr(shape)+"]";
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(shape);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if ( this == obj )
+ return true;
+ if ( obj == null )
+ return false;
+ if ( !(obj instanceof ListMemberShape other) )
+ return false;
+ return shape.sameTermAs(other.shape);
+ }
+}
diff --git a/jena-shacl/src/main/java/org/apache/jena/shacl/engine/constraint/ListMinLength.java b/jena-shacl/src/main/java/org/apache/jena/shacl/engine/constraint/ListMinLength.java
new file mode 100644
index 00000000000..6fff10ba560
--- /dev/null
+++ b/jena-shacl/src/main/java/org/apache/jena/shacl/engine/constraint/ListMinLength.java
@@ -0,0 +1,86 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package org.apache.jena.shacl.engine.constraint;
+
+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.parser.ConstraintVisitor;
+import org.apache.jena.shacl.validation.ReportItem;
+import org.apache.jena.shacl.vocabulary.SHACL;
+
+/** sh:memberShape */
+
+public class ListMinLength extends ConstraintList {
+
+ public ListMinLength(Node node) { }
+
+ @Override
+ public void visit(ConstraintVisitor visitor){
+ visitor.visit(this);
+ }
+
+ @Override
+ public void printCompact(IndentedWriter out, NodeFormatter nodeFmt) {
+ throw new NotImplemented();
+ }
+
+ @Override
+ protected ReportItem validateList(ValidationContext vCxt, Graph data, Node headNode) {
+ throw new NotImplemented();
+ }
+
+ @Override
+ public Node getComponent() {
+ return SHACL.ListMinLengthConstraintComponent;
+ }
+
+ @Override
+ public void print(IndentedWriter out, NodeFormatter nodeFmt) {
+ out.print(toString());
+ }
+
+ @Override
+ public String toString() {
+ return "ListMemberShape[]";
+ }
+
+ @Override
+ public int hashCode() {
+ throw new NotImplemented();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if ( this == obj )
+ return true;
+ if ( obj == null )
+ return false;
+ if ( !(obj instanceof ListMinLength other) )
+ return false;
+ throw new NotImplemented();
+ }
+
+}
diff --git a/jena-shacl/src/main/java/org/apache/jena/shacl/engine/constraint/ListUniqueMembers.java b/jena-shacl/src/main/java/org/apache/jena/shacl/engine/constraint/ListUniqueMembers.java
new file mode 100644
index 00000000000..4c7893015a8
--- /dev/null
+++ b/jena-shacl/src/main/java/org/apache/jena/shacl/engine/constraint/ListUniqueMembers.java
@@ -0,0 +1,89 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package org.apache.jena.shacl.engine.constraint;
+
+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.parser.ConstraintVisitor;
+import org.apache.jena.shacl.validation.ReportItem;
+import org.apache.jena.shacl.vocabulary.SHACL;
+
+/** sh:memberShape */
+
+public class ListUniqueMembers extends ConstraintList {
+
+ public ListUniqueMembers(Node node) {}
+
+ @Override
+ public void visit(ConstraintVisitor visitor){
+ visitor.visit(this);
+ }
+
+ @Override
+ public void printCompact(IndentedWriter out, NodeFormatter nodeFmt) {
+ //compact(out, nodeFmt, "nodeKind", getKind());
+ // Property context only.
+// String s = getKind().getLocalName();
+// out.print(s);
+ }
+
+ @Override
+ protected ReportItem validateList(ValidationContext vCxt, Graph data, Node headNode) {
+ throw new NotImplemented();
+ }
+
+ @Override
+ public Node getComponent() {
+ return SHACL.UniqueMembersConstraintComponent;
+ }
+
+ @Override
+ public void print(IndentedWriter out, NodeFormatter nodeFmt) {
+ out.print(toString());
+ }
+
+ @Override
+ public String toString() {
+ return "ListMemberShape[]";
+ }
+
+ @Override
+ public int hashCode() {
+ throw new NotImplemented();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if ( this == obj )
+ return true;
+ if ( obj == null )
+ return false;
+ if ( !(obj instanceof ListUniqueMembers other) )
+ return false;
+ 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 ee955f022b8..6404af002e9 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 @@
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 void printCompact(IndentedWriter out, NodeFormatter nodeFmt) {
}
@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 0e1b551befb..02088e026cc 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 String getFlagsStr() {
}
@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 727e5824c02..9b4ab0f4778 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
@@ -52,7 +52,7 @@ public ReportItem validate(ValidationContext vCxt, Graph data, Node node) {
for ( Shape sh : others ) {
ValidationContext vCxt2 = ValidationContext.create(vCxt);
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 c4d7167f9d4..a7c139274d6 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 StrLanguageIn(List langs) {
}
@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 8c450ba8e0b..4504448c81e 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 int getMaxLength() {
}
@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 c043ec9a48c..ee77623dc36 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 int getMinLength() {
}
@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 da605c88a1e..813d248181a 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 NodeValue getNodeValue() {
}
@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 401879d29e2..9a44b18a8db 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 24708af8d9d..6e54e27d08b 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 @@ private static Constraint parseConstraint(Graph g, Node s, Node p, Node o, MapSpecifies the node kind (e.g. IRI or literal) each value node.
*/
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" );
+
+ /** Node kind -- literal.
*/
+ public static final Node Literal = createResource( "http://www.w3.org/ns/shacl#Literal" );
+
+ /** Node kind -- IRI.
*/
+ public static final Node IRI = createResource( "http://www.w3.org/ns/shacl#IRI" );
+
+ /** Node kind -- blank node.
*/
+ public static final Node BlankNode = createResource( "http://www.w3.org/ns/shacl#BlankNode" );
+
+ /** Node kind -- triple term
*/
+ public static final Node TripleTerm = createResource( "http://www.w3.org/ns/shacl#TripleTerm" );
+
+ /** Node kind -- IRI or literal.
*/
+ public static final Node IRIOrLiteral = createResource( "http://www.w3.org/ns/shacl#IRIOrLiteral" );
+
+ /** Node kind -- blank node or IRI.
*/
+ public static final Node BlankNodeOrIRI = createResource( "http://www.w3.org/ns/shacl#BlankNodeOrIRI" );
+
+ /** Node kind -- blank nodes or literals.
*/
+ public static final Node BlankNodeOrLiteral = createResource( "http://www.w3.org/ns/shacl#BlankNodeOrLiteral" );
+
/** The validator(s) used to evaluate a constraint in the context of a node shape.
*/
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" );
- /** The node kind of all blank nodes.
*/
- public static final Node BlankNode = createResource( "http://www.w3.org/ns/shacl#BlankNode" );
-
- /** The node kind of all blank nodes or IRIs.
*/
- public static final Node BlankNodeOrIRI = createResource( "http://www.w3.org/ns/shacl#BlankNodeOrIRI" );
-
- /** The node kind of all blank nodes or literals.
*/
- public static final Node BlankNodeOrLiteral = createResource( "http://www.w3.org/ns/shacl#BlankNodeOrLiteral" );
-
/** A constraint component that can be used to verify that each value node is
* an instance of a given type.
*/
@@ -499,12 +524,6 @@ public class SHACL {
public static final Node HasValueConstraintComponent_hasValue = createResource( "http://www.w3.org/ns/shacl#HasValueConstraintComponent-hasValue" );
- /** The node kind of all IRIs.
*/
- public static final Node IRI = createResource( "http://www.w3.org/ns/shacl#IRI" );
-
- /** The node kind of all IRIs or literals.
*/
- public static final Node IRIOrLiteral = createResource( "http://www.w3.org/ns/shacl#IRIOrLiteral" );
-
/** A constraint component that can be used to exclusively enumerate the permitted
* value nodes.
*/
@@ -575,9 +594,6 @@ public class SHACL {
public static final Node LessThanOrEqualsConstraintComponent_lessThanOrEquals = createResource( "http://www.w3.org/ns/shacl#LessThanOrEqualsConstraintComponent-lessThanOrEquals" );
- /** The node kind of all literals.
*/
- public static final Node Literal = createResource( "http://www.w3.org/ns/shacl#Literal" );
-
/** A constraint component that can be used to restrict the maximum number of
* value nodes.
*/
@@ -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" );
+
/** A node shape is a shape that specifies constraint that need to be met with
* respect to focus nodes.
*/