diff --git a/generator/src/main/java/net/codecrete/qrbill/canvas/SVGCanvas.java b/generator/src/main/java/net/codecrete/qrbill/canvas/SVGCanvas.java index 09db43c..c235005 100644 --- a/generator/src/main/java/net/codecrete/qrbill/canvas/SVGCanvas.java +++ b/generator/src/main/java/net/codecrete/qrbill/canvas/SVGCanvas.java @@ -27,6 +27,9 @@ public class SVGCanvas extends AbstractCanvas implements ByteArrayResult { private double lastPositionY; private StringBuilder path; private int approxPathLength; + private final DecimalFormat numberFormat = new DecimalFormat("#.###", new DecimalFormatSymbols(Locale.UK)); + + private final DecimalFormat angleFormat = new DecimalFormat("#.#####", new DecimalFormatSymbols(Locale.UK)); /** * Creates a new instance of the specified size. @@ -235,7 +238,7 @@ public void setTransformation(double translateX, double translateY, double rotat stream.write(formatCoordinate(-translateY)); if (rotate != 0) { stream.write(") rotate("); - stream.write(ANGLE_FORMAT.get().format(-rotate / Math.PI * 180)); + stream.write(angleFormat.format(-rotate / Math.PI * 180)); } if (scaleX != 1 || scaleY != 1) { stream.write(") scale("); @@ -280,18 +283,12 @@ public void saveAs(Path path) throws IOException { } } - private static final ThreadLocal NUMBER_FORMAT - = ThreadLocal.withInitial(() -> new DecimalFormat("#.###", new DecimalFormatSymbols(Locale.UK))); - - private static final ThreadLocal ANGLE_FORMAT - = ThreadLocal.withInitial(() -> new DecimalFormat("#.#####", new DecimalFormatSymbols(Locale.UK))); - - private static String formatNumber(double value) { - return NUMBER_FORMAT.get().format(value); + private String formatNumber(double value) { + return numberFormat.format(value); } - private static String formatCoordinate(double value) { - return NUMBER_FORMAT.get().format(value * MM_TO_PT); + private String formatCoordinate(double value) { + return numberFormat.format(value * MM_TO_PT); } private static String formatColor(int color) { diff --git a/generator/src/main/java/net/codecrete/qrbill/generator/BillTextFormatter.java b/generator/src/main/java/net/codecrete/qrbill/generator/BillTextFormatter.java index e4f9869..593735d 100644 --- a/generator/src/main/java/net/codecrete/qrbill/generator/BillTextFormatter.java +++ b/generator/src/main/java/net/codecrete/qrbill/generator/BillTextFormatter.java @@ -15,15 +15,6 @@ */ public class BillTextFormatter { - private static final ThreadLocal AMOUNT_DISPLAY_FORMAT = ThreadLocal.withInitial(() -> { - DecimalFormat format = new DecimalFormat("###,##0.00"); - DecimalFormatSymbols symbols = new DecimalFormatSymbols(Locale.US); - symbols.setDecimalSeparator('.'); - symbols.setGroupingSeparator(' '); - format.setDecimalFormatSymbols(symbols); - return format; - }); - private final Bill bill; /** @@ -168,7 +159,7 @@ public String getAdditionalInformation() { } private static String formatAmountForDisplay(BigDecimal amount) { - return AMOUNT_DISPLAY_FORMAT.get().format(amount); + return createAmountFormatter().format(amount); } private static String formatAddressForDisplay(Address address, boolean withCountryCode) { @@ -251,4 +242,14 @@ private Address createReducedAddress(Address address) { return reducedAddress; } + + private static DecimalFormat createAmountFormatter() { + DecimalFormat format = new DecimalFormat("###,##0.00"); + DecimalFormatSymbols symbols = new DecimalFormatSymbols(Locale.US); + symbols.setDecimalSeparator('.'); + symbols.setGroupingSeparator(' '); + format.setDecimalFormatSymbols(symbols); + return format; + } + } diff --git a/generator/src/main/java/net/codecrete/qrbill/generator/QRCodeText.java b/generator/src/main/java/net/codecrete/qrbill/generator/QRCodeText.java index e8a4854..1fb5edc 100644 --- a/generator/src/main/java/net/codecrete/qrbill/generator/QRCodeText.java +++ b/generator/src/main/java/net/codecrete/qrbill/generator/QRCodeText.java @@ -102,17 +102,17 @@ private void appendDataField(String value) { textBuilder.append('\n').append(value); } - private static final ThreadLocal AMOUNT_FIELD_FORMAT = ThreadLocal.withInitial(() -> { + private static DecimalFormat createAmountFormatter() { DecimalFormat format = new DecimalFormat("0.00"); DecimalFormatSymbols symbols = new DecimalFormatSymbols(Locale.US); symbols.setDecimalSeparator('.'); format.setDecimalFormatSymbols(symbols); format.setParseBigDecimal(true); return format; - }); + } private static String formatAmountForCode(BigDecimal amount) { - return AMOUNT_FIELD_FORMAT.get().format(amount); + return createAmountFormatter().format(amount); } // According to a letter from SIX dated August 5, 2020, only the major number (leading "02") should be checked @@ -156,7 +156,7 @@ public static Bill decode(String text) { if (lines[18].length() > 0) { ParsePosition position = new ParsePosition(0); - BigDecimal amount = (BigDecimal) AMOUNT_FIELD_FORMAT.get().parse(lines[18], position); + BigDecimal amount = (BigDecimal) createAmountFormatter().parse(lines[18], position); if (position.getIndex() == lines[18].length()) bill.setAmount(amount); else diff --git a/generator/src/main/java/net/codecrete/qrbill/generator/SwicoS1Decoder.java b/generator/src/main/java/net/codecrete/qrbill/generator/SwicoS1Decoder.java index a2ca26c..8f8ec3b 100644 --- a/generator/src/main/java/net/codecrete/qrbill/generator/SwicoS1Decoder.java +++ b/generator/src/main/java/net/codecrete/qrbill/generator/SwicoS1Decoder.java @@ -6,15 +6,6 @@ // package net.codecrete.qrbill.generator; -/// -/// Decodes structured bill information according to Swico S1 syntax. -/// -/// The encoded bill information can be found in a Swiss QR bill in th field StrdBkgInf. -/// -/// -/// Also see http://swiss-qr-invoice.org/downloads/qr-bill-s1-syntax-de.pdf -/// -/// import net.codecrete.qrbill.generator.SwicoBillInformation.PaymentCondition; import net.codecrete.qrbill.generator.SwicoBillInformation.RateDetail; @@ -22,6 +13,7 @@ import java.math.BigDecimal; import java.text.DecimalFormat; import java.text.DecimalFormatSymbols; +import java.text.NumberFormat; import java.text.ParsePosition; import java.time.LocalDate; import java.time.format.DateTimeFormatter; @@ -58,8 +50,10 @@ public class SwicoS1Decoder { private static final int PAYMENT_CONDITIONS_TAG = 40; + private NumberFormat numberFormat; + + private SwicoS1Decoder() { - // don't instantiate } /** @@ -72,6 +66,10 @@ private SwicoS1Decoder() { * @return the decoded bill information (or {@code null} if no valid Swico bill information is found) */ static SwicoBillInformation decode(String billInfoText) { + return new SwicoS1Decoder().decodeIt(billInfoText); + } + + private SwicoBillInformation decodeIt(String billInfoText) { if (billInfoText == null || !billInfoText.startsWith("//S1/")) return null; @@ -98,7 +96,7 @@ static SwicoBillInformation decode(String billInfoText) { return billInformation; } - private static void decodeElement(SwicoBillInformation billInformation, int tag, String value) { + private void decodeElement(SwicoBillInformation billInformation, int tag, String value) { if (value.length() == 0) return; @@ -156,7 +154,7 @@ private static void setVatDates(SwicoBillInformation billInformation, String val } } - private static void setVatRateDetails(SwicoBillInformation billInformation, String value) { + private void setVatRateDetails(SwicoBillInformation billInformation, String value) { // Test for single VAT rate vs list of tuples if (!value.contains(":") && !value.contains(";")) { billInformation.setVatRate(getDecimalValue(value)); @@ -167,7 +165,7 @@ private static void setVatRateDetails(SwicoBillInformation billInformation, Stri } } - private static void setPaymentConditions(SwicoBillInformation billInformation, String value) { + private void setPaymentConditions(SwicoBillInformation billInformation, String value) { // Split into tuples String[] tuples = value.split(";"); @@ -188,7 +186,7 @@ private static void setPaymentConditions(SwicoBillInformation billInformation, S billInformation.setPaymentConditions(list); } - private static List parseDetailList(String text) { + private List parseDetailList(String text) { // Split into tuples String[] tuples = text.split(";"); @@ -245,15 +243,15 @@ private static Integer getIntValue(String intText) { } } - private static final ThreadLocal SWICO_NUMBER_FORMAT = ThreadLocal.withInitial(() -> { - DecimalFormat format = new DecimalFormat("0.###", new DecimalFormatSymbols(Locale.UK)); - format.setParseBigDecimal(true); - return format; - }); + private BigDecimal getDecimalValue(String decimalText) { + if (numberFormat == null) { + DecimalFormat format = new DecimalFormat("0.###", new DecimalFormatSymbols(Locale.UK)); + format.setParseBigDecimal(true); + numberFormat = format; + } - private static BigDecimal getDecimalValue(String decimalText) { ParsePosition position = new ParsePosition(0); - BigDecimal decimal = (BigDecimal) SWICO_NUMBER_FORMAT.get().parse(decimalText, position); + BigDecimal decimal = (BigDecimal) numberFormat.parse(decimalText, position); return (position.getIndex() == decimalText.length()) ? decimal : null; } diff --git a/generator/src/main/java/net/codecrete/qrbill/generator/SwicoS1Encoder.java b/generator/src/main/java/net/codecrete/qrbill/generator/SwicoS1Encoder.java index 34827c6..18a5beb 100644 --- a/generator/src/main/java/net/codecrete/qrbill/generator/SwicoS1Encoder.java +++ b/generator/src/main/java/net/codecrete/qrbill/generator/SwicoS1Encoder.java @@ -30,8 +30,9 @@ */ class SwicoS1Encoder { + private DecimalFormat numberFormat; + private SwicoS1Encoder() { - // may not instantiate } /** @@ -41,6 +42,10 @@ private SwicoS1Encoder() { * @return encoded bill information text */ static String encode(SwicoBillInformation billInfo) { + return new SwicoS1Encoder().encodeIt(billInfo); + } + + private String encodeIt(SwicoBillInformation billInfo) { StringBuilder sb = new StringBuilder(); sb.append("//S1"); @@ -88,21 +93,17 @@ private static String escapedText(String text) { return text.replace("\\", "\\\\").replace("/", "\\/"); } - private static final DateTimeFormatter SWICO_DATE_FORMAT - = DateTimeFormatter.ofPattern("yyMMdd", Locale.UK); - private static String s1Date(LocalDate date) { - return date.format(SWICO_DATE_FORMAT); + return date.format(DATE_FORMAT); } - private static final ThreadLocal SWICO_NUMBER_FORMAT - = ThreadLocal.withInitial(() -> new DecimalFormat("0.###", new DecimalFormatSymbols(Locale.UK))); - - private static String s1Number(BigDecimal num) { - return SWICO_NUMBER_FORMAT.get().format(num); + private String s1Number(BigDecimal num) { + if (numberFormat == null) + numberFormat = new DecimalFormat("0.###", new DecimalFormatSymbols(Locale.UK)); + return numberFormat.format(num); } - private static void appendRateDetailTupleList(StringBuilder sb, List list) { + private void appendRateDetailTupleList(StringBuilder sb, List list) { boolean isFirst = true; for (RateDetail e : list) { if (!isFirst) @@ -113,7 +114,7 @@ private static void appendRateDetailTupleList(StringBuilder sb, List } } - private static void appendConditionTupleList(StringBuilder sb, List list) { + private void appendConditionTupleList(StringBuilder sb, List list) { boolean isFirst = true; for (PaymentCondition e : list) { if (!isFirst) @@ -123,4 +124,6 @@ private static void appendConditionTupleList(StringBuilder sb, List