package de.kosit.validationtool.config; import static org.apache.commons.lang3.ObjectUtils.isNotEmpty; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.stream.Collectors; import javax.xml.validation.Schema; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.tuple.Pair; import lombok.AccessLevel; import lombok.Getter; import lombok.RequiredArgsConstructor; import de.kosit.validationtool.impl.ContentRepository; import de.kosit.validationtool.impl.Scenario; import de.kosit.validationtool.impl.Scenario.Transformation; import de.kosit.validationtool.impl.model.Result; import de.kosit.validationtool.model.scenarios.CreateReportType; import de.kosit.validationtool.model.scenarios.DescriptionType; import de.kosit.validationtool.model.scenarios.NamespaceType; import de.kosit.validationtool.model.scenarios.ObjectFactory; import de.kosit.validationtool.model.scenarios.ScenarioType; import de.kosit.validationtool.model.scenarios.ValidateWithSchematron; import de.kosit.validationtool.model.scenarios.ValidateWithXmlSchema; import net.sf.saxon.s9api.XPathExecutable; /** * Builder for {@link Scenario} configuration. * * @author Andreas Penski */ @RequiredArgsConstructor public class ScenarioBuilder implements Builder { private static int nameCount = 0; private static final String DEFAULT_DESCRIPTION = "Dieses Scenario wurde per API erstellt"; private final Map namespaces = new HashMap<>(); private final XPathBuilder matchConfig = new XPathBuilder(); private final XPathBuilder acceptConfig = new XPathBuilder(); @Getter(AccessLevel.PACKAGE) private final String name; private SchemaBuilder schemaBuilder; private final List schematronBuilders = new ArrayList<>(); private ReportBuilder reportBuilder; private String description; @Override public Result build(final ContentRepository repository) { final List errors = new ArrayList<>(); final Scenario scenario = new Scenario(createType()); buildMatch(repository, errors, scenario); buildSchema(repository, errors, scenario); buildSchematron(repository, errors, scenario); buildReport(repository, errors, scenario); buildAccept(repository, errors, scenario); buildNamespaces(scenario); return new Result<>(scenario, errors); } /** * Add a preconfiguration {@link XPathExecutable} to match the scenario * * @param executable the xpath executable * @return this */ public ScenarioBuilder match(final XPathExecutable executable) { this.matchConfig.setExecutable(executable); return this; } /** * Add an xpath expression to match the scenario. You can leverage declared namespaces. * * @param xpath the expression * @return this */ public ScenarioBuilder match(final String xpath) { this.matchConfig.setXpath(xpath); return this; } /** * Declare a namespace to use for match and accept configurations. * * @param prefix the prefix to use * @param uri the uri of this namespace * @return this */ public ScenarioBuilder declareNamespace(final String prefix, final String uri) { this.namespaces.put(prefix, uri); return this; } /** * Add a preconfiguration {@link XPathExecutable} to compute acceptance for the scenario * * @param executable the xpath executable * @return this */ public ScenarioBuilder acceptWith(final XPathExecutable executable) { this.acceptConfig.setExecutable(executable); return this; } /** * Add an xpath expression to compute acceptance for the scenario. You can leverage declared namespaces. * * @param acceptXpath the xpath expresison * @return this */ public ScenarioBuilder acceptWith(final String acceptXpath) { this.acceptConfig.setXpath(acceptXpath); return this; } /** * Add a schematron validation configuration for this scenario. * * @param schematron the schematron configuration * @return this */ public ScenarioBuilder validate(final SchematronBuilder schematron) { if (schematron != null) { this.schematronBuilders.add(schematron); } return this; } /** * Validate matching {@link de.kosit.validationtool.api.Input Inputs} with the specified schema configuration. * * @param schema the schema configuration * @return this */ public ScenarioBuilder validate(final SchemaBuilder schema) { this.schemaBuilder = schema; return this; } /** * Add description for this scenario. This is part of the * {@link de.kosit.validationtool.model.reportInput.CreateReportInput} configuration and can be used while creating the * report * * @param description the description * @return this */ public ScenarioBuilder description(final String description) { this.description = description; return this; } /** * Add a configuration for generating the final report for the {@link de.kosit.validationtool.api.Input}. * * @param reportBuilder the report configuration * @return this */ public ScenarioBuilder with(final ReportBuilder reportBuilder) { this.reportBuilder = reportBuilder; return this; } private static String generateName() { return "manually created scenario " + nameCount++; } private void buildNamespaces(final Scenario scenario) { this.namespaces.putAll(this.acceptConfig.getNamespaces()); this.namespaces.putAll(this.matchConfig.getNamespaces()); final List all = this.namespaces.entrySet().stream().map(e -> { final NamespaceType n = new NamespaceType(); n.setPrefix(e.getKey()); n.setValue(e.getValue()); return n; }).collect(Collectors.toList()); scenario.getConfiguration().getNamespace().addAll(all); } private void buildMatch(final ContentRepository repository, final List errors, final Scenario scenario) { this.matchConfig.setNamespaces(this.namespaces); final Result result = this.matchConfig.build(repository); if (result.isValid()) { scenario.setMatchExecutable(result.getObject()); scenario.getConfiguration().setMatch(this.matchConfig.getXPath()); this.namespaces.putAll(this.matchConfig.getNamespaces()); } else { errors.addAll(result.getErrors()); } } private void buildAccept(final ContentRepository repository, final List errors, final Scenario scenario) { this.acceptConfig.setNamespaces(this.namespaces); final Result result = this.acceptConfig.build(repository); if (result.isValid()) { scenario.setAcceptExecutable(result.getObject()); scenario.getConfiguration().setAcceptMatch(this.acceptConfig.getXPath()); this.namespaces.putAll(this.acceptConfig.getNamespaces()); } else { errors.addAll(result.getErrors()); } } private void buildReport(final ContentRepository repository, final List errors, final Scenario scenario) { if (this.reportBuilder == null) { errors.add("Must supply report configuration"); } else { final Result, String> result = this.reportBuilder.build(repository); if (result.isValid()) { scenario.setReportTransformation(result.getObject().getRight()); scenario.getConfiguration().setCreateReport(result.getObject().getLeft()); } else { errors.addAll(result.getErrors()); } } } private void buildSchematron(final ContentRepository repository, final List errors, final Scenario scenario) { this.schematronBuilders.forEach(e -> { final Result, String> result = e.build(repository); if (result.isValid()) { scenario.getConfiguration().getValidateWithSchematron().add(result.getObject().getLeft()); scenario.getSchematronValidations().add(result.getObject().getRight()); } else { errors.addAll(result.getErrors()); } }); } private void buildSchema(final ContentRepository repository, final List errors, final Scenario scenario) { if (this.schemaBuilder == null) { errors.add("Must supply schema for validation"); } else { final Result, String> result = this.schemaBuilder.build(repository); if (result.isValid()) { scenario.setSchema(result.getObject().getRight()); scenario.getConfiguration().setValidateWithXmlSchema(result.getObject().getLeft()); } else { errors.addAll(result.getErrors()); } } } private ScenarioType createType() { final ScenarioType type = new ScenarioType(); type.setName(isNotEmpty(this.name) ? this.name : generateName()); final DescriptionType desc = new DescriptionType(); desc.getPOrOlOrUl() .add(new ObjectFactory().createDescriptionTypeP(StringUtils.defaultIfBlank(this.description, DEFAULT_DESCRIPTION))); type.setDescription(desc); return type; } }