diff --git a/libraryBasic/src/test/java/org/mustangproject/ZUGFeRD/ZF2InvoiceImporterTest.java b/libraryBasic/src/test/java/org/mustangproject/ZUGFeRD/ZF2InvoiceImporterTest.java index 12ef39d3..26098f4e 100644 --- a/libraryBasic/src/test/java/org/mustangproject/ZUGFeRD/ZF2InvoiceImporterTest.java +++ b/libraryBasic/src/test/java/org/mustangproject/ZUGFeRD/ZF2InvoiceImporterTest.java @@ -39,7 +39,7 @@ import junit.framework.TestSuite; @FixMethodOrder(MethodSorters.NAME_ASCENDING) public class ZF2InvoiceImporterTest extends TestCase { - final String TARGET_PDF = "./target/testout-ZF2New.pdf"; + final String TARGET_PDF = "./target/testout-ZF2new.pdf"; public void testInvoiceImport() { diff --git a/libraryExtended/ZUV-Architektur.graphml b/libraryExtended/ZUV-Architektur.graphml new file mode 100644 index 00000000..f1e1e09d --- /dev/null +++ b/libraryExtended/ZUV-Architektur.graphml @@ -0,0 +1,993 @@ + + + + + + + + + + + + + + + + + + + + + + + PDF+XML + + + + + + + + + + + + + + + + + + + ZUGFeRD + + + + + + + + + + Folder 2 + + + + + + + + + + + + + + + + Schematron files + + + + + + + + + + + + + + + + Schema files + + + + + + + + + + + + + + + + Codelist XMLs + + + + + + + + + + + + + + + + + + + + + EN16931 + + + + + + + + + + Folder 3 + + + + + + + + + + + + + + + + CEF Codelist Excel + + + + + + + + + + + + + + + + CEN Schematron + + + + + + + + + + + + + + + + + + + + + UN/CEFACT + + + + + + + + + + Folder 4 + + + + + + + + + + + + + + + + Schema files + + + + + + + + + + + + + + + + + + XML Report + + + + + + + + + + + + + + + + + + + ZUV + + + + + + + + + + Folder 5 + + + + + + + + + + + + + + + + PH-Schematron + + + + + + + + + + + + + + + + XML + + + + + + + + + + + + + + + + Metadata + + + + + + + + + + + + + + + + Metadata check + + + + + + + + + + + + + + + + XSLT files + + + + + + + + + + + + + + + + XSLT files + + + + + + + + + + + + + + + + Additional data + + + + + + + + + + + + + + + + Additional data check + + + + + + + + + + + + + + + + PH-Schematron + + + + + + + + + + + + + + + + + + + Mustangproject + + + + + + + + + + Folder 1 + + + + + + + + + + + + + + + + PDFBox + + + + + + + + + + + + + + + + Mustang + + + + + + + + + + + + + + + + + + + + + VeraPDF + + + + + + + + + + Folder 6 + + + + + + + + + + + + + + + + VeraPDF + + + + + + + + + + + + + + + + + + Schema check + + + + + + + + + + + + + + + + XSLT files + + + + + + + + + + + + + + + + PH-Schematron + + + + + + + + + + + + + + + + + + + + + + XRechnung + + + + + + + + + + Folder 7 + + + + + + + + + + + + + + + + + Schematron + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libraryExtended/ZUV-Architektur.svg b/libraryExtended/ZUV-Architektur.svg new file mode 100644 index 00000000..f3eda531 --- /dev/null +++ b/libraryExtended/ZUV-Architektur.svg @@ -0,0 +1,1117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + PDF+XML + + + + + + + + + ZUGFeRD + + + + + + + + + + + + + + + + Schematron files + + + + + + + + + + + + + + + Schema files + + + + + + + + + + + + + + + Codelist XMLs + + + + + + + EN16931 + + + + + + + + + + + + + + + + CEF Codelist Excel + + + + + + + + + + + + + + + CEN Schematron + + + + + + + + + + + UN/CEFACT + + + + + + + + + + + + + + + + Schema files + + + + + + + + + XML Report + + + + + + + + + + + ZUV + + + + + + + + + + + + + + + + PH-Schematron + + + + + + + + + + + + + + + XML + + + + + + + + + + + + + + + Metadata + + + + + + + + + + + + + + + Metadata check + + + + + + + + + + + + + + + XSLT files + + + + + + + + + + + + + + + XSLT files + + + + + + + + + + + + + + + Additional data + + + + + + + + + + + + + + + Additional data check + + + + + + + + + + + + + + + PH-Schematron + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Mustangproject + + + + + + + + + + + + + + + + PDFBox + + + + + + + + + + + + + + + Mustang + + + + + + + + + + + + + + + + + + + VeraPDF + + + + + + + + + + + + + + + + VeraPDF + + + + + + + + + + + + + + + Schema check + + + + + + + + + + + + + + + XSLT files + + + + + + + + + + + + + + + PH-Schematron + + + + + + + + + + + + + + + + + + + + + + + + + XRechnung + + + + + + + + + + Schematron + + + + + + + diff --git a/libraryExtended/pom.xml b/libraryExtended/pom.xml index 1df61ee1..4527336e 100644 --- a/libraryExtended/pom.xml +++ b/libraryExtended/pom.xml @@ -68,7 +68,7 @@ com.helger ph-schematron - 5.0.8 + 5.6.0 org.mustangproject diff --git a/libraryExtended/src/main/java/org/mustangproject/library/extended/EPart.java b/libraryExtended/src/main/java/org/mustangproject/library/extended/EPart.java index 827f2da5..486bd65e 100644 --- a/libraryExtended/src/main/java/org/mustangproject/library/extended/EPart.java +++ b/libraryExtended/src/main/java/org/mustangproject/library/extended/EPart.java @@ -1,5 +1,5 @@ package org.mustangproject.library.extended; public enum EPart { - xml, pdf, none + fx, xr, pdf, none } diff --git a/libraryExtended/src/main/java/org/mustangproject/library/extended/Main.java b/libraryExtended/src/main/java/org/mustangproject/library/extended/Main.java new file mode 100644 index 00000000..06d05c7a --- /dev/null +++ b/libraryExtended/src/main/java/org/mustangproject/library/extended/Main.java @@ -0,0 +1,94 @@ +package org.mustangproject.library.extended; + +import java.io.File; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.sanityinc.jargs.CmdLineParser; +import com.sanityinc.jargs.CmdLineParser.Option; + +public class Main { + + static final ClassLoader cl = Main.class.getClassLoader(); + + private static final Logger LOGGER = LoggerFactory.getLogger(Main.class.getCanonicalName()); // log output is + // ignored for the + // time being + + public void run(String[] args) { + + /*** + * prerequisite is a mvn generate-resources + */ + + CmdLineParser parser = new CmdLineParser(); + Option actionOption = parser.addStringOption('a', "action"); + Option filenameOption = parser.addStringOption('f', "Filename"); + + Option licenseOption = parser.addBooleanOption('l', "license"); + Option helpOption = parser.addBooleanOption('h', "help"); + Option logAppendOption = parser.addStringOption("logAppend"); + + boolean optionsRecognized = false; + + try { + parser.parse(args); + } catch (CmdLineParser.OptionException e) { + System.err.println(e.getMessage()); + System.exit(-2); + } + + Boolean helpRequested = parser.getOptionValue(helpOption); + + if (parser.getOptionValue(licenseOption) != null) { + optionsRecognized = true; + + System.out.println("Copyright 2018 Jochen Stärk\n" + "\n" + + "Licensed under the Apache License, Version 2.0 (the \"License\");\n" + + "you may not use this file except in compliance with the License.\n" + + "You may obtain a copy of the License at\n" + "\n" + + " http://www.apache.org/licenses/LICENSE-2.0\n" + "\n" + + "Unless required by applicable law or agreed to in writing, software\n" + + "distributed under the License is distributed on an \"AS IS\" BASIS,\n" + + "WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n" + + "See the License for the specific language governing permissions and\n" + + "limitations under the License.\n\n\n\n\n" + + "This software is embedding the PDF/A validator VeraPDF, " + + "http://verapdf.org/, which is available under GPL and MPL licenses."); + + System.exit(0); + } + String filename = parser.getOptionValue(filenameOption); + String action = parser.getOptionValue(actionOption); + File logdir = new File("log"); + if (!logdir.exists() || !logdir.isDirectory() || !logdir.canWrite()) { + System.err.println("Need writable subdirectory 'log' for log files."); + } + + if ((action != null) && (action.equals("validate"))) { + ZUGFeRDValidator zfv=new ZUGFeRDValidator(); + zfv.setLogAppend(parser.getOptionValue(logAppendOption)); + System.out.println(zfv.validate(filename)); + + optionsRecognized = !zfv.hasOptionsError(); + if (!zfv.wasCompletelyValid()) { + System.exit(-1); + } + + } + + if ((!optionsRecognized) || (helpRequested != null && helpRequested.booleanValue())) { + System.out.println( + "usage: --action validate -f | [-l (shows license)][--logAppend \"String to be appended to validation result log\"]"); + System.exit(-1); + } + + } + + public static void main(String[] args) { + new Main().run(args); + } + + +} diff --git a/libraryExtended/src/main/java/org/mustangproject/library/extended/ValidationContext.java b/libraryExtended/src/main/java/org/mustangproject/library/extended/ValidationContext.java index e71fd702..29397fe4 100644 --- a/libraryExtended/src/main/java/org/mustangproject/library/extended/ValidationContext.java +++ b/libraryExtended/src/main/java/org/mustangproject/library/extended/ValidationContext.java @@ -1,5 +1,6 @@ package org.mustangproject.library.extended; +import java.util.ArrayList; import java.util.Vector; import org.slf4j.Logger; @@ -115,6 +116,19 @@ public class ValidationContext { return res; } + /*** + * + * @return the unique error types as comma separated string + */ + public String getCSVResult() { + ArrayList errorcodes = new ArrayList(); + for (ValidationResultItem validationResultItem : results) { + String errorCodeStr=Integer.toString(validationResultItem.getSection()); + errorcodes.add(errorCodeStr); + } + return String.join(",", errorcodes); + } + public void setInvalid() { isValid = false; } diff --git a/libraryExtended/src/main/java/org/mustangproject/library/extended/ValidationResultItem.java b/libraryExtended/src/main/java/org/mustangproject/library/extended/ValidationResultItem.java index 870aba6e..67804850 100644 --- a/libraryExtended/src/main/java/org/mustangproject/library/extended/ValidationResultItem.java +++ b/libraryExtended/src/main/java/org/mustangproject/library/extended/ValidationResultItem.java @@ -22,10 +22,12 @@ public class ValidationResultItem { private EPart part; + private XMLTools xt; public ValidationResultItem(ESeverity sev, String msg) { setSeverity(sev); setMessage(msg); + xt=new XMLTools(); } public ValidationResultItem setMessage(String msg) { message=msg; @@ -72,16 +74,16 @@ public class ValidationResultItem { additionalAttributes+=" type=\""+section+"\""; } if (location!=null) { - additionalAttributes+=" location=\""+XMLTools.encodeAttribute(location)+"\""; + additionalAttributes+=" location=\""+xt.escapeAttributeEntities(location)+"\""; } if (criterion!=null) { - additionalAttributes+=" criterion=\""+XMLTools.encodeAttribute(criterion)+"\""; + additionalAttributes+=" criterion=\""+xt.escapeAttributeEntities(criterion)+"\""; } if (stacktrace!=null) { - additionalContents+=""+XMLTools.encodeAttribute(stacktrace)+""; + additionalContents+=""+xt.escapeAttributeEntities(stacktrace)+""; } hasBeenOutputted=true; - return "<"+tagname+additionalAttributes+">"+XMLTools.encodeXML(message+additionalContents)+""; + return "<"+tagname+additionalAttributes+">"+xt.escapeElementEntities(message+additionalContents)+""; } public String getXMLOnce() { diff --git a/libraryExtended/src/main/java/org/mustangproject/library/extended/Validator.java b/libraryExtended/src/main/java/org/mustangproject/library/extended/Validator.java index 46540ff7..ff3e4ec3 100644 --- a/libraryExtended/src/main/java/org/mustangproject/library/extended/Validator.java +++ b/libraryExtended/src/main/java/org/mustangproject/library/extended/Validator.java @@ -57,4 +57,4 @@ public abstract class Validator { -} \ No newline at end of file +} diff --git a/libraryExtended/src/main/java/org/mustangproject/library/extended/XMLTools.java b/libraryExtended/src/main/java/org/mustangproject/library/extended/XMLTools.java index 69edb6c1..ac3936e3 100644 --- a/libraryExtended/src/main/java/org/mustangproject/library/extended/XMLTools.java +++ b/libraryExtended/src/main/java/org/mustangproject/library/extended/XMLTools.java @@ -1,50 +1,13 @@ package org.mustangproject.library.extended; +import org.dom4j.io.XMLWriter; -public class XMLTools { - public static String encodeXML(CharSequence s) { - StringBuilder sb = new StringBuilder(); - int len = s.length(); - for (int i=0;i= 0xd800 && c <= 0xdbff && i + 1 < len) { - c = ((c-0xd7c0)<<10) | (s.charAt(++i)&0x3ff); // UTF16 decode - } - if (c < 0x80) { // ASCII range: test most common case first - if (c < 0x20 && (c != '\t' && c != '\r' && c != '\n')) { - // Illegal XML character, even encoded. Skip or substitute - sb.append("�"); // Unicode replacement character - } else { - switch(c) { - case '&': sb.append("&"); break; - case '>': sb.append(">"); break; - case '<': sb.append("<"); break; - // Uncomment next two if encoding for an XML attribute -// case '\'' sb.append("'"); break; -// case '\"' sb.append("""); break; - // Uncomment next three if you prefer, but not required -// case '\n' sb.append(" "); break; -// case '\r' sb.append(" "); break; -// case '\t' sb.append(" "); break; - - default: sb.append((char)c); - } - } - } else if ((c >= 0xd800 && c <= 0xdfff) || c == 0xfffe || c == 0xffff) { - // Illegal XML character, even encoded. Skip or substitute - sb.append("�"); // Unicode replacement character - } else { - sb.append("&#x"); - sb.append(Integer.toHexString(c)); - sb.append(';'); - } - } - return sb.toString(); +public class XMLTools extends XMLWriter { + public String escapeAttributeEntities(String s) { + return super.escapeAttributeEntities(s); } - public static String encodeAttribute(String s) { - String res=s.replace("\n", "").replace("\r", "").replace("\"", ""); - - return XMLTools.encodeXML(res); + public String escapeElementEntities(String s) { + return super.escapeElementEntities(s); } diff --git a/libraryExtended/src/main/java/org/mustangproject/library/extended/XMLValidator.java b/libraryExtended/src/main/java/org/mustangproject/library/extended/XMLValidator.java index f7f30afc..430e12c3 100644 --- a/libraryExtended/src/main/java/org/mustangproject/library/extended/XMLValidator.java +++ b/libraryExtended/src/main/java/org/mustangproject/library/extended/XMLValidator.java @@ -1,10 +1,6 @@ package org.mustangproject.library.extended; -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.PrintWriter; -import java.io.StringReader; -import java.io.StringWriter; +import java.io.*; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Paths; @@ -13,349 +9,409 @@ import java.util.List; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.transform.*; +import javax.xml.transform.stream.StreamResult; import javax.xml.transform.stream.StreamSource; import javax.xml.xpath.XPath; import javax.xml.xpath.XPathConstants; import javax.xml.xpath.XPathExpression; import javax.xml.xpath.XPathFactory; -import org.oclc.purl.dsdl.svrl.FailedAssert; -import org.oclc.purl.dsdl.svrl.FiredRule; -import org.oclc.purl.dsdl.svrl.SchematronOutputType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; - +import com.helger.schematron.svrl.jaxb.FailedAssert; +import com.helger.schematron.svrl.jaxb.FiredRule; +import com.helger.schematron.svrl.jaxb.SchematronOutputType; import com.helger.schematron.ISchematronResource; import com.helger.schematron.xslt.SchematronResourceXSLT; +import com.helger.schematron.svrl.SVRLHelper; +import org.xml.sax.InputSource; public class XMLValidator extends Validator { - public XMLValidator(ValidationContext ctx) { - super(ctx); - } + public XMLValidator(ValidationContext ctx) { + super(ctx); + } - private static final Logger LOGGER = LoggerFactory.getLogger(XMLValidator.class.getCanonicalName()); // log output - // is - // ignored for the - // time being + private static final Logger LOGGER = LoggerFactory.getLogger(XMLValidator.class.getCanonicalName()); // log output + // is + // ignored for the + // time being - protected String zfXML = ""; - protected String filename = ""; - int firedRules=0; - int failedRules=0; - ISchematronResource aResSCH = null; + protected String zfXML = ""; + protected String filename = ""; + int firedRules = 0; + int failedRules = 0; + ISchematronResource aResSCH = null; - public void setFilename(String name) throws IrrecoverableValidationError { // from XML Filename - filename = name; - // file existence must have been checked before + public void setFilename(String name) throws IrrecoverableValidationError { // from XML Filename + filename = name; + // file existence must have been checked before - try { - zfXML = removeBOMFromString(Files.readAllBytes(Paths.get(name))); - } catch (IOException e) { + try { + zfXML = new String(Files.readAllBytes(Paths.get(name))); + } catch (IOException e) { - ValidationResultItem vri = new ValidationResultItem(ESeverity.exception, e.getMessage()).setSection(9) - .setPart(EPart.xml); - StringWriter sw = new StringWriter(); - PrintWriter pw = new PrintWriter(sw); - e.printStackTrace(pw); - vri.setStacktrace(sw.toString()); - context.addResultItem(vri); - } - } + ValidationResultItem vri = new ValidationResultItem(ESeverity.exception, e.getMessage()).setSection(9) + .setPart(EPart.fx); + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + e.printStackTrace(pw); + vri.setStacktrace(sw.toString()); + context.addResultItem(vri); + } + } - public void setStringContent(String xml) { - zfXML = xml; - } + public void setStringContent(String xml) { + zfXML = xml; + } - protected String removeBOMFromString(byte[] rawXML) { - byte[] bomlessData; + public static boolean matchesURI(String uri1, String uri2) { + return (uri1.equals(uri2) || uri1.startsWith(uri2 + "#")); + } - if ((rawXML != null) && (rawXML.length > 3) && (rawXML[0] == (byte) 0xEF) && (rawXML[1] == (byte) 0xBB) - && (rawXML[2] == (byte) 0xBF)) { - // I don't like BOMs, lets remove it - bomlessData = new byte[rawXML.length - 3]; - System.arraycopy(rawXML, 3, bomlessData, 0, rawXML.length - 3); - } else { - bomlessData = rawXML; - } + /*** + * + * @param xmlString + * @param overrideProfileCheck + * if set to true, all ZF2 files will be checked against EN16931 + * schematron, since no other schematron is available + * @return + */ + @Override + public void validate() throws IrrecoverableValidationError { + long startXMLTime = Calendar.getInstance().getTimeInMillis(); + firedRules = 0; + failedRules = 0; - return new String(bomlessData); - } - - public static boolean matchesURI(String uri1, String uri2) { - return (uri1.equals(uri2)||uri1.startsWith(uri2+"#")); - } + ByteArrayInputStream xmlByteInputStream = new ByteArrayInputStream(zfXML.getBytes(StandardCharsets.UTF_8)); - /*** - * - * @param xmlString - * @param overrideProfileCheck - * if set to true, all ZF2 files will be checked against EN16931 - * schematron, since no other schematron is available - * @return - */ - @Override - public void validate() throws IrrecoverableValidationError { - long startXMLTime = Calendar.getInstance().getTimeInMillis(); - firedRules=0; - failedRules=0; + if (zfXML.isEmpty()) { + ValidationResultItem res = new ValidationResultItem(ESeverity.exception, + "XML data not found in " + filename + + ": did you specify a pdf or xml file and does the xml file contain an embedded XML file?") + .setSection(3); + context.addResultItem(res); - - ByteArrayInputStream xmlByteInputStream = new ByteArrayInputStream(zfXML.getBytes(StandardCharsets.UTF_8)); + } else { - if (zfXML.isEmpty()) { - ValidationResultItem res = new ValidationResultItem(ESeverity.exception, - "XML data not found in " + filename - + ": did you specify a pdf or xml file and does the xml file contain an embedded XML file?") - .setSection(3); - context.addResultItem(res); + // final ISchematronResource aResSCH = + // SchematronResourceSCH.fromFile (new File("ZUGFeRD_1p0.scmt")); + // ... DOES work but is highly deprecated (and rightly so) because + // it takes 30-40min, - } else { + try { - // final ISchematronResource aResSCH = - // SchematronResourceSCH.fromFile (new File("ZUGFeRD_1p0.scmt")); - // ... DOES work but is highly deprecated (and rightly so) because - // it takes 30-40min, + /*** + * private static final String VALID_SCHEMATRON = "test-sch/valid01.sch"; + * private static final String VALID_XMLINSTANCE = "test-xml/valid01.xml"; + * + * @Test public void testWriteValid () throws Exception { final Document aDoc = + * SchematronResourceSCH.fromClassPath (VALID_SCHEMATRON) + * .applySchematronValidation (new ClassPathResource (VALID_XMLINSTANCE)); + * + */ - try { + DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); + dbf.setNamespaceAware(true); // otherwise we can not act namespace independently, i.e. use + // document.getElementsByTagNameNS("*",... - /*** - * private static final String VALID_SCHEMATRON = "test-sch/valid01.sch"; - * private static final String VALID_XMLINSTANCE = "test-xml/valid01.xml"; - * - * @Test public void testWriteValid () throws Exception { final Document aDoc = - * SchematronResourceSCH.fromClassPath (VALID_SCHEMATRON) - * .applySchematronValidation (new ClassPathResource (VALID_XMLINSTANCE)); - * - */ - - DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); - dbf.setNamespaceAware(true); // otherwise we can not act namespace independently, i.e. use - // document.getElementsByTagNameNS("*",... + DocumentBuilder db = dbf.newDocumentBuilder(); - DocumentBuilder db = dbf.newDocumentBuilder(); + Document doc = db.parse(xmlByteInputStream); - Document doc = db.parse(xmlByteInputStream); + Element root = doc.getDocumentElement(); - Element root = doc.getDocumentElement(); + NodeList ndList; - NodeList ndList; + // rootNode = document.getDocumentElement(); + // ApplicableSupplyChainTradeSettlement - // rootNode = document.getDocumentElement(); - // ApplicableSupplyChainTradeSettlement + // Create XPathFactory object + XPathFactory xpathFactory = XPathFactory.newInstance(); - // Create XPathFactory object - XPathFactory xpathFactory = XPathFactory.newInstance(); + // Create XPath object + XPath xpath = xpathFactory.newXPath(); + XPathExpression expr = xpath.compile( + "//*[local-name()=\"GuidelineSpecifiedDocumentContextParameter\"]/*[local-name()=\"ID\"]/text()"); + // evaluate expression result on XML document + ndList = (NodeList) expr.evaluate(doc, XPathConstants.NODESET); - // Create XPath object - XPath xpath = xpathFactory.newXPath(); - XPathExpression expr = xpath.compile( - "//*[local-name()=\"GuidelineSpecifiedDocumentContextParameter\"]/*[local-name()=\"ID\"]/text()"); - // evaluate expression result on XML document - ndList = (NodeList) expr.evaluate(doc, XPathConstants.NODESET); + for (int bookingIndex = 0; bookingIndex < ndList.getLength(); bookingIndex++) { + Node booking = ndList.item(bookingIndex); + // if there is a attribute in the tag number:value + // urn:ferd:CrossIndustryDocument:invoice:1p0:extended + // setForeignReference(booking.getTextContent()); - for (int bookingIndex = 0; bookingIndex < ndList.getLength(); bookingIndex++) { - Node booking = ndList.item(bookingIndex); - // if there is a attribute in the tag number:value - // urn:ferd:CrossIndustryDocument:invoice:1p0:extended - // setForeignReference(booking.getTextContent()); + context.setProfile(booking.getNodeValue()); + } + boolean isMiniumum = false; + boolean isBasic = false; + boolean isBasicWithoutLines = false; + boolean isEN16931 = false; + boolean isExtended = false; + String xsltFilename = null; + // urn:ferd:CrossIndustryDocument:invoice:1p0:extended, + // urn:ferd:CrossIndustryDocument:invoice:1p0:comfort, + // urn:ferd:CrossIndustryDocument:invoice:1p0:basic, - context.setProfile(booking.getNodeValue()); - } - boolean isMiniumum = false; - boolean isBasic = false; - boolean isBasicWithoutLines = false; - boolean isEN16931 = false; - boolean isExtended = false; - String xsltFilename = null; - // urn:ferd:CrossIndustryDocument:invoice:1p0:extended, - // urn:ferd:CrossIndustryDocument:invoice:1p0:comfort, - // urn:ferd:CrossIndustryDocument:invoice:1p0:basic, + // urn:cen.eu:en16931:2017 + // urn:cen.eu:en16931:2017:compliant:factur-x.eu:1p0:basic + if (root.getNodeName().equalsIgnoreCase("rsm:CrossIndustryInvoice")) { // ZUGFeRD 2.0 or Factur-X + context.setVersion("2"); - // urn:cen.eu:en16931:2017 - // urn:cen.eu:en16931:2017:compliant:factur-x.eu:1p0:basic - if (root.getNodeName().equalsIgnoreCase("rsm:CrossIndustryInvoice")) { // ZUGFeRD 2.0 or Factur-X - context.setVersion("2"); + isMiniumum = context.getProfile().contains("minimum"); + isBasic = context.getProfile().contains("basic"); + isBasicWithoutLines = context.getProfile().contains("basicwl"); + if (isBasicWithoutLines) { + isBasic = false;// basicwl also contains the string basic... + } + isEN16931 = matchesURI(context.getProfile(), "urn:cen.eu:en16931:2017:compliant:factur-x.eu:1p0:en16931") + || matchesURI(context.getProfile(), "urn:cen.eu:en16931:2017"); - isMiniumum = context.getProfile().contains("minimum"); - isBasic = context.getProfile().contains("basic"); - isBasicWithoutLines = context.getProfile().contains("basicwl"); - if(isBasicWithoutLines) { - isBasic=false;// basicwl also contains the string basic... - } - isEN16931 = matchesURI(context.getProfile(),"urn:cen.eu:en16931:2017:compliant:factur-x.eu:1p0:en16931") - || matchesURI(context.getProfile(),"urn:cen.eu:en16931:2017"); + isExtended = context.getProfile().contains("extended"); + if (isExtended) { + isEN16931 = false;// the uri for extended is urn:cen.eu:en16931:2017#conformant#urn:zugferd.de:2p0:extended and thus contains en16931... + } + if (isMiniumum) { + LOGGER.debug("is Minimum"); + validateSchema(zfXML.getBytes(StandardCharsets.UTF_8), "zf2/MINIMUM/FACTUR-X_MINIMUM.xsd", 18, EPart.fx); + xsltFilename = "/xslt/zugferd21_minimum.xsl"; + } else if (isBasicWithoutLines) { + LOGGER.debug("is Basic/WL"); + validateSchema(zfXML.getBytes(StandardCharsets.UTF_8), "zf2/BASIC-WL/FACTUR-X_BASIC-WL.xsd", 18, EPart.fx); + xsltFilename = "/xslt/zugferd21_basicwl.xsl"; + } else if (isBasic) { + LOGGER.debug("is Basic"); + validateSchema(zfXML.getBytes(StandardCharsets.UTF_8), "zf2/BASIC/FACTUR-X_BASIC.xsd", 18, EPart.fx); + xsltFilename = "/xslt/zugferd21_basic.xsl"; + } else if (isEN16931) { + LOGGER.debug("is EN16931"); + validateSchema(zfXML.getBytes(StandardCharsets.UTF_8), "zf2/EN16931/FACTUR-X_EN16931.xsd", 18, EPart.fx); + xsltFilename = "/xslt/zugferd21_en16931.xsl"; + } else if (isExtended) { + LOGGER.debug("is EXTENDED"); + validateSchema(zfXML.getBytes(StandardCharsets.UTF_8), "zf2/EXTENDED/FACTUR-X_EXTENDED.xsd", 18, EPart.fx); + xsltFilename = "/xslt/zugferd21_extended.xsl"; + } /* + * ISchematronResource aResSCH = SchematronResourceXSLT.fromFile(new File( + * "/Users/jstaerk/workspace/ZUV/src/main/resources/ZUGFeRDSchematronStylesheet.xsl" + * )); + */ - isExtended = context.getProfile().contains("extended"); - if(isExtended) { - isEN16931=false;// the uri for extended is urn:cen.eu:en16931:2017#conformant#urn:zugferd.de:2p0:extended and thus contains en16931... - } - if (isMiniumum) { - LOGGER.debug("is Minimum"); - validateSchema(zfXML.getBytes(StandardCharsets.UTF_8),"zf2/MINIMUM/FACTUR-X_MINIMUM.xsd", 18, EPart.xml); - xsltFilename="/xslt/zugferd21_minimum.xsl"; - } else if (isBasicWithoutLines) { - LOGGER.debug("is Basic/WL"); - validateSchema(zfXML.getBytes(StandardCharsets.UTF_8),"zf2/BASIC-WL/FACTUR-X_BASIC-WL.xsd", 18, EPart.xml); - xsltFilename="/xslt/zugferd21_basicwl.xsl"; - } else if (isBasic) { - LOGGER.debug("is Basic"); - validateSchema(zfXML.getBytes(StandardCharsets.UTF_8),"zf2/BASIC/FACTUR-X_BASIC.xsd", 18, EPart.xml); - xsltFilename="/xslt/zugferd21_basic.xsl"; - } else if (isEN16931) { - LOGGER.debug("is EN16931"); - validateSchema(zfXML.getBytes(StandardCharsets.UTF_8),"zf2/EN16931/FACTUR-X_EN16931.xsd", 18, EPart.xml); - xsltFilename="/xslt/zugferd21_en16931.xsl"; - } else if (isExtended) { - LOGGER.debug("is EXTENDED"); - validateSchema(zfXML.getBytes(StandardCharsets.UTF_8),"zf2/EXTENDED/FACTUR-X_EXTENDED.xsd", 18, EPart.xml); - xsltFilename="/xslt/zugferd21_extended.xsl"; - } /* - * ISchematronResource aResSCH = SchematronResourceXSLT.fromFile(new File( - * "/Users/jstaerk/workspace/ZUV/src/main/resources/ZUGFeRDSchematronStylesheet.xsl" - * )); - */ + // takes around 10 Seconds. // + // http://www.bentoweb.org/refs/TCDL2.0/tsdtf_schematron.html // explains that + // this xslt can be created using sth like + // saxon java net.sf.saxon.Transform -o tcdl2.0.tsdtf.sch.tmp.xsl -s + // tcdl2.0.tsdtf.sch iso_svrl.xsl - // takes around 10 Seconds. // - // http://www.bentoweb.org/refs/TCDL2.0/tsdtf_schematron.html // explains that - // this xslt can be created using sth like - // saxon java net.sf.saxon.Transform -o tcdl2.0.tsdtf.sch.tmp.xsl -s - // tcdl2.0.tsdtf.sch iso_svrl.xsl + } else { // ZUGFeRD 1.0 + context.setVersion("1"); + // + if ((!matchesURI(context.getProfile(), "urn:ferd:CrossIndustryDocument:invoice:1p0:basic")) + && (!matchesURI(context.getProfile(), "urn:ferd:CrossIndustryDocument:invoice:1p0:comfort")) + && (!matchesURI(context.getProfile(), "urn:ferd:CrossIndustryDocument:invoice:1p0:extended"))) { + context.addResultItem(new ValidationResultItem(ESeverity.error, "Unsupported profile type") + .setSection(25).setPart(EPart.fx)); + } + validateSchema(zfXML.getBytes(StandardCharsets.UTF_8), "zf1/ZUGFeRD1p0.xsd", 18, EPart.fx); - } else { // ZUGFeRD 1.0 - context.setVersion("1"); - // - if ((!matchesURI(context.getProfile(),"urn:ferd:CrossIndustryDocument:invoice:1p0:basic")) - && (!matchesURI(context.getProfile(), "urn:ferd:CrossIndustryDocument:invoice:1p0:comfort")) - && (!matchesURI(context.getProfile(), "urn:ferd:CrossIndustryDocument:invoice:1p0:extended"))) { - context.addResultItem(new ValidationResultItem(ESeverity.error, "Unsupported profile type") - .setSection(25).setPart(EPart.xml)); - } - validateSchema(zfXML.getBytes(StandardCharsets.UTF_8),"zf1/ZUGFeRD1p0.xsd", 18, EPart.xml); - - xsltFilename="/xslt/ZUGFeRD_1p0.xslt"; - } - if (context.getVersion().equals("2")) { - if ((!matchesURI(context.getProfile(), "urn:factur-x.eu:1p0:minimum")) - && (!matchesURI(context.getProfile(),"urn:zugferd.de:2p0:minimum")) - && (!matchesURI(context.getProfile(),"urn:factur-x.eu:1p0:basicwl")) - && (!matchesURI(context.getProfile(),"urn:zugferd.de:2p0:basicwl")) - && (!matchesURI(context.getProfile(),"urn:cen.eu:en16931:2017#compliant#urn:factur-x.eu:1p0:basic")) - && (!matchesURI(context.getProfile(),"urn:cen.eu:en16931:2017#compliant#urn:zugferd.de:2p0:basic")) - && (!matchesURI(context.getProfile(),"urn:cen.eu:en16931:2017")) - && (!matchesURI(context.getProfile(),"urn:cen.eu:en16931:2017#conformant#urn:factur-x.eu:1p0:extended")) - && (!matchesURI(context.getProfile(),"urn:cen.eu:en16931:2017#conformant#urn:zugferd.de:2p0:extended"))) + xsltFilename = "/xslt/ZUGFeRD_1p0.xslt"; + } + if (context.getVersion().equals("2")) { + if ((!matchesURI(context.getProfile(), "urn:factur-x.eu:1p0:minimum")) + && (!matchesURI(context.getProfile(), "urn:zugferd.de:2p0:minimum")) + && (!matchesURI(context.getProfile(), "urn:factur-x.eu:1p0:basicwl")) + && (!matchesURI(context.getProfile(), "urn:zugferd.de:2p0:basicwl")) + && (!matchesURI(context.getProfile(), "urn:cen.eu:en16931:2017#compliant#urn:factur-x.eu:1p0:basic")) + && (!matchesURI(context.getProfile(), "urn:cen.eu:en16931:2017#compliant#urn:zugferd.de:2p0:basic")) + && (!matchesURI(context.getProfile(), "urn:cen.eu:en16931:2017")) + && (!matchesURI(context.getProfile(), "urn:cen.eu:en16931:2017#conformant#urn:factur-x.eu:1p0:extended")) + && (!matchesURI(context.getProfile(), "urn:cen.eu:en16931:2017#conformant#urn:zugferd.de:2p0:extended"))) { + context.addResultItem( + new ValidationResultItem(ESeverity.error, "Unsupported profile type " + context.getProfile()) + .setSection(25).setPart(EPart.fx)); - { - context.addResultItem( - new ValidationResultItem(ESeverity.error, "Unsupported profile type "+context.getProfile()) - .setSection(25).setPart(EPart.xml)); + } + } else /** v1 */ {//urn:ferd:invoice:rc:comfort + if ((!matchesURI(context.getProfile(), "urn:ferd:CrossIndustryDocument:invoice:1p0:basic")) + && (!matchesURI(context.getProfile(), "urn:ferd:CrossIndustryDocument:invoice:1p0:comfort")) + && (!matchesURI(context.getProfile(), "urn:ferd:CrossIndustryDocument:invoice:1p0:extended"))) { + context.addResultItem(new ValidationResultItem(ESeverity.error, "Unsupported profile type") + .setSection(25).setPart(EPart.fx)); - } - } else /** v1 */ - {//urn:ferd:invoice:rc:comfort - if ((!matchesURI(context.getProfile(),"urn:ferd:CrossIndustryDocument:invoice:1p0:basic")) - && (!matchesURI(context.getProfile(),"urn:ferd:CrossIndustryDocument:invoice:1p0:comfort")) - && (!matchesURI(context.getProfile(),"urn:ferd:CrossIndustryDocument:invoice:1p0:extended"))) { - context.addResultItem(new ValidationResultItem(ESeverity.error, "Unsupported profile type") - .setSection(25).setPart(EPart.xml)); + } + } - } - } - - // main schematron validation - validateSchematron(zfXML, xsltFilename, 4); + // main schematron validation + validateSchematron(zfXML, xsltFilename, 4, ESeverity.error); - - if (context.getVersion().equals("2") - && isEN16931) { - //additionally validate against CEN - validateSchematron(zfXML, "/xslt/cii16931schematron/EN16931-CII-validation.xslt", 24); - } - + if (context.getVersion().equals("2") + && isEN16931) { + //additionally validate against CEN + validateSchematron(zfXML, "/xslt/cii16931schematron/EN16931-CII-validation.xslt", 24, ESeverity.error); - } catch (IrrecoverableValidationError er) { - throw er; - } catch (Exception e) { - ValidationResultItem vri = new ValidationResultItem(ESeverity.exception, e.getMessage()).setSection(22) - .setPart(EPart.xml); - StringWriter sw = new StringWriter(); - PrintWriter pw = new PrintWriter(sw); - e.printStackTrace(pw); - vri.setStacktrace(sw.toString()); - context.addResultItem(vri); - } + validateXR(zfXML); + } - } - long endTime = Calendar.getInstance().getTimeInMillis(); - - context.addCustomXML("" + ((context.getVersion() != null) ? context.getVersion() : "invalid") - + "" + ((context.getProfile() != null) ? context.getProfile() : "invalid") + - ""+firedRules+""+failedRules+"" + "" + (endTime - startXMLTime) + ""); - } - - public void validateSchematron(String xml, String xsltFilename, int section) throws IrrecoverableValidationError{ - ISchematronResource aResSCH = null; - aResSCH = SchematronResourceXSLT.fromClassPath(xsltFilename); - if (aResSCH != null) { - if (!aResSCH.isValidSchematron()) { - throw new IllegalArgumentException(xsltFilename+" is invalid Schematron!"); - } + } catch (IrrecoverableValidationError er) { + throw er; + } catch (Exception e) { + ValidationResultItem vri = new ValidationResultItem(ESeverity.exception, e.getMessage()).setSection(22) + .setPart(EPart.fx); + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + e.printStackTrace(pw); + vri.setStacktrace(sw.toString()); + context.addResultItem(vri); + } - SchematronOutputType sout; - try { - sout = aResSCH - .applySchematronValidationToSVRL(new StreamSource(new StringReader(xml))); - } catch (Exception e) { - throw new IrrecoverableValidationError(e.getMessage()); - } + } + long endTime = Calendar.getInstance().getTimeInMillis(); - List failedAsserts = sout.getActivePatternAndFiredRuleAndFailedAssert(); - if (failedAsserts.size() > 0) { - for (Object object : failedAsserts) { - if (object instanceof FailedAssert) { + context.addCustomXML("" + ((context.getVersion() != null) ? context.getVersion() : "invalid") + + "" + ((context.getProfile() != null) ? context.getProfile() : "invalid") + + "" + firedRules + "" + failedRules + "" + "" + (endTime - startXMLTime) + ""); - FailedAssert failedAssert = (FailedAssert) object; - LOGGER.info("FailedAssert ",failedAssert); + } - context.addResultItem(new ValidationResultItem(ESeverity.error, failedAssert.getText()) - .setLocation(failedAssert.getLocation()).setCriterion(failedAssert.getTest()).setSection(section) - .setPart(EPart.xml)); - failedRules++; - } else if (object instanceof FiredRule) { - firedRules++; - } - } + protected String getXRValidationResult(String xml) { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + TransformerFactory factory = TransformerFactory.newInstance(); - } - if (firedRules==0) { - context.addResultItem(new ValidationResultItem(ESeverity.error, "No rules matched, XML to minimal?").setSection(26) - .setPart(EPart.xml)); - - } - for (String currentString : sout.getText()) { - // schematronValidationString += "" + currentString + ""; - } + try { + // Use the factory to create a template containing the xsl file + Templates template = factory.newTemplates(new StreamSource( + this.getClass().getResourceAsStream("/xslt/XRechnung-CII-validation.xsl"))); - // schematronValidationString += new SVRLMarshaller ().getAsString (sout); - // returns the complete SVRL - - } - } - + // Use the template to create a transformer + Transformer xformer = template.newTransformer(); - public int getFiredRules() { - return firedRules; - } - - public int getFailedRules() { - return failedRules; - } + // Prepare the input and output files + + Source source = new StreamSource(new ByteArrayInputStream(xml.getBytes())); + Result result = new StreamResult(baos); + + // Apply the xsl file to the source file and write the result + // to the output file + xformer.transform(source, result); + + } catch (Exception ex) { + LOGGER.error(ex.getMessage(), ex); + } + return baos.toString(); + + } + + public void validateXR(String xml) throws IrrecoverableValidationError { + +/* + DocumentBuilderFactory docbfactory = DocumentBuilderFactory.newInstance(); + try { + DocumentBuilder builder = docbfactory.newDocumentBuilder(); + InputSource is = new InputSource(new StringReader(getXRValidationResult(xml))); + Document docXMP = builder.parse(is); + + XPathFactory xpathFactory = XPathFactory.newInstance(); + + // Create XPath object XPath xpath = xpathFactory.newXPath(); XPathExpression + + XPath xpath = xpathFactory.newXPath(); + // xpath.compile("//*[local-name()=\"GuidelineSpecifiedDocumentContextParameter\"]/[local-name()=\"ID\"]"); + // evaluate expression result on XML document ndList = (NodeList) + + // get the first element + XPathExpression xpr = xpath.compile( + "//*[local-name()=\"failed-assert\"]"); + NodeList nodes = (NodeList) xpr.evaluate(docXMP, XPathConstants.NODESET); + for (int nodeIndex = 0; nodeIndex < nodes.getLength(); nodeIndex++) { + String loc=nodes.item(nodeIndex).getAttributes().getNamedItem("location").getTextContent(); + NodeList failedSubNodes=nodes.item(nodeIndex).getChildNodes(); + for (int failedSubNodeIndex = 0; failedSubNodeIndex < nodes.getLength(); failedSubNodeIndex++) { + + if (failedSubNodes.item(failedSubNodeIndex).getNodeName().equals("svrl:text")) { + context.addResultItem( + new ValidationResultItem(ESeverity.warning, failedSubNodes.item(failedSubNodeIndex).getTextContent()) + .setLocation(loc).setSection(27).setPart(EPart.xr)); + + } + } + + + } + } catch (Exception ex) { + LOGGER.error(ex.getMessage(), ex); + + } +*/ + + validateSchematron(xml, "/xslt/XRechnung-CII-validation.xslt",27, ESeverity.notice); + + } + + + public void validateSchematron(String xml, String xsltFilename, int section, ESeverity severity) throws IrrecoverableValidationError { + ISchematronResource aResSCH = null; + aResSCH = SchematronResourceXSLT.fromClassPath(xsltFilename); + if (aResSCH != null) { + if (!aResSCH.isValidSchematron()) { + throw new IllegalArgumentException(xsltFilename + " is invalid Schematron!"); + } + + SchematronOutputType sout; + try { + sout = aResSCH + .applySchematronValidationToSVRL(new StreamSource(new StringReader(xml))); + } catch (Exception e) { + throw new IrrecoverableValidationError(e.getMessage()); + } + + List failedAsserts = sout.getActivePatternAndFiredRuleAndFailedAssert(); + if (failedAsserts.size() > 0) { + for (Object object : failedAsserts) { + if (object instanceof FailedAssert) { + + FailedAssert failedAssert = (FailedAssert) object; + LOGGER.info("FailedAssert ", failedAssert); + + context.addResultItem(new ValidationResultItem(severity, SVRLHelper.getAsString(failedAssert.getText())) + .setLocation(failedAssert.getLocation()).setCriterion(failedAssert.getTest()).setSection(section) + .setPart(EPart.fx)); + failedRules++; + } else if (object instanceof FiredRule) { + firedRules++; + } + } + + } + if (firedRules == 0) { + context.addResultItem(new ValidationResultItem(ESeverity.error, "No rules matched, XML to minimal?").setSection(26) + .setPart(EPart.fx)); + + } + // for (String currentString : sout.getText()) { + // schematronValidationString += "" + currentString + ""; + // } + + // schematronValidationString += new SVRLMarshaller ().getAsString (sout); + // returns the complete SVRL + + } + } + + + public int getFiredRules() { + return firedRules; + } + + public int getFailedRules() { + return failedRules; + } } diff --git a/libraryExtended/src/main/java/org/mustangproject/library/extended/ZUGFeRDValidator.java b/libraryExtended/src/main/java/org/mustangproject/library/extended/ZUGFeRDValidator.java index 052f3df8..6b01b662 100644 --- a/libraryExtended/src/main/java/org/mustangproject/library/extended/ZUGFeRDValidator.java +++ b/libraryExtended/src/main/java/org/mustangproject/library/extended/ZUGFeRDValidator.java @@ -34,13 +34,14 @@ public class ZUGFeRDValidator { private static final Logger LOGGER = LoggerFactory.getLogger(ZUGFeRDValidator.class.getCanonicalName()); // log // output protected ValidationContext context = new ValidationContext(LOGGER); - private String sha1Checksum; - private boolean pdfValidity; - private boolean displayXMLValidationOutput; - private long startTime; - private boolean optionsRecognized; - private String Signature; - private boolean wasCompletelyValid = false; + protected String sha1Checksum; + protected boolean pdfValidity; + protected boolean displayXMLValidationOutput; + protected long startTime; + protected boolean optionsRecognized; + protected String Signature; + protected boolean wasCompletelyValid = false; + protected String logAppend=null; /*** * within the validation it turned out something in the options was wrong, e.g. @@ -52,6 +53,10 @@ public class ZUGFeRDValidator { } + public void setLogAppend(String tobeappended) { + logAppend = tobeappended; + } + /*** * in case the result was not valid the error code of the app will be set to -1 * @@ -74,7 +79,7 @@ public class ZUGFeRDValidator { StringBuffer finalStringResult = new StringBuffer(); SimpleDateFormat isoDF = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); //$NON-NLS-1$ Date date = new Date(); - + startTime = Calendar.getInstance().getTimeInMillis(); try { Path path = Paths.get(filename); context.setFilename(path.getFileName().toString());// set filename without path @@ -135,11 +140,7 @@ public class ZUGFeRDValidator { pdfValidity = context.isValid(); Signature = context.getSignature(); - context.clear(); - if (!pdfValidity) { - // clear sets valid to true again - context.setInvalid(); - } + context.clear();// clear sets valid to true again if (pdfv.getRawXML() != null) { xv.setStringContent(pdfv.getRawXML()); displayXMLValidationOutput = true; @@ -202,6 +203,10 @@ public class ZUGFeRDValidator { context.clearCustomXML(); } + if ((isPDF)&&(!pdfValidity)) { + context.setInvalid(); + } + } } @@ -231,9 +236,16 @@ public class ZUGFeRDValidator { xmlValidity = context.isValid(); long duration = Calendar.getInstance().getTimeInMillis() - startTime; + String toBeAppended=""; + if (logAppend!=null) { + toBeAppended=logAppend; + } + + + LOGGER.info("Parsed PDF:" + (pdfValidity ? "valid" : "invalid") + " XML:" + (xmlValidity ? "valid" : "invalid") + " Signature:" + Signature + " Checksum:" + sha1Checksum + " Profile:" + context.getProfile() - + " Version:" + context.getVersion() + " Took:" + duration + "ms"); + + " Version:" + context.getVersion() + " Took:" + duration + "ms Errors:["+context.getCSVResult()+"] "+toBeAppended); wasCompletelyValid = ((pdfValidity) && (xmlValidity)); return sw.toString(); } @@ -277,4 +289,4 @@ public class ZUGFeRDValidator { } } -} \ No newline at end of file +} diff --git a/libraryExtended/src/main/resources/xslt/XRechnung-CII-validation.xslt b/libraryExtended/src/main/resources/xslt/XRechnung-CII-validation.xslt new file mode 100644 index 00000000..c411ac93 --- /dev/null +++ b/libraryExtended/src/main/resources/xslt/XRechnung-CII-validation.xslt @@ -0,0 +1,544 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + / + + + + + + *: + + [namespace-uri()=' + + '] + + + + [ + + ] + + + + / + + @ + + + @*[local-name()=' + + ' and namespace-uri()=' + + '] + + + + + + + + + / + + + [ + + ] + + + + /@ + + + + + + + + / + + + [ + + ] + + + + /@ + + + + + + + + + + + + + + + + + + + + + + + + . + + + + +U + + U + + + + U. + + n + + + + U. + + _ + + _ + + + + + + + + +   +   +   + + + + + + + + + + + + CII-model + CII-model + + + + + + + +Schematron Version 1.3.0 - XRechnung + 1.2.2 compatible - CII + + + + + + + + + + + + + + BR-DE-1 + fatal + + + + [BR-DE-1] Eine Rechnung (INVOICE) muss Angaben zu "PAYMENT INSTRUCTIONS" (BG-16) enthalten. + + + + + + + + + + BR-DE-13 + fatal + + + + [BR-DE-13] In der Rechnung müssen Angaben zu genau einer der drei Gruppen "CREDIT TRANSFER" (BG-17), "PAYMENT CARD INFORMATION" (BG-18) oder "DIRECT DEBIT" (BG-19) übermittelt werden. + + + + + + + + + + BR-DE-15 + fatal + + + + [BR-DE-15] Das Element "Buyer reference" (BT-10) muss übermittelt werden. + + + + + + + + + + BR-DE-16 + fatal + + + + [BR-DE-16] In der Rechnung muss mindestens eines der Elemente "Seller VAT identifier" (BT-31), "Seller tax registration identifier" (BT-32) oder "SELLER TAX REPRESENTATIVE PARTY" (BG-11) übermittelt werden. + + + + + + + + + + BR-DE-17 + warning + + + + [BR-DE-17] Mit dem Element "Invoice type code" (BT-3) sollen ausschließlich folgende Codes aus der Codeliste UNTDID 1001 übermittelt werden: 326 (Partial invoice), 380 (Commercial invoice), 384 (Corrected invoice), 389 (Self-billed invoice) und 381 (Credit note),875 (Partial construction invoice), 876 (Partial final construction invoice), 877 (Final construction invoice). + + + + + + + + + + BR-DE-18 + fatal + + + + [BR-DE-18] Die Informationen zur Gewährung von Skonto oder zur Berechnung von Verzugszinsen müssen wie folgt im Element "Payment terms" (BT-20) übermittelt werden: Anzugeben ist im ersten Segment "SKONTO" oder "VERZUG", im zweiten "TAGE=n", im dritten "PROZENT=n". Prozentzahlen sind ohne Vorzeichen sowie mit Punkt getrennt von zwei Nachkommastellen anzugeben. Liegt dem zu berechnenden Betrag nicht BT-115, "fälliger Betrag" zugrunde, sondern nur ein Teil des fälligen Betrags der Rechnung, ist der Grundwert zur Berechnung von Skonto oder Verzugszins als viertes Segment "BASISBETRAG=n" gemäß dem semantischen Datentypen Amount anzugeben. Jeder Eintrag beginnt mit einer #, die Segmente sind mit einer # getrennt und eine Zeile schließt mit einer # ab. Am Ende einer vollständigen Skonto oder Verzugsangabe muss ein XML-konformer Zeilenumbruch folgen. Alle Angaben zur Gewährung von Skonto oder zur Berechnung von Verzugszinsen müssen in Großbuchstaben gemacht werden. Zusätzliches Whitespace (Leerzeichen, Tabulatoren oder Zeilenumbrüche) ist nicht zulässig. Andere Zeichen oder Texte als in den oberen Vorgaben genannt sind nicht zulässig. + + + + + + + + + + + + + + + + BR-DE-21 + warning + + + + [BR-DE-21] Das Element "Specification identifier" (BT-24) soll syntaktisch der Kennung des Standards XRechnung entsprechen. + + + + + + + + + + + + + + + + BR-DE-2 + fatal + + + + [BR-DE-2] Die Gruppe "SELLER CONTACT" (BG-6) muss übermittelt werden. + + + + + + + + + + + + + + + + BR-DE-3 + fatal + + + + [BR-DE-3] Das Element "Seller city" (BT-37) muss übermittelt werden. + + + + + + + + + + BR-DE-4 + fatal + + + + [BR-DE-4] Das Element "Seller post code" (BT-38) muss übermittelt werden. + + + + + + + + + + + + + + + + BR-DE-5 + fatal + + + + [BR-DE-5] Das Element "Seller contact point" (BT-41) muss übermittelt werden. + + + + + + + + + + BR-DE-6 + fatal + + + + [BR-DE-6] Das Element "Seller contact telephone number" (BT-42) muss übermittelt werden. + + + + + + + + + + BR-DE-7 + fatal + + + + [BR-DE-7] Das Element "Seller contact email address" (BT-43) muss übermittelt werden. + + + + + + + + + + + + + + + + BR-DE-8 + fatal + + + + [BR-DE-8] Das Element "Buyer city" (BT-52) muss übermittelt werden. + + + + + + + + + + BR-DE-9 + fatal + + + + [BR-DE-9] Das Element "Buyer post code" (BT-53) muss übermittelt werden. + + + + + + + + + + + + + + + + BR-DE-10 + fatal + + + + [BR-DE-10] Das Element "Deliver to city" (BT-77) muss übermittelt werden, wenn die Gruppe "DELIVER TO ADDRESS" (BG-15) übermittelt wird. + + + + + + + + + + BR-DE-11 + fatal + + + + [BR-DE-11] Das Element "Deliver to post code" (BT-78) muss übermittelt werden, wenn die Gruppe "DELIVER TO ADDRESS" (BG-15) übermittelt wird. + + + + + + + + + + + + + + + + BR-DE-19 + warning + + + + [BR-DE-19] "Payment account identifier" (BT-84) soll eine korrekte IBAN enthalten, wenn in "Payment means type code" (BT-81) mit dem Code 58 SEPA als Zahlungsmittel gefordert wird. + + + + + + + + + + BR-DE-20 + warning + + + + [BR-DE-20] "Debited account identifier" (BT-91) soll eine korrekte IBAN enthalten, wenn in "Payment means type code" (BT-81) mit dem Code 59 SEPA als Zahlungsmittel gefordert wird. + + + + + + + + + + + + + + + + BR-DE-14 + fatal + + + + [BR-DE-14] Das Element "VAT category rate" (BT-119) muss übermittelt werden. + + + + + + + + + + diff --git a/libraryExtended/src/test/java/org/mustangproject/library/extended/XMLValidatorTest.java b/libraryExtended/src/test/java/org/mustangproject/library/extended/XMLValidatorTest.java index c88806de..707f5f6f 100644 --- a/libraryExtended/src/test/java/org/mustangproject/library/extended/XMLValidatorTest.java +++ b/libraryExtended/src/test/java/org/mustangproject/library/extended/XMLValidatorTest.java @@ -99,7 +99,7 @@ public class XMLValidatorTest extends ResourceCase { } }*/ - content = "" + res + ""; + content = "" + res + ""; assertThat(content).valueByXPath("count(//error)") .asInt() @@ -131,8 +131,8 @@ public class XMLValidatorTest extends ResourceCase { xv.setFilename(tempFile.getAbsolutePath()); xv.validate(); - source = Input.fromString("" + xv.getXMLResult() + "").build(); - content = xpath.evaluate("/xml/summary/@status", source); + source = Input.fromString("" + xv.getXMLResult() + "").build(); + content = xpath.evaluate("/validation/summary/@status", source); assertEquals("invalid", content); // assertEquals(true, xv.getXMLResult().contains("valid") && @@ -157,8 +157,8 @@ public class XMLValidatorTest extends ResourceCase { xv.setFilename(tempFile.getAbsolutePath()); xv.validate(); - source = Input.fromString("" + xv.getXMLResult() + "").build(); - content = xpath.evaluate("/xml/summary/@status", source); + source = Input.fromString("" + xv.getXMLResult() + "").build(); + content = xpath.evaluate("/validation/summary/@status", source); assertEquals("invalid", content); } catch (IrrecoverableValidationError e) { diff --git a/libraryExtended/src/test/java/org/mustangproject/library/extended/ZUGFeRDValidatorTest.java b/libraryExtended/src/test/java/org/mustangproject/library/extended/ZUGFeRDValidatorTest.java index af4fdb8b..5e09d74b 100644 --- a/libraryExtended/src/test/java/org/mustangproject/library/extended/ZUGFeRDValidatorTest.java +++ b/libraryExtended/src/test/java/org/mustangproject/library/extended/ZUGFeRDValidatorTest.java @@ -1,29 +1,50 @@ package org.mustangproject.library.extended; +import static org.xmlunit.assertj.XmlAssert.assertThat; import java.io.File; import java.util.regex.Matcher; import java.util.regex.Pattern; +import org.xmlunit.builder.Input; +import org.xmlunit.xpath.JAXPXPathEngine; +import org.xmlunit.xpath.XPathEngine; + +import static org.xmlunit.assertj.XmlAssert.assertThat; public class ZUGFeRDValidatorTest extends ResourceCase { public void testPDFValidation() { File tempFile = getResourceAsFile("invalidPDF.pdf"); + /**used to be Rule Status + Specification: ISO 19005-3:2012, Clause: 6.2.11.4, Test number: 4 + If the FontDescriptor dictionary of an embedded CID font contains a CIDSet stream, then it shall identify all CIDs which are present in the font program, regardless of whether a CID in the font is referenced or used by the PDF or not. Failed + 2 occurrences Hide + PDCIDFont + fontFile_size == 0 || fontName.search(/[A-Z]{6}\+/) != 0 || CIDSet_size == 0 || cidSetListsAllGlyphs == true + root/document[0]/pages[1](9 0 obj PDPage)/contentStream[0](18 0 obj PDContentStream)/operators[166]/font[0](WIUIIO+CIDFont+F2)/DescendantFonts[0](WIUIIO+CIDFont+F2) + root/document[0]/pages[1](9 0 obj PDPage)/contentStream[0](18 0 obj PDContentStream)/operators[192]/font[0](VEXQUA+CIDFont+F1)/DescendantFonts[0](VEXQUA+CIDFont+F1) + but new sample since that has been downgraded to warning + */ ZUGFeRDValidator zfv = new ZUGFeRDValidator(); - String report = zfv.validate(tempFile.getAbsolutePath()); - Pattern regex = Pattern.compile(".*status=\"invalid\".*/>\n ", Pattern.DOTALL); - Matcher regexMatcher = regex.matcher(report); - assertTrue(regexMatcher.find()); - regex = Pattern.compile(".*status=\"invalid\".*/>\n", Pattern.DOTALL); - regexMatcher = regex.matcher(report); - assertTrue(regexMatcher.find()); - + String res = zfv.validate(tempFile.getAbsolutePath()); + + + assertThat(res).valueByXPath("/validation/pdf/summary/@status") + .isEqualTo("invalid"); + + assertThat(res).valueByXPath("/validation/xml/summary/@status") + .isEqualTo("valid"); + + assertThat(res).valueByXPath("/validation/summary/@status") + .isEqualTo("invalid"); + + tempFile = getResourceAsFile("validAvoir_FR_type380_BASICWL.pdf"); zfv = new ZUGFeRDValidator(); - report = zfv.validate(tempFile.getAbsolutePath()); - assertEquals(true, report.contains("status=\"valid\"")); - assertEquals(false, report.contains("status=\"invalid\"")); + res = zfv.validate(tempFile.getAbsolutePath()); + assertEquals(true, res.contains("status=\"valid\"")); + assertEquals(false, res.contains("status=\"invalid\"")); } } diff --git a/libraryExtended/src/test/resources/invalidPDF.pdf b/libraryExtended/src/test/resources/invalidPDF.pdf index 35cd7d11..1dbd238a 100644 Binary files a/libraryExtended/src/test/resources/invalidPDF.pdf and b/libraryExtended/src/test/resources/invalidPDF.pdf differ