1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package com.opencsv.bean;
17
18 import com.opencsv.ICSVParser;
19 import com.opencsv.exceptions.CsvBadConverterException;
20 import com.opencsv.exceptions.CsvDataTypeMismatchException;
21 import org.apache.commons.lang3.ClassUtils;
22 import org.apache.commons.lang3.ObjectUtils;
23 import org.apache.commons.lang3.StringUtils;
24
25 import java.math.BigDecimal;
26 import java.math.BigInteger;
27 import java.math.RoundingMode;
28 import java.text.DecimalFormat;
29 import java.text.NumberFormat;
30 import java.text.ParseException;
31 import java.util.Locale;
32 import java.util.ResourceBundle;
33 import java.util.function.UnaryOperator;
34
35
36
37
38
39
40
41
42 public class ConverterNumber extends AbstractCsvConverter {
43
44 private final DecimalFormat readFormatter, writeFormatter;
45 private final UnaryOperator<Number> readConversionFunction;
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61 public ConverterNumber(Class<?> type, String locale, String writeLocale, Locale errorLocale,
62 String readFormat, String writeFormat, RoundingMode roundingMode)
63 throws CsvBadConverterException {
64 super(type, locale, writeLocale, errorLocale);
65
66
67 if(!Number.class.isAssignableFrom(
68 this.type.isPrimitive()
69 ? ClassUtils.primitiveToWrapper(this.type)
70 : this.type)) {
71 throw new CsvBadConverterException(
72 ConverterNumber.class,
73 ResourceBundle.getBundle(
74 ICSVParser.DEFAULT_BUNDLE_NAME,
75 this.errorLocale)
76 .getString("csvnumber.not.number"));
77 }
78
79
80 readFormatter = createDecimalFormat(readFormat, this.locale, roundingMode);
81
82
83
84 if(this.type == BigInteger.class || this.type == BigDecimal.class) {
85 readFormatter.setParseBigDecimal(true);
86 }
87
88
89 if(this.type == Byte.class || this.type == Byte.TYPE) {
90 readConversionFunction = Number::byteValue;
91 }
92 else if(this.type == Short.class || this.type == Short.TYPE) {
93 readConversionFunction = Number::shortValue;
94 }
95 else if(this.type == Integer.class || this.type == Integer.TYPE) {
96 readConversionFunction = Number::intValue;
97 }
98 else if(this.type == Long.class || this.type == Long.TYPE) {
99 readConversionFunction = Number::longValue;
100 }
101 else if(this.type == Float.class || this.type == Float.TYPE) {
102 readConversionFunction = Number::floatValue;
103 }
104 else if(this.type == Double.class || this.type == Double.TYPE) {
105 readConversionFunction = Number::doubleValue;
106 }
107 else if(this.type == BigInteger.class) {
108 readConversionFunction = n -> ((BigDecimal) n).toBigInteger();
109 }
110 else {
111
112
113
114
115 readConversionFunction = n -> n;
116 }
117
118
119 writeFormatter = createDecimalFormat(writeFormat, this.writeLocale, roundingMode);
120 }
121
122 private DecimalFormat createDecimalFormat(String format, Locale locale, RoundingMode roundingMode) {
123 NumberFormat nf = NumberFormat.getInstance(ObjectUtils.defaultIfNull(locale, Locale.getDefault(Locale.Category.FORMAT)));
124 if (!(nf instanceof DecimalFormat)) {
125 throw new CsvBadConverterException(
126 ConverterNumber.class,
127 ResourceBundle.getBundle(
128 ICSVParser.DEFAULT_BUNDLE_NAME,
129 this.errorLocale)
130 .getString("numberformat.not.decimalformat"));
131 }
132 DecimalFormat formatter = (DecimalFormat) nf;
133
134 try {
135 formatter.applyLocalizedPattern(format);
136 } catch (IllegalArgumentException e) {
137 CsvBadConverterException csve = new CsvBadConverterException(
138 ConverterNumber.class,
139 String.format(ResourceBundle.getBundle(
140 ICSVParser.DEFAULT_BUNDLE_NAME,
141 this.errorLocale)
142 .getString("invalid.number.pattern"),
143 format));
144 csve.initCause(e);
145 throw csve;
146 }
147
148 formatter.setRoundingMode(roundingMode);
149
150 return formatter;
151 }
152
153 @Override
154 public Object convertToRead(String value) throws CsvDataTypeMismatchException {
155 Number n = null;
156 if(StringUtils.isNotEmpty(value)) {
157 try {
158 synchronized (readFormatter) {
159 n = readFormatter.parse(value);
160 }
161 }
162 catch(ParseException | ArithmeticException e) {
163 CsvDataTypeMismatchException csve = new CsvDataTypeMismatchException(
164 value, type,
165 String.format(ResourceBundle.getBundle(
166 ICSVParser.DEFAULT_BUNDLE_NAME,
167 errorLocale)
168 .getString("unparsable.number"), value, readFormatter.toPattern()));
169 csve.initCause(e);
170 throw csve;
171 }
172
173 n = readConversionFunction.apply(n);
174 }
175 return n;
176 }
177
178
179
180
181
182
183 @Override
184 public String convertToWrite(Object value) throws CsvDataTypeMismatchException {
185 synchronized (writeFormatter) {
186 try {
187 return value != null ? writeFormatter.format(value) : null;
188 }
189 catch (ArithmeticException e) {
190 CsvDataTypeMismatchException csve = new CsvDataTypeMismatchException(
191 value, type,
192 String.format(ResourceBundle.getBundle(
193 ICSVParser.DEFAULT_BUNDLE_NAME,
194 errorLocale)
195 .getString("unparsable.number"), value, writeFormatter.toPattern()));
196 csve.initCause(e);
197 throw csve;
198 }
199 }
200 }
201 }