mirror of
https://github.com/itplr-kosit/validator.git
synced 2026-05-25 16:55:39 +00:00
178 lines
7.9 KiB
Java
178 lines
7.9 KiB
Java
/*
|
|
* Copyright 2017-2022 Koordinierungsstelle für IT-Standards (KoSIT)
|
|
*
|
|
* Licensed 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 de.kosit.validationtool.impl;
|
|
|
|
import static de.kosit.validationtool.impl.DateFactory.createTimestamp;
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.Arrays;
|
|
import java.util.Collections;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.Optional;
|
|
import java.util.stream.Collectors;
|
|
|
|
import org.oclc.purl.dsdl.svrl.FailedAssert;
|
|
import org.oclc.purl.dsdl.svrl.SchematronOutput;
|
|
|
|
import de.kosit.validationtool.api.Check;
|
|
import de.kosit.validationtool.api.Configuration;
|
|
import de.kosit.validationtool.api.Input;
|
|
import de.kosit.validationtool.api.Result;
|
|
import de.kosit.validationtool.api.XmlError;
|
|
import de.kosit.validationtool.impl.model.CustomFailedAssert;
|
|
import de.kosit.validationtool.impl.tasks.CheckAction;
|
|
import de.kosit.validationtool.impl.tasks.CheckAction.Bag;
|
|
import de.kosit.validationtool.impl.tasks.ComputeAcceptanceAction;
|
|
import de.kosit.validationtool.impl.tasks.CreateDocumentIdentificationAction;
|
|
import de.kosit.validationtool.impl.tasks.CreateReportAction;
|
|
import de.kosit.validationtool.impl.tasks.DocumentParseAction;
|
|
import de.kosit.validationtool.impl.tasks.ScenarioSelectionAction;
|
|
import de.kosit.validationtool.impl.tasks.SchemaValidationAction;
|
|
import de.kosit.validationtool.impl.tasks.SchematronValidationAction;
|
|
import de.kosit.validationtool.impl.tasks.ValidateReportInputAction;
|
|
import de.kosit.validationtool.impl.xml.ProcessorProvider;
|
|
import de.kosit.validationtool.model.reportInput.CreateReportInput;
|
|
import de.kosit.validationtool.model.reportInput.EngineType;
|
|
import de.kosit.validationtool.model.reportInput.XMLSyntaxError;
|
|
import de.kosit.validationtool.model.scenarios.ErrorLevelType;
|
|
import de.kosit.validationtool.model.scenarios.ScenarioType;
|
|
import lombok.Getter;
|
|
import lombok.extern.slf4j.Slf4j;
|
|
import net.sf.saxon.s9api.Processor;
|
|
|
|
/**
|
|
* The reference implementation for the validation process. After initialisation, instances are threadsafe and should be
|
|
* reused since initializing saxon runtime objects is an rather heavyweight process.
|
|
*
|
|
* @author Andreas Penski
|
|
*/
|
|
@Slf4j
|
|
public class DefaultCheck implements Check {
|
|
|
|
@Getter
|
|
private final ConversionService conversionService;
|
|
|
|
@Getter
|
|
private final List<Configuration> configuration;
|
|
|
|
@Getter
|
|
private final List<CheckAction> checkSteps;
|
|
|
|
@Getter
|
|
private final Processor processor;
|
|
|
|
public DefaultCheck(final Configuration... configuration) {
|
|
this(ProcessorProvider.getProcessor(), configuration);
|
|
}
|
|
|
|
/**
|
|
* Creates a new instance for the {@link Configuration}.
|
|
*
|
|
* @param configuration the Configuration
|
|
*/
|
|
public DefaultCheck(final Processor processor, final Configuration... configuration) {
|
|
this.configuration = Arrays.asList(configuration);
|
|
this.processor = processor;
|
|
this.conversionService = new ConversionService();
|
|
|
|
this.checkSteps = new ArrayList<>();
|
|
this.checkSteps.add(new DocumentParseAction(processor));
|
|
this.checkSteps.add(new CreateDocumentIdentificationAction());
|
|
this.checkSteps.add(new ScenarioSelectionAction(new ScenarioRepository(configuration)));
|
|
this.checkSteps.add(new SchemaValidationAction(processor));
|
|
this.checkSteps.add(new SchematronValidationAction(this.conversionService));
|
|
this.checkSteps.add(new ValidateReportInputAction(this.conversionService, SchemaProvider.getReportInputSchema()));
|
|
this.checkSteps.add(new CreateReportAction(processor, this.conversionService));
|
|
this.checkSteps.add(new ComputeAcceptanceAction());
|
|
}
|
|
|
|
protected static CreateReportInput createReport() {
|
|
final CreateReportInput type = new CreateReportInput();
|
|
final EngineType e = new EngineType();
|
|
e.setName(EngineInformation.getName() + " " + EngineInformation.getVersion());
|
|
type.setEngine(e);
|
|
type.setTimestamp(createTimestamp());
|
|
type.setFrameworkVersion(EngineInformation.getFrameworkVersion());
|
|
return type;
|
|
}
|
|
|
|
protected boolean isSuccessful(final Map<String, Result> results) {
|
|
return results.entrySet().stream().allMatch(e -> e.getValue().isAcceptable());
|
|
}
|
|
|
|
@Override
|
|
public Result checkInput(final Input input) {
|
|
final CheckAction.Bag t = new CheckAction.Bag(input, createReport());
|
|
return runCheckInternal(t);
|
|
}
|
|
|
|
protected Result runCheckInternal(final CheckAction.Bag t) {
|
|
final long started = System.currentTimeMillis();
|
|
log.info("Checking content of {}", t.getInput().getName());
|
|
for (final CheckAction action : this.checkSteps) {
|
|
final long start = System.currentTimeMillis();
|
|
if (!action.isSkipped(t)) {
|
|
action.check(t);
|
|
}
|
|
log.debug("Step {} finished in {}ms", action.getClass().getSimpleName(), System.currentTimeMillis() - start);
|
|
}
|
|
t.setFinished(true);
|
|
log.info("Finished check of {} in {}ms\n", t.getInput().getName(), System.currentTimeMillis() - started);
|
|
return createResult(t);
|
|
}
|
|
|
|
private Result createResult(final Bag t) {
|
|
final DefaultResult result = new DefaultResult(t.getReport(), t.getAcceptStatus(), new HtmlExtractor(this.processor));
|
|
result.setWellformed(t.getParserResult().isValid());
|
|
result.setReportInput(t.getReportInput());
|
|
if (t.getSchemaValidationResult() != null) {
|
|
result.setSchemaViolations(convertErrors(t.getSchemaValidationResult().getErrors()));
|
|
}
|
|
result.setProcessingSuccessful(!t.isStopped() && t.isFinished());
|
|
result.setSchematronResult(t.getReportInput().getValidationResultsSchematron().stream().filter(e -> e.getResults() != null)
|
|
.map(e -> e.getResults().getSchematronOutput()).collect(Collectors.toList()));
|
|
|
|
result.setCustomFailedAsserts(buildCustomFailedAssertsList(t, result.getSchematronResult()));
|
|
|
|
return result;
|
|
}
|
|
|
|
private List<CustomFailedAssert> buildCustomFailedAssertsList(final Bag t, final List<SchematronOutput> schematronResult) {
|
|
// Get Map of Assertion ID to custom error levels for the current scenario
|
|
final Map<String, ErrorLevelType> customLevels = Optional.ofNullable(t.getScenarioSelectionResult())
|
|
.map(de.kosit.validationtool.impl.model.Result::getObject).map(Scenario::getConfiguration)
|
|
.map(ScenarioType::getCreateReport)
|
|
.map(r -> r.getCustomLevel().stream()
|
|
.flatMap(customLevel -> customLevel.getValue().stream().map(id -> Map.entry(id, customLevel.getLevel())))
|
|
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)))
|
|
.orElse(Collections.emptyMap());
|
|
|
|
// Now check all failed assertions of all schematron validations if they contain a failed assertion with one of
|
|
// the changed IDs
|
|
return schematronResult.stream().flatMap(x -> x.getActivePatternAndFiredRuleAndFailedAssert().stream())
|
|
.filter(FailedAssert.class::isInstance).map(FailedAssert.class::cast).filter(fa -> customLevels.containsKey(fa.getId()))
|
|
.map(fa -> new CustomFailedAssert(fa, customLevels.get(fa.getId()))).collect(Collectors.toList());
|
|
}
|
|
|
|
@SuppressWarnings("unchecked")
|
|
private static List<XmlError> convertErrors(final List<XMLSyntaxError> errors) {
|
|
// noinspection unchecked
|
|
return (List<XmlError>) (List<?>) errors;
|
|
}
|
|
|
|
}
|