killbill-memoizeit

Details

diff --git a/invoice/src/main/java/org/killbill/billing/invoice/template/formatters/DefaultInvoiceFormatter.java b/invoice/src/main/java/org/killbill/billing/invoice/template/formatters/DefaultInvoiceFormatter.java
index 14e2bd7..a3fed26 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/template/formatters/DefaultInvoiceFormatter.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/template/formatters/DefaultInvoiceFormatter.java
@@ -21,13 +21,13 @@ import java.text.DecimalFormat;
 import java.text.DecimalFormatSymbols;
 import java.util.ArrayList;
 import java.util.Collection;
-import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
 import java.util.UUID;
 
+import org.joda.money.CurrencyUnit;
 import org.joda.time.DateTime;
 import org.joda.time.LocalDate;
 import org.joda.time.format.DateTimeFormat;
@@ -55,8 +55,6 @@ import com.google.common.base.Objects;
 import com.google.common.base.Strings;
 import com.google.common.collect.ImmutableList;
 
-import static org.killbill.billing.util.DefaultAmountFormatter.round;
-
 /**
  * Format invoice fields
  */
@@ -71,20 +69,11 @@ public class DefaultInvoiceFormatter implements InvoiceFormatter {
     private final CurrencyConversionApi currencyConversionApi;
     private final InternalTenantContext context;
     private final ResourceBundleFactory bundleFactory;
+    private Map<java.util.Currency, Locale> currencyLocaleMap;
 
-    public static Map<java.util.Currency, Locale> currencyLocaleMap;
-    {
-        currencyLocaleMap = new HashMap<java.util.Currency, Locale>();
-        for (Locale localeItem : Locale.getAvailableLocales()) {
-            try {
-                java.util.Currency currency = java.util.Currency.getInstance(localeItem);
-                currencyLocaleMap.put(currency, localeItem);
-            }catch (Exception e){
-            }
-        }
-    }
-
-    public DefaultInvoiceFormatter(final TranslatorConfig config, final Invoice invoice, final Locale locale, final CurrencyConversionApi currencyConversionApi, final ResourceBundleFactory bundleFactory, final InternalTenantContext context) {
+    public DefaultInvoiceFormatter(final TranslatorConfig config, final Invoice invoice, final Locale locale,
+                                   final CurrencyConversionApi currencyConversionApi, final ResourceBundleFactory bundleFactory,
+                                   final InternalTenantContext context, Map<java.util.Currency, Locale> currencyLocaleMap) {
         this.config = config;
         this.invoice = invoice;
         this.dateFormatter = DateTimeFormat.mediumDate().withLocale(locale);
@@ -92,6 +81,7 @@ public class DefaultInvoiceFormatter implements InvoiceFormatter {
         this.currencyConversionApi = currencyConversionApi;
         this.bundleFactory = bundleFactory;
         this.context = context;
+        this.currencyLocaleMap = currencyLocaleMap;
     }
 
     @Override
@@ -211,17 +201,17 @@ public class DefaultInvoiceFormatter implements InvoiceFormatter {
 
     @Override
     public BigDecimal getChargedAmount() {
-        return round(Objects.firstNonNull(invoice.getChargedAmount(), BigDecimal.ZERO));
+        return Objects.firstNonNull(invoice.getChargedAmount(), BigDecimal.ZERO);
     }
 
     @Override
     public BigDecimal getOriginalChargedAmount() {
-        return round(Objects.firstNonNull(invoice.getOriginalChargedAmount(), BigDecimal.ZERO));
+        return Objects.firstNonNull(invoice.getOriginalChargedAmount(), BigDecimal.ZERO);
     }
 
     @Override
     public BigDecimal getBalance() {
-        return round(Objects.firstNonNull(invoice.getBalance(), BigDecimal.ZERO));
+        return Objects.firstNonNull(invoice.getBalance(), BigDecimal.ZERO);
     }
 
     @Override
@@ -239,22 +229,28 @@ public class DefaultInvoiceFormatter implements InvoiceFormatter {
         return getFormattedAmountByLocaleAndInvoiceCurrency(getBalance());
     }
 
-    /**
-     * Returns the amount with the correct currency symbol
-     *
-     * @param amount
-     * @return
-     */
+    // Returns the formatted amount with the correct currency symbol that is get from the invoice currency.
     private String getFormattedAmountByLocaleAndInvoiceCurrency(BigDecimal amount) {
-        final DecimalFormat number = (DecimalFormat) DecimalFormat.getCurrencyInstance(locale);
 
-        java.util.Currency currency = java.util.Currency.getInstance(invoice.getCurrency().toString());
-        DecimalFormatSymbols dfs = number.getDecimalFormatSymbols();
-        dfs.setInternationalCurrencySymbol(currency.getCurrencyCode());
-        dfs.setCurrencySymbol(currency.getSymbol(currencyLocaleMap.get(currency)));
-        number.setDecimalFormatSymbols(dfs);
+        String invoiceCurrencyCode = invoice.getCurrency().toString();
+        CurrencyUnit currencyUnit = CurrencyUnit.of(invoiceCurrencyCode);
+
+        final DecimalFormat numberFormatter = (DecimalFormat) DecimalFormat.getCurrencyInstance(locale);
+        DecimalFormatSymbols dfs = numberFormatter.getDecimalFormatSymbols();
+        dfs.setInternationalCurrencySymbol(currencyUnit.getCurrencyCode());
 
-        return number.format(amount.doubleValue());
+        try {
+            final java.util.Currency currency = java.util.Currency.getInstance(invoiceCurrencyCode);
+            dfs.setCurrencySymbol(currency.getSymbol(currencyLocaleMap.get(currency)));
+        } catch (Exception e) {
+            dfs.setCurrencySymbol(currencyUnit.getSymbol(locale));
+        }
+
+        numberFormatter.setDecimalFormatSymbols(dfs);
+        numberFormatter.setMinimumFractionDigits(currencyUnit.getDefaultFractionDigits());
+        numberFormatter.setMaximumFractionDigits(currencyUnit.getDefaultFractionDigits());
+
+        return numberFormatter.format(amount.doubleValue());
     }
 
     @Override
@@ -316,7 +312,7 @@ public class DefaultInvoiceFormatter implements InvoiceFormatter {
 
     @Override
     public BigDecimal getPaidAmount() {
-        return round(Objects.firstNonNull(invoice.getPaidAmount(), BigDecimal.ZERO));
+        return Objects.firstNonNull(invoice.getPaidAmount(), BigDecimal.ZERO);
     }
 
     @Override
@@ -365,13 +361,18 @@ public class DefaultInvoiceFormatter implements InvoiceFormatter {
         return invoice;
     }
 
+    @SuppressWarnings("UnusedDeclaration")
+    protected Map<java.util.Currency, Locale> getCurrencyLocaleMap() {
+        return currencyLocaleMap;
+    }
+
     @Override
     public BigDecimal getCreditedAmount() {
-        return round(Objects.firstNonNull(invoice.getCreditedAmount(), BigDecimal.ZERO));
+        return Objects.firstNonNull(invoice.getCreditedAmount(), BigDecimal.ZERO);
     }
 
     @Override
     public BigDecimal getRefundedAmount() {
-        return round(Objects.firstNonNull(invoice.getRefundedAmount(), BigDecimal.ZERO));
+        return Objects.firstNonNull(invoice.getRefundedAmount(), BigDecimal.ZERO);
     }
 }
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/template/formatters/DefaultInvoiceFormatterFactory.java b/invoice/src/main/java/org/killbill/billing/invoice/template/formatters/DefaultInvoiceFormatterFactory.java
index 890fb30..6496df7 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/template/formatters/DefaultInvoiceFormatterFactory.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/template/formatters/DefaultInvoiceFormatterFactory.java
@@ -16,7 +16,10 @@
 
 package org.killbill.billing.invoice.template.formatters;
 
+import java.util.Currency;
+import java.util.HashMap;
 import java.util.Locale;
+import java.util.Map;
 
 import org.killbill.billing.callcontext.InternalTenantContext;
 import org.killbill.billing.currency.api.CurrencyConversionApi;
@@ -24,7 +27,6 @@ import org.killbill.billing.invoice.api.Invoice;
 import org.killbill.billing.invoice.api.formatters.InvoiceFormatter;
 import org.killbill.billing.invoice.api.formatters.InvoiceFormatterFactory;
 import org.killbill.billing.invoice.api.formatters.ResourceBundleFactory;
-import org.killbill.billing.tenant.api.TenantInternalApi;
 import org.killbill.billing.util.template.translation.TranslatorConfig;
 
 public class DefaultInvoiceFormatterFactory implements InvoiceFormatterFactory {
@@ -32,6 +34,17 @@ public class DefaultInvoiceFormatterFactory implements InvoiceFormatterFactory {
     @Override
     public InvoiceFormatter createInvoiceFormatter(final TranslatorConfig config, final Invoice invoice, final Locale locale, CurrencyConversionApi currencyConversionApi,
                                                    final ResourceBundleFactory bundleFactory, final InternalTenantContext context) {
-        return new DefaultInvoiceFormatter(config, invoice, locale, currencyConversionApi, bundleFactory, context);
+
+        // this initialization relies on System.currentTimeMillis() instead of the Kill Bill clock (it won't be accurate when moving the clock)
+        Map<Currency, Locale> currencyLocaleMap = new HashMap<Currency, Locale>();
+        for (Locale localeItem : Locale.getAvailableLocales()) {
+            try {
+                java.util.Currency currency = java.util.Currency.getInstance(localeItem);
+                currencyLocaleMap.put(currency, localeItem);
+            }catch (Exception e){
+            }
+        }
+
+        return new DefaultInvoiceFormatter(config, invoice, locale, currencyConversionApi, bundleFactory, context, currencyLocaleMap);
     }
 }
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/template/formatters/DefaultInvoiceItemFormatter.java b/invoice/src/main/java/org/killbill/billing/invoice/template/formatters/DefaultInvoiceItemFormatter.java
index 51ddcc6..5516c8c 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/template/formatters/DefaultInvoiceItemFormatter.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/template/formatters/DefaultInvoiceItemFormatter.java
@@ -36,14 +36,10 @@ import org.killbill.billing.util.LocaleUtils;
 import org.killbill.billing.util.template.translation.DefaultCatalogTranslator;
 import org.killbill.billing.util.template.translation.Translator;
 import org.killbill.billing.util.template.translation.TranslatorConfig;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
 import com.google.common.base.Objects;
 import com.google.common.base.Strings;
 
-import static org.killbill.billing.util.DefaultAmountFormatter.round;
-
 /**
  * Format invoice item fields
  */
@@ -71,7 +67,7 @@ public class DefaultInvoiceItemFormatter implements InvoiceItemFormatter {
 
     @Override
     public BigDecimal getAmount() {
-        return round(Objects.firstNonNull(item.getAmount(), BigDecimal.ZERO));
+        return Objects.firstNonNull(item.getAmount(), BigDecimal.ZERO);
     }
 
     @Override
@@ -168,7 +164,7 @@ public class DefaultInvoiceItemFormatter implements InvoiceItemFormatter {
 
     @Override
     public BigDecimal getRate() {
-        return round(BigDecimal.ZERO);
+        return BigDecimal.ZERO;
     }
 
     @Override
diff --git a/invoice/src/test/java/org/killbill/billing/invoice/template/formatters/TestDefaultInvoiceFormatter.java b/invoice/src/test/java/org/killbill/billing/invoice/template/formatters/TestDefaultInvoiceFormatter.java
index 82008a6..6d5cc0a 100644
--- a/invoice/src/test/java/org/killbill/billing/invoice/template/formatters/TestDefaultInvoiceFormatter.java
+++ b/invoice/src/test/java/org/killbill/billing/invoice/template/formatters/TestDefaultInvoiceFormatter.java
@@ -88,7 +88,7 @@ public class TestDefaultInvoiceFormatter extends InvoiceTestSuiteNoDB {
         Assert.assertEquals(invoice.getCreditedAmount().doubleValue(), 0.00);
 
         // Verify the merge
-        final InvoiceFormatter formatter = new DefaultInvoiceFormatter(config, invoice, Locale.US, null, resourceBundleFactory, internalCallContext);
+        final InvoiceFormatter formatter = new DefaultInvoiceFormatter(config, invoice, Locale.US, null, resourceBundleFactory, internalCallContext, getAvailableLocales());
         final List<InvoiceItem> invoiceItems = formatter.getInvoiceItems();
         Assert.assertEquals(invoiceItems.size(), 1);
         Assert.assertEquals(invoiceItems.get(0).getInvoiceItemType(), InvoiceItemType.FIXED);
@@ -142,7 +142,7 @@ public class TestDefaultInvoiceFormatter extends InvoiceTestSuiteNoDB {
         Assert.assertEquals(invoice.getRefundedAmount().doubleValue(), -1.00);
 
         // Verify the merge
-        final InvoiceFormatter formatter = new DefaultInvoiceFormatter(config, invoice, Locale.US, null, resourceBundleFactory, internalCallContext);
+        final InvoiceFormatter formatter = new DefaultInvoiceFormatter(config, invoice, Locale.US, null, resourceBundleFactory, internalCallContext, getAvailableLocales());
         final List<InvoiceItem> invoiceItems = formatter.getInvoiceItems();
         Assert.assertEquals(invoiceItems.size(), 4);
         Assert.assertEquals(invoiceItems.get(0).getInvoiceItemType(), InvoiceItemType.FIXED);
@@ -159,7 +159,7 @@ public class TestDefaultInvoiceFormatter extends InvoiceTestSuiteNoDB {
     public void testFormattedAmount() throws Exception {
         final FixedPriceInvoiceItem fixedItemEUR = new FixedPriceInvoiceItem(UUID.randomUUID(), UUID.randomUUID(), null, null,
                                                                              UUID.randomUUID().toString(), UUID.randomUUID().toString(),
-                                                                             new LocalDate(), new BigDecimal("1499.95"), Currency.EUR);
+                                                                             new LocalDate(), new BigDecimal("1499.952"), Currency.EUR);
         final Invoice invoiceEUR = new DefaultInvoice(UUID.randomUUID(), new LocalDate(), new LocalDate(), Currency.EUR);
         invoiceEUR.addInvoiceItem(fixedItemEUR);
 
@@ -191,10 +191,10 @@ public class TestDefaultInvoiceFormatter extends InvoiceTestSuiteNoDB {
         final FixedPriceInvoiceItem fixedItem = new FixedPriceInvoiceItem(UUID.randomUUID(), UUID.randomUUID(), null, null,
                                                                              UUID.randomUUID().toString(), UUID.randomUUID().toString(),
                                                                              new LocalDate(), new BigDecimal("1500.00"), Currency.JPY);
-        final Invoice invoiceEUR = new DefaultInvoice(UUID.randomUUID(), new LocalDate(), new LocalDate(), Currency.JPY);
-        invoiceEUR.addInvoiceItem(fixedItem);
+        final Invoice invoice = new DefaultInvoice(UUID.randomUUID(), new LocalDate(), new LocalDate(), Currency.JPY);
+        invoice.addInvoiceItem(fixedItem);
 
-        checkOutput(invoiceEUR,
+        checkOutput(invoice,
                     "<tr>\n" +
                     "    <td class=\"amount\"><strong>{{invoice.formattedChargedAmount}}</strong></td>\n" +
                     "</tr>\n" +
@@ -205,26 +205,57 @@ public class TestDefaultInvoiceFormatter extends InvoiceTestSuiteNoDB {
                     "    <td class=\"amount\"><strong>{{invoice.formattedBalance}}</strong></td>\n" +
                     "</tr>",
                     "<tr>\n" +
-                    "    <td class=\"amount\"><strong>1 500,00 ¥</strong></td>\n" +
+                    "    <td class=\"amount\"><strong>1 500 ¥</strong></td>\n" +
                     "</tr>\n" +
                     "<tr>\n" +
-                    "    <td class=\"amount\"><strong>0,00 ¥</strong></td>\n" +
+                    "    <td class=\"amount\"><strong>0 ¥</strong></td>\n" +
                     "</tr>\n" +
                     "<tr>\n" +
-                    "    <td class=\"amount\"><strong>1 500,00 ¥</strong></td>\n" +
+                    "    <td class=\"amount\"><strong>1 500 ¥</strong></td>\n" +
                     "</tr>",
                     Locale.FRANCE);
     }
 
     @Test(groups = "fast")
+    public void testFormattedAmountUSAndBTC() throws Exception {
+
+        final FixedPriceInvoiceItem fixedItem = new FixedPriceInvoiceItem(UUID.randomUUID(), UUID.randomUUID(), null, null,
+                                                                          UUID.randomUUID().toString(), UUID.randomUUID().toString(),
+                                                                          new LocalDate(), new BigDecimal("1105.28843439"), Currency.BTC);
+        final Invoice invoice = new DefaultInvoice(UUID.randomUUID(), new LocalDate(), new LocalDate(), Currency.BTC);
+        invoice.addInvoiceItem(fixedItem);
+
+        checkOutput(invoice,
+                    "<tr>\n" +
+                    "    <td class=\"amount\"><strong>{{invoice.formattedChargedAmount}}</strong></td>\n" +
+                    "</tr>\n" +
+                    "<tr>\n" +
+                    "    <td class=\"amount\"><strong>{{invoice.formattedPaidAmount}}</strong></td>\n" +
+                    "</tr>\n" +
+                    "<tr>\n" +
+                    "    <td class=\"amount\"><strong>{{invoice.formattedBalance}}</strong></td>\n" +
+                    "</tr>",
+                    "<tr>\n" +
+                    "    <td class=\"amount\"><strong>BTC1,105.28843439</strong></td>\n" +
+                    "</tr>\n" +
+                    "<tr>\n" +
+                    "    <td class=\"amount\"><strong>BTC0.00000000</strong></td>\n" +
+                    "</tr>\n" +
+                    "<tr>\n" +
+                    "    <td class=\"amount\"><strong>BTC1,105.28843439</strong></td>\n" +
+                    "</tr>",
+                    Locale.US);
+    }
+
+    @Test(groups = "fast")
     public void testFormattedAmountUSAndEUR() throws Exception {
-        final FixedPriceInvoiceItem fixedItemEUR = new FixedPriceInvoiceItem(UUID.randomUUID(), UUID.randomUUID(), null, null,
+        final FixedPriceInvoiceItem fixedItem = new FixedPriceInvoiceItem(UUID.randomUUID(), UUID.randomUUID(), null, null,
                                                                              UUID.randomUUID().toString(), UUID.randomUUID().toString(),
                                                                              new LocalDate(), new BigDecimal("2635.14"), Currency.EUR);
-        final Invoice invoiceEUR = new DefaultInvoice(UUID.randomUUID(), new LocalDate(), new LocalDate(), Currency.EUR);
-        invoiceEUR.addInvoiceItem(fixedItemEUR);
+        final Invoice invoice = new DefaultInvoice(UUID.randomUUID(), new LocalDate(), new LocalDate(), Currency.EUR);
+        invoice.addInvoiceItem(fixedItem);
 
-        checkOutput(invoiceEUR,
+        checkOutput(invoice,
                     "<tr>\n" +
                     "    <td class=\"amount\"><strong>{{invoice.formattedChargedAmount}}</strong></td>\n" +
                     "</tr>\n" +
@@ -278,13 +309,13 @@ public class TestDefaultInvoiceFormatter extends InvoiceTestSuiteNoDB {
 
     @Test(groups = "fast")
     public void testFormattedAmountUSAndGBP() throws Exception {
-        final FixedPriceInvoiceItem fixedItemEUR = new FixedPriceInvoiceItem(UUID.randomUUID(), UUID.randomUUID(), null, null,
+        final FixedPriceInvoiceItem fixedItem = new FixedPriceInvoiceItem(UUID.randomUUID(), UUID.randomUUID(), null, null,
                                                                              UUID.randomUUID().toString(), UUID.randomUUID().toString(),
                                                                              new LocalDate(), new BigDecimal("1499.95"), Currency.GBP);
-        final Invoice invoiceEUR = new DefaultInvoice(UUID.randomUUID(), new LocalDate(), new LocalDate(), Currency.GBP);
-        invoiceEUR.addInvoiceItem(fixedItemEUR);
+        final Invoice invoice = new DefaultInvoice(UUID.randomUUID(), new LocalDate(), new LocalDate(), Currency.GBP);
+        invoice.addInvoiceItem(fixedItem);
 
-        checkOutput(invoiceEUR,
+        checkOutput(invoice,
                     "<tr>\n" +
                     "    <td class=\"amount\"><strong>{{invoice.formattedChargedAmount}}</strong></td>\n" +
                     "</tr>\n" +
@@ -449,7 +480,7 @@ public class TestDefaultInvoiceFormatter extends InvoiceTestSuiteNoDB {
 
         data.put("text", translator);
 
-        data.put("invoice", new DefaultInvoiceFormatter(config, invoice, Locale.US, currencyConversionApi, resourceBundleFactory, internalCallContext));
+        data.put("invoice", new DefaultInvoiceFormatter(config, invoice, Locale.US, currencyConversionApi, resourceBundleFactory, internalCallContext, getAvailableLocales()));
 
         final String formattedText = templateEngine.executeTemplateText(template, data);
 
@@ -458,9 +489,23 @@ public class TestDefaultInvoiceFormatter extends InvoiceTestSuiteNoDB {
 
     private void checkOutput(final Invoice invoice, final String template, final String expected, final Locale locale) {
         final Map<String, Object> data = new HashMap<String, Object>();
-        data.put("invoice", new DefaultInvoiceFormatter(config, invoice, locale, null, resourceBundleFactory, internalCallContext));
+        data.put("invoice", new DefaultInvoiceFormatter(config, invoice, locale, null, resourceBundleFactory, internalCallContext, getAvailableLocales()));
 
         final String formattedText = templateEngine.executeTemplateText(template, data);
         Assert.assertEquals(formattedText, expected);
     }
+
+    private Map<java.util.Currency, Locale> getAvailableLocales() {
+        Map<java.util.Currency, Locale> currencyLocaleMap = new HashMap<java.util.Currency, Locale>();
+
+        currencyLocaleMap.put(java.util.Currency.getInstance(Locale.US), Locale.US);
+        currencyLocaleMap.put(java.util.Currency.getInstance(Locale.FRANCE), Locale.FRANCE);
+        currencyLocaleMap.put(java.util.Currency.getInstance(Locale.JAPAN), Locale.JAPAN);
+        currencyLocaleMap.put(java.util.Currency.getInstance(Locale.UK), Locale.UK);
+        Locale brLocale = new Locale("pt", "BR");
+        currencyLocaleMap.put(java.util.Currency.getInstance(brLocale), brLocale);
+
+        return currencyLocaleMap;
+    }
+
 }