diff --git a/build.gradle b/build.gradle index c17f5bd3c18..176c4278dfa 100644 --- a/build.gradle +++ b/build.gradle @@ -36,7 +36,7 @@ configure(allprojects) { project -> ext.groovyVersion = "2.4.3" ext.gsonVersion = "2.3.1" ext.hibernate3Version = "3.6.10.Final" - ext.hibernate4Version = "4.3.9.Final" + ext.hibernate4Version = "4.3.10.Final" ext.hibval4Version = "4.3.2.Final" ext.hibval5Version = "5.2.0.Beta1" // to be upgraded to 5.2 final in time for Spring Framework 4.2 GA ext.hsqldbVersion = "2.3.2" @@ -462,7 +462,7 @@ project("spring-context") { optional("javax.inject:javax.inject:1") optional("javax.ejb:ejb-api:3.0") optional("javax.enterprise.concurrent:javax.enterprise.concurrent-api:1.0") - optional("javax.money:money-api:1.0-RC3") + optional("javax.money:money-api:1.0") optional("org.eclipse.persistence:javax.persistence:2.0.0") optional("javax.validation:validation-api:1.0.0.GA") optional("org.hibernate:hibernate-validator:${hibval4Version}") @@ -472,7 +472,7 @@ project("spring-context") { optional("org.beanshell:bsh:2.0b4") optional("org.jruby:jruby:${jrubyVersion}") testCompile("javax.inject:javax.inject-tck:1") - testCompile("org.javamoney:moneta:1.0-RC3") + testCompile("org.javamoney:moneta:1.0") testCompile("commons-dbcp:commons-dbcp:1.4") testCompile("org.apache.commons:commons-pool2:2.2") testCompile("org.slf4j:slf4j-api:${slf4jVersion}") diff --git a/spring-context/src/main/java/org/springframework/format/number/money/CurrencyUnitFormatter.java b/spring-context/src/main/java/org/springframework/format/number/money/CurrencyUnitFormatter.java index e98e2e3c2b8..d3f4749d958 100644 --- a/spring-context/src/main/java/org/springframework/format/number/money/CurrencyUnitFormatter.java +++ b/spring-context/src/main/java/org/springframework/format/number/money/CurrencyUnitFormatter.java @@ -18,7 +18,7 @@ package org.springframework.format.number.money; import java.util.Locale; import javax.money.CurrencyUnit; -import javax.money.MonetaryCurrencies; +import javax.money.Monetary; import org.springframework.format.Formatter; @@ -38,7 +38,7 @@ public class CurrencyUnitFormatter implements Formatter { @Override public CurrencyUnit parse(String text, Locale locale) { - return MonetaryCurrencies.getCurrency(text); + return Monetary.getCurrency(text); } } diff --git a/spring-context/src/main/java/org/springframework/format/number/money/Jsr354NumberFormatAnnotationFormatterFactory.java b/spring-context/src/main/java/org/springframework/format/number/money/Jsr354NumberFormatAnnotationFormatterFactory.java index 4061fe74ed3..6cec059b167 100644 --- a/spring-context/src/main/java/org/springframework/format/number/money/Jsr354NumberFormatAnnotationFormatterFactory.java +++ b/spring-context/src/main/java/org/springframework/format/number/money/Jsr354NumberFormatAnnotationFormatterFactory.java @@ -22,9 +22,8 @@ import java.util.Currency; import java.util.Locale; import java.util.Set; import javax.money.CurrencyUnit; +import javax.money.Monetary; import javax.money.MonetaryAmount; -import javax.money.MonetaryAmounts; -import javax.money.MonetaryCurrencies; import org.springframework.context.support.EmbeddedValueResolutionSupport; import org.springframework.format.AnnotationFormatterFactory; @@ -49,6 +48,9 @@ import org.springframework.util.StringUtils; public class Jsr354NumberFormatAnnotationFormatterFactory extends EmbeddedValueResolutionSupport implements AnnotationFormatterFactory { + private static final String CURRENCY_CODE_PATTERN = "\u00A4\u00A4"; + + @Override @SuppressWarnings("unchecked") public Set> getFieldTypes() { @@ -68,18 +70,18 @@ public class Jsr354NumberFormatAnnotationFormatterFactory extends EmbeddedValueR private Formatter configureFormatterFrom(NumberFormat annotation) { if (StringUtils.hasLength(annotation.pattern())) { - return new NumberDecoratingFormatter(null, resolveEmbeddedValue(annotation.pattern())); + return new PatternDecoratingFormatter(resolveEmbeddedValue(annotation.pattern())); } else { Style style = annotation.style(); - if (style == Style.PERCENT) { - return new NumberDecoratingFormatter(new PercentStyleFormatter(), null); + if (style == Style.NUMBER) { + return new NumberDecoratingFormatter(new NumberStyleFormatter()); } - else if (style == Style.NUMBER) { - return new NumberDecoratingFormatter(new NumberStyleFormatter(), null); + else if (style == Style.PERCENT) { + return new NumberDecoratingFormatter(new PercentStyleFormatter()); } else { - return new NumberDecoratingFormatter(null, null); + return new NumberDecoratingFormatter(new CurrencyStyleFormatter()); } } } @@ -89,38 +91,72 @@ public class Jsr354NumberFormatAnnotationFormatterFactory extends EmbeddedValueR private final Formatter numberFormatter; + public NumberDecoratingFormatter(Formatter numberFormatter) { + this.numberFormatter = numberFormatter; + } + + @Override + public String print(MonetaryAmount object, Locale locale) { + return this.numberFormatter.print(object.getNumber(), locale); + } + + @Override + public MonetaryAmount parse(String text, Locale locale) throws ParseException { + CurrencyUnit currencyUnit = Monetary.getCurrency(locale); + Number numberValue = this.numberFormatter.parse(text, locale); + return Monetary.getDefaultAmountFactory().setNumber(numberValue).setCurrency(currencyUnit).create(); + } + } + + + private static class PatternDecoratingFormatter implements Formatter { + private final String pattern; - public NumberDecoratingFormatter(Formatter numberFormatter, String pattern) { - this.numberFormatter = numberFormatter; + public PatternDecoratingFormatter(String pattern) { this.pattern = pattern; } @Override public String print(MonetaryAmount object, Locale locale) { - Formatter formatterToUse = this.numberFormatter; - if (formatterToUse == null) { - CurrencyStyleFormatter formatter = new CurrencyStyleFormatter(); - formatter.setCurrency(Currency.getInstance(object.getCurrency().getCurrencyCode())); - formatter.setPattern(this.pattern); - formatterToUse = formatter; - } - return formatterToUse.print(object.getNumber(), locale); + CurrencyStyleFormatter formatter = new CurrencyStyleFormatter(); + formatter.setCurrency(Currency.getInstance(object.getCurrency().getCurrencyCode())); + formatter.setPattern(this.pattern); + return formatter.print(object.getNumber(), locale); } @Override public MonetaryAmount parse(String text, Locale locale) throws ParseException { - Currency currency = Currency.getInstance(locale); - Formatter formatterToUse = this.numberFormatter; - if (formatterToUse == null) { - CurrencyStyleFormatter formatter = new CurrencyStyleFormatter(); - formatter.setCurrency(currency); - formatter.setPattern(this.pattern); - formatterToUse = formatter; + CurrencyStyleFormatter formatter = new CurrencyStyleFormatter(); + Currency currency = determineCurrency(text, locale); + CurrencyUnit currencyUnit = Monetary.getCurrency(currency.getCurrencyCode()); + formatter.setCurrency(currency); + formatter.setPattern(this.pattern); + Number numberValue = formatter.parse(text, locale); + return Monetary.getDefaultAmountFactory().setNumber(numberValue).setCurrency(currencyUnit).create(); + } + + private Currency determineCurrency(String text, Locale locale) { + try { + if (text.length() < 3) { + // Could not possibly contain a currency code -> + // try with locale and likely let it fail on parse. + return Currency.getInstance(locale); + } + else if (this.pattern.startsWith(CURRENCY_CODE_PATTERN)) { + return Currency.getInstance(text.substring(0, 3)); + } + else if (this.pattern.endsWith(CURRENCY_CODE_PATTERN)) { + return Currency.getInstance(text.substring(text.length() - 3)); + } + else { + // A pattern without a currency code... + return Currency.getInstance(locale); + } + } + catch (IllegalArgumentException ex) { + throw new IllegalArgumentException("Cannot determine currency for number value [" + text + "]", ex); } - Number numberValue = formatterToUse.parse(text, locale); - CurrencyUnit currencyUnit = MonetaryCurrencies.getCurrency(currency.getCurrencyCode()); - return MonetaryAmounts.getDefaultAmountFactory().setNumber(numberValue).setCurrency(currencyUnit).create(); } } diff --git a/spring-context/src/test/java/org/springframework/format/number/money/MoneyFormattingTests.java b/spring-context/src/test/java/org/springframework/format/number/money/MoneyFormattingTests.java index 8a3801a5d38..28df4463766 100644 --- a/spring-context/src/test/java/org/springframework/format/number/money/MoneyFormattingTests.java +++ b/spring-context/src/test/java/org/springframework/format/number/money/MoneyFormattingTests.java @@ -55,8 +55,10 @@ public class MoneyFormattingTests { @Test public void testAmountAndUnit() { - DataBinder binder = new DataBinder(new MoneyHolder()); + MoneyHolder bean = new MoneyHolder(); + DataBinder binder = new DataBinder(bean); binder.setConversionService(conversionService); + MutablePropertyValues propertyValues = new MutablePropertyValues(); propertyValues.add("amount", "USD 10.50"); propertyValues.add("unit", "USD"); @@ -64,11 +66,23 @@ public class MoneyFormattingTests { assertEquals(0, binder.getBindingResult().getErrorCount()); assertEquals("USD10.50", binder.getBindingResult().getFieldValue("amount")); assertEquals("USD", binder.getBindingResult().getFieldValue("unit")); + assertTrue(bean.getAmount().getNumber().doubleValue() == 10.5d); + assertEquals("USD", bean.getAmount().getCurrency().getCurrencyCode()); + + LocaleContextHolder.setLocale(Locale.CANADA); + binder.bind(propertyValues); + LocaleContextHolder.setLocale(Locale.US); + assertEquals(0, binder.getBindingResult().getErrorCount()); + assertEquals("USD10.50", binder.getBindingResult().getFieldValue("amount")); + assertEquals("USD", binder.getBindingResult().getFieldValue("unit")); + assertTrue(bean.getAmount().getNumber().doubleValue() == 10.5d); + assertEquals("USD", bean.getAmount().getCurrency().getCurrencyCode()); } @Test public void testAmountWithNumberFormat1() { - DataBinder binder = new DataBinder(new FormattedMoneyHolder1()); + FormattedMoneyHolder1 bean = new FormattedMoneyHolder1(); + DataBinder binder = new DataBinder(bean); binder.setConversionService(conversionService); MutablePropertyValues propertyValues = new MutablePropertyValues(); @@ -76,19 +90,22 @@ public class MoneyFormattingTests { binder.bind(propertyValues); assertEquals(0, binder.getBindingResult().getErrorCount()); assertEquals("$10.50", binder.getBindingResult().getFieldValue("amount")); + assertTrue(bean.getAmount().getNumber().doubleValue() == 10.5d); + assertEquals("USD", bean.getAmount().getCurrency().getCurrencyCode()); - /* TODO: preserve currency from given value LocaleContextHolder.setLocale(Locale.CANADA); binder.bind(propertyValues); LocaleContextHolder.setLocale(Locale.US); assertEquals(0, binder.getBindingResult().getErrorCount()); assertEquals("$10.50", binder.getBindingResult().getFieldValue("amount")); - */ + assertTrue(bean.getAmount().getNumber().doubleValue() == 10.5d); + assertEquals("CAD", bean.getAmount().getCurrency().getCurrencyCode()); } @Test public void testAmountWithNumberFormat2() { - DataBinder binder = new DataBinder(new FormattedMoneyHolder2()); + FormattedMoneyHolder2 bean = new FormattedMoneyHolder2(); + DataBinder binder = new DataBinder(bean); binder.setConversionService(conversionService); MutablePropertyValues propertyValues = new MutablePropertyValues(); @@ -96,11 +113,14 @@ public class MoneyFormattingTests { binder.bind(propertyValues); assertEquals(0, binder.getBindingResult().getErrorCount()); assertEquals("10.5", binder.getBindingResult().getFieldValue("amount")); + assertTrue(bean.getAmount().getNumber().doubleValue() == 10.5d); + assertEquals("USD", bean.getAmount().getCurrency().getCurrencyCode()); } @Test public void testAmountWithNumberFormat3() { - DataBinder binder = new DataBinder(new FormattedMoneyHolder3()); + FormattedMoneyHolder3 bean = new FormattedMoneyHolder3(); + DataBinder binder = new DataBinder(bean); binder.setConversionService(conversionService); MutablePropertyValues propertyValues = new MutablePropertyValues(); @@ -108,11 +128,14 @@ public class MoneyFormattingTests { binder.bind(propertyValues); assertEquals(0, binder.getBindingResult().getErrorCount()); assertEquals("10%", binder.getBindingResult().getFieldValue("amount")); + assertTrue(bean.getAmount().getNumber().doubleValue() == 0.1d); + assertEquals("USD", bean.getAmount().getCurrency().getCurrencyCode()); } @Test public void testAmountWithNumberFormat4() { - DataBinder binder = new DataBinder(new FormattedMoneyHolder4()); + FormattedMoneyHolder4 bean = new FormattedMoneyHolder4(); + DataBinder binder = new DataBinder(bean); binder.setConversionService(conversionService); MutablePropertyValues propertyValues = new MutablePropertyValues(); @@ -120,26 +143,31 @@ public class MoneyFormattingTests { binder.bind(propertyValues); assertEquals(0, binder.getBindingResult().getErrorCount()); assertEquals("010.500", binder.getBindingResult().getFieldValue("amount")); + assertTrue(bean.getAmount().getNumber().doubleValue() == 10.5d); + assertEquals("USD", bean.getAmount().getCurrency().getCurrencyCode()); } @Test public void testAmountWithNumberFormat5() { - DataBinder binder = new DataBinder(new FormattedMoneyHolder5()); + FormattedMoneyHolder5 bean = new FormattedMoneyHolder5(); + DataBinder binder = new DataBinder(bean); binder.setConversionService(conversionService); MutablePropertyValues propertyValues = new MutablePropertyValues(); - propertyValues.add("amount", "$ 10.50"); + propertyValues.add("amount", "USD 10.50"); binder.bind(propertyValues); assertEquals(0, binder.getBindingResult().getErrorCount()); - assertEquals("$ 010.500", binder.getBindingResult().getFieldValue("amount")); + assertEquals("USD 010.500", binder.getBindingResult().getFieldValue("amount")); + assertTrue(bean.getAmount().getNumber().doubleValue() == 10.5d); + assertEquals("USD", bean.getAmount().getCurrency().getCurrencyCode()); - /* TODO: preserve currency from given value LocaleContextHolder.setLocale(Locale.CANADA); binder.bind(propertyValues); LocaleContextHolder.setLocale(Locale.US); assertEquals(0, binder.getBindingResult().getErrorCount()); - assertEquals("$ 010.500", binder.getBindingResult().getFieldValue("amount")); - */ + assertEquals("USD 010.500", binder.getBindingResult().getFieldValue("amount")); + assertTrue(bean.getAmount().getNumber().doubleValue() == 10.5d); + assertEquals("USD", bean.getAmount().getCurrency().getCurrencyCode()); } @@ -229,7 +257,7 @@ public class MoneyFormattingTests { public static class FormattedMoneyHolder5 { - @NumberFormat(pattern = "\u00A4 #000.000#") + @NumberFormat(pattern = "\u00A4\u00A4 #000.000#") private MonetaryAmount amount; public MonetaryAmount getAmount() {