From ff9d1544c53ec63b874f06e02fadc8bd02fa42b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jochen=20Sta=CC=88rk?= Date: Sun, 11 Aug 2019 18:47:36 +0200 Subject: [PATCH] closes #102 --- History.md | 6 + doc/development_documentation.md | 7 +- .../ZUGFeRD/ZUGFeRD2PullProvider.java | 103 +++++++++++++----- .../mustangproject/ZUGFeRD/ZF2EdgeTest.java | 3 +- 4 files changed, 86 insertions(+), 33 deletions(-) diff --git a/History.md b/History.md index fdfa3e44..81ce7e8c 100644 --- a/History.md +++ b/History.md @@ -1,3 +1,9 @@ +1.7.4 +===== + +- #102 XML entities for ZF2 export + + 1.7.3 ===== 2019-08-01 diff --git a/doc/development_documentation.md b/doc/development_documentation.md index 5d8dc9e6..f9439a5e 100644 --- a/doc/development_documentation.md +++ b/doc/development_documentation.md @@ -11,9 +11,12 @@ mvn clean package ## Test -`package -Dmaven.surefire.debug="-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 -Xnoagent -Djava.compiler=NONE"` -can be used as debug configuration goal in eclipse +`package -Dmaven.surefire.debug="-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8001 -Xnoagent -Djava.compiler=NONE"` +can be used as debug configuration goal in Eclipse. In that case you can set breakpoints in tests. +## Validate + +[ZUV](https://github.com/ZUGFeRD/ZUV/) can be used to validate generated files. ## Deployment diff --git a/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRD2PullProvider.java b/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRD2PullProvider.java index ed19e23f..bd8a70ff 100644 --- a/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRD2PullProvider.java +++ b/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRD2PullProvider.java @@ -201,6 +201,46 @@ public class ZUGFeRD2PullProvider implements IXMLProvider, IProfileProvider { public String getProfile() { return "urn:cen.eu:en16931:2017"; } + + 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(); + } @Override public void generateXML(IZUGFeRDExportableTransaction trans) { @@ -210,7 +250,7 @@ public class ZUGFeRD2PullProvider implements IXMLProvider, IProfileProvider { String senderReg = ""; if (trans.getOwnOrganisationFullPlaintextInfo() != null) { senderReg = "" + "\n" + " \n" - + trans.getOwnOrganisationFullPlaintextInfo() + " \n" + + encodeXML(trans.getOwnOrganisationFullPlaintextInfo()) + " \n" + "REG\n" + "\n"; } @@ -232,7 +272,7 @@ public class ZUGFeRD2PullProvider implements IXMLProvider, IProfileProvider { + " \n" //$NON-NLS-1$ + " \n" //$NON-NLS-1$ + " \n" //$NON-NLS-1$ - + " " + trans.getNumber() + "\n" //$NON-NLS-1$ //$NON-NLS-2$ + + " " + encodeXML(trans.getNumber()) + "\n" //$NON-NLS-1$ //$NON-NLS-2$ // + " RECHNUNG\n" //$NON-NLS-1$ + " 380\n" //$NON-NLS-1$ + " " //$NON-NLS-1$ @@ -268,8 +308,8 @@ public class ZUGFeRD2PullProvider implements IXMLProvider, IProfileProvider { // + " 4012345001235\n" // + " KR3M\n" // + " 55T01\n" - + " " + currentItem.getProduct().getName() + "\n" //$NON-NLS-1$ //$NON-NLS-2$ - + " " + currentItem.getProduct().getDescription() //$NON-NLS-1$ + + " " + encodeXML(currentItem.getProduct().getName()) + "\n" //$NON-NLS-1$ //$NON-NLS-2$ + + " " + encodeXML(currentItem.getProduct().getDescription()) //$NON-NLS-1$ + "\n" //$NON-NLS-1$ + " \n" //$NON-NLS-1$ @@ -277,7 +317,7 @@ public class ZUGFeRD2PullProvider implements IXMLProvider, IProfileProvider { + " \n" //$NON-NLS-1$ + " " + priceFormat(currentItem.getPrice()) //$NON-NLS-1$ + "\n" //$NON-NLS-1$ //currencyID=\"EUR\" - + " 1.0000\n" //$NON-NLS-1$ // + " \n" // + " false\n" @@ -288,13 +328,13 @@ public class ZUGFeRD2PullProvider implements IXMLProvider, IProfileProvider { + " \n" //$NON-NLS-1$ + " " + priceFormat(currentItem.getPrice()) //$NON-NLS-1$ + "\n" //$NON-NLS-1$ // currencyID=\"EUR\" - + " 1.0000\n" //$NON-NLS-1$ + " \n" //$NON-NLS-1$ + " \n" //$NON-NLS-1$ + " \n" //$NON-NLS-1$ - + " " //$NON-NLS-1$ //$NON-NLS-2$ + + " " //$NON-NLS-1$ //$NON-NLS-2$ + quantityFormat(currentItem.getQuantity()) + "\n" //$NON-NLS-1$ + " \n" //$NON-NLS-1$ + " \n" //$NON-NLS-1$ @@ -315,34 +355,34 @@ public class ZUGFeRD2PullProvider implements IXMLProvider, IProfileProvider { xml = xml + " \n"; //$NON-NLS-1$ if (trans.getReferenceNumber() != null) { - xml = xml + " " + trans.getReferenceNumber() + "\n"; + xml = xml + " " + encodeXML(trans.getReferenceNumber()) + "\n"; } xml = xml + " \n" //$NON-NLS-1$ // + " 4000001123452\n" - + " " + trans.getOwnOrganisationName() + "\n"; //$NON-NLS-1$ //$NON-NLS-2$ + + " " + encodeXML(trans.getOwnOrganisationName()) + "\n"; //$NON-NLS-1$ //$NON-NLS-2$ if ((trans.getOwnVATID()!=null)&&(trans.getOwnOrganisationName()!=null)) { xml = xml + " \n" + " " - + trans.getOwnVATID() + "\n" + " " - + trans.getOwnOrganisationName() + "\n" + + encodeXML(trans.getOwnVATID()) + "\n" + " " + + encodeXML(trans.getOwnOrganisationName()) + "\n" + " "; } if (trans.getOwnContact() != null) { - xml = xml + "\n" + " " + trans.getOwnContact().getName() + xml = xml + "\n" + " " + encodeXML(trans.getOwnContact().getName()) + "\n"; if (trans.getOwnContact().getPhone() != null) { xml = xml + " \n" + " " - + trans.getOwnContact().getPhone() + "\n" + + encodeXML(trans.getOwnContact().getPhone()) + "\n" + " \n"; } if (trans.getOwnContact().getEMail() != null) { xml = xml + " \n" + " " - + trans.getOwnContact().getEMail() + "\n" + + encodeXML(trans.getOwnContact().getEMail()) + "\n" + " \n"; } xml = xml + " "; @@ -350,33 +390,33 @@ public class ZUGFeRD2PullProvider implements IXMLProvider, IProfileProvider { } xml = xml + " \n" + " " - + trans.getOwnZIP() + "\n" + " " - + trans.getOwnStreet() + "\n" + " " + trans.getOwnLocation() - + "\n" + " " + trans.getOwnCountry() + + encodeXML(trans.getOwnZIP()) + "\n" + " " + + encodeXML(trans.getOwnStreet()) + "\n" + " " + encodeXML(trans.getOwnLocation()) + + "\n" + " " + encodeXML(trans.getOwnCountry()) + "\n" + " \n" + " \n" //$NON-NLS-1$ - + " " + trans.getOwnTaxID() + "\n" //$NON-NLS-1$ //$NON-NLS-2$ + + " " + encodeXML(trans.getOwnTaxID()) + "\n" //$NON-NLS-1$ //$NON-NLS-2$ + " \n" //$NON-NLS-1$ + " \n" //$NON-NLS-1$ - + " " + trans.getOwnVATID() + "\n" //$NON-NLS-1$ //$NON-NLS-2$ + + " " + encodeXML(trans.getOwnVATID()) + "\n" //$NON-NLS-1$ //$NON-NLS-2$ + " \n" //$NON-NLS-1$ + " \n" //$NON-NLS-1$ + " \n" //$NON-NLS-1$ // + " GE2020211\n" // + " 4000001987658\n" - + " " + trans.getRecipient().getName() + "\n" //$NON-NLS-1$ //$NON-NLS-2$ + + " " + encodeXML(trans.getRecipient().getName()) + "\n" //$NON-NLS-1$ //$NON-NLS-2$ // + " \n" // + " xxx\n" // + " \n" + " \n" //$NON-NLS-1$ - + " " + trans.getRecipient().getZIP() + "\n" //$NON-NLS-1$ //$NON-NLS-2$ - + " " + trans.getRecipient().getStreet() + "\n" //$NON-NLS-1$ //$NON-NLS-2$ - + " " + trans.getRecipient().getLocation() + "\n" //$NON-NLS-1$ //$NON-NLS-2$ - + " " + trans.getRecipient().getCountry() + "\n" //$NON-NLS-1$ //$NON-NLS-2$ + + " " + encodeXML(trans.getRecipient().getZIP()) + "\n" //$NON-NLS-1$ //$NON-NLS-2$ + + " " + encodeXML(trans.getRecipient().getStreet()) + "\n" //$NON-NLS-1$ //$NON-NLS-2$ + + " " + encodeXML(trans.getRecipient().getLocation()) + "\n" //$NON-NLS-1$ //$NON-NLS-2$ + + " " + encodeXML(trans.getRecipient().getCountry()) + "\n" //$NON-NLS-1$ //$NON-NLS-2$ + " \n"; //$NON-NLS-1$ if (trans.getRecipient().getVATID() != null) { xml += " \n" //$NON-NLS-1$ - + " " + trans.getRecipient().getVATID() + "\n" //$NON-NLS-1$ //$NON-NLS-2$ + + " " + encodeXML(trans.getRecipient().getVATID()) + "\n" //$NON-NLS-1$ //$NON-NLS-2$ + " \n"; //$NON-NLS-1$ } xml += " \n" //$NON-NLS-1$ @@ -396,7 +436,7 @@ public class ZUGFeRD2PullProvider implements IXMLProvider, IProfileProvider { * " \n" */ + " \n" + " \n" //$NON-NLS-2$ - + " " + trans.getNumber() + "\n" //$NON-NLS-1$ //$NON-NLS-2$ + + " " + encodeXML(trans.getNumber()) + "\n" //$NON-NLS-1$ //$NON-NLS-2$ + " EUR\n"; //$NON-NLS-1$ for (IZUGFeRDTradeSettlementPayment payment : trans.getTradeSettlementPayment()) { @@ -404,11 +444,14 @@ public class ZUGFeRD2PullProvider implements IXMLProvider, IProfileProvider { + " 42\n" //$NON-NLS-1$ + " Überweisung\n" //$NON-NLS-1$ + " \n" //$NON-NLS-1$ - + " " + payment.getOwnIBAN() + "\n" //$NON-NLS-1$ //$NON-NLS-2$ - + " " + payment.getOwnKto() + "\n" //$NON-NLS-1$ //$NON-NLS-2$ - + " \n" //$NON-NLS-1$ + + " " + encodeXML(payment.getOwnIBAN()) + "\n"; //$NON-NLS-1$ //$NON-NLS-2$ + if (payment.getOwnKto()!=null) { + xml+= " " + encodeXML(payment.getOwnKto()) + "\n"; //$NON-NLS-1$ //$NON-NLS-2$ + + } + xml+= " \n" //$NON-NLS-1$ + " \n" //$NON-NLS-1$ - + " " + payment.getOwnBIC() + "\n" //$NON-NLS-1$ //$NON-NLS-2$ + + " " + encodeXML(payment.getOwnBIC()) + "\n" //$NON-NLS-1$ //$NON-NLS-2$ // + " "+trans.getOwnBankName()+"\n" //$NON-NLS-1$ // //$NON-NLS-2$ + " \n" //$NON-NLS-1$ diff --git a/src/test/java/org/mustangproject/ZUGFeRD/ZF2EdgeTest.java b/src/test/java/org/mustangproject/ZUGFeRD/ZF2EdgeTest.java index 830e657d..a4f4dc13 100644 --- a/src/test/java/org/mustangproject/ZUGFeRD/ZF2EdgeTest.java +++ b/src/test/java/org/mustangproject/ZUGFeRD/ZF2EdgeTest.java @@ -116,7 +116,7 @@ public class ZF2EdgeTest extends MustangReaderTestCase { public IZUGFeRDExportableItem[] getZFItems() { Item[] allItems = new Item[3]; Product designProduct = new Product("", "Künstlerische Gestaltung (Stunde): Einer Beispielrechnung", "HUR", new BigDecimal("7.000000")); - Product balloonProduct = new Product("", "Bestellerweiterung für E&F Umbau", "C62", new BigDecimal("19.000000"));// test for issue 102 + Product balloonProduct = new Product("", "Bestellerweiterung für E&F Umbau", "C62", new BigDecimal("19.000000"));// test for issue 103 Product airProduct = new Product("", "Heiße Luft pro Liter", "LTR", new BigDecimal("19.000000")); allItems[0] = new Item(new BigDecimal("160"), new BigDecimal("1"), designProduct); @@ -167,6 +167,7 @@ public class ZF2EdgeTest extends MustangReaderTestCase { return new TestSuite(ZF2EdgeTest.class); } + // //////// TESTS ////////////////////////////////////////////////////////////////////////////////////////// /**