kinow commented on a change in pull request #586: JENA-1733: SHACL engine, command line validator and Fuseki service. URL: https://github.com/apache/jena/pull/586#discussion_r306685687
########## File path: jena-shacl/src/main/java/org/apache/jena/shacl/validation/ReportEntry.java ########## @@ -0,0 +1,303 @@ +/* + * 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 + * + * http://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. + */ + +package org.apache.jena.shacl.validation; + +import static org.apache.jena.shacl.lib.ShLib.displayStr; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Objects; +import java.util.StringJoiner; +import java.util.function.Function; + +import org.apache.jena.atlas.lib.CollectionUtils; +import org.apache.jena.graph.Node; +import org.apache.jena.graph.NodeFactory; +import org.apache.jena.graph.Triple; +import org.apache.jena.rdf.model.Model; +import org.apache.jena.rdf.model.RDFNode; +import org.apache.jena.rdf.model.Resource; +import org.apache.jena.riot.out.NodeFmtLib; +import org.apache.jena.shacl.ShaclException; +import org.apache.jena.shacl.engine.ShaclPaths; +import org.apache.jena.shacl.parser.Constraint; +import org.apache.jena.shacl.vocabulary.SHACLM; +import org.apache.jena.sparql.path.Path; +import org.apache.jena.sparql.util.ModelUtils; +import org.apache.jena.vocabulary.RDF; + +/** + * A report entry. + */ +public class ReportEntry { + // 3.6 Validation Report + // 3.6.1 Validation Report (sh:ValidationReport) + // 3.6.1.1 Conforms (sh:conforms) + // 3.6.1.2 Result (sh:result) + // 3.6.1.3 Syntax Checking of Shapes Graph (sh:shapesGraphWellFormed) + // 3.6.2 Validation Result (sh:ValidationResult) + // 3.6.2.1 Focus node (sh:focusNode) + // 3.6.2.2 Path (sh:resultPath) + // 3.6.2.3 Value (sh:value) + // 3.6.2.4 Source (sh:sourceShape) + // 3.6.2.5 Constraint Component (sh:sourceConstraintComponent) + // 3.6.2.6 Details (sh:detail) + // 3.6.2.7 Message (sh:resultMessage) + // 3.6.2.8 Severity (sh:resultSeverity) + + private Node focusNode = null; + private Path resultPath; + private Node value; + private Node source; + private Constraint constraint; + private Node sourceConstraint; + private Node sourceConstraintComponent; + private Severity severity; + private final Collection<Node> messages = new ArrayList<>(5); + private Node detail; + // Extras + private Triple triple; + + private ReportEntry() {} + + public static ReportEntry create() { + return new ReportEntry(); + } + +// sh:result [ a sh:ValidationResult ; +// sh:focusNode ex:x ; +// sh:resultMessage "Value does not have class http://example/ns#T" ; +// sh:resultSeverity sh:Violation ; +// sh:sourceConstraintComponent sh:ClassConstraintComponent ; +// sh:sourceShape shx:class_T ; +// sh:value ex:x +// ] +// sh:result [ +// a sh:ValidationResult ; +// sh:resultSeverity sh:Violation ; +// sh:focusNode ex:Bob ; +// sh:resultPath ex:age ; +// sh:value "twenty two" ; +// sh:resultMessage "ex:age expects a literal of datatype xsd:integer." ; +// sh:sourceConstraintComponent sh:DatatypeConstraintComponent ; +// sh:sourceShape ex:PersonShape-age ; +// ] + + public Node focusNode() { return focusNode; } + + public ReportEntry focusNode(Node node) { + this.focusNode = node; + return this; + } + + public Path resultPath() { return resultPath; } + + public ReportEntry resultPath(Path path) { + this.resultPath = path; + return this; + } + + public Triple triple() { return triple; } + + public ReportEntry triple(Triple triple) { + this.triple = triple; + return this; + } + + public Node value() { return value; } + + public ReportEntry value(Node node) { + this.value = node; + return this; + } + + public Node source() { return source; } + + public ReportEntry source(Node node) { + this.source = node; + return this; + } + + public Constraint constraint() { return constraint; } + + public ReportEntry constraint(Constraint constraint) { + this.constraint = constraint; + if ( constraint != null ) + sourceConstraintComponent(constraint.getComponent()); + return this; + } + + public Node sourceConstraint() { return sourceConstraint; } + + public ReportEntry sourceConstraint(Node sourceConstraint) { + this.sourceConstraint = sourceConstraint; + return this; + } + + public Constraint sourceConstraintComponent() { return constraint; } + + public ReportEntry sourceConstraintComponent(Node component) { + this.sourceConstraintComponent = component; + return this; + } + + public Node detail() { return detail; } + + public ReportEntry detail(Node n) { + detail = n; + return this; + } + + public ReportEntry message(Node node) { + if ( node != null ) + messages.add(node); + return this; + } + + public ReportEntry message(String msg) { + if ( msg != null ) + messages.add(NodeFactory.createLiteral(msg)); + return this; + } + + public String message() { + if ( messages() != null && messages().size() == 1 ) + return asString(CollectionUtils.oneElt(messages)); + else { + StringJoiner sj = new StringJoiner(",", "[","]"); + for ( Node n : messages ) + sj.add(NodeFmtLib.displayStr(n)); + return sj.toString(); + } + } + + public Collection<Node> messages() { return messages; } + + private static String asString(Node message) { + if ( message == null ) + return null; + if ( message.isLiteral() ) + return message.getLiteralLexicalForm(); + else if ( message.isURI() ) + return message.getURI(); + else + throw new ShaclException("Not a literal or URI: "+message); + } + + public Severity severity() { return severity; } + + public ReportEntry severity(Severity severity) { + this.severity = severity; + return this; + } + + public ReportEntry severity(Node severity) { + this.severity = (severity == null) ? null : Severity.create(severity); + return this; + } + + public void generate(Model model, Resource report) { + Resource entry = model.createResource(); + entry.addProperty(RDF.type, SHACLM.ValidationResult); + report.addProperty(SHACLM.result, entry); + + Function<Node, RDFNode> rdfNode = (gn)->ModelUtils.convertGraphNodeToRDFNode(gn, entry.getModel()); + Function<String, RDFNode> str = (s)->entry.getModel().createLiteral(s); + + // Section 3.6.2 + // The properties sh:focusNode, sh:resultSeverity and + // sh:sourceConstraintComponent are the only properties that are mandatory + // for all validation results. + + Objects.requireNonNull(focusNode,"focus node"); + //Objects.requireNonNull(message,"message"); + Objects.requireNonNull(severity,"severity"); + Objects.requireNonNull(sourceConstraintComponent, "sourceConstraintComponent"); + + entry.addProperty(SHACLM.focusNode, rdfNode.apply(focusNode)); + entry.addProperty(SHACLM.resultSeverity, rdfNode.apply(severity.level())); + entry.addProperty(SHACLM.sourceConstraintComponent, rdfNode.apply(sourceConstraintComponent)); + + if ( messages != null ) { Review comment: It's never `null` as it's a private/final `ArrayList` initialized only in this class (other methods use `messages` without defensive check) ---------------------------------------------------------------- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. For queries about this service, please contact Infrastructure at: [email protected] With regards, Apache Git Services
