1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.beanutils2.converters;
18
19 import java.math.BigDecimal;
20 import java.math.BigInteger;
21 import java.text.DecimalFormat;
22 import java.text.DecimalFormatSymbols;
23 import java.text.NumberFormat;
24 import java.text.ParsePosition;
25 import java.util.Calendar;
26 import java.util.Date;
27 import java.util.Locale;
28
29 import org.apache.commons.beanutils2.ConversionException;
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81 public abstract class NumberConverter<N extends Number> extends AbstractConverter<N> {
82
83 private static final Integer ZERO = Integer.valueOf(0);
84 private static final Integer ONE = Integer.valueOf(1);
85
86 private String pattern;
87 private final boolean allowDecimals;
88 private boolean useLocaleFormat;
89 private Locale locale;
90
91
92
93
94
95
96 public NumberConverter(final boolean allowDecimals) {
97 this.allowDecimals = allowDecimals;
98 }
99
100
101
102
103
104
105
106 public NumberConverter(final boolean allowDecimals, final N defaultValue) {
107 this.allowDecimals = allowDecimals;
108 setDefaultValue(defaultValue);
109 }
110
111
112
113
114
115
116
117
118 @Override
119 protected String convertToString(final Object value) {
120
121 String result = null;
122 if (useLocaleFormat && value instanceof Number) {
123 final NumberFormat format = getFormat();
124 format.setGroupingUsed(false);
125 result = format.format(value);
126 if (log().isDebugEnabled()) {
127 log().debug(" Converted to String using format '" + result + "'");
128 }
129
130 } else {
131 result = value.toString();
132 if (log().isDebugEnabled()) {
133 log().debug(" Converted to String using toString() '" + result + "'");
134 }
135 }
136 return result;
137
138 }
139
140
141
142
143
144
145
146
147
148
149 @Override
150 protected <T> T convertToType(final Class<T> targetType, final Object value) throws Throwable {
151 final Class<?> sourceType = value.getClass();
152
153 if (value instanceof Number) {
154 return toNumber(sourceType, targetType, (Number) value);
155 }
156
157
158 if (value instanceof Boolean) {
159 return toNumber(sourceType, targetType, ((Boolean) value).booleanValue() ? ONE : ZERO);
160 }
161
162
163 if (value instanceof Date && Long.class.equals(targetType)) {
164 return targetType.cast(Long.valueOf(((Date) value).getTime()));
165 }
166
167
168 if (value instanceof Calendar && Long.class.equals(targetType)) {
169 return targetType.cast(Long.valueOf(((Calendar) value).getTime().getTime()));
170 }
171
172
173 final String stringValue = toTrim(value);
174 if (stringValue.isEmpty()) {
175 return handleMissing(targetType);
176 }
177
178
179 Number number = null;
180 if (useLocaleFormat) {
181 final NumberFormat format = getFormat();
182 number = parse(sourceType, targetType, stringValue, format);
183 } else {
184 if (log().isDebugEnabled()) {
185 log().debug(" No NumberFormat, using default conversion");
186 }
187 number = toNumber(sourceType, targetType, stringValue);
188 }
189
190
191 return toNumber(sourceType, targetType, number);
192 }
193
194
195
196
197
198
199 private NumberFormat getFormat() {
200 NumberFormat format = null;
201 if (pattern != null) {
202 if (locale == null) {
203 if (log().isDebugEnabled()) {
204 log().debug(" Using pattern '" + pattern + "'");
205 }
206 format = new DecimalFormat(pattern);
207 } else {
208 if (log().isDebugEnabled()) {
209 log().debug(" Using pattern '" + pattern + "'" + " with Locale[" + locale + "]");
210 }
211 final DecimalFormatSymbols symbols = new DecimalFormatSymbols(locale);
212 format = new DecimalFormat(pattern, symbols);
213 }
214 } else if (locale == null) {
215 if (log().isDebugEnabled()) {
216 log().debug(" Using default Locale format");
217 }
218 format = NumberFormat.getInstance();
219 } else {
220 if (log().isDebugEnabled()) {
221 log().debug(" Using Locale[" + locale + "] format");
222 }
223 format = NumberFormat.getInstance(locale);
224 }
225 if (!allowDecimals) {
226 format.setParseIntegerOnly(true);
227 }
228 return format;
229 }
230
231
232
233
234
235
236 public Locale getLocale() {
237 return locale;
238 }
239
240
241
242
243
244
245
246
247 public String getPattern() {
248 return pattern;
249 }
250
251
252
253
254
255
256 public boolean isAllowDecimals() {
257 return allowDecimals;
258 }
259
260
261
262
263
264
265
266
267
268
269
270 private Number parse(final Class<?> sourceType, final Class<?> targetType, final String value, final NumberFormat format) {
271 final ParsePosition pos = new ParsePosition(0);
272 final Number parsedNumber = format.parse(value, pos);
273 if (pos.getErrorIndex() >= 0 || pos.getIndex() != value.length() || parsedNumber == null) {
274 String msg = "Error converting from '" + toString(sourceType) + "' to '" + toString(targetType) + "'";
275 if (format instanceof DecimalFormat) {
276 msg += " using pattern '" + ((DecimalFormat) format).toPattern() + "'";
277 }
278 if (locale != null) {
279 msg += " for locale=[" + locale + "]";
280 }
281 if (log().isDebugEnabled()) {
282 log().debug(" " + msg);
283 }
284 throw new ConversionException(msg);
285 }
286 return parsedNumber;
287 }
288
289
290
291
292
293
294 public void setLocale(final Locale locale) {
295 this.locale = locale;
296 setUseLocaleFormat(true);
297 }
298
299
300
301
302
303
304
305
306 public void setPattern(final String pattern) {
307 this.pattern = pattern;
308 setUseLocaleFormat(true);
309 }
310
311
312
313
314
315
316 public void setUseLocaleFormat(final boolean useLocaleFormat) {
317 this.useLocaleFormat = useLocaleFormat;
318 }
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340 private Number toNumber(final Class<?> sourceType, final Class<?> targetType, final String value) {
341
342 if (targetType.equals(Byte.class)) {
343 return Byte.valueOf(value);
344 }
345
346
347 if (targetType.equals(Short.class)) {
348 return Short.valueOf(value);
349 }
350
351
352 if (targetType.equals(Integer.class)) {
353 return Integer.valueOf(value);
354 }
355
356
357 if (targetType.equals(Long.class)) {
358 return Long.valueOf(value);
359 }
360
361
362 if (targetType.equals(Float.class)) {
363 return Float.valueOf(value);
364 }
365
366
367 if (targetType.equals(Double.class)) {
368 return Double.valueOf(value);
369 }
370
371
372 if (targetType.equals(BigDecimal.class)) {
373 return new BigDecimal(value);
374 }
375
376
377 if (targetType.equals(BigInteger.class)) {
378 return new BigInteger(value);
379 }
380
381 final String msg = toString(getClass()) + " cannot handle conversion from '" + toString(sourceType) + "' to '" + toString(targetType) + "'";
382 if (log().isWarnEnabled()) {
383 log().warn(" " + msg);
384 }
385 throw new ConversionException(msg);
386 }
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408 private <T> T toNumber(final Class<?> sourceType, final Class<T> targetType, final Number value) {
409
410 if (targetType.equals(value.getClass())) {
411 return targetType.cast(value);
412 }
413
414
415 if (targetType.equals(Byte.class)) {
416 final long longValue = value.longValue();
417 if (longValue > Byte.MAX_VALUE) {
418 throw ConversionException.format("%s value '%s' is too large for %s", toString(sourceType), value, toString(targetType));
419 }
420 if (longValue < Byte.MIN_VALUE) {
421 throw ConversionException.format("%s value '%s' is too small %s", toString(sourceType), value, toString(targetType));
422 }
423 return targetType.cast(Byte.valueOf(value.byteValue()));
424 }
425
426
427 if (targetType.equals(Short.class)) {
428 final long longValue = value.longValue();
429 if (longValue > Short.MAX_VALUE) {
430 throw ConversionException.format("%s value '%s' is too large for %s", toString(sourceType), value, toString(targetType));
431 }
432 if (longValue < Short.MIN_VALUE) {
433 throw ConversionException.format("%s value '%s' is too small %s", toString(sourceType), value, toString(targetType));
434 }
435 return targetType.cast(Short.valueOf(value.shortValue()));
436 }
437
438
439 if (targetType.equals(Integer.class)) {
440 final long longValue = value.longValue();
441 if (longValue > Integer.MAX_VALUE) {
442 throw ConversionException.format("%s value '%s' is too large for %s", toString(sourceType), value, toString(targetType));
443 }
444 if (longValue < Integer.MIN_VALUE) {
445 throw ConversionException.format("%s value '%s' is too small %s", toString(sourceType), value, toString(targetType));
446 }
447 return targetType.cast(Integer.valueOf(value.intValue()));
448 }
449
450
451 if (targetType.equals(Long.class)) {
452 return targetType.cast(Long.valueOf(value.longValue()));
453 }
454
455
456 if (targetType.equals(Float.class)) {
457 if (value.doubleValue() > Float.MAX_VALUE) {
458 throw ConversionException.format("%s value '%s' is too large for %s", toString(sourceType), value, toString(targetType));
459 }
460 return targetType.cast(Float.valueOf(value.floatValue()));
461 }
462
463
464 if (targetType.equals(Double.class)) {
465 return targetType.cast(Double.valueOf(value.doubleValue()));
466 }
467
468
469 if (targetType.equals(BigDecimal.class)) {
470 if (value instanceof Float || value instanceof Double) {
471 return targetType.cast(new BigDecimal(value.toString()));
472 }
473 if (value instanceof BigInteger) {
474 return targetType.cast(new BigDecimal((BigInteger) value));
475 }
476 if (value instanceof BigDecimal) {
477 return targetType.cast(new BigDecimal(value.toString()));
478 }
479 return targetType.cast(BigDecimal.valueOf(value.longValue()));
480 }
481
482
483 if (targetType.equals(BigInteger.class)) {
484 if (value instanceof BigDecimal) {
485 return targetType.cast(((BigDecimal) value).toBigInteger());
486 }
487 return targetType.cast(BigInteger.valueOf(value.longValue()));
488 }
489
490 final String msg = toString(getClass()) + " cannot handle conversion to '" + toString(targetType) + "'";
491 if (log().isWarnEnabled()) {
492 log().warn(" " + msg);
493 }
494 throw new ConversionException(msg);
495 }
496
497
498
499
500
501
502 @Override
503 public String toString() {
504 final StringBuilder buffer = new StringBuilder();
505 buffer.append(toString(getClass()));
506 buffer.append("[UseDefault=");
507 buffer.append(isUseDefault());
508 buffer.append(", UseLocaleFormat=");
509 buffer.append(useLocaleFormat);
510 if (pattern != null) {
511 buffer.append(", Pattern=");
512 buffer.append(pattern);
513 }
514 if (locale != null) {
515 buffer.append(", Locale=");
516 buffer.append(locale);
517 }
518 buffer.append(']');
519 return buffer.toString();
520 }
521
522 }