1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.commons.configuration2.convert;
19
20 import java.awt.Color;
21 import java.io.File;
22 import java.lang.reflect.Constructor;
23 import java.lang.reflect.InvocationTargetException;
24 import java.math.BigDecimal;
25 import java.math.BigInteger;
26 import java.net.InetAddress;
27 import java.net.MalformedURLException;
28 import java.net.URI;
29 import java.net.URISyntaxException;
30 import java.net.URL;
31 import java.net.UnknownHostException;
32 import java.nio.file.Path;
33 import java.nio.file.Paths;
34 import java.text.ParseException;
35 import java.text.SimpleDateFormat;
36 import java.time.Duration;
37 import java.time.format.DateTimeParseException;
38 import java.util.Calendar;
39 import java.util.Date;
40 import java.util.Locale;
41 import java.util.Objects;
42 import java.util.regex.Pattern;
43 import java.util.regex.PatternSyntaxException;
44
45 import org.apache.commons.configuration2.ex.ConversionException;
46 import org.apache.commons.lang3.BooleanUtils;
47 import org.apache.commons.lang3.StringUtils;
48
49
50
51
52
53
54 public final class PropertyConverter {
55
56
57 private static final String HEX_PREFIX = "0x";
58
59
60 private static final int HEX_RADIX = 16;
61
62
63 private static final String BIN_PREFIX = "0b";
64
65
66 private static final int BIN_RADIX = 2;
67
68
69 private static final Class<?>[] CONSTR_ARGS = {String.class};
70
71
72 private static final String INTERNET_ADDRESS_CLASSNAME_JAVAX = "javax.mail.internet.InternetAddress";
73
74
75 private static final String INTERNET_ADDRESS_CLASSNAME_JAKARTA = "jakarta.mail.internet.InternetAddress";
76
77
78
79
80
81
82
83
84 @SuppressWarnings("unchecked")
85
86 private static Object convertToEnum(final Class<?> enumClass, final Object value) {
87 return toEnum(value, enumClass.asSubclass(Enum.class));
88 }
89
90
91
92
93
94
95
96
97
98
99
100
101
102 public static Object to(final Class<?> cls, final Object value, final DefaultConversionHandler convHandler) throws ConversionException {
103 if (cls.isInstance(value)) {
104 return value;
105 }
106
107 if (String.class.equals(cls)) {
108 return String.valueOf(value);
109 }
110 if (Boolean.class.equals(cls) || Boolean.TYPE.equals(cls)) {
111 return toBoolean(value);
112 }
113 if (Character.class.equals(cls) || Character.TYPE.equals(cls)) {
114 return toCharacter(value);
115 }
116 if (Number.class.isAssignableFrom(cls) || cls.isPrimitive()) {
117 if (Integer.class.equals(cls) || Integer.TYPE.equals(cls)) {
118 return toInteger(value);
119 }
120 if (Long.class.equals(cls) || Long.TYPE.equals(cls)) {
121 return toLong(value);
122 }
123 if (Byte.class.equals(cls) || Byte.TYPE.equals(cls)) {
124 return toByte(value);
125 }
126 if (Short.class.equals(cls) || Short.TYPE.equals(cls)) {
127 return toShort(value);
128 }
129 if (Float.class.equals(cls) || Float.TYPE.equals(cls)) {
130 return toFloat(value);
131 }
132 if (Double.class.equals(cls) || Double.TYPE.equals(cls)) {
133 return toDouble(value);
134 }
135 if (BigInteger.class.equals(cls)) {
136 return toBigInteger(value);
137 }
138 if (BigDecimal.class.equals(cls)) {
139 return toBigDecimal(value);
140 }
141 return toNumber(value, cls);
142 }
143 if (Date.class.equals(cls)) {
144 return toDate(value, convHandler.getDateFormat());
145 }
146 if (Calendar.class.equals(cls)) {
147 return toCalendar(value, convHandler.getDateFormat());
148 }
149 if (File.class.equals(cls)) {
150 return toFile(value);
151 }
152 if (Path.class.equals(cls)) {
153 return toPath(value);
154 }
155 if (URI.class.equals(cls)) {
156 return toURI(value);
157 }
158 if (URL.class.equals(cls)) {
159 return toURL(value);
160 }
161 if (Pattern.class.equals(cls)) {
162 return toPattern(value);
163 }
164 if (Locale.class.equals(cls)) {
165 return toLocale(value);
166 }
167 if (cls.isEnum()) {
168 return convertToEnum(cls, value);
169 }
170 if (Color.class.equals(cls)) {
171 return toColor(value);
172 }
173 if (cls.getName().equals(INTERNET_ADDRESS_CLASSNAME_JAVAX)) {
174
175 return toInternetAddress(value, INTERNET_ADDRESS_CLASSNAME_JAVAX);
176 }
177 if (cls.getName().equals(INTERNET_ADDRESS_CLASSNAME_JAKARTA)) {
178
179 return toInternetAddress(value, INTERNET_ADDRESS_CLASSNAME_JAKARTA);
180 }
181 if (InetAddress.class.isAssignableFrom(cls)) {
182 return toInetAddress(value);
183 }
184 if (Duration.class.equals(cls)) {
185 return toDuration(value);
186 }
187
188 throw new ConversionException("The value '" + value + "' (" + value.getClass() + ") can't be converted to a " + cls.getName() + " object");
189 }
190
191
192
193
194
195
196
197
198 public static BigDecimal toBigDecimal(final Object value) throws ConversionException {
199 final Number n = toNumber(value, BigDecimal.class);
200 if (n instanceof BigDecimal) {
201 return (BigDecimal) n;
202 }
203 return BigDecimal.valueOf(n.doubleValue());
204 }
205
206
207
208
209
210
211
212
213 public static BigInteger toBigInteger(final Object value) throws ConversionException {
214 final Number n = toNumber(value, BigInteger.class);
215 if (n instanceof BigInteger) {
216 return (BigInteger) n;
217 }
218 return BigInteger.valueOf(n.longValue());
219 }
220
221
222
223
224
225
226
227
228
229
230
231 public static Boolean toBoolean(final Object value) throws ConversionException {
232 if (value instanceof Boolean) {
233 return (Boolean) value;
234 }
235 if (!(value instanceof String)) {
236 throw new ConversionException("The value " + value + " can't be converted to a Boolean object");
237 }
238 final Boolean b = BooleanUtils.toBooleanObject((String) value);
239 if (b == null) {
240 throw new ConversionException("The value " + value + " can't be converted to a Boolean object");
241 }
242 return b;
243 }
244
245
246
247
248
249
250
251
252 public static Byte toByte(final Object value) throws ConversionException {
253 final Number n = toNumber(value, Byte.class);
254 if (n instanceof Byte) {
255 return (Byte) n;
256 }
257 return n.byteValue();
258 }
259
260
261
262
263
264
265
266
267
268 public static Calendar toCalendar(final Object value, final String format) throws ConversionException {
269 if (value instanceof Calendar) {
270 return (Calendar) value;
271 }
272 if (value instanceof Date) {
273 final Calendar calendar = Calendar.getInstance();
274 calendar.setTime((Date) value);
275 return calendar;
276 }
277 if (!(value instanceof String)) {
278 throw new ConversionException("The value " + value + " can't be converted to a Calendar");
279 }
280 try {
281 final Calendar calendar = Calendar.getInstance();
282 calendar.setTime(new SimpleDateFormat(format).parse((String) value));
283 return calendar;
284 } catch (final ParseException e) {
285 throw new ConversionException("The value " + value + " can't be converted to a Calendar", e);
286 }
287 }
288
289
290
291
292
293
294
295
296
297 public static Character toCharacter(final Object value) throws ConversionException {
298 final String strValue = String.valueOf(value);
299 if (strValue.length() == 1) {
300 return Character.valueOf(strValue.charAt(0));
301 }
302 throw new ConversionException(String.format("The value '%s' cannot be converted to a Character object!", strValue));
303 }
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319 public static Color toColor(final Object value) throws ConversionException {
320 if (value instanceof Color) {
321 return (Color) value;
322 }
323 if (!(value instanceof String) || StringUtils.isBlank((String) value)) {
324 throw new ConversionException("The value " + value + " can't be converted to a Color");
325 }
326 String color = ((String) value).trim();
327
328 final int[] components = new int[3];
329
330
331 final int minlength = components.length * 2;
332 if (color.length() < minlength) {
333 throw new ConversionException("The value " + value + " can't be converted to a Color");
334 }
335
336
337 if (color.startsWith("#")) {
338 color = color.substring(1);
339 }
340
341 try {
342
343 for (int i = 0; i < components.length; i++) {
344 components[i] = Integer.parseInt(color.substring(2 * i, 2 * i + 2), HEX_RADIX);
345 }
346
347
348 final int alpha;
349 if (color.length() >= minlength + 2) {
350 alpha = Integer.parseInt(color.substring(minlength, minlength + 2), HEX_RADIX);
351 } else {
352 alpha = Color.black.getAlpha();
353 }
354
355 return new Color(components[0], components[1], components[2], alpha);
356 } catch (final Exception e) {
357 throw new ConversionException("The value " + value + " can't be converted to a Color", e);
358 }
359 }
360
361
362
363
364
365
366
367
368
369 public static Date toDate(final Object value, final String format) throws ConversionException {
370 if (value instanceof Date) {
371 return (Date) value;
372 }
373 if (value instanceof Calendar) {
374 return ((Calendar) value).getTime();
375 }
376 if (!(value instanceof String)) {
377 throw new ConversionException("The value " + value + " can't be converted to a Date");
378 }
379 try {
380 return new SimpleDateFormat(format).parse((String) value);
381 } catch (final ParseException e) {
382 throw new ConversionException("The value " + value + " can't be converted to a Date", e);
383 }
384 }
385
386
387
388
389
390
391
392
393 public static Double toDouble(final Object value) throws ConversionException {
394 final Number n = toNumber(value, Double.class);
395 if (n instanceof Double) {
396 return (Double) n;
397 }
398 return Double.valueOf(n.doubleValue());
399 }
400
401
402
403
404
405
406
407
408
409 public static Duration toDuration(final Object value) throws ConversionException {
410 if (value instanceof Duration) {
411 return (Duration) value;
412 }
413 if (value instanceof CharSequence) {
414 try {
415 return Duration.parse((CharSequence) value);
416 } catch (final DateTimeParseException e) {
417 throw new ConversionException("Could not convert " + value + " to Duration", e);
418 }
419 }
420 throw new ConversionException("The value " + value + " can't be converted to a Duration");
421 }
422
423
424
425
426
427
428
429
430
431
432 static <E extends Enum<E>> E toEnum(final Object value, final Class<E> cls) throws ConversionException {
433 if (value.getClass().equals(cls)) {
434 return cls.cast(value);
435 }
436 if (value instanceof String) {
437 try {
438 return Enum.valueOf(cls, (String) value);
439 } catch (final Exception e) {
440 throw new ConversionException("The value " + value + " can't be converted to a " + cls.getName());
441 }
442 }
443 if (!(value instanceof Number)) {
444 throw new ConversionException("The value " + value + " can't be converted to a " + cls.getName());
445 }
446 try {
447 final E[] enumConstants = cls.getEnumConstants();
448 return enumConstants[((Number) value).intValue()];
449 } catch (final Exception e) {
450 throw new ConversionException("The value " + value + " can't be converted to a " + cls.getName());
451 }
452 }
453
454
455
456
457
458
459
460
461
462 public static File toFile(final Object value) throws ConversionException {
463 if (value instanceof File) {
464 return (File) value;
465 }
466 if (value instanceof Path) {
467 return ((Path) value).toFile();
468 }
469 if (value instanceof String) {
470 return new File((String) value);
471 }
472 throw new ConversionException("The value " + value + " can't be converted to a File");
473 }
474
475
476
477
478
479
480
481
482 public static Float toFloat(final Object value) throws ConversionException {
483 final Number n = toNumber(value, Float.class);
484 if (n instanceof Float) {
485 return (Float) n;
486 }
487 return Float.valueOf(n.floatValue());
488 }
489
490
491
492
493
494
495
496
497
498 static InetAddress toInetAddress(final Object value) throws ConversionException {
499 if (value instanceof InetAddress) {
500 return (InetAddress) value;
501 }
502 if (!(value instanceof String)) {
503 throw new ConversionException("The value " + value + " can't be converted to a InetAddress");
504 }
505 try {
506 return InetAddress.getByName((String) value);
507 } catch (final UnknownHostException e) {
508 throw new ConversionException("The value " + value + " can't be converted to a InetAddress", e);
509 }
510 }
511
512
513
514
515
516
517
518
519 public static Integer toInteger(final Object value) throws ConversionException {
520 final Number n = toNumber(value, Integer.class);
521 if (n instanceof Integer) {
522 return (Integer) n;
523 }
524 return n.intValue();
525 }
526
527
528
529
530
531
532
533
534
535
536
537 static Object toInternetAddress(final Object value, final String targetClassName) throws ConversionException {
538 if (value.getClass().getName().equals(targetClassName)) {
539 return value;
540 }
541 if (!(value instanceof String)) {
542 throw new ConversionException("The value " + value + " can't be converted to an InternetAddress");
543 }
544 try {
545 final Constructor<?> ctor = Class.forName(targetClassName).getConstructor(String.class);
546 return ctor.newInstance(value);
547 } catch (final Exception e) {
548 throw new ConversionException("The value " + value + " can't be converted to an InternetAddress", e);
549 }
550 }
551
552
553
554
555
556
557
558
559 public static Locale toLocale(final Object value) throws ConversionException {
560 if (value instanceof Locale) {
561 return (Locale) value;
562 }
563 if (!(value instanceof String)) {
564 throw new ConversionException("The value " + value + " can't be converted to a Locale");
565 }
566 final String[] elements = ((String) value).split("_");
567 final int size = elements.length;
568
569 if (size >= 1 && (elements[0].length() == 2 || elements[0].isEmpty())) {
570 final String language = elements[0];
571 final String country = size >= 2 ? elements[1] : "";
572 final String variant = size >= 3 ? elements[2] : "";
573
574 return new Locale(language, country, variant);
575 }
576 throw new ConversionException("The value " + value + " can't be converted to a Locale");
577 }
578
579
580
581
582
583
584
585
586 public static Long toLong(final Object value) throws ConversionException {
587 final Number n = toNumber(value, Long.class);
588 if (n instanceof Long) {
589 return (Long) n;
590 }
591 return n.longValue();
592 }
593
594
595
596
597
598
599
600
601
602
603
604 static Number toNumber(final Object value, final Class<?> targetClass) throws ConversionException {
605 if (value instanceof Number) {
606 return (Number) value;
607 }
608 final String str = Objects.toString(value, null);
609 if (StringUtils.startsWithAny(str, HEX_PREFIX)) {
610 try {
611 return new BigInteger(str.substring(HEX_PREFIX.length()), HEX_RADIX);
612 } catch (final NumberFormatException nex) {
613 throw new ConversionException("Could not convert " + str + " to " + targetClass.getName() + "! Invalid hex number.", nex);
614 }
615 }
616
617 if (StringUtils.startsWithAny(str, BIN_PREFIX)) {
618 try {
619 return new BigInteger(str.substring(BIN_PREFIX.length()), BIN_RADIX);
620 } catch (final NumberFormatException nex) {
621 throw new ConversionException("Could not convert " + str + " to " + targetClass.getName() + "! Invalid binary number.", nex);
622 }
623 }
624
625 try {
626 final Constructor<?> constr = targetClass.getConstructor(CONSTR_ARGS);
627 return (Number) constr.newInstance(str);
628 } catch (final InvocationTargetException itex) {
629 throw new ConversionException("Could not convert " + str + " to " + targetClass.getName(), itex.getTargetException());
630 } catch (final Exception ex) {
631
632 throw new ConversionException("Conversion error when trying to convert " + str + " to " + targetClass.getName(), ex);
633 }
634 }
635
636
637
638
639
640
641
642
643
644 public static Path toPath(final Object value) throws ConversionException {
645 if (value instanceof File) {
646 return ((File) value).toPath();
647 }
648 if (value instanceof Path) {
649 return (Path) value;
650 }
651 if (value instanceof String) {
652 return Paths.get((String) value);
653 }
654 throw new ConversionException("The value " + value + " can't be converted to a Path");
655 }
656
657
658
659
660
661
662
663
664 public static Pattern toPattern(final Object value) throws ConversionException {
665 if (value instanceof Pattern) {
666 return (Pattern) value;
667 }
668 if (!(value instanceof String)) {
669 throw new ConversionException("The value " + value + " can't be converted to a Pattern");
670 }
671 try {
672 return Pattern.compile((String) value);
673 } catch (final PatternSyntaxException e) {
674 throw new ConversionException("The value " + value + " can't be converted to a Pattern", e);
675 }
676 }
677
678
679
680
681
682
683
684
685 public static Short toShort(final Object value) throws ConversionException {
686 final Number n = toNumber(value, Short.class);
687 if (n instanceof Short) {
688 return (Short) n;
689 }
690 return n.shortValue();
691 }
692
693
694
695
696
697
698
699
700 public static URI toURI(final Object value) throws ConversionException {
701 if (value instanceof URI) {
702 return (URI) value;
703 }
704 if (!(value instanceof String)) {
705 throw new ConversionException("The value " + value + " can't be converted to an URI");
706 }
707 try {
708 return new URI((String) value);
709 } catch (final URISyntaxException e) {
710 throw new ConversionException("The value " + value + " can't be converted to an URI", e);
711 }
712 }
713
714
715
716
717
718
719
720
721 public static URL toURL(final Object value) throws ConversionException {
722 if (value instanceof URL) {
723 return (URL) value;
724 }
725 if (!(value instanceof String)) {
726 throw new ConversionException("The value " + value + " can't be converted to an URL");
727 }
728 try {
729 return new URL((String) value);
730 } catch (final MalformedURLException e) {
731 throw new ConversionException("The value " + value + " can't be converted to an URL", e);
732 }
733 }
734
735
736
737
738 private PropertyConverter() {
739
740 }
741 }