Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Customizing a MonetaryAmountFormat using the Moneta (JavaMoney) JSR354 implemenation

Tags:

java

jsr354

I'm really confused about how to customize a MonetaryAmountFormat using the Moneta JSR-354 implementation.

My intention is to be able to parse both 1.23 and $3.45 as MonetaryAmounts.

Here is my unit test:

@Test
public void testString() {
    Bid bid = new Bid("1.23");
    assertEquals(1.23, bid.getValue(), 0.0);
    System.out.println(bid);

    bid = new Bid("$3.45");
    assertEquals(3.45, bid.getValue(), 0.0);
    System.out.println(bid);
}

Here is my class:

public final class Bid {

    private static final CurrencyUnit USD = Monetary.getCurrency("USD");
    private MonetaryAmount bid;

    /**
     * Constructor.
     *
     * @param bidStr the bid
     */
    public Bid(String bidStr) {
        MonetaryAmountFormat format = MonetaryFormats.getAmountFormat(
                AmountFormatQueryBuilder.of(Locale.US)
                .set("pattern", "###.##")
                .build());
        if (StringUtils.isNotBlank(bidStr)) {
            bidStr = bidStr.trim();
            bidStr = bidStr.startsWith("$") ? bidStr.substring(1) : bidStr;
            try {
                bid = FastMoney.parse(bidStr, format);
            } catch (NumberFormatException e) {
                bid = FastMoney.of(0, USD);
            }
        }
    }

    /**
     * Constructor.
     *
     * @param bidDouble the bid
     */
    public Bid(double bidDouble) {
        bid = FastMoney.of(bidDouble, USD);
    }

    public double getValue() {
        return bid.getNumber().doubleValue();
    }
}

I would have really liked to be able to parse the bidStr with or without the $ using the single MonetaryAmountFormat, but after spending a lot of time trying to find out how to make $ optional, I gave up. Unfortunately, I can't even figure out how to make 1.23 get parsed as USD. Moneta throws a NullPointerException. Am I supposed to set a currency using the AmountFormatQueryBuilder? What are all the keys that can be set using the AmountFormatQueryBuilder? I searched for documentation, but couldn't find any anywhere, except for a couple of common keys, like pattern.

like image 484
dhalsim2 Avatar asked Feb 08 '23 04:02

dhalsim2


1 Answers

JavaMoney does not seem to work well when trying to parse unqualified numbers (like 1.23).

By default, the MonetaryAmountFormat wants you to provide the currency name (like USD 1.23):

MonetaryAmountFormat format = MonetaryFormats.getAmountFormat(Locale.US);
MonetaryAmount amount = format.parse("USD 1.23");

If you set CurrencyStyle.SYMBOL, then you can parse by currency name or symbol (so, either USD 1.23 or $3.45):

AmountFormatQuery formatQuery = AmountFormatQueryBuilder.of(Locale.US)
    .set(CurrencyStyle.SYMBOL)
    .build();
MonetaryAmountFormat format = MonetaryFormats.getAmountFormat(formatQuery);
MonetaryAmount amount1 = format.parse("USD 1.23");
MonetaryAmount amount2 = format.parse("$3.45");

That is probably the closest you are going to get with JavaMoney.

If you know you are only getting numbers from the same currency, you might be better off parsing the string elsewhere (with regexs or something) and converting to a consistent input format because (as you discovered), you easily get NullPointerExceptions with no explanation when JavaMoney is unhappy.

As far as the available keys, you can peek at the constants on org.javamoney.moneta.format.AmountFormatParams: pattern, groupingSizes, groupingSeparators.

When setting the format, it is important to note that you must use the generic currency sign: ¤. For instance, you might do .set("pattern", "¤#,##0.00").

Finally, rather than directly using the FastMoney class from the Moneta RI, you can parse right from a MonetaryAmountFormat:

// rather than using Monata directly:
MonetaryAmount amount = FastMoney.parse(bidStr, format);

// use the API:
MonetaryAmount amount = format.parse(bidStr);
like image 142
Brian Kent Avatar answered Feb 16 '23 02:02

Brian Kent