deleted redundant files (had been moved from library/extended to validator). Corrected commandline import.
This commit is contained in:
@@ -1,5 +0,0 @@
|
||||
package org.mustangproject.library.extended;
|
||||
|
||||
public enum EPart {
|
||||
fx, xr, pdf, none
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
package org.mustangproject.library.extended;
|
||||
|
||||
public enum ESeverity {
|
||||
notice, warning, error, fatal, exception
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
package org.mustangproject.library.extended;
|
||||
|
||||
public class IrrecoverableValidationError extends Exception {
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private static final long serialVersionUID = 1L;
|
||||
public IrrecoverableValidationError(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,94 +0,0 @@
|
||||
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<String> actionOption = parser.addStringOption('a', "action");
|
||||
Option<String> filenameOption = parser.addStringOption('f', "Filename");
|
||||
|
||||
Option<Boolean> licenseOption = parser.addBooleanOption('l', "license");
|
||||
Option<Boolean> helpOption = parser.addBooleanOption('h', "help");
|
||||
Option<String> 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 <ZUGFeRD PDF Filename.pdf>|<ZUGFeRD XML Filename.xml> [-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);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,343 +0,0 @@
|
||||
package org.mustangproject.library.extended;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringReader;
|
||||
import java.io.StringWriter;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Calendar;
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
import javax.xml.XMLConstants;
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import javax.xml.transform.Source;
|
||||
import javax.xml.transform.stream.StreamSource;
|
||||
import javax.xml.validation.Schema;
|
||||
import javax.xml.validation.SchemaFactory;
|
||||
import javax.xml.xpath.XPath;
|
||||
import javax.xml.xpath.XPathConstants;
|
||||
import javax.xml.xpath.XPathExpression;
|
||||
import javax.xml.xpath.XPathExpressionException;
|
||||
import javax.xml.xpath.XPathFactory;
|
||||
|
||||
import org.mustangproject.ZUGFeRD.ZUGFeRDImporter;
|
||||
import org.riversun.bigdoc.bin.BigFileSearcher;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.verapdf.core.VeraPDFException;
|
||||
import org.verapdf.features.FeatureExtractorConfig;
|
||||
import org.verapdf.features.FeatureFactory;
|
||||
import org.verapdf.metadata.fixer.FixerFactory;
|
||||
import org.verapdf.metadata.fixer.MetadataFixerConfig;
|
||||
import org.verapdf.pdfa.VeraGreenfieldFoundryProvider;
|
||||
import org.verapdf.pdfa.validation.validators.ValidatorConfig;
|
||||
import org.verapdf.pdfa.validation.validators.ValidatorFactory;
|
||||
import org.verapdf.processor.BatchProcessor;
|
||||
import org.verapdf.processor.FormatOption;
|
||||
import org.verapdf.processor.ProcessorConfig;
|
||||
import org.verapdf.processor.ProcessorFactory;
|
||||
import org.verapdf.processor.TaskType;
|
||||
import org.verapdf.processor.plugins.PluginsCollectionConfig;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.NodeList;
|
||||
import org.xml.sax.InputSource;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
public class PDFValidator extends Validator {
|
||||
|
||||
public PDFValidator(ValidationContext ctx) {
|
||||
super(ctx);
|
||||
// TODO Auto-generated constructor stub
|
||||
}
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(PDFValidator.class.getCanonicalName()); // log output
|
||||
// is
|
||||
|
||||
private String pdfFilename;
|
||||
|
||||
private String pdfReport;
|
||||
|
||||
private String Signature;
|
||||
|
||||
private String zfXML = null;
|
||||
|
||||
protected static boolean stringArrayContains(String[] arr, String targetValue) {
|
||||
return Arrays.asList(arr).contains(targetValue);
|
||||
}
|
||||
|
||||
public void validate() throws IrrecoverableValidationError {
|
||||
|
||||
zfXML = null;
|
||||
File file = new File(pdfFilename);
|
||||
// file existence must have been checked before
|
||||
BigFileSearcher searcher = new BigFileSearcher();
|
||||
|
||||
byte[] pdfSignature = { '%', 'P', 'D', 'F' };
|
||||
if (searcher.indexOf(file, pdfSignature) != 0) {
|
||||
context.addResultItem(
|
||||
new ValidationResultItem(ESeverity.fatal, "Not a PDF file "+pdfFilename).setSection(20).setPart(EPart.pdf));
|
||||
|
||||
}
|
||||
|
||||
long startPDFTime = Calendar.getInstance().getTimeInMillis();
|
||||
|
||||
// Step 1 Validate PDF
|
||||
|
||||
VeraGreenfieldFoundryProvider.initialise();
|
||||
// Default validator config
|
||||
ValidatorConfig validatorConfig = ValidatorFactory.defaultConfig();
|
||||
// Default features config
|
||||
FeatureExtractorConfig featureConfig = FeatureFactory.defaultConfig();
|
||||
// Default plugins config
|
||||
PluginsCollectionConfig pluginsConfig = PluginsCollectionConfig.defaultConfig();
|
||||
// Default fixer config
|
||||
MetadataFixerConfig fixerConfig = FixerFactory.defaultConfig();
|
||||
// Tasks configuring
|
||||
EnumSet tasks = EnumSet.noneOf(TaskType.class);
|
||||
tasks.add(TaskType.VALIDATE);
|
||||
// tasks.add(TaskType.EXTRACT_FEATURES);
|
||||
// tasks.add(TaskType.FIX_METADATA);
|
||||
// Creating processor config
|
||||
ProcessorConfig processorConfig = ProcessorFactory.fromValues(validatorConfig, featureConfig, pluginsConfig,
|
||||
fixerConfig, tasks);
|
||||
// Creating processor and output stream.
|
||||
ByteArrayOutputStream reportStream = new ByteArrayOutputStream();
|
||||
try (BatchProcessor processor = ProcessorFactory.fileBatchProcessor(processorConfig)) {
|
||||
// Generating list of files for processing
|
||||
List<File> files = new ArrayList<>();
|
||||
files.add(new File(pdfFilename));
|
||||
// starting the processor
|
||||
processor.process(files, ProcessorFactory.getHandler(FormatOption.MRR, true, reportStream, 100,
|
||||
processorConfig.getValidatorConfig().isRecordPasses()));
|
||||
|
||||
pdfReport = reportStream.toString("utf-8").replaceAll("<\\?xml version=\"1\\.0\" encoding=\"utf-8\"\\?>",
|
||||
"");
|
||||
} catch (VeraPDFException e) {
|
||||
ValidationResultItem vri = new ValidationResultItem(ESeverity.exception, e.getMessage()).setSection(6)
|
||||
.setPart(EPart.pdf);
|
||||
StringWriter sw = new StringWriter();
|
||||
PrintWriter pw = new PrintWriter(sw);
|
||||
e.printStackTrace(pw);
|
||||
vri.setStacktrace(sw.toString());
|
||||
context.addResultItem(vri);
|
||||
} catch (IOException excep) {
|
||||
context.addResultItem(new ValidationResultItem(ESeverity.exception, excep.getMessage()).setSection(7)
|
||||
.setPart(EPart.pdf).setStacktrace(excep.getStackTrace().toString()));
|
||||
}
|
||||
|
||||
// step 2 validate XMP
|
||||
ZUGFeRDImporter zi = new ZUGFeRDImporter(pdfFilename);
|
||||
String xmp = zi.getXMP();
|
||||
|
||||
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
|
||||
Document docXMP;
|
||||
|
||||
if (xmp.length() == 0) {
|
||||
context.addResultItem(new ValidationResultItem(ESeverity.error, "Invalid XMP Metadata not found")
|
||||
.setSection(17).setPart(EPart.pdf));
|
||||
}
|
||||
/*
|
||||
* checking for sth like <zf:ConformanceLevel>EXTENDED</zf:ConformanceLevel>
|
||||
* <zf:DocumentType>INVOICE</zf:DocumentType>
|
||||
* <zf:DocumentFileName>ZUGFeRD-invoice.xml</zf:DocumentFileName>
|
||||
* <zf:Version>1.0</zf:Version>
|
||||
*/
|
||||
try {
|
||||
DocumentBuilder builder = factory.newDocumentBuilder();
|
||||
InputSource is = new InputSource(new StringReader(xmp));
|
||||
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()=\"ConformanceLevel\"]|//*[local-name()=\"Description\"]/@ConformanceLevel");
|
||||
NodeList nodes = (NodeList) xpr.evaluate(docXMP, XPathConstants.NODESET);
|
||||
|
||||
if (nodes.getLength() == 0) {
|
||||
context.addResultItem(
|
||||
new ValidationResultItem(ESeverity.error, "XMP Metadata: ConformanceLevel not found")
|
||||
.setSection(11).setPart(EPart.pdf));
|
||||
}
|
||||
|
||||
boolean conformanceLevelValid=false;
|
||||
for (int i = 0; i < nodes.getLength(); i++) {
|
||||
|
||||
String[] valueArray = { "BASIC WL", "BASIC", "MINIMUM", "EN 16931", "COMFORT", "CIUS", "EXTENDED" };
|
||||
if (stringArrayContains(valueArray, nodes.item(i).getTextContent())) {
|
||||
conformanceLevelValid=true;
|
||||
}
|
||||
}
|
||||
if (!conformanceLevelValid) {
|
||||
context.addResultItem(new ValidationResultItem(ESeverity.error,
|
||||
"XMP Metadata: ConformanceLevel contains invalid value").setSection(12).setPart(EPart.pdf));
|
||||
|
||||
}
|
||||
xpr = xpath.compile("//*[local-name()=\"DocumentType\"]|//*[local-name()=\"Description\"]/@DocumentType");
|
||||
nodes = (NodeList) xpr.evaluate(docXMP, XPathConstants.NODESET);
|
||||
|
||||
if (nodes.getLength() == 0) {
|
||||
context.addResultItem(new ValidationResultItem(ESeverity.error, "XMP Metadata: DocumentType not found")
|
||||
.setSection(13).setPart(EPart.pdf));
|
||||
}
|
||||
|
||||
boolean documentTypeValid=false;
|
||||
for (int i = 0; i < nodes.getLength(); i++) {
|
||||
if (nodes.item(i).getTextContent().equals("INVOICE")) {
|
||||
documentTypeValid=true;
|
||||
}
|
||||
}
|
||||
if (!documentTypeValid) {
|
||||
context.addResultItem(
|
||||
new ValidationResultItem(ESeverity.error, "XMP Metadata: DocumentType invalid")
|
||||
.setSection(14).setPart(EPart.pdf));
|
||||
|
||||
}
|
||||
xpr = xpath.compile(
|
||||
"//*[local-name()=\"DocumentFileName\"]|//*[local-name()=\"Description\"]/@DocumentFileName");
|
||||
nodes = (NodeList) xpr.evaluate(docXMP, XPathConstants.NODESET);
|
||||
|
||||
if (nodes.getLength() == 0) {
|
||||
context.addResultItem(
|
||||
new ValidationResultItem(ESeverity.error, "XMP Metadata: DocumentFileName not found")
|
||||
.setSection(21).setPart(EPart.pdf));
|
||||
}
|
||||
boolean documentFilenameValid=false;
|
||||
for (int i = 0; i < nodes.getLength(); i++) {
|
||||
String[] valueArray = { "factur-x.xml", "ZUGFeRD-invoice.xml", "zugferd-invoice.xml" };
|
||||
if (stringArrayContains(valueArray, nodes.item(i).getTextContent())) {
|
||||
documentFilenameValid=true;
|
||||
}
|
||||
|
||||
// e.g. ZUGFeRD-invoice.xml
|
||||
}
|
||||
if (!documentFilenameValid) {
|
||||
|
||||
context.addResultItem(new ValidationResultItem(ESeverity.error,
|
||||
"XMP Metadata: DocumentFileName contains invalid value").setSection(19).setPart(EPart.pdf));
|
||||
}
|
||||
xpr = xpath.compile("//*[local-name()=\"Version\"]|//*[local-name()=\"Description\"]/@Version");
|
||||
nodes = (NodeList) xpr.evaluate(docXMP, XPathConstants.NODESET);
|
||||
|
||||
// get all child nodes
|
||||
// NodeList nodes = element.getChildNodes();
|
||||
// expr.evaluate(docXMP, XPathConstants.NODESET);
|
||||
// print the text content of each child
|
||||
if (nodes.getLength() == 0) {
|
||||
context.addResultItem(new ValidationResultItem(ESeverity.error, "XMP Metadata: Version not found")
|
||||
.setSection(15).setPart(EPart.pdf));
|
||||
}
|
||||
|
||||
boolean versionValid=false;
|
||||
for (int i = 0; i < nodes.getLength(); i++) {
|
||||
String[] valueArray = { "1.0", "2p0" };
|
||||
if (stringArrayContains(valueArray, nodes.item(i).getTextContent())) {
|
||||
versionValid=true;
|
||||
} // e.g. 1.0
|
||||
}
|
||||
if (!versionValid) {
|
||||
context.addResultItem(
|
||||
new ValidationResultItem(ESeverity.error, "XMP Metadata: Version contains invalid value")
|
||||
.setSection(16).setPart(EPart.pdf));
|
||||
|
||||
}
|
||||
|
||||
} catch (SAXException e) {
|
||||
LOGGER.error(e.getMessage(), e);
|
||||
} catch (IOException e) {
|
||||
LOGGER.error(e.getMessage(), e);
|
||||
} catch (ParserConfigurationException e) {
|
||||
LOGGER.error(e.getMessage(), e);
|
||||
} catch (XPathExpressionException e) {
|
||||
LOGGER.error(e.getMessage(), e);
|
||||
}
|
||||
zfXML = zi.getUTF8();
|
||||
|
||||
// step 3 find signatures
|
||||
try {
|
||||
byte[] symtraxSignature = "Symtrax".getBytes("UTF-8");
|
||||
byte[] mustangSignature = "via mustangproject".getBytes("UTF-8");
|
||||
byte[] facturxpythonSignature = "by Alexis de Lattre".getBytes("UTF-8");
|
||||
byte[] intarsysSignature = "intarsys ".getBytes("UTF-8");
|
||||
byte[] konikSignature = "Konik".getBytes("UTF-8");
|
||||
byte[] pdfMachineSignature = "pdfMachine from Broadgun Software".getBytes("UTF-8");
|
||||
|
||||
if (searcher.indexOf(file, symtraxSignature) != -1) {
|
||||
Signature = "Symtrax";
|
||||
} else if (searcher.indexOf(file, mustangSignature) != -1) {
|
||||
Signature = "Mustang";
|
||||
} else if (searcher.indexOf(file, facturxpythonSignature) != -1) {
|
||||
Signature = "Factur/X Python";
|
||||
} else if (searcher.indexOf(file, intarsysSignature) != -1) {
|
||||
Signature = "Intarsys";
|
||||
} else if (searcher.indexOf(file, konikSignature) != -1) {
|
||||
Signature = "Konik";
|
||||
} else if (searcher.indexOf(file, pdfMachineSignature) != -1) {
|
||||
Signature = "pdfMachine";
|
||||
}
|
||||
|
||||
context.setSignature(Signature);
|
||||
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
LOGGER.error(e.getMessage(), e);
|
||||
}
|
||||
|
||||
// step 4:validate additional data
|
||||
HashMap<String, byte[]> additionalData=zi.getAdditionalData();
|
||||
for (String filename : additionalData.keySet()) {
|
||||
// validating xml in byte[] additionalData.get(filename)
|
||||
LOGGER.info("validating additionalData " + filename);
|
||||
validateSchema(additionalData.get(filename), "ad/basic/additional_data_base_schema.xsd", 2, EPart.pdf);
|
||||
}
|
||||
|
||||
|
||||
//end
|
||||
|
||||
long endTime = Calendar.getInstance().getTimeInMillis();
|
||||
if (!pdfReport.contains("validationReports compliant=\"1\"")) {
|
||||
context.setInvalid();
|
||||
}
|
||||
if (!pdfReport.contains("PDF/A-3")) {
|
||||
context.addResultItem(
|
||||
new ValidationResultItem(ESeverity.error, "Not a PDF/A-3").setSection(23).setPart(EPart.pdf));
|
||||
|
||||
}
|
||||
context.addCustomXML(pdfReport + "<info><signature>"
|
||||
+ ((context.getSignature() != null) ? context.getSignature() : "unknown")
|
||||
+ "</signature><duration unit='ms'>" + (endTime - startPDFTime) + "</duration></info>");
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void setFilename(String filename) throws IrrecoverableValidationError {
|
||||
this.pdfFilename = filename;
|
||||
|
||||
}
|
||||
|
||||
public String getRawXML() {
|
||||
return zfXML;
|
||||
|
||||
}
|
||||
|
||||
public String getSignature() {
|
||||
return Signature;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
package org.mustangproject.library.extended;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import javax.xml.transform.Source;
|
||||
import javax.xml.transform.Templates;
|
||||
import javax.xml.transform.Transformer;
|
||||
import javax.xml.transform.TransformerConfigurationException;
|
||||
import javax.xml.transform.TransformerException;
|
||||
import javax.xml.transform.TransformerFactory;
|
||||
import javax.xml.transform.URIResolver;
|
||||
import javax.xml.transform.stream.StreamResult;
|
||||
import javax.xml.transform.stream.StreamSource;
|
||||
|
||||
|
||||
public class SchematronPipeline {
|
||||
static final ClassLoader cl = SchematronPipeline.class.getClassLoader();
|
||||
private static final TransformerFactory factory = getTransformerFactory();
|
||||
private static final String xslExt = ".xsl"; //$NON-NLS-1$
|
||||
private static final String resourcePath = "iso-schematron-xslt2/"; //$NON-NLS-1$
|
||||
private static final String isoDsdlXsl = resourcePath + "iso_dsdl_include" + xslExt; //$NON-NLS-1$
|
||||
private static final String isoExpXsl = resourcePath + "iso_abstract_expand" + xslExt; //$NON-NLS-1$
|
||||
private static final String isoSvrlXsl = resourcePath + "iso_svrl_for_xslt2" + xslExt; //$NON-NLS-1$
|
||||
private static final Templates cachedIsoDsdXsl = createCachedTransform(isoDsdlXsl);
|
||||
private static final Templates cachedExpXsl = createCachedTransform(isoExpXsl);
|
||||
private static final Templates cachedIsoSvrlXsl = createCachedTransform(isoSvrlXsl);
|
||||
|
||||
private static TransformerFactory getTransformerFactory() {
|
||||
TransformerFactory fact = TransformerFactory.newInstance();
|
||||
fact.setURIResolver(new ClasspathResourceURIResolver());
|
||||
return fact;
|
||||
}
|
||||
|
||||
static Templates createCachedTransform(final String transName) {
|
||||
try {
|
||||
return factory.newTemplates(new StreamSource(cl.getResourceAsStream(transName)));
|
||||
} catch (TransformerConfigurationException excep) {
|
||||
throw new IllegalStateException("Policy Schematron transformer XSL " + transName + " not found.", excep); //$NON-NLS-1$ //$NON-NLS-2$
|
||||
}
|
||||
}
|
||||
|
||||
public static void processSchematron(InputStream schematronSource, OutputStream xslDest)
|
||||
throws TransformerException, IOException {
|
||||
File isoDsdResult = createTempFileResult(cachedIsoDsdXsl.newTransformer(), new StreamSource(schematronSource),
|
||||
"IsoDsd"); //$NON-NLS-1$
|
||||
File isoExpResult = createTempFileResult(cachedExpXsl.newTransformer(), new StreamSource(isoDsdResult),
|
||||
"ExpXsl"); //$NON-NLS-1$
|
||||
cachedIsoSvrlXsl.newTransformer().transform(new StreamSource(isoExpResult), new StreamResult(xslDest));
|
||||
isoDsdResult.delete();
|
||||
isoExpResult.delete();
|
||||
}
|
||||
|
||||
private static File createTempFileResult(final Transformer transformer, final StreamSource toTransform,
|
||||
final String suffix) throws TransformerException, IOException {
|
||||
File result = File.createTempFile("ZUV_", suffix); //$NON-NLS-1$
|
||||
result.deleteOnExit();
|
||||
|
||||
try (FileOutputStream fos = new FileOutputStream(result)) {
|
||||
transformer.transform(toTransform, new StreamResult(fos));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static class ClasspathResourceURIResolver implements URIResolver {
|
||||
ClasspathResourceURIResolver() {
|
||||
// Do nothing, just prevents synthetic access warning.
|
||||
}
|
||||
|
||||
@Override
|
||||
public Source resolve(String href, String base) throws TransformerException {
|
||||
return new StreamSource(cl.getResourceAsStream(resourcePath + href));
|
||||
}
|
||||
}
|
||||
|
||||
public static void applySchematronXsl(final InputStream xmlFile,
|
||||
final OutputStream policyReport) throws TransformerException {
|
||||
Transformer transformer = factory.newTransformer(new StreamSource(cl.getResourceAsStream(resourcePath+"ZUGFeRDSchematronStylesheetXSLT1.xsl")));
|
||||
transformer.transform(new StreamSource(xmlFile), new StreamResult(policyReport));
|
||||
}
|
||||
}
|
||||
@@ -1,146 +0,0 @@
|
||||
package org.mustangproject.library.extended;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Vector;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
|
||||
public class ValidationContext {
|
||||
protected Vector<ValidationResultItem> results;
|
||||
protected String customXML = "";
|
||||
private String version = null;
|
||||
private String profile = null;
|
||||
private String signature = null;
|
||||
private boolean isValid = true;
|
||||
protected Logger logger;
|
||||
private String filename;
|
||||
|
||||
public ValidationContext(Logger log) {
|
||||
logger = log;
|
||||
results = new Vector<ValidationResultItem>();
|
||||
}
|
||||
|
||||
public void addResultItem(ValidationResultItem vr) throws IrrecoverableValidationError {
|
||||
results.add(vr);
|
||||
|
||||
if ((vr.getSeverity() == ESeverity.fatal) || (vr.getSeverity() == ESeverity.exception)
|
||||
|| (vr.getSeverity() == ESeverity.error)) {
|
||||
isValid = false;
|
||||
|
||||
}
|
||||
if (logger != null) {
|
||||
if ((vr.getSeverity() == ESeverity.fatal) || (vr.getSeverity() == ESeverity.exception)) {
|
||||
logger.error("Fatal Error " + vr.getSection() + ": " + vr.getMessage());
|
||||
} else if ((vr.getSeverity() == ESeverity.error)) {
|
||||
logger.error("Error " + vr.getSection() + ": " + vr.getMessage());
|
||||
} else if (vr.getSeverity() == ESeverity.warning) {
|
||||
logger.warn("Warning " + vr.getSection() + ": " + vr.getMessage());
|
||||
} else if (vr.getSeverity() == ESeverity.notice) {
|
||||
logger.info("Notice " + vr.getSection() + ": " + vr.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
if ((vr.getSeverity() == ESeverity.fatal) || (vr.getSeverity() == ESeverity.exception)) {
|
||||
throw new IrrecoverableValidationError(vr.getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void clearCustomXML() {
|
||||
customXML = "";
|
||||
}
|
||||
|
||||
public void addCustomXML(String XML) {
|
||||
customXML += XML;
|
||||
}
|
||||
|
||||
public String getCustomXML() {
|
||||
return customXML;
|
||||
}
|
||||
|
||||
public ValidationContext setVersion(String version) {
|
||||
this.version = version;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ValidationContext setProfile(String profile) {
|
||||
this.profile = profile;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ValidationContext setSignature(String signature) {
|
||||
this.signature = signature;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public String getProfile() {
|
||||
return profile;
|
||||
}
|
||||
|
||||
public String getSignature() {
|
||||
return signature;
|
||||
}
|
||||
|
||||
public boolean isValid() {
|
||||
return isValid;
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
results.clear();
|
||||
isValid = true;
|
||||
clearCustomXML();
|
||||
version = null;
|
||||
profile = null;
|
||||
signature = null;
|
||||
|
||||
}
|
||||
|
||||
public String getXMLResult() {
|
||||
String res = getCustomXML();
|
||||
if (results.size() > 0) {
|
||||
res += "<messages>";
|
||||
}
|
||||
|
||||
for (ValidationResultItem validationResultItem : results) {
|
||||
// xml and pdf are handled in their respective sections
|
||||
res += validationResultItem.getXMLOnce() + "\n";
|
||||
}
|
||||
if (results.size() > 0) {
|
||||
res += "</messages>";
|
||||
}
|
||||
res += "<summary status='" + (isValid ? "valid" : "invalid") + "'/>";
|
||||
return res;
|
||||
}
|
||||
|
||||
/***
|
||||
*
|
||||
* @return the unique error types as comma separated string
|
||||
*/
|
||||
public String getCSVResult() {
|
||||
ArrayList<String> errorcodes = new ArrayList<String>();
|
||||
for (ValidationResultItem validationResultItem : results) {
|
||||
String errorCodeStr=Integer.toString(validationResultItem.getSection());
|
||||
errorcodes.add(errorCodeStr);
|
||||
}
|
||||
return String.join(",", errorcodes);
|
||||
}
|
||||
|
||||
public void setInvalid() {
|
||||
isValid = false;
|
||||
}
|
||||
|
||||
public void setFilename(String filename) {
|
||||
this.filename=filename;
|
||||
}
|
||||
public String getFilename() {
|
||||
if (filename==null) {
|
||||
return "";
|
||||
} else {
|
||||
return filename;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,114 +0,0 @@
|
||||
package org.mustangproject.library.extended;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class ValidationResultItem {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(ValidationResultItem.class.getCanonicalName()); // log output is
|
||||
// ignored for the
|
||||
// time being
|
||||
|
||||
|
||||
protected String message, location=null;
|
||||
protected int section =-1;
|
||||
|
||||
|
||||
private ESeverity severity=ESeverity.error;
|
||||
private String criterion=null;
|
||||
|
||||
|
||||
private String stacktrace=null;
|
||||
private boolean hasBeenOutputted=false;
|
||||
|
||||
|
||||
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;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ValidationResultItem setSection(int sec) {
|
||||
section=sec;
|
||||
return this;
|
||||
}
|
||||
public ValidationResultItem setLocation(String loc) {
|
||||
location=loc;
|
||||
return this;
|
||||
}
|
||||
public ValidationResultItem setPart(EPart loc) {
|
||||
part=loc;
|
||||
return this;
|
||||
}
|
||||
public EPart getPart() {
|
||||
return part;
|
||||
}
|
||||
public ValidationResultItem setSeverity(ESeverity sev) {
|
||||
severity=sev;
|
||||
return this;
|
||||
}
|
||||
public ValidationResultItem setStacktrace(String stack) {
|
||||
stacktrace=stack;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public String getXML() {
|
||||
String tagname="error";
|
||||
if (severity==ESeverity.exception) {
|
||||
tagname="exception";
|
||||
} else if (severity==ESeverity.warning) {
|
||||
tagname="warning";
|
||||
} else if (severity==ESeverity.notice) {
|
||||
tagname="notice";
|
||||
}
|
||||
String additionalAttributes="";
|
||||
String additionalContents="";
|
||||
if (section!=-1) {
|
||||
additionalAttributes+=" type=\""+section+"\"";
|
||||
}
|
||||
if (location!=null) {
|
||||
additionalAttributes+=" location=\""+xt.escapeAttributeEntities(location)+"\"";
|
||||
}
|
||||
if (criterion!=null) {
|
||||
additionalAttributes+=" criterion=\""+xt.escapeAttributeEntities(criterion)+"\"";
|
||||
}
|
||||
if (stacktrace!=null) {
|
||||
additionalContents+="<stacktrace>"+xt.escapeAttributeEntities(stacktrace)+"</stacktrace>";
|
||||
}
|
||||
hasBeenOutputted=true;
|
||||
return "<"+tagname+additionalAttributes+">"+xt.escapeElementEntities(message+additionalContents)+"</"+tagname+">";
|
||||
}
|
||||
|
||||
public String getXMLOnce() {
|
||||
if (!hasBeenOutputted) {
|
||||
return getXML();
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
public ValidationResultItem setCriterion(String test) {
|
||||
criterion = test;
|
||||
return this;
|
||||
}
|
||||
public ESeverity getSeverity() {
|
||||
return severity;
|
||||
}
|
||||
|
||||
public int getSection() {
|
||||
return section;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
package org.mustangproject.library.extended;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
|
||||
import javax.xml.XMLConstants;
|
||||
import javax.xml.transform.Source;
|
||||
import javax.xml.transform.stream.StreamSource;
|
||||
import javax.xml.validation.Schema;
|
||||
import javax.xml.validation.SchemaFactory;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
//abstract class
|
||||
public abstract class Validator {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(Validator.class.getCanonicalName()); // log output
|
||||
|
||||
protected ValidationContext context;
|
||||
|
||||
public Validator(ValidationContext ctx){
|
||||
this.context=ctx;
|
||||
}
|
||||
|
||||
//abstract method
|
||||
|
||||
public abstract void setFilename(String filename) throws IrrecoverableValidationError;
|
||||
public abstract void validate() throws IrrecoverableValidationError;
|
||||
|
||||
public String getXMLResult() {
|
||||
return context.getXMLResult();
|
||||
}
|
||||
|
||||
/***
|
||||
* validates a schema, which can only be needed in XML validation - and in pdf validation for additional data
|
||||
* @param xmlRawData
|
||||
* @param schemaPath
|
||||
*/
|
||||
protected void validateSchema(byte[] xmlRawData, String schemaPath,int section, EPart part) throws IrrecoverableValidationError {
|
||||
URL schemaFile = ClassLoader.getSystemResource("schema/" + schemaPath);
|
||||
Source xmlData = new StreamSource(new ByteArrayInputStream(xmlRawData));
|
||||
SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
|
||||
try {
|
||||
Schema schema = schemaFactory.newSchema(schemaFile);
|
||||
javax.xml.validation.Validator validator = schema.newValidator();
|
||||
validator.validate(xmlData);
|
||||
} catch (SAXException e) {
|
||||
context.addResultItem(new ValidationResultItem(ESeverity.error, "schema validation fails:" + e)
|
||||
.setSection(section).setPart(part));
|
||||
} catch (IOException e) {
|
||||
LOGGER.error(e.getMessage(), e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
package org.mustangproject.library.extended;
|
||||
|
||||
import org.dom4j.io.XMLWriter;
|
||||
|
||||
public class XMLTools extends XMLWriter {
|
||||
public String escapeAttributeEntities(String s) {
|
||||
return super.escapeAttributeEntities(s);
|
||||
}
|
||||
public String escapeElementEntities(String s) {
|
||||
return super.escapeElementEntities(s);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,417 +0,0 @@
|
||||
package org.mustangproject.library.extended;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Calendar;
|
||||
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.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);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
public void setFilename(String name) throws IrrecoverableValidationError { // from XML Filename
|
||||
filename = name;
|
||||
// file existence must have been checked before
|
||||
|
||||
try {
|
||||
zfXML = new String(Files.readAllBytes(Paths.get(name)));
|
||||
} catch (IOException e) {
|
||||
|
||||
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 static boolean matchesURI(String uri1, String uri2) {
|
||||
return (uri1.equals(uri2) || uri1.startsWith(uri2 + "#"));
|
||||
}
|
||||
|
||||
/***
|
||||
*
|
||||
* @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;
|
||||
|
||||
|
||||
ByteArrayInputStream xmlByteInputStream = new ByteArrayInputStream(zfXML.getBytes(StandardCharsets.UTF_8));
|
||||
|
||||
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);
|
||||
|
||||
} else {
|
||||
|
||||
// final ISchematronResource aResSCH =
|
||||
// SchematronResourceSCH.fromFile (new File("ZUGFeRD_1p0.scmt"));
|
||||
// ... DOES work but is highly deprecated (and rightly so) because
|
||||
// it takes 30-40min,
|
||||
|
||||
try {
|
||||
|
||||
/***
|
||||
* 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();
|
||||
|
||||
Document doc = db.parse(xmlByteInputStream);
|
||||
|
||||
Element root = doc.getDocumentElement();
|
||||
|
||||
NodeList ndList;
|
||||
|
||||
// rootNode = document.getDocumentElement();
|
||||
// ApplicableSupplyChainTradeSettlement
|
||||
|
||||
// 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);
|
||||
|
||||
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,
|
||||
|
||||
// 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");
|
||||
|
||||
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"
|
||||
* ));
|
||||
*/
|
||||
|
||||
// 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);
|
||||
|
||||
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));
|
||||
|
||||
}
|
||||
} 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));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// 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, ESeverity.error);
|
||||
|
||||
validateXR(zfXML);
|
||||
}
|
||||
|
||||
|
||||
} 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);
|
||||
}
|
||||
|
||||
}
|
||||
long endTime = Calendar.getInstance().getTimeInMillis();
|
||||
|
||||
context.addCustomXML("<info><version>" + ((context.getVersion() != null) ? context.getVersion() : "invalid")
|
||||
+ "</version><profile>" + ((context.getProfile() != null) ? context.getProfile() : "invalid") +
|
||||
"</profile><validator version=\"" + Main.class.getPackage().getImplementationVersion() + "\"></validator><rules><fired>" + firedRules + "</fired><failed>" + failedRules + "</failed></rules>" + "<duration unit='ms'>" + (endTime - startXMLTime) + "</duration></info>");
|
||||
|
||||
}
|
||||
|
||||
protected String getXRValidationResult(String xml) {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
TransformerFactory factory = TransformerFactory.newInstance();
|
||||
|
||||
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")));
|
||||
|
||||
// Use the template to create a transformer
|
||||
Transformer xformer = template.newTransformer();
|
||||
|
||||
// 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<Object> 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 += "<output>" + currentString + "</output>";
|
||||
// }
|
||||
|
||||
// schematronValidationString += new SVRLMarshaller ().getAsString (sout);
|
||||
// returns the complete SVRL
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public int getFiredRules() {
|
||||
return firedRules;
|
||||
}
|
||||
|
||||
public int getFailedRules() {
|
||||
return failedRules;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,292 +0,0 @@
|
||||
package org.mustangproject.library.extended;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.StringWriter;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
|
||||
import javax.xml.bind.annotation.adapters.HexBinaryAdapter;
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
|
||||
import org.dom4j.DocumentException;
|
||||
import org.dom4j.DocumentHelper;
|
||||
import org.dom4j.io.OutputFormat;
|
||||
import org.dom4j.io.XMLWriter;
|
||||
import org.riversun.bigdoc.bin.BigFileSearcher;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
import org.xml.sax.SAXParseException;
|
||||
|
||||
//abstract class
|
||||
public class ZUGFeRDValidator {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(ZUGFeRDValidator.class.getCanonicalName()); // log
|
||||
// output
|
||||
protected ValidationContext context = new ValidationContext(LOGGER);
|
||||
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.
|
||||
* the file did not exist. recommendation to show the help text again. Should be
|
||||
* false if XML or PDF file was found
|
||||
*/
|
||||
public boolean hasOptionsError() {
|
||||
return !optionsRecognized;
|
||||
|
||||
}
|
||||
|
||||
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
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public boolean wasCompletelyValid() {
|
||||
return wasCompletelyValid;
|
||||
|
||||
}
|
||||
|
||||
/***
|
||||
* performs a validation on the file filename
|
||||
*
|
||||
* @param filename
|
||||
* @return
|
||||
*/
|
||||
public String validate(String filename) {
|
||||
boolean xmlValidity;
|
||||
context.clear();
|
||||
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
|
||||
|
||||
} catch (NullPointerException ex) {
|
||||
// ignore
|
||||
}
|
||||
finalStringResult
|
||||
.append("<validation filename='" + context.getFilename() + "' datetime='" + isoDF.format(date) + "'>");
|
||||
|
||||
try {
|
||||
|
||||
if (filename == null) {
|
||||
optionsRecognized = false;
|
||||
context.addResultItem(new ValidationResultItem(ESeverity.fatal, "Filename not specified").setSection(10)
|
||||
.setPart(EPart.pdf));
|
||||
}
|
||||
|
||||
PDFValidator pdfv = new PDFValidator(context);
|
||||
File file = new File(filename);
|
||||
if (!file.exists()) {
|
||||
context.addResultItem(
|
||||
new ValidationResultItem(ESeverity.fatal, "File not found").setSection(1).setPart(EPart.pdf));
|
||||
} else if (file.length() < 32) {
|
||||
// with less then 32 bytes it can not even be a proper XML file
|
||||
context.addResultItem(
|
||||
new ValidationResultItem(ESeverity.fatal, "File too small").setSection(5).setPart(EPart.pdf));
|
||||
} else {
|
||||
BigFileSearcher searcher = new BigFileSearcher();
|
||||
XMLValidator xv = new XMLValidator(context);
|
||||
|
||||
byte[] pdfSignature = { '%', 'P', 'D', 'F' };
|
||||
boolean isPDF = searcher.indexOf(file, pdfSignature) == 0;
|
||||
if (isPDF) {
|
||||
pdfv.setFilename(filename);
|
||||
|
||||
optionsRecognized = true;
|
||||
try {
|
||||
if (!file.exists()) {
|
||||
context.addResultItem(
|
||||
new ValidationResultItem(ESeverity.exception, "File " + filename + " not found")
|
||||
.setSection(1));
|
||||
}
|
||||
} catch (IrrecoverableValidationError irx) {
|
||||
// @todo log
|
||||
}
|
||||
|
||||
finalStringResult.append("<pdf>");
|
||||
optionsRecognized = true;
|
||||
try {
|
||||
pdfv.validate();
|
||||
|
||||
sha1Checksum = calcSHA1(file);
|
||||
|
||||
// Validate PDF
|
||||
|
||||
finalStringResult.append(pdfv.getXMLResult());
|
||||
pdfValidity = context.isValid();
|
||||
|
||||
Signature = context.getSignature();
|
||||
context.clear();// clear sets valid to true again
|
||||
if (pdfv.getRawXML() != null) {
|
||||
xv.setStringContent(pdfv.getRawXML());
|
||||
displayXMLValidationOutput = true;
|
||||
} else {
|
||||
context.addResultItem(
|
||||
new ValidationResultItem(ESeverity.exception, "XML could not be extracted")
|
||||
.setSection(17));
|
||||
}
|
||||
} catch (IrrecoverableValidationError irx) {
|
||||
// @todo log
|
||||
}
|
||||
|
||||
finalStringResult.append("</pdf>\n");
|
||||
|
||||
context.clearCustomXML();
|
||||
} else {
|
||||
boolean isXML = false;
|
||||
try {
|
||||
|
||||
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
|
||||
DocumentBuilder db = dbf.newDocumentBuilder();
|
||||
|
||||
Document doc = db.parse(file);
|
||||
|
||||
Element root = doc.getDocumentElement();
|
||||
isXML=true;//no exception so far
|
||||
|
||||
}
|
||||
catch (Exception ex) {
|
||||
// probably no xml file, sth like SAXParseException content not allowed in prolog
|
||||
// ignore isXML is already false
|
||||
// in the tests, this may error-out anyway
|
||||
}
|
||||
if (isXML) {
|
||||
pdfValidity = true;
|
||||
optionsRecognized = true;
|
||||
xv.setFilename(filename);
|
||||
if (file.exists()) {
|
||||
sha1Checksum = calcSHA1(file);
|
||||
}
|
||||
|
||||
displayXMLValidationOutput = true;
|
||||
|
||||
} else {
|
||||
optionsRecognized = false;
|
||||
context.addResultItem(new ValidationResultItem(ESeverity.exception,
|
||||
"File does not look like PDF nor XML (contains neither %PDF nor <?xml)").setSection(8));
|
||||
|
||||
}
|
||||
}
|
||||
if ((optionsRecognized) && (displayXMLValidationOutput)) {
|
||||
finalStringResult.append("<xml>");
|
||||
try {
|
||||
xv.validate();
|
||||
} catch (IrrecoverableValidationError irx) {
|
||||
// @todo log
|
||||
}
|
||||
finalStringResult.append(xv.getXMLResult());
|
||||
finalStringResult.append("</xml>");
|
||||
context.clearCustomXML();
|
||||
}
|
||||
|
||||
if ((isPDF)&&(!pdfValidity)) {
|
||||
context.setInvalid();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
catch (IrrecoverableValidationError irx) {
|
||||
// @todo log
|
||||
} finally {
|
||||
finalStringResult.append(context.getXMLResult());
|
||||
finalStringResult.append("</validation>");
|
||||
|
||||
}
|
||||
|
||||
OutputFormat format = OutputFormat.createPrettyPrint();
|
||||
StringWriter sw = new StringWriter();
|
||||
org.dom4j.Document document = null;
|
||||
try {
|
||||
document = DocumentHelper.parseText(new String(finalStringResult));
|
||||
} catch (DocumentException e1) {
|
||||
LOGGER.error(e1.getMessage());
|
||||
}
|
||||
XMLWriter writer = new XMLWriter(sw, format);
|
||||
try {
|
||||
writer.write(document);
|
||||
} catch (Exception e) {
|
||||
LOGGER.error(e.getMessage());
|
||||
}
|
||||
|
||||
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 Errors:["+context.getCSVResult()+"] "+toBeAppended);
|
||||
wasCompletelyValid = ((pdfValidity) && (xmlValidity));
|
||||
return sw.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the file and calculate the SHA-1 checksum
|
||||
*
|
||||
* @param file the file to read
|
||||
* @return the hex representation of the SHA-1 using uppercase chars
|
||||
* @throws FileNotFoundException if the file does not exist, is a directory
|
||||
* rather than a regular file, or for some
|
||||
* other reason cannot be opened for reading
|
||||
* @throws IOException if an I/O error occurs
|
||||
* @throws NoSuchAlgorithmException should never happen
|
||||
*/
|
||||
private static String calcSHA1(File file) {
|
||||
MessageDigest sha1 = null;
|
||||
try {
|
||||
|
||||
sha1 = MessageDigest.getInstance("SHA-1");
|
||||
InputStream input = new FileInputStream(file);
|
||||
byte[] buffer = new byte[8192];
|
||||
int len = input.read(buffer);
|
||||
|
||||
while (len != -1) {
|
||||
sha1.update(buffer, 0, len);
|
||||
len = input.read(buffer);
|
||||
}
|
||||
input.close();
|
||||
} catch (FileNotFoundException e) {
|
||||
LOGGER.error(e.getMessage(), e);
|
||||
} catch (IOException e) {
|
||||
LOGGER.error(e.getMessage(), e);
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
LOGGER.error(e.getMessage(), e);
|
||||
}
|
||||
if (sha1 == null) {
|
||||
return "";
|
||||
} else {
|
||||
return new HexBinaryAdapter().marshal(sha1.digest());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
package org.mustangproject.library.extended;
|
||||
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
|
||||
public class MiscValidatorTest extends ResourceCase {
|
||||
|
||||
|
||||
public void testInvalidFileValidation() {
|
||||
|
||||
ZUGFeRDValidator zfv=new ZUGFeRDValidator();
|
||||
|
||||
String res=zfv.validate(null);
|
||||
assertTrue(res.matches("<\\?xml version=\"1.0\" encoding=\"UTF-8\"\\?>\n" +
|
||||
"\n" +
|
||||
"<validation filename=\"\" datetime=\".*?\">\n" +
|
||||
" <messages>\n" +
|
||||
" <error type=\"10\">Filename not specified</error> \n" +
|
||||
" </messages>\n" +
|
||||
" <summary status=\"invalid\"/>\n" +
|
||||
"</validation>\n" +
|
||||
""));
|
||||
|
||||
res=zfv.validate("/dhfkbv/sfjkh");
|
||||
assertTrue(res.matches("<\\?xml version=\"1.0\" encoding=\"UTF-8\"\\?>\n" +
|
||||
"\n" +
|
||||
"<validation filename=\"sfjkh\" datetime=\".*?\">\n" +
|
||||
" <messages>\n" +
|
||||
" <error type=\"1\">File not found</error> \n" +
|
||||
" </messages>\n" +
|
||||
" <summary status=\"invalid\"/>\n" +
|
||||
"</validation>\n"));
|
||||
|
||||
boolean noExceptionOccurred=true;
|
||||
File tempFile=null;
|
||||
try {
|
||||
tempFile = File.createTempFile("hello", ".tmp");
|
||||
} catch (IOException e) {
|
||||
noExceptionOccurred=true;
|
||||
}
|
||||
assertTrue(noExceptionOccurred);
|
||||
|
||||
res=zfv.validate(tempFile.getAbsolutePath());
|
||||
assertTrue(res.matches("<\\?xml version=\"1.0\" encoding=\"UTF-8\"\\?>\n" +
|
||||
"\n" +
|
||||
"<validation filename=\".*\" datetime=\".*\">\n" +
|
||||
" <messages>\n" +
|
||||
" <error type=\"5\">File too small</error> \n" +
|
||||
" </messages>\n" +
|
||||
" <summary status=\"invalid\"/>\n" +
|
||||
"</validation>\n" +
|
||||
""));
|
||||
|
||||
|
||||
String fileContent = "ladhvkdbfk wkhfbkhdhkb svbkfsvbksfbvk sdvsdvbksjdvbkfdsv sdvbskdvbsjhkvbfskh dvbskfvbkfsbvke"
|
||||
+ "ladhvkdbfk wkhfbkhdhkb svbkfsvbksfbvk sdvsdvbksjdvbkfdsv sdvbskdvbsjhkvbfskh dvbskfvbkfsbvke";
|
||||
noExceptionOccurred=true;
|
||||
BufferedWriter writer;
|
||||
try {
|
||||
writer = new BufferedWriter(new FileWriter(tempFile));
|
||||
writer.write(fileContent);
|
||||
writer.close();
|
||||
} catch (IOException e) {
|
||||
noExceptionOccurred=false;
|
||||
}
|
||||
assertTrue(noExceptionOccurred);
|
||||
|
||||
|
||||
res=zfv.validate(tempFile.getAbsolutePath());
|
||||
assertTrue(res.matches("<\\?xml version=\"1.0\" encoding=\"UTF-8\"\\?>\n" +
|
||||
"\n" +
|
||||
"<validation filename=\".*\" datetime=\".*\">\n" +
|
||||
" <messages>\n" +
|
||||
" <exception type=\"8\">File does not look like PDF nor XML \\(contains neither %PDF nor <\\?xml\\)</exception> \n" +
|
||||
" </messages>\n" +
|
||||
" <summary status=\"invalid\"/>\n" +
|
||||
"</validation>\n" +
|
||||
""));
|
||||
|
||||
|
||||
// clean up
|
||||
tempFile.delete();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,139 +0,0 @@
|
||||
package org.mustangproject.library.extended;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class PDFValidatorTest extends ResourceCase {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(ZUGFeRDValidator.class.getCanonicalName()); // log
|
||||
|
||||
public void testPDFValidation() {
|
||||
ValidationContext vc = new ValidationContext(null);
|
||||
PDFValidator pv = new PDFValidator(vc);
|
||||
|
||||
try {
|
||||
|
||||
File tempFile = getResourceAsFile("XMLinvalidV2PDF.pdf");// need a more invalid file here
|
||||
|
||||
pv.setFilename(tempFile.getAbsolutePath());
|
||||
pv.validate();
|
||||
// assertEquals("", pv.getXMLResult());
|
||||
|
||||
//
|
||||
tempFile = getResourceAsFile("Facture_F20180027.pdf");
|
||||
pv.setFilename(tempFile.getAbsolutePath());
|
||||
pv.validate();
|
||||
String actual = pv.getXMLResult();
|
||||
assertEquals(true, actual.contains("summary status='valid"));
|
||||
assertEquals(false, actual.contains("summary status='invalid"));
|
||||
|
||||
XMLValidator xv = new XMLValidator(vc);
|
||||
xv.setStringContent(pv.getRawXML());
|
||||
xv.validate();
|
||||
actual = vc.getXMLResult();
|
||||
|
||||
assertEquals(true, actual.contains("validationReport profileName=\"PDF/A-3"));
|
||||
assertEquals(true, actual.contains("batchSummary totalJobs=\"1\" failedToParse=\"0\" encrypted=\"0\""));
|
||||
assertEquals(true,
|
||||
actual.contains("validationReports compliant=\"1\" nonCompliant=\"0\" failedJobs=\"0\">"));
|
||||
// test some xml
|
||||
// assertEquals(true, actual.contains("<error
|
||||
// location=\"/*:CrossIndustryInvoice[namespace-uri()='urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100'][1]/*:SupplyChainTradeTransaction[namespace-uri()='urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100'][1]/*:ApplicableHeaderTradeSettlement[namespace-uri()='urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100'][1]/*:SpecifiedTradeSettlementHeaderMonetarySummation[namespace-uri()='urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100'][1]/*:DuePayableAmount[namespace-uri()='urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100'][1]\"
|
||||
// criterion=\"not(@currencyID)\">[CII-DT-031] - currencyID should not be
|
||||
// present</error>"));
|
||||
// test some binary signature recognition
|
||||
assertEquals(true, actual.contains("<version>2</version>"));
|
||||
|
||||
// valid one
|
||||
tempFile = getResourceAsFile("validV2PDF.pdf");
|
||||
|
||||
pv.setFilename(tempFile.getAbsolutePath());
|
||||
vc.clear();
|
||||
pv.validate();
|
||||
actual = pv.getXMLResult();
|
||||
assertEquals(true, actual.contains("validationReport profileName=\"PDF/A-3"));
|
||||
assertEquals(true, actual.contains("batchSummary totalJobs=\"1\" failedToParse=\"0\" encrypted=\"0\""));
|
||||
assertEquals(true,
|
||||
actual.contains("validationReports compliant=\"1\" nonCompliant=\"0\" failedJobs=\"0\">"));
|
||||
|
||||
assertEquals(false, actual.contains("<error"));
|
||||
} catch (IrrecoverableValidationError e) {
|
||||
// ignore, will be in XML output anyway
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void testPDFXMLValidation() {
|
||||
ValidationContext vc = new ValidationContext(null);
|
||||
try {
|
||||
PDFValidator pv = new PDFValidator(vc);
|
||||
|
||||
File tempFile = getResourceAsFile("attributeBasedXMP_zugferd_2p0_EN16931_Einfach.pdf");// need a more
|
||||
// invalid file here
|
||||
|
||||
pv.setFilename(tempFile.getAbsolutePath());
|
||||
pv.validate();
|
||||
String pdfvres = pv.getXMLResult();
|
||||
|
||||
XMLValidator xv = new XMLValidator(vc);
|
||||
|
||||
xv.setStringContent(pv.getRawXML());
|
||||
xv.validate();
|
||||
String xmlvres = xv.getXMLResult();
|
||||
|
||||
assertEquals(true, pdfvres.contains("valid") && !pdfvres.contains("invalid"));
|
||||
assertEquals(true, xmlvres.contains("invalid"));
|
||||
|
||||
vc.clear();
|
||||
tempFile = getResourceAsFile("validV1WithAdditionalData.pdf");// need a more invalid file here
|
||||
|
||||
pv.setFilename(tempFile.getAbsolutePath());
|
||||
pv.validate();
|
||||
pdfvres = pv.getXMLResult();
|
||||
|
||||
xv = new XMLValidator(vc);
|
||||
|
||||
xv.setStringContent(pv.getRawXML());
|
||||
xv.validate();
|
||||
xmlvres = xv.getXMLResult();
|
||||
assertEquals(true, pdfvres.contains("valid") && !pdfvres.contains("invalid"));
|
||||
assertEquals(true, xmlvres.contains("valid") && !xmlvres.contains("invalid"));
|
||||
} catch (IrrecoverableValidationError e) {
|
||||
// ignore, will be in XML output anyway
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void testXMPValidation() {
|
||||
|
||||
ValidationContext vc = new ValidationContext(null);
|
||||
PDFValidator pv = new PDFValidator(vc);
|
||||
try {
|
||||
|
||||
File tempFile = getResourceAsFile("invalidXMP.pdf");
|
||||
|
||||
pv.setFilename(tempFile.getAbsolutePath());
|
||||
vc.clear();
|
||||
pv.validate();
|
||||
String actual = pv.getXMLResult();
|
||||
|
||||
assertEquals(true, actual
|
||||
.contains("<error type=\"12\">XMP Metadata: ConformanceLevel contains invalid value</error>"));
|
||||
|
||||
tempFile = getResourceAsFile("attributeBasedXMP_zugferd_2p0_EN16931_Einfach.pdf");
|
||||
|
||||
pv.setFilename(tempFile.getAbsolutePath());
|
||||
vc.clear();
|
||||
pv.validate();
|
||||
actual = pv.getXMLResult();
|
||||
|
||||
assertEquals(false, actual.contains("<error"));// issue 18: "ConformanceLevel not found" should not be
|
||||
// reported since it's actually there
|
||||
} catch (IrrecoverableValidationError e) {
|
||||
// ignore, will be in XML output anyway
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
package org.mustangproject.library.extended;
|
||||
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
public class ResourceCase extends TestCase {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(ResourceCase.class.getCanonicalName()); // log output is
|
||||
|
||||
public static File getResourceAsFile(String resourcePath) {
|
||||
try {
|
||||
InputStream in = ClassLoader.getSystemClassLoader().getResourceAsStream(resourcePath);
|
||||
if (in == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
File tempFile = File.createTempFile(String.valueOf(in.hashCode()), ".tmp");
|
||||
tempFile.deleteOnExit();
|
||||
|
||||
try (FileOutputStream out = new FileOutputStream(tempFile)) {
|
||||
// copy stream
|
||||
byte[] buffer = new byte[1024];
|
||||
int bytesRead;
|
||||
while ((bytesRead = in.read(buffer)) != -1) {
|
||||
out.write(buffer, 0, bytesRead);
|
||||
}
|
||||
}
|
||||
return tempFile;
|
||||
} catch (IOException e) {
|
||||
LOGGER.error(e.getMessage(), e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,195 +0,0 @@
|
||||
package org.mustangproject.library.extended;
|
||||
|
||||
import static org.xmlunit.assertj.XmlAssert.assertThat;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import javax.xml.transform.Source;
|
||||
|
||||
import org.xmlunit.builder.Input;
|
||||
import org.xmlunit.xpath.JAXPXPathEngine;
|
||||
import org.xmlunit.xpath.XPathEngine;
|
||||
|
||||
public class XMLValidatorTest extends ResourceCase {
|
||||
|
||||
public void testZF2XMLValidation() {
|
||||
// ignored for the
|
||||
// time being
|
||||
|
||||
ValidationContext ctx = new ValidationContext(null);
|
||||
XMLValidator xv = new XMLValidator(ctx);
|
||||
XPathEngine xpath = new JAXPXPathEngine();
|
||||
File tempFile = getResourceAsFile("invalidV2.xml");
|
||||
Source source;
|
||||
String content;
|
||||
|
||||
try {
|
||||
xv.setFilename(tempFile.getAbsolutePath());
|
||||
|
||||
xv.validate();
|
||||
|
||||
/*
|
||||
* assertEquals(true, xv.getXMLResult().
|
||||
* contains("<error location=\"/*[local-name()='CrossIndustryInvoice']/*[local-name()='SupplyChainTradeTransaction']/*[local-name()='ApplicableHeaderTradeSettlement']/*[local-name()='SpecifiedTradeSettlementHeaderMonetarySummation']\" criterion=\"(ram:LineTotalAmount)\">\n"
|
||||
* +
|
||||
* " Eine Rechnung (INVOICE) muss die Summe der Rechnungspositionen-Nettobeträge „Sum of Invoice line net amount“ (BT-106) enthalten.</error>\n"
|
||||
* +
|
||||
* "<error location=\"/*[local-name()='CrossIndustryInvoice']/*[local-name()='SupplyChainTradeTransaction']/*[local-name()='ApplicableHeaderTradeSettlement']/*[local-name()='SpecifiedTradeSettlementHeaderMonetarySummation']\" criterion=\"(ram:TaxBasisTotalAmount = ram:LineTotalAmount - ram:AllowanceTotalAmount + ram:ChargeTotalAmount) or ((ram:TaxBasisTotalAmount = ram:LineTotalAmount - ram:AllowanceTotalAmount) and not (ram:ChargeTotalAmount)) or ((ram:TaxBasisTotalAmount = ram:LineTotalAmount + ram:ChargeTotalAmount) and not (ram:AllowanceTotalAmount)) or ((ram:TaxBasisTotalAmount = ram:LineTotalAmount) and not (ram:ChargeTotalAmount) and not (ram:AllowanceTotalAmount))\">\n"
|
||||
* +
|
||||
* " Der Inhalt des Elementes „Invoice total amount without VAT“ (BT-109) entspricht der Summe aller Inhalte der Elemente „Invoice line net amount“ (BT-131) abzüglich der Summe aller in der Rechnung enthaltenen Nachlässe der Dokumentenebene „Sum of allowances on document level“ (BT-107) zuzüglich der Summe aller in der Rechnung enthaltenen Abgaben der Dokumentenebene „Sum of charges on document level“ (BT-108).</error>\n"
|
||||
* +
|
||||
* "<error location=\"/*[local-name()='CrossIndustryInvoice']/*[local-name()='SupplyChainTradeTransaction']/*[local-name()='ApplicableHeaderTradeSettlement']/*[local-name()='SpecifiedTradeSettlementHeaderMonetarySummation']\" criterion=\"(ram:GrandTotalAmount = round(ram:TaxBasisTotalAmount*100 + ram:TaxTotalAmount[@currencyID=/rsm:CrossIndustryInvoice/rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeSettlement/ram:InvoiceCurrencyCode]*100 +0) div 100) or ((ram:GrandTotalAmount = ram:TaxBasisTotalAmount) and not (ram:TaxTotalAmount[@currencyID=/rsm:CrossIndustryInvoice/rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeSettlement/ram:InvoiceCurrencyCode]))\">\n"
|
||||
* +
|
||||
* " Der Inhalt des Elementes „Invoice total amount with VAT“ (BT-112) entspricht der Summe des Inhalts des Elementes „Invoice total amount without VAT“ (BT-109) und des Elementes „Invoice total VAT amount“ (BT-110).</error>\n"
|
||||
* +
|
||||
* "<error location=\"/*[local-name()='CrossIndustryInvoice']/*[local-name()='SupplyChainTradeTransaction']/*[local-name()='ApplicableHeaderTradeSettlement']/*[local-name()='SpecifiedTradeSettlementHeaderMonetarySummation']\" criterion=\"ram:LineTotalAmount = (round(sum(../../ram:IncludedSupplyChainTradeLineItem/ram:SpecifiedLineTradeSettlement/ram:SpecifiedTradeSettlementLineMonetarySummation/ram:LineTotalAmount) * 10 * 10)div 100)\">\n"
|
||||
* +
|
||||
* " Der Inhalt des Elementes „Sum of Invoice line net amount“ (BT-106) entspricht der Summe aller Inhalte der Elemente „Invoice line net amount“ (BT-131).</error>\n"
|
||||
* +
|
||||
* "<error location=\"/*[local-name()='CrossIndustryInvoice']\" criterion=\"(rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeAgreement/ram:BuyerTradeParty/ram:Name!='')\">\n"
|
||||
* +
|
||||
* " Eine Rechnung (INVOICE) muss den Erwerbernamen „Buyer name“ (BT-44) enthalten.</error>\n"
|
||||
* +
|
||||
* "<error location=\"/*[local-name()='CrossIndustryInvoice']/*[local-name()='SupplyChainTradeTransaction']/*[local-name()='ApplicableHeaderTradeAgreement']/*[local-name()='BuyerTradeParty']\" criterion=\"count(ram:Name)=1\">\n"
|
||||
* + " Das Element 'ram:Name' muss genau 1 mal auftreten.</error>\n" +
|
||||
* "<error location=\"/*[local-name()='CrossIndustryInvoice']/*[local-name()='SupplyChainTradeTransaction']/*[local-name()='ApplicableHeaderTradeSettlement']/*[local-name()='SpecifiedTradeSettlementHeaderMonetarySummation']\" criterion=\"count(ram:LineTotalAmount)=1\">\n"
|
||||
* + " Das Element 'ram:LineTotalAmount' muss genau 1 mal auftreten.</error>\n"
|
||||
* +
|
||||
* "<error location=\"/*[local-name()='CrossIndustryInvoice']/*[local-name()='SupplyChainTradeTransaction']/*[local-name()='IncludedSupplyChainTradeLineItem'][2]/*[local-name()='SpecifiedLineTradeDelivery']/*[local-name()='BilledQuantity']\" criterion=\"document('zugferd2p0_extended_codedb.xml')//cl[@id=7]/enumeration[@value=$codeValue7]\">\n"
|
||||
* + " Wert von '@unitCode' ist unzulässig.</error>\n" +
|
||||
* "</messages><summary status='invalid'/>"));
|
||||
*
|
||||
*/
|
||||
|
||||
tempFile = getResourceAsFile("invalidV2Profile.xml");
|
||||
|
||||
xv.setFilename(tempFile.getAbsolutePath());
|
||||
|
||||
xv.validate();
|
||||
} catch (IrrecoverableValidationError e) {
|
||||
// ignore, will be in XML output anyway
|
||||
}
|
||||
assertTrue(xv.getXMLResult().contains("<error type=\"25\""));
|
||||
ctx.clear();
|
||||
|
||||
try {
|
||||
|
||||
tempFile = getResourceAsFile("FAIL_zugferd_2p1_MINIMUM_Rechnung_380.xml");
|
||||
|
||||
xv.setFilename(tempFile.getAbsolutePath());
|
||||
|
||||
xv.validate();
|
||||
} catch (IrrecoverableValidationError e) {
|
||||
// ignore, will be in XML output anyway
|
||||
}
|
||||
String res = xv.getXMLResult();
|
||||
/*OutputStream os = null;
|
||||
try {
|
||||
os = new FileOutputStream(new File("return.xml"));
|
||||
os.write(res.getBytes(), 0, res.length());
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
try {
|
||||
os.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}*/
|
||||
|
||||
content = "<validation>" + res + "</validation>";
|
||||
|
||||
assertThat(content).valueByXPath("count(//error)")
|
||||
.asInt()
|
||||
.isGreaterThan(1); //2 errors are OK because there is a known bug
|
||||
|
||||
|
||||
assertThat(content).valueByXPath("//error[@type=\"4\"]")
|
||||
.asString()
|
||||
.contains(
|
||||
"In Deutschland sind die Profile MINIMUM und BASIC WL nur als Buchungshilfe (TypeCode: 751) zugelassen.");
|
||||
|
||||
|
||||
ctx.clear();
|
||||
tempFile = getResourceAsFile("validV2Basic.xml");
|
||||
try {
|
||||
|
||||
xv.setFilename(tempFile.getAbsolutePath());
|
||||
xv.validate();
|
||||
assertEquals(true, xv.getXMLResult().contains("valid") && !xv.getXMLResult().contains("invalid"));
|
||||
|
||||
ctx.clear();
|
||||
tempFile = getResourceAsFile("ZUGFeRD-invoice_rabatte_3_abschlag_duepayableamount.xml");
|
||||
xv.setFilename(tempFile.getAbsolutePath());
|
||||
xv.validate();
|
||||
assertEquals(true, xv.getXMLResult().contains("valid") && !xv.getXMLResult().contains("invalid"));
|
||||
|
||||
ctx.clear();
|
||||
tempFile = getResourceAsFile("valid_Avoir_FR_type380_minimum_factur-x.xml");
|
||||
xv.setFilename(tempFile.getAbsolutePath());
|
||||
xv.validate();
|
||||
|
||||
source = Input.fromString("<validation>" + xv.getXMLResult() + "</validation>").build();
|
||||
content = xpath.evaluate("/validation/summary/@status", source);
|
||||
assertEquals("invalid", content);
|
||||
|
||||
// assertEquals(true, xv.getXMLResult().contains("valid") &&
|
||||
// !xv.getXMLResult().contains("invalid"));
|
||||
|
||||
/*
|
||||
* this test failure might have to be upstreamed ctx.clear(); tempFile =
|
||||
* getResourceAsFile(
|
||||
* "ZUGFeRD-invoice_rabatte_4_abschlag_taxbasistotalamount.xml");
|
||||
* xv.setFilename(tempFile.getAbsolutePath()); xv.validate(); assertEquals(true,
|
||||
* xv.getXMLResult().contains("valid") &&
|
||||
* !xv.getXMLResult().contains("invalid"));
|
||||
*/
|
||||
ctx.clear();
|
||||
tempFile = getResourceAsFile("attributeBasedXMP_zugferd_2p0_EN16931_Einfach_corrected.xml");
|
||||
xv.setFilename(tempFile.getAbsolutePath());
|
||||
xv.validate();
|
||||
assertEquals(true, xv.getXMLResult().contains("valid") && !xv.getXMLResult().contains("invalid"));
|
||||
|
||||
ctx.clear();
|
||||
tempFile = getResourceAsFile("validZREtestZugferd.xml");
|
||||
xv.setFilename(tempFile.getAbsolutePath());
|
||||
xv.validate();
|
||||
|
||||
source = Input.fromString("<validation>" + xv.getXMLResult() + "</validation>").build();
|
||||
content = xpath.evaluate("/validation/summary/@status", source);
|
||||
assertEquals("invalid", content);
|
||||
|
||||
} catch (IrrecoverableValidationError e) {
|
||||
// ignore, will be in XML output anyway
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void testZF1XMLValidation() {
|
||||
ValidationContext ctx = new ValidationContext(null);
|
||||
XMLValidator xv = new XMLValidator(ctx);
|
||||
File tempFile = getResourceAsFile("validV1.xml");
|
||||
try {
|
||||
xv.setFilename(tempFile.getAbsolutePath());
|
||||
xv.validate();
|
||||
assertEquals(true, xv.getXMLResult().contains("valid") && !xv.getXMLResult().contains("invalid"));
|
||||
|
||||
tempFile = getResourceAsFile("invalidV1ExtraTags.xml");
|
||||
xv.setFilename(tempFile.getAbsolutePath());
|
||||
xv.validate();
|
||||
assertEquals(true, xv.getXMLResult().contains("invalid"));
|
||||
|
||||
tempFile = getResourceAsFile("invalidV1TooMinimal.xml");
|
||||
xv.setFilename(tempFile.getAbsolutePath());
|
||||
xv.validate();
|
||||
assertEquals(true, xv.getXMLResult().contains("<error type=\"26\""));
|
||||
|
||||
} catch (IrrecoverableValidationError e) {
|
||||
// ignore, will be in XML output anyway
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
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 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();
|
||||
|
||||
res = zfv.validate(tempFile.getAbsolutePath());
|
||||
assertEquals(true, res.contains("status=\"valid\""));
|
||||
assertEquals(false, res.contains("status=\"invalid\""));
|
||||
|
||||
tempFile = getResourceAsFile("validAvoir_FR_type380_BASICWL.pdf");
|
||||
zfv = new ZUGFeRDValidator();
|
||||
|
||||
res = zfv.validate(tempFile.getAbsolutePath());
|
||||
assertEquals(true, res.contains("status=\"valid\""));
|
||||
assertEquals(false, res.contains("status=\"invalid\""));
|
||||
|
||||
}
|
||||
|
||||
/***
|
||||
* the XMLValidatorTests only cover the <xml></xml> part, this one includes the root element and
|
||||
* the global <summary></summary> part as well
|
||||
*/
|
||||
public void testXMLValidation() {
|
||||
File tempFile = getResourceAsFile("validV2.xml");
|
||||
ZUGFeRDValidator zfv = new ZUGFeRDValidator();
|
||||
|
||||
String res = zfv.validate(tempFile.getAbsolutePath());
|
||||
|
||||
assertThat(res).valueByXPath("count(//error)")
|
||||
.asInt()
|
||||
.isEqualTo(0);
|
||||
|
||||
assertThat(res).valueByXPath("count(//notice)")
|
||||
.asInt()
|
||||
.isEqualTo(3); // 3 notices RE XRechnung
|
||||
assertThat(res).valueByXPath("/validation/summary/@status")
|
||||
.asString()
|
||||
.isEqualTo("valid");// expect to be valid because XR notices are, well, only notices
|
||||
assertThat(res).valueByXPath("/validation/xml/summary/@status")
|
||||
.asString()
|
||||
.isEqualTo("valid");
|
||||
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user