Repository: camel Updated Branches: refs/heads/master e1f9e26e6 -> 2854e18ff
CAMEL-7742: Add rounding, decimal, grouping attributes for @DateField, add unit tests Project: http://git-wip-us.apache.org/repos/asf/camel/repo Commit: http://git-wip-us.apache.org/repos/asf/camel/commit/2854e18f Tree: http://git-wip-us.apache.org/repos/asf/camel/tree/2854e18f Diff: http://git-wip-us.apache.org/repos/asf/camel/diff/2854e18f Branch: refs/heads/master Commit: 2854e18ff985ffb9abaca305038801f6c508f0c4 Parents: e1f9e26 Author: Charles Moulliard <ch0...@gmail.com> Authored: Tue Aug 26 10:34:12 2014 +0200 Committer: Charles Moulliard <ch0...@gmail.com> Committed: Tue Aug 26 10:34:12 2014 +0200 ---------------------------------------------------------------------- .../camel/dataformat/bindy/FormatFactory.java | 38 +++---- .../dataformat/bindy/annotation/DataField.java | 21 ++++ .../bindy/format/BigDecimalPatternFormat.java | 34 ++++++ .../bindy/format/NumberPatternFormat.java | 52 ++++++++- .../bindy/number/BindyFormatUnmarshallTest.java | 105 ------------------- .../BindyBigDecimalGroupingUnmarshallTest.java | 93 ++++++++++++++++ .../BindyBigDecimalRoundingUnmarshallTest.java | 89 ++++++++++++++++ 7 files changed, 303 insertions(+), 129 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/camel/blob/2854e18f/components/camel-bindy/src/main/java/org/apache/camel/dataformat/bindy/FormatFactory.java ---------------------------------------------------------------------- diff --git a/components/camel-bindy/src/main/java/org/apache/camel/dataformat/bindy/FormatFactory.java b/components/camel-bindy/src/main/java/org/apache/camel/dataformat/bindy/FormatFactory.java index d5dc68b..794aace 100755 --- a/components/camel-bindy/src/main/java/org/apache/camel/dataformat/bindy/FormatFactory.java +++ b/components/camel-bindy/src/main/java/org/apache/camel/dataformat/bindy/FormatFactory.java @@ -24,25 +24,7 @@ import java.util.Locale; import org.apache.camel.dataformat.bindy.annotation.DataField; import org.apache.camel.dataformat.bindy.annotation.KeyValuePairField; -import org.apache.camel.dataformat.bindy.format.BigDecimalFormat; -import org.apache.camel.dataformat.bindy.format.BigIntegerFormat; -import org.apache.camel.dataformat.bindy.format.BooleanFormat; -import org.apache.camel.dataformat.bindy.format.ByteFormat; -import org.apache.camel.dataformat.bindy.format.BytePatternFormat; -import org.apache.camel.dataformat.bindy.format.CharacterFormat; -import org.apache.camel.dataformat.bindy.format.DatePatternFormat; -import org.apache.camel.dataformat.bindy.format.DoubleFormat; -import org.apache.camel.dataformat.bindy.format.DoublePatternFormat; -import org.apache.camel.dataformat.bindy.format.EnumFormat; -import org.apache.camel.dataformat.bindy.format.FloatFormat; -import org.apache.camel.dataformat.bindy.format.FloatPatternFormat; -import org.apache.camel.dataformat.bindy.format.IntegerFormat; -import org.apache.camel.dataformat.bindy.format.IntegerPatternFormat; -import org.apache.camel.dataformat.bindy.format.LongFormat; -import org.apache.camel.dataformat.bindy.format.LongPatternFormat; -import org.apache.camel.dataformat.bindy.format.ShortFormat; -import org.apache.camel.dataformat.bindy.format.ShortPatternFormat; -import org.apache.camel.dataformat.bindy.format.StringFormat; +import org.apache.camel.dataformat.bindy.format.*; import org.apache.camel.util.ObjectHelper; @@ -61,13 +43,17 @@ public final class FormatFactory { * @param pattern is the pattern to be used during the formatting of the data * @param locale optional locale for NumberFormat and DateFormat parsing. * @param precision optional scale for BigDecimal parsing. + * @param rounding optional rounding mode to be used to scale BigDecimal with precision value * @param impliedDecimalSeparator optional flag for floating-point values + * @param decimalSeparator optional decimal separator for BigDecimal + * @param groupingSeparator optional grouping separator for BigDecimal * @return Format the formatter * @throws IllegalArgumentException if not suitable formatter is found */ @SuppressWarnings("unchecked") private static Format<?> doGetFormat(Class<?> clazz, String pattern, String locale, - String timezone, int precision, boolean impliedDecimalSeparator) + String timezone, int precision, String rounding, + boolean impliedDecimalSeparator, String decimalSeparator, String groupingSeparator) throws Exception { if (clazz == byte.class || clazz == Byte.class) { return ObjectHelper.isNotEmpty(pattern) @@ -94,7 +80,9 @@ public final class FormatFactory { ? new DoublePatternFormat(pattern, getLocale(locale)) : new DoubleFormat(impliedDecimalSeparator, precision, getLocale(locale)); } else if (clazz == BigDecimal.class) { - return new BigDecimalFormat(impliedDecimalSeparator, precision, getLocale(locale)); + return ObjectHelper.isNotEmpty(pattern) + ? new BigDecimalPatternFormat(pattern, getLocale(locale), precision, rounding, decimalSeparator, groupingSeparator) + : new BigDecimalFormat(impliedDecimalSeparator, precision, getLocale(locale)); } else if (clazz == BigInteger.class) { return new BigIntegerFormat(); } else if (clazz == String.class) { @@ -124,8 +112,11 @@ public final class FormatFactory { String pattern = data.pattern(); String timezone = data.timezone(); int precision = data.precision(); + String decimalSeparator = data.decimalSeparator(); + String groupingSeparator = data.groupingSeparator(); + String rounding = data.rounding(); - return doGetFormat(clazz, pattern, locale, timezone, precision, data.impliedDecimalSeparator()); + return doGetFormat(clazz, pattern, locale, timezone, precision, rounding, data.impliedDecimalSeparator(), decimalSeparator, groupingSeparator); } /** @@ -135,13 +126,14 @@ public final class FormatFactory { * @param locale optional locale for NumberFormat and DateFormat parsing. * @return Format the formatter * @throws IllegalArgumentException if not suitable formatter is found + * TODO : Check if KeyValuePair could also use decimal/groupingSeparator/rounding for BigDecimal */ public static Format<?> getFormat(Class<?> clazz, String locale, KeyValuePairField data) throws Exception { String pattern = data.pattern(); String timezone = data.timezone(); int precision = data.precision(); - return doGetFormat(clazz, pattern, locale, timezone, precision, data.impliedDecimalSeparator()); + return doGetFormat(clazz, pattern, locale, timezone, precision, null, data.impliedDecimalSeparator(), null, null); } private static Locale getLocale(String locale) { http://git-wip-us.apache.org/repos/asf/camel/blob/2854e18f/components/camel-bindy/src/main/java/org/apache/camel/dataformat/bindy/annotation/DataField.java ---------------------------------------------------------------------- diff --git a/components/camel-bindy/src/main/java/org/apache/camel/dataformat/bindy/annotation/DataField.java b/components/camel-bindy/src/main/java/org/apache/camel/dataformat/bindy/annotation/DataField.java index cacbb52..cec86c8 100755 --- a/components/camel-bindy/src/main/java/org/apache/camel/dataformat/bindy/annotation/DataField.java +++ b/components/camel-bindy/src/main/java/org/apache/camel/dataformat/bindy/annotation/DataField.java @@ -123,4 +123,25 @@ public @interface DataField { * Indicates if there is a decimal point implied at a specified location */ boolean impliedDecimalSeparator() default false; + + /** + * Decimal Separator to be used with BigDecimal number + */ + String decimalSeparator() default ""; + + /** + * Grouping Separator to be used with BigDecimal number + * when we would like to format/parse to number with grouping + * e.g. 123,456.789 + */ + String groupingSeparator() default ""; + + /** + * Round mode to be used to round/scale a BigDecimal + * Values : UP, DOWN, CEILING, FLOOR, HALF_UP, HALF_DOWN,HALF_EVEN, UNNECESSARY + * e.g : Number = 123456.789, Precision = 2, Rounding = CEILING + * Result : 123456.79 + */ + String rounding() default "CEILING"; + } http://git-wip-us.apache.org/repos/asf/camel/blob/2854e18f/components/camel-bindy/src/main/java/org/apache/camel/dataformat/bindy/format/BigDecimalPatternFormat.java ---------------------------------------------------------------------- diff --git a/components/camel-bindy/src/main/java/org/apache/camel/dataformat/bindy/format/BigDecimalPatternFormat.java b/components/camel-bindy/src/main/java/org/apache/camel/dataformat/bindy/format/BigDecimalPatternFormat.java new file mode 100644 index 0000000..ae1200d --- /dev/null +++ b/components/camel-bindy/src/main/java/org/apache/camel/dataformat/bindy/format/BigDecimalPatternFormat.java @@ -0,0 +1,34 @@ +package org.apache.camel.dataformat.bindy.format; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.text.DecimalFormat; +import java.text.NumberFormat; +import java.util.Locale; + +public class BigDecimalPatternFormat extends NumberPatternFormat<BigDecimal> { + + public void BigDecimalPatternFormat() { + } + + public BigDecimalPatternFormat(String pattern, Locale locale, int precision, String rounding, String decimalSeparator, String groupingSeparator) { + super(pattern, locale, precision, rounding, decimalSeparator, groupingSeparator); + } + + @Override + public BigDecimal parse(String string) throws Exception { + if (getNumberFormat() != null) { + Locale.setDefault(super.getLocale()); + DecimalFormat df = (DecimalFormat)getNumberFormat(); + df.setParseBigDecimal(true); + BigDecimal bd = (BigDecimal)df.parse(string.trim()); + if(super.getPrecision() != -1) { + bd = bd.setScale(super.getPrecision(), RoundingMode.valueOf(super.getRounding())); + } + Locale.getDefault(); + return bd; + } else { + return new BigDecimal(string.trim()); + } + } +} http://git-wip-us.apache.org/repos/asf/camel/blob/2854e18f/components/camel-bindy/src/main/java/org/apache/camel/dataformat/bindy/format/NumberPatternFormat.java ---------------------------------------------------------------------- diff --git a/components/camel-bindy/src/main/java/org/apache/camel/dataformat/bindy/format/NumberPatternFormat.java b/components/camel-bindy/src/main/java/org/apache/camel/dataformat/bindy/format/NumberPatternFormat.java index 5e17e96..5cfe146 100755 --- a/components/camel-bindy/src/main/java/org/apache/camel/dataformat/bindy/format/NumberPatternFormat.java +++ b/components/camel-bindy/src/main/java/org/apache/camel/dataformat/bindy/format/NumberPatternFormat.java @@ -17,6 +17,7 @@ package org.apache.camel.dataformat.bindy.format; import java.text.DecimalFormat; +import java.text.DecimalFormatSymbols; import java.text.NumberFormat; import java.util.Locale; @@ -26,6 +27,10 @@ public abstract class NumberPatternFormat<T> implements PatternFormat<T> { private String pattern; private Locale locale; + private String decimalSeparator; + private String groupingSeparator; + private int precision; + private String rounding; public NumberPatternFormat() { } @@ -35,6 +40,15 @@ public abstract class NumberPatternFormat<T> implements PatternFormat<T> { this.locale = locale; } + public NumberPatternFormat(String pattern, Locale locale, int precision, String rounding, String decimalSeparator, String groupingSeparator) { + this.pattern = pattern; + this.locale = locale; + this.decimalSeparator = decimalSeparator; + this.groupingSeparator = groupingSeparator; + this.precision = precision; + this.rounding = rounding; + } + public String format(T object) throws Exception { if (getNumberFormat() != null) { return this.getNumberFormat().format(object); @@ -57,7 +71,18 @@ public abstract class NumberPatternFormat<T> implements PatternFormat<T> { NumberFormat format = NumberFormat.getNumberInstance(locale); if (format instanceof DecimalFormat) { - ((DecimalFormat)format).applyLocalizedPattern(pattern); + DecimalFormat df = (DecimalFormat) format; + if (decimalSeparator != null && groupingSeparator != null) { + if (!decimalSeparator.isEmpty() && !groupingSeparator.isEmpty()) { + DecimalFormatSymbols dfs = new DecimalFormatSymbols(locale); + dfs.setDecimalSeparator(decimalSeparator.charAt(0)); + dfs.setGroupingSeparator(groupingSeparator.charAt(0)); + df.setDecimalFormatSymbols(dfs); + } + } + if (!pattern.isEmpty()) { + df.applyPattern(pattern); + } } return format; } @@ -69,4 +94,29 @@ public abstract class NumberPatternFormat<T> implements PatternFormat<T> { public void setPattern(String pattern) { this.pattern = pattern; } + + public int getPrecision() { + return precision; + } + + public void setPrecision(int precision) { + this.precision = precision; + } + + public String getRounding() { + return rounding; + } + + public void setRounding(String rounding) { + this.rounding = rounding; + } + + public Locale getLocale() { + return locale; + } + + public void setLocale(Locale locale) { + this.locale = locale; + } + } http://git-wip-us.apache.org/repos/asf/camel/blob/2854e18f/components/camel-bindy/src/test/java/org/apache/camel/dataformat/bindy/number/BindyFormatUnmarshallTest.java ---------------------------------------------------------------------- diff --git a/components/camel-bindy/src/test/java/org/apache/camel/dataformat/bindy/number/BindyFormatUnmarshallTest.java b/components/camel-bindy/src/test/java/org/apache/camel/dataformat/bindy/number/BindyFormatUnmarshallTest.java deleted file mode 100644 index 6341041..0000000 --- a/components/camel-bindy/src/test/java/org/apache/camel/dataformat/bindy/number/BindyFormatUnmarshallTest.java +++ /dev/null @@ -1,105 +0,0 @@ -package org.apache.camel.dataformat.bindy.number; - -import org.apache.camel.EndpointInject; -import org.apache.camel.Produce; -import org.apache.camel.ProducerTemplate; -import org.apache.camel.builder.RouteBuilder; -import org.apache.camel.component.mock.MockEndpoint; -import org.apache.camel.dataformat.bindy.annotation.CsvRecord; -import org.apache.camel.dataformat.bindy.annotation.DataField; -import org.apache.camel.model.dataformat.BindyDataFormat; -import org.apache.camel.model.dataformat.BindyType; -import org.apache.camel.test.junit4.CamelTestSupport; -import org.junit.Assert; -import org.junit.Test; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.math.BigDecimal; -import java.util.List; -import java.util.Map; - -public class BindyFormatUnmarshallTest extends CamelTestSupport { - - private static final Logger LOG = LoggerFactory.getLogger(BindyFormatUnmarshallTest.class); - - private static final String URI_MOCK_RESULT = "mock:result"; - private static final String URI_DIRECT_START = "direct:start"; - - @Produce(uri = URI_DIRECT_START) - private ProducerTemplate template; - - @EndpointInject(uri = URI_MOCK_RESULT) - private MockEndpoint result; - - private String record; - - @Test - public void testInteger() throws Exception { - - record = "10000,25.12"; - String intVal = "10000"; - String bigDecimal = "25.12"; - - template.sendBody(record); - - result.expectedMessageCount(1); - result.assertIsSatisfied(); - - Math math = (Math)result.getExchanges().get(0).getIn().getBody(); - Assert.assertEquals(math.getIntAmount().toString(),intVal); - Assert.assertEquals(math.getBigDecimal().toString(),bigDecimal); - - LOG.info("Math object received : " + math); - } - - @Override - protected RouteBuilder createRouteBuilder() { - - return new RouteBuilder() { - @Override - public void configure() throws Exception { - BindyDataFormat bindy = new BindyDataFormat(); - bindy.setType(BindyType.Csv); - bindy.setClassType(BindyFormatUnmarshallTest.Math.class); - - from(URI_DIRECT_START) - .unmarshal(bindy) - .to(URI_MOCK_RESULT); - } - - }; - } - - @CsvRecord(separator = ",") - public static class Math { - - @DataField(pos = 1, pattern = "00") - private Integer intAmount; - - @DataField(pos = 2, precision = 2) - private BigDecimal bigDecimal; - - public Integer getIntAmount() { - return intAmount; - } - - public void setIntAmount(Integer intAmount) { - this.intAmount = intAmount; - } - - public BigDecimal getBigDecimal() { - return bigDecimal; - } - - public void setBigDecimal(BigDecimal bigDecimal) { - this.bigDecimal = bigDecimal; - } - - @Override - public String toString() { - return "intAmount : " + this.intAmount + ", " + - "bigDecimal : " + this.bigDecimal; - } - } -} http://git-wip-us.apache.org/repos/asf/camel/blob/2854e18f/components/camel-bindy/src/test/java/org/apache/camel/dataformat/bindy/number/grouping/BindyBigDecimalGroupingUnmarshallTest.java ---------------------------------------------------------------------- diff --git a/components/camel-bindy/src/test/java/org/apache/camel/dataformat/bindy/number/grouping/BindyBigDecimalGroupingUnmarshallTest.java b/components/camel-bindy/src/test/java/org/apache/camel/dataformat/bindy/number/grouping/BindyBigDecimalGroupingUnmarshallTest.java new file mode 100644 index 0000000..714c678 --- /dev/null +++ b/components/camel-bindy/src/test/java/org/apache/camel/dataformat/bindy/number/grouping/BindyBigDecimalGroupingUnmarshallTest.java @@ -0,0 +1,93 @@ +package org.apache.camel.dataformat.bindy.number.grouping; + +import org.apache.camel.EndpointInject; +import org.apache.camel.Produce; +import org.apache.camel.ProducerTemplate; +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.component.mock.MockEndpoint; +import org.apache.camel.dataformat.bindy.annotation.CsvRecord; +import org.apache.camel.dataformat.bindy.annotation.DataField; +import org.apache.camel.model.dataformat.BindyDataFormat; +import org.apache.camel.model.dataformat.BindyType; +import org.apache.camel.test.junit4.CamelTestSupport; +import org.junit.Assert; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.math.BigDecimal; + +public class BindyBigDecimalGroupingUnmarshallTest extends CamelTestSupport { + + private static final Logger LOG = LoggerFactory.getLogger(BindyBigDecimalGroupingUnmarshallTest.class); + + private static final String URI_MOCK_RESULT = "mock:result"; + private static final String URI_DIRECT_START = "direct:start"; + + @Produce(uri = URI_DIRECT_START) + private ProducerTemplate template; + + @EndpointInject(uri = URI_MOCK_RESULT) + private MockEndpoint result; + + private String record; + + @Test + public void testBigDecimalPattern() throws Exception { + + record = "'123.456,234'"; + String bigDecimal = "123456.24"; + + template.sendBody(record); + + result.expectedMessageCount(1); + result.assertIsSatisfied(); + + NumberModel bd = (NumberModel)result.getExchanges().get(0).getIn().getBody(); + Assert.assertEquals(bigDecimal, bd.getGrouping().toString()); + } + + @Override + protected RouteBuilder createRouteBuilder() { + + return new RouteBuilder() { + @Override + public void configure() throws Exception { + BindyDataFormat bindy = new BindyDataFormat(); + bindy.setType(BindyType.Csv); + bindy.setClassType(NumberModel.class); + bindy.setLocale("en"); + + from(URI_DIRECT_START) + .unmarshal(bindy) + .to(URI_MOCK_RESULT); + } + + }; + } + + @CsvRecord(separator = ",", quote = "'") + public static class NumberModel { + + @DataField(pos = 1, precision = 2, + rounding = "CEILING", + pattern = "###,###.###", + decimalSeparator = ",", + groupingSeparator = ".") + private BigDecimal grouping; + + public BigDecimal getGrouping() { + return grouping; + } + + public void setGrouping(BigDecimal grouping) { + this.grouping = grouping; + } + + + @Override + public String toString() { + return "bigDecimal grouping : " + this.grouping; + } + } +} http://git-wip-us.apache.org/repos/asf/camel/blob/2854e18f/components/camel-bindy/src/test/java/org/apache/camel/dataformat/bindy/number/rounding/BindyBigDecimalRoundingUnmarshallTest.java ---------------------------------------------------------------------- diff --git a/components/camel-bindy/src/test/java/org/apache/camel/dataformat/bindy/number/rounding/BindyBigDecimalRoundingUnmarshallTest.java b/components/camel-bindy/src/test/java/org/apache/camel/dataformat/bindy/number/rounding/BindyBigDecimalRoundingUnmarshallTest.java new file mode 100644 index 0000000..ba7262b --- /dev/null +++ b/components/camel-bindy/src/test/java/org/apache/camel/dataformat/bindy/number/rounding/BindyBigDecimalRoundingUnmarshallTest.java @@ -0,0 +1,89 @@ +package org.apache.camel.dataformat.bindy.number.rounding; + +import org.apache.camel.EndpointInject; +import org.apache.camel.Produce; +import org.apache.camel.ProducerTemplate; +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.component.mock.MockEndpoint; +import org.apache.camel.dataformat.bindy.annotation.CsvRecord; +import org.apache.camel.dataformat.bindy.annotation.DataField; +import org.apache.camel.model.dataformat.BindyDataFormat; +import org.apache.camel.model.dataformat.BindyType; +import org.apache.camel.test.junit4.CamelTestSupport; +import org.junit.Assert; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.math.BigDecimal; + +public class BindyBigDecimalRoundingUnmarshallTest extends CamelTestSupport { + + private static final Logger LOG = LoggerFactory.getLogger(BindyBigDecimalRoundingUnmarshallTest.class); + + private static final String URI_MOCK_RESULT = "mock:result"; + private static final String URI_DIRECT_START = "direct:start"; + + @Produce(uri = URI_DIRECT_START) + private ProducerTemplate template; + + @EndpointInject(uri = URI_MOCK_RESULT) + private MockEndpoint result; + + private String record; + + + @Test + public void testBigDecimalRoundingUp() throws Exception { + + record = "'12345.789'"; + String bigDecimal = "12345.79"; + + template.sendBody(record); + + result.expectedMessageCount(1); + result.assertIsSatisfied(); + + NumberModel bd = (NumberModel)result.getExchanges().get(0).getIn().getBody(); + Assert.assertEquals(bigDecimal,bd.getRoundingUp().toString()); + } + + @Override + protected RouteBuilder createRouteBuilder() { + + return new RouteBuilder() { + @Override + public void configure() throws Exception { + BindyDataFormat bindy = new BindyDataFormat(); + bindy.setType(BindyType.Csv); + bindy.setClassType(NumberModel.class); + bindy.setLocale("en"); + + from(URI_DIRECT_START) + .unmarshal(bindy) + .to(URI_MOCK_RESULT); + } + + }; + } + + @CsvRecord(separator = ",", quote = "'") + public static class NumberModel { + + @DataField(pos = 1, precision = 2, rounding = "UP", pattern = "#####,##") + private BigDecimal roundingUp; + + public BigDecimal getRoundingUp() { + return roundingUp; + } + + public void setRoundingUp(BigDecimal roundingUp) { + this.roundingUp = roundingUp; + } + + @Override + public String toString() { + return "BigDecimal rounding : " + this.roundingUp; + } + } +}