diff --git a/src/main/java/de/kosit/validationtool/api/Result.java b/src/main/java/de/kosit/validationtool/api/Result.java index 6d9eef7..5eca1f2 100644 --- a/src/main/java/de/kosit/validationtool/api/Result.java +++ b/src/main/java/de/kosit/validationtool/api/Result.java @@ -1,11 +1,9 @@ package de.kosit.validationtool.api; +import java.util.List; + import org.w3c.dom.Document; -import lombok.Getter; -import lombok.RequiredArgsConstructor; - -import net.sf.saxon.dom.NodeOverNodeInfo; import net.sf.saxon.s9api.XdmNode; /** @@ -13,32 +11,33 @@ import net.sf.saxon.s9api.XdmNode; * * @author Andreas Penski */ -@Getter -@RequiredArgsConstructor -public class Result { + +public interface Result { /** Der generierte Report. */ - private final XdmNode report; + XdmNode getReport(); /** Das evaluierte Ergebnis. */ - private final AcceptRecommendation acceptRecommendation; + AcceptRecommendation getAcceptRecommendation(); /** * Gibt den Report als W3C-{@link Document} zurück. * * @return der Report */ - public Document getReportDocument() { - return (Document) NodeOverNodeInfo.wrap(getReport().getUnderlyingNode()); - } + Document getReportDocument(); /** * Schnellzugriff auf die Empfehlung zur Weiterverarbeitung des Dokuments. * * @return true wenn {@link AcceptRecommendation#ACCEPTABLE} */ - public boolean isAcceptable() { - return AcceptRecommendation.ACCEPTABLE.equals(acceptRecommendation); - } + boolean isAcceptable(); + + /** + * Gibt eine Liste mit gefundenen Schema-Validation-Fehler zurück. Diese Liste ist leer, wenn keine Fehler gefunden + * wurden. + */ + List getSchemaViolations(); } diff --git a/src/main/java/de/kosit/validationtool/api/XmlError.java b/src/main/java/de/kosit/validationtool/api/XmlError.java new file mode 100644 index 0000000..962b61e --- /dev/null +++ b/src/main/java/de/kosit/validationtool/api/XmlError.java @@ -0,0 +1,43 @@ +package de.kosit.validationtool.api; + +/** + * Fehlerobjekt für die Bereitstellung von Fehlern aus der internen Verarbeitung, bspw. Schema-Validation-Fehler. + * + * @author Andreas Penski + */ +public interface XmlError { + + /** + * Gibt die Fehlermeldung zurück. + * + * @return die Fehlermeldung + */ + String getMessage(); + + /** + * Zeigt den Schweregrad der Fehlermeldung an + * + * @return der Schweregrad + * @see Severity + */ + Severity getSeverity(); + + /** + * Gibt optional eine Zeilennummer an, aus der der Fehler resultiert. + * + * @return die Zeitelnnummer + */ + Integer getRowNumber(); + + /** + * Gibt optional eine Spaltennummer an, aus der der Fehler resultiert. + * + * @return die Spaltennummer + */ + Integer getColumnNumber(); + + enum Severity { + SEVERITY_WARNING, SEVERITY_ERROR, SEVERITY_FATAL_ERROR; + } + +} diff --git a/src/main/java/de/kosit/validationtool/cmd/ExtractHtmlContentAction.java b/src/main/java/de/kosit/validationtool/cmd/ExtractHtmlContentAction.java index 9124649..41f4f49 100644 --- a/src/main/java/de/kosit/validationtool/cmd/ExtractHtmlContentAction.java +++ b/src/main/java/de/kosit/validationtool/cmd/ExtractHtmlContentAction.java @@ -20,20 +20,17 @@ package de.kosit.validationtool.cmd; import java.nio.file.Path; -import java.util.HashMap; -import java.util.Map; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import de.kosit.validationtool.impl.ContentRepository; +import de.kosit.validationtool.impl.HtmlExtraction; import de.kosit.validationtool.impl.tasks.CheckAction; import net.sf.saxon.s9api.QName; import net.sf.saxon.s9api.SaxonApiException; import net.sf.saxon.s9api.Serializer; -import net.sf.saxon.s9api.XPathExecutable; -import net.sf.saxon.s9api.XPathSelector; import net.sf.saxon.s9api.XdmItem; import net.sf.saxon.s9api.XdmNode; @@ -48,49 +45,38 @@ class ExtractHtmlContentAction implements CheckAction { private static final QName NAME_ATTRIBUTE = new QName("data-report-type"); - private final ContentRepository repository; - private final Path outputDirectory; - private XPathExecutable executable; + private HtmlExtraction htmlExtraction; - @Override - public void check(Bag results) { - try { - final XPathSelector selector = getSelector(); - final XdmNode xdmSource = results.getReport(); - selector.setContextItem(xdmSource); - selector.forEach(m -> print(results.getName(), m)); + private ContentRepository repository; - } catch (SaxonApiException e) { - throw new IllegalStateException("Can not extract html content", e); - } + public ExtractHtmlContentAction(final ContentRepository repository, final Path outputDirectory) { + this.outputDirectory = outputDirectory; + this.htmlExtraction = new HtmlExtraction(repository); + this.repository = repository; } - private void print(String origName, XdmItem xdmItem) { - XdmNode node = (XdmNode) xdmItem; + @Override + public void check(final Bag results) { + this.htmlExtraction.extract(results.getReport()).forEach(i -> print(results.getName(), i)); + } + + private void print(final String origName, final XdmItem xdmItem) { + final XdmNode node = (XdmNode) xdmItem; final String name = origName + "-" + node.getAttributeValue(NAME_ATTRIBUTE); - final Path file = outputDirectory.resolve(name + ".html"); - final Serializer serializer = repository.getProcessor().newSerializer(file.toFile()); + final Path file = this.outputDirectory.resolve(name + ".html"); + final Serializer serializer = this.repository.getProcessor().newSerializer(file.toFile()); try { log.info("Writing report html '{}' to {}", name, file.toAbsolutePath()); serializer.serializeNode(node); - } catch (SaxonApiException e) { + } catch (final SaxonApiException e) { log.info("Error extracting html content to {}", file.toAbsolutePath(), e); } } - private XPathSelector getSelector() { - if (executable == null) { - Map ns = new HashMap<>(); - ns.put("html", "http://www.w3.org/1999/xhtml"); - executable = repository.createXPath("//html:html", ns); - } - return executable.load(); - } - @Override - public boolean isSkipped(Bag results) { + public boolean isSkipped(final Bag results) { if (results.getReport() == null) { log.warn("Can not extract html content. No report document found"); return true; diff --git a/src/main/java/de/kosit/validationtool/impl/CollectingErrorEventHandler.java b/src/main/java/de/kosit/validationtool/impl/CollectingErrorEventHandler.java index 580ffb1..d88c940 100644 --- a/src/main/java/de/kosit/validationtool/impl/CollectingErrorEventHandler.java +++ b/src/main/java/de/kosit/validationtool/impl/CollectingErrorEventHandler.java @@ -51,26 +51,26 @@ public class CollectingErrorEventHandler implements ValidationEventHandler, Erro private static final int DEFAULT_ABORT_COUNT = 50; - private Collection errors = new ArrayList<>(); + private final Collection errors = new ArrayList<>(); - private int stopProcessCount = DEFAULT_ABORT_COUNT; + private final int stopProcessCount = DEFAULT_ABORT_COUNT; - private static XMLSyntaxError createError(XMLSyntaxErrorSeverity severity, String message) { - XMLSyntaxError e = new XMLSyntaxError(); + private static XMLSyntaxError createError(final XMLSyntaxErrorSeverity severity, final String message) { + final XMLSyntaxError e = new XMLSyntaxError(); e.setSeverity(severity); e.setMessage(message); return e; } - private static XMLSyntaxError createError(XMLSyntaxErrorSeverity severity, SAXParseException exception) { - XMLSyntaxError e = createError(severity, exception.getMessage()); + private static XMLSyntaxError createError(final XMLSyntaxErrorSeverity severity, final SAXParseException exception) { + final XMLSyntaxError e = createError(severity, exception.getMessage()); e.setRowNumber(exception.getLineNumber()); e.setColumnNumber(exception.getColumnNumber()); return e; } - private static XMLSyntaxError createError(XMLSyntaxErrorSeverity severity, TransformerException exception) { - XMLSyntaxError e = createError(severity, exception.getMessage()); + private static XMLSyntaxError createError(final XMLSyntaxErrorSeverity severity, final TransformerException exception) { + final XMLSyntaxError e = createError(severity, exception.getMessage()); if (exception.getLocator() != null) { e.setRowNumber(exception.getLocator().getLineNumber()); e.setColumnNumber(exception.getLocator().getColumnNumber()); @@ -78,7 +78,7 @@ public class CollectingErrorEventHandler implements ValidationEventHandler, Erro return e; } - private static XMLSyntaxErrorSeverity translateSeverity(int severity) { + private static XMLSyntaxErrorSeverity translateSeverity(final int severity) { switch (severity) { case ValidationEvent.WARNING: return XMLSyntaxErrorSeverity.SEVERITY_WARNING; @@ -92,12 +92,12 @@ public class CollectingErrorEventHandler implements ValidationEventHandler, Erro } @Override - public boolean handleEvent(ValidationEvent event) { - XMLSyntaxError e = createError(translateSeverity(event.getSeverity()), event.getMessage()); + public boolean handleEvent(final ValidationEvent event) { + final XMLSyntaxError e = createError(translateSeverity(event.getSeverity()), event.getMessage()); e.setColumnNumber(event.getLocator().getColumnNumber()); e.setRowNumber(event.getLocator().getLineNumber()); - errors.add(e); - return stopProcessCount != errors.size(); + this.errors.add(e); + return this.stopProcessCount != this.errors.size(); } /** @@ -106,7 +106,7 @@ public class CollectingErrorEventHandler implements ValidationEventHandler, Erro * @return true wenn mindestens ein Fehler vorhanden ist. */ public boolean hasErrors() { - return hasEvents() && errors.stream().anyMatch(e -> e.getSeverity() != XMLSyntaxErrorSeverity.SEVERITY_WARNING); + return hasEvents() && this.errors.stream().anyMatch(e -> e.getSeverityCode() != XMLSyntaxErrorSeverity.SEVERITY_WARNING); } /** @@ -115,27 +115,27 @@ public class CollectingErrorEventHandler implements ValidationEventHandler, Erro * @return true wenn mindestens ein Validierungsereignis aufgetreten ist */ public boolean hasEvents() { - return !errors.isEmpty(); + return !this.errors.isEmpty(); } @Override - public void warning(SAXParseException exception) throws SAXException { - errors.add(createError(XMLSyntaxErrorSeverity.SEVERITY_WARNING, exception)); + public void warning(final SAXParseException exception) throws SAXException { + this.errors.add(createError(XMLSyntaxErrorSeverity.SEVERITY_WARNING, exception)); } @Override - public void error(SAXParseException exception) throws SAXException { - errors.add(createError(XMLSyntaxErrorSeverity.SEVERITY_ERROR, exception)); + public void error(final SAXParseException exception) throws SAXException { + this.errors.add(createError(XMLSyntaxErrorSeverity.SEVERITY_ERROR, exception)); } @Override - public void fatalError(SAXParseException exception) throws SAXException { - errors.add(createError(XMLSyntaxErrorSeverity.SEVERITY_FATAL_ERROR, exception)); + public void fatalError(final SAXParseException exception) throws SAXException { + this.errors.add(createError(XMLSyntaxErrorSeverity.SEVERITY_FATAL_ERROR, exception)); } @Override - public void message(XdmNode content, boolean terminate, SourceLocator locator) { - XMLSyntaxError e = new XMLSyntaxError(); + public void message(final XdmNode content, final boolean terminate, final SourceLocator locator) { + final XMLSyntaxError e = new XMLSyntaxError(); if (locator != null) { e.setColumnNumber(locator.getColumnNumber()); e.setRowNumber(locator.getLineNumber()); @@ -145,24 +145,25 @@ public class CollectingErrorEventHandler implements ValidationEventHandler, Erro } @Override - public void warning(TransformerException exception) throws TransformerException { - errors.add(createError(XMLSyntaxErrorSeverity.SEVERITY_WARNING, exception)); + public void warning(final TransformerException exception) throws TransformerException { + this.errors.add(createError(XMLSyntaxErrorSeverity.SEVERITY_WARNING, exception)); } @Override - public void error(TransformerException exception) throws TransformerException { - errors.add(createError(XMLSyntaxErrorSeverity.SEVERITY_ERROR, exception)); + public void error(final TransformerException exception) throws TransformerException { + this.errors.add(createError(XMLSyntaxErrorSeverity.SEVERITY_ERROR, exception)); } @Override - public void fatalError(TransformerException exception) throws TransformerException { - errors.add(createError(XMLSyntaxErrorSeverity.SEVERITY_FATAL_ERROR, exception)); + public void fatalError(final TransformerException exception) throws TransformerException { + this.errors.add(createError(XMLSyntaxErrorSeverity.SEVERITY_FATAL_ERROR, exception)); } public String getErrorDescription() { final StringJoiner joiner = new StringJoiner("\n"); - errors.forEach(e -> joiner - .add(e.getSeverity().value() + " " + e.getMessage() + " At row " + e.getRowNumber() + " at pos " + e.getColumnNumber())); + this.errors.forEach(e -> joiner + .add(e.getSeverityCode().value() + " " + e.getMessage() + " At row " + e.getRowNumber() + " at pos " + + e.getColumnNumber())); return joiner.toString(); } } \ No newline at end of file diff --git a/src/main/java/de/kosit/validationtool/impl/DefaultCheck.java b/src/main/java/de/kosit/validationtool/impl/DefaultCheck.java index e540b92..52afd11 100644 --- a/src/main/java/de/kosit/validationtool/impl/DefaultCheck.java +++ b/src/main/java/de/kosit/validationtool/impl/DefaultCheck.java @@ -20,6 +20,7 @@ package de.kosit.validationtool.impl; import java.util.ArrayList; +import java.util.Collection; import java.util.List; import lombok.Getter; @@ -29,7 +30,9 @@ import de.kosit.validationtool.api.Check; import de.kosit.validationtool.api.CheckConfiguration; import de.kosit.validationtool.api.Input; import de.kosit.validationtool.api.Result; +import de.kosit.validationtool.api.XmlError; 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.CreateReportAction; import de.kosit.validationtool.impl.tasks.DocumentParseAction; @@ -41,6 +44,7 @@ import de.kosit.validationtool.model.reportInput.CreateReportInput; import de.kosit.validationtool.model.reportInput.DocumentIdentificationType; import de.kosit.validationtool.model.reportInput.EngineType; import de.kosit.validationtool.model.reportInput.ProcessingError; +import de.kosit.validationtool.model.reportInput.XMLSyntaxError; import net.sf.saxon.s9api.Processor; @@ -125,7 +129,20 @@ public class DefaultCheck implements Check { } t.setFinished(true); log.info("Finished check of {} in {}ms\n", t.getInput().getName(), System.currentTimeMillis() - started); - return new Result(t.getReport(), t.getAcceptStatus()); + return createResult(t); + } + + private Result createResult(final Bag t) { + final DefaultResult result = new DefaultResult(t.getReport(), t.getAcceptStatus(), this.contentRepository); + if (t.getSchemaValidationResult() != null) { + result.setSchemaViolations(convertErrors(t.getSchemaValidationResult().getErrors())); + } + return result; + } + + private static List convertErrors(final Collection errors) { + // noinspection unchecked + return (List) (List) errors; } private static void createDocumentIdentification(final CheckAction.Bag transporter) { diff --git a/src/main/java/de/kosit/validationtool/impl/DefaultResult.java b/src/main/java/de/kosit/validationtool/impl/DefaultResult.java new file mode 100644 index 0000000..aaa2712 --- /dev/null +++ b/src/main/java/de/kosit/validationtool/impl/DefaultResult.java @@ -0,0 +1,97 @@ +package de.kosit.validationtool.impl; + +import java.io.StringWriter; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +import lombok.AccessLevel; +import lombok.Getter; +import lombok.Setter; + +import de.kosit.validationtool.api.AcceptRecommendation; +import de.kosit.validationtool.api.Result; +import de.kosit.validationtool.api.XmlError; + +import net.sf.saxon.dom.NodeOverNodeInfo; +import net.sf.saxon.s9api.SaxonApiException; +import net.sf.saxon.s9api.Serializer; +import net.sf.saxon.s9api.XdmNode; + +/** + * @author Andreas Penski + */ + +public class DefaultResult implements Result { + + /** Der generierte Report. */ + @Getter + private final XdmNode report; + + /** Das evaluierte Ergebnis. */ + @Getter + private final AcceptRecommendation acceptRecommendation; + + private final HtmlExtraction htmlExtraction; + + @Setter(AccessLevel.PACKAGE) + @Getter + private List schemaViolations = new ArrayList<>(); + + public DefaultResult(final XdmNode report, final AcceptRecommendation recommendation, final ContentRepository repository) { + this.report = report; + this.acceptRecommendation = recommendation; + this.htmlExtraction = new HtmlExtraction(repository); + } + + /** + * Gibt den Report als W3C-{@link Document} zurück. + * + * @return der Report + */ + @Override + public Document getReportDocument() { + return (Document) NodeOverNodeInfo.wrap(getReport().getUnderlyingNode()); + } + + /** + * Schnellzugriff auf die Empfehlung zur Weiterverarbeitung des Dokuments. + * + * @return true wenn {@link AcceptRecommendation#ACCEPTABLE} + */ + @Override + public boolean isAcceptable() { + return AcceptRecommendation.ACCEPTABLE.equals(this.acceptRecommendation); + } + + public List extractHtmlAsString() { + return extractHtml().stream().map(this::convertToString).collect(Collectors.toList()); + } + + private String convertToString(final XdmNode element) { + try { + final StringWriter writer = new StringWriter(); + final Serializer serializer = ObjectFactory.createProcessor().newSerializer(writer); + serializer.serializeNode(element); + return writer.toString(); + } catch (SaxonApiException e) { + throw new IllegalStateException("Can not convert to string", e); + } + } + + public List extractHtmlAsElement() { + return extractHtml().stream().map(DefaultResult::convertToElement).collect(Collectors.toList()); + } + + private static Element convertToElement(final XdmNode xdmItem) { + return (Element) NodeOverNodeInfo.wrap(xdmItem.getUnderlyingNode()); + } + + public List extractHtml() { + return this.htmlExtraction.extract(getReport()); + } + +} diff --git a/src/main/java/de/kosit/validationtool/impl/HtmlExtraction.java b/src/main/java/de/kosit/validationtool/impl/HtmlExtraction.java new file mode 100644 index 0000000..3506976 --- /dev/null +++ b/src/main/java/de/kosit/validationtool/impl/HtmlExtraction.java @@ -0,0 +1,49 @@ +package de.kosit.validationtool.impl; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import lombok.RequiredArgsConstructor; + +import net.sf.saxon.s9api.SaxonApiException; +import net.sf.saxon.s9api.XPathExecutable; +import net.sf.saxon.s9api.XPathSelector; +import net.sf.saxon.s9api.XdmItem; +import net.sf.saxon.s9api.XdmNode; + +/** + * @author Andreas Penski + */ +@RequiredArgsConstructor +public class HtmlExtraction { + + private final ContentRepository repository; + + private XPathExecutable executable; + + public List extract(XdmNode xdmSource) { + try { + final XPathSelector selector = getSelector(); + selector.setContextItem(xdmSource); + return selector.stream().map(this::castToNode).collect(Collectors.toList()); + + } catch (SaxonApiException e) { + throw new IllegalStateException("Can not extract html content", e); + } + } + + private XdmNode castToNode(final XdmItem xdmItem) { + return (XdmNode) xdmItem; + } + + private XPathSelector getSelector() { + if (executable == null) { + Map ns = new HashMap<>(); + ns.put("html", "http://www.w3.org/1999/xhtml"); + executable = repository.createXPath("//html:html", ns); + } + return executable.load(); + } +} diff --git a/src/main/java/de/kosit/validationtool/impl/model/BaseXMLSyntaxError.java b/src/main/java/de/kosit/validationtool/impl/model/BaseXMLSyntaxError.java index d248332..fd83d67 100644 --- a/src/main/java/de/kosit/validationtool/impl/model/BaseXMLSyntaxError.java +++ b/src/main/java/de/kosit/validationtool/impl/model/BaseXMLSyntaxError.java @@ -21,6 +21,7 @@ package de.kosit.validationtool.impl.model; import org.slf4j.Logger; +import de.kosit.validationtool.api.XmlError; import de.kosit.validationtool.model.reportInput.XMLSyntaxErrorSeverity; /** @@ -29,17 +30,17 @@ import de.kosit.validationtool.model.reportInput.XMLSyntaxErrorSeverity; * * @author Andreas Penski */ -public abstract class BaseXMLSyntaxError { +public abstract class BaseXMLSyntaxError implements XmlError { /** * Logged den Syntax-Fehler über einen definierten Logger. * * @param logger der Logger */ - public void log(Logger logger) { - String msgTemplate = "{} At row {} at pos {}"; - Object[] params = { getMessage(), getRowNumber(), getColumnNumber() }; - if (getSeverity() == XMLSyntaxErrorSeverity.SEVERITY_WARNING) { + public void log(final Logger logger) { + final String msgTemplate = "{} At row {} at pos {}"; + final Object[] params = { getMessage(), getRowNumber(), getColumnNumber() }; + if (getSeverityCode() == XMLSyntaxErrorSeverity.SEVERITY_WARNING) { logger.warn(msgTemplate, params); } else { logger.error(msgTemplate, params); @@ -57,6 +58,7 @@ public abstract class BaseXMLSyntaxError { * * @return Spalte des Fehlers */ + @Override public abstract Integer getColumnNumber(); /** @@ -64,6 +66,7 @@ public abstract class BaseXMLSyntaxError { * * @return Zeile des Fehlers */ + @Override public abstract Integer getRowNumber(); /** @@ -71,6 +74,7 @@ public abstract class BaseXMLSyntaxError { * * @return Fehlermeldung */ + @Override public abstract String getMessage(); /** @@ -78,5 +82,11 @@ public abstract class BaseXMLSyntaxError { * * @return severity */ - public abstract XMLSyntaxErrorSeverity getSeverity(); + public abstract XMLSyntaxErrorSeverity getSeverityCode(); + + @Override + public Severity getSeverity() { + final XMLSyntaxErrorSeverity code = getSeverityCode(); + return code != null ? Severity.valueOf(code.name()) : null; + } } diff --git a/src/main/model/xsd/createReportInput.xsd b/src/main/model/xsd/createReportInput.xsd index 0323008..07bf634 100644 --- a/src/main/model/xsd/createReportInput.xsd +++ b/src/main/model/xsd/createReportInput.xsd @@ -68,7 +68,7 @@ - + diff --git a/src/test/java/de/kosit/validationtool/impl/SimpleScenarioCheck.java b/src/test/java/de/kosit/validationtool/impl/SimpleScenarioCheck.java index 7f42f2c..8310c22 100644 --- a/src/test/java/de/kosit/validationtool/impl/SimpleScenarioCheck.java +++ b/src/test/java/de/kosit/validationtool/impl/SimpleScenarioCheck.java @@ -42,6 +42,7 @@ public class SimpleScenarioCheck { final Result result = this.implementation.checkInput(InputFactory.read(Simple.INVALID.toURL())); assertThat(result).isNotNull(); assertThat(result.getAcceptRecommendation()).isEqualTo(AcceptRecommendation.REJECT); + assertThat(result.getSchemaViolations()).isNotEmpty(); } @Test