LazyValidationReport.java
/*******************************************************************************
* Copyright (c) 2020 Eclipse RDF4J contributors.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Distribution License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: BSD-3-Clause
*******************************************************************************/
package org.eclipse.rdf4j.sail.shacl.results.lazy;
import static org.eclipse.rdf4j.model.util.Values.literal;
import java.io.StringWriter;
import java.util.HashSet;
import java.util.List;
import org.eclipse.rdf4j.common.annotation.InternalUseOnly;
import org.eclipse.rdf4j.model.Model;
import org.eclipse.rdf4j.model.Resource;
import org.eclipse.rdf4j.model.impl.BooleanLiteral;
import org.eclipse.rdf4j.model.impl.DynamicModel;
import org.eclipse.rdf4j.model.impl.DynamicModelFactory;
import org.eclipse.rdf4j.model.vocabulary.DASH;
import org.eclipse.rdf4j.model.vocabulary.OWL;
import org.eclipse.rdf4j.model.vocabulary.RDF;
import org.eclipse.rdf4j.model.vocabulary.RDF4J;
import org.eclipse.rdf4j.model.vocabulary.RDFS;
import org.eclipse.rdf4j.model.vocabulary.RSX;
import org.eclipse.rdf4j.model.vocabulary.SHACL;
import org.eclipse.rdf4j.model.vocabulary.XSD;
import org.eclipse.rdf4j.rio.RDFFormat;
import org.eclipse.rdf4j.rio.Rio;
import org.eclipse.rdf4j.rio.WriterConfig;
import org.eclipse.rdf4j.rio.helpers.BasicWriterSettings;
import org.eclipse.rdf4j.sail.shacl.results.ValidationReport;
import org.eclipse.rdf4j.sail.shacl.results.ValidationResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* A ValidationReport that will defer calculating any ValidationResults until the user asks for them
*/
@InternalUseOnly
public class LazyValidationReport extends ValidationReport {
private static final Logger logger = LoggerFactory.getLogger(LazyValidationReport.class);
private List<ValidationResultIterator> validationResultIterators;
private final long limit;
public LazyValidationReport(List<ValidationResultIterator> validationResultIterators, long limit) {
this.validationResultIterators = validationResultIterators;
this.limit = limit;
}
private void evaluateLazyAspect() {
try {
if (validationResultIterators != null) {
long counter = 0;
for (ValidationResultIterator validationResultIterator : validationResultIterators) {
while (validationResultIterator.hasNext()) {
if (limit >= 0 && counter >= limit) {
truncated = true;
break;
}
counter++;
validationResult.add(validationResultIterator.next());
}
this.conforms = conforms && validationResultIterator.conforms();
this.truncated = truncated || validationResultIterator.isTruncated();
}
validationResultIterators = null;
}
} catch (Throwable e) {
logger.warn("Error evaluating lazy validation report", e);
throw e;
}
}
public Model asModel(Model model) {
try {
evaluateLazyAspect();
model.add(getId(), SHACL.CONFORMS, literal(conforms));
model.add(getId(), RDF.TYPE, SHACL.VALIDATION_REPORT);
model.add(getId(), RDF4J.TRUNCATED, BooleanLiteral.valueOf(truncated));
HashSet<Resource> rdfListDedupe = new HashSet<>();
for (ValidationResult result : validationResult) {
model.add(getId(), SHACL.RESULT, result.getId());
result.asModel(model, rdfListDedupe);
}
return model;
} catch (Throwable e) {
logger.warn("Error converting validation report to model", e);
throw e;
}
}
public Model asModel() {
DynamicModel emptyModel = new DynamicModelFactory().createEmptyModel();
emptyModel.setNamespace(SHACL.NS);
emptyModel.setNamespace(RSX.NS);
emptyModel.setNamespace(DASH.NS);
emptyModel.setNamespace(RDF.NS);
emptyModel.setNamespace(RDFS.NS);
emptyModel.setNamespace(OWL.NS);
emptyModel.setNamespace(XSD.NS);
emptyModel.setNamespace(RDF4J.NS);
return asModel(emptyModel);
}
/**
* @return false if the changes violated a SHACL Shape
*/
public boolean conforms() {
evaluateLazyAspect();
return conforms;
}
/**
* @return list of ValidationResult with more information about each violation
*/
public List<ValidationResult> getValidationResult() {
evaluateLazyAspect();
return validationResult;
}
@Override
public String toString() {
StringWriter stringWriter = new StringWriter();
WriterConfig writerConfig = new WriterConfig()
.set(BasicWriterSettings.PRETTY_PRINT, true)
.set(BasicWriterSettings.INLINE_BLANK_NODES, true);
Rio.write(asModel(), stringWriter, RDFFormat.TURTLE, writerConfig);
return stringWriter.toString()
.replaceAll("(?m)^(@prefix)(.*)(\\.)$", "") // remove all lines that are prefix declarations
.trim();
}
public boolean isTruncated() {
evaluateLazyAspect();
return truncated;
}
}