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 import org.apache.commons.lang3.Strings;
49
50
51
52
53
54
55 public final class PropertyConverter {
56
57
58 private static final String HEX_PREFIX = "0x";
59
60
61 private static final int HEX_RADIX = 16;
62
63
64 private static final String BIN_PREFIX = "0b";
65
66
67 private static final int BIN_RADIX = 2;
68
69
70 private static final Class<?>[] CONSTR_ARGS = {String.class};
71
72
73 private static final String INTERNET_ADDRESS_CLASSNAME_JAVAX = "javax.mail.internet.InternetAddress";
74
75
76 private static final String INTERNET_ADDRESS_CLASSNAME_JAKARTA = "jakarta.mail.internet.InternetAddress";
77
78
79
80
81
82
83
84
85 @SuppressWarnings("unchecked")
86
87 private static Object convertToEnum(final Class<?> enumClass, final Object value) {
88 return toEnum(value, enumClass.asSubclass(Enum.class));
89 }
90
91
92
93
94
95
96
97
98
99
100
101
102
103 public static Object to(final Class<?> cls, final Object value, final DefaultConversionHandler convHandler) throws ConversionException {
104 if (cls.isInstance(value)) {
105 return value;
106 }
107
108 if (String.class.equals(cls)) {
109 return String.valueOf(value);
110 }
111 if (Boolean.class.equals(cls) || Boolean.TYPE.equals(cls)) {
112 return toBoolean(value);
113 }
114 if (Character.class.equals(cls) || Character.TYPE.equals(cls)) {
115 return toCharacter(value);
116 }
117 if (Number.class.isAssignableFrom(cls) || cls.isPrimitive()) {
118 if (Integer.class.equals(cls) || Integer.TYPE.equals(cls)) {
119 return toInteger(value);
120 }
121 if (Long.class.equals(cls) || Long.TYPE.equals(cls)) {
122 return toLong(value);
123 }
124 if (Byte.class.equals(cls) || Byte.TYPE.equals(cls)) {
125 return toByte(value);
126 }
127 if (Short.class.equals(cls) || Short.TYPE.equals(cls)) {
128 return toShort(value);
129 }
130 if (Float.class.equals(cls) || Float.TYPE.equals(cls)) {
131 return toFloat(value);
132 }
133 if (Double.class.equals(cls) || Double.TYPE.equals(cls)) {
134 return toDouble(value);
135 }
136 if (BigInteger.class.equals(cls)) {
137 return toBigInteger(value);
138 }
139 if (BigDecimal.class.equals(cls)) {
140 return toBigDecimal(value);
141 }
142 return toNumber(value, cls);
143 }
144 if (Date.class.equals(cls)) {
145 return toDate(value, convHandler.getDateFormat());
146 }
147 if (Calendar.class.equals(cls)) {
148 return toCalendar(value, convHandler.getDateFormat());
149 }
150 if (File.class.equals(cls)) {
151 return toFile(value);
152 }
153 if (Path.class.equals(cls)) {
154 return toPath(value);
155 }
156 if (URI.class.equals(cls)) {
157 return toURI(value);
158 }
159 if (URL.class.equals(cls)) {
160 return toURL(value);
161 }
162 if (Pattern.class.equals(cls)) {
163 return toPattern(value);
164 }
165 if (Locale.class.equals(cls)) {
166 return toLocale(value);
167 }
168 if (cls.isEnum()) {
169 return convertToEnum(cls, value);
170 }
171 if (Color.class.equals(cls)) {
172 return toColor(value);
173 }
174 if (cls.getName().equals(INTERNET_ADDRESS_CLASSNAME_JAVAX)) {
175
176 return toInternetAddress(value, INTERNET_ADDRESS_CLASSNAME_JAVAX);
177 }
178 if (cls.getName().equals(INTERNET_ADDRESS_CLASSNAME_JAKARTA)) {
179
180 return toInternetAddress(value, INTERNET_ADDRESS_CLASSNAME_JAKARTA);
181 }
182 if (InetAddress.class.isAssignableFrom(cls)) {
183 return toInetAddress(value);
184 }
185 if (Duration.class.equals(cls)) {
186 return toDuration(value);
187 }
188 throw new ConversionException("The value '%s' (%s) can't be converted to a %s object", value, value.getClass(), cls.getName());
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 %s can't be converted to a Boolean object", value);
237 }
238 final Boolean b = BooleanUtils.toBooleanObject((String) value);
239 if (b == null) {
240 throw new ConversionException("The value %s can't be converted to a Boolean object", value);
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 %s can't be converted to a Calendar", value);
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(e, "The value %s can't be converted to a Calendar", value);
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("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 %s can't be converted to a Color", value);
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 %s can't be converted to a Color", value);
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(e, "The value %s can't be converted to a Color", value);
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 %s can't be converted to a Date", value);
378 }
379 try {
380 return new SimpleDateFormat(format).parse((String) value);
381 } catch (final ParseException e) {
382 throw new ConversionException(e, "The value %s can't be converted to a Date", value);
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(e, "Could not convert %s to Duration", value);
418 }
419 }
420 throw new ConversionException("The value %s can't be converted to a Duration", value);
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(e, "The value %s can't be converted to a %s", value, cls.getName());
441 }
442 }
443 if (!(value instanceof Number)) {
444 throw new ConversionException("The value %s can't be converted to a ", value, 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(e, "The value %s can't be converted to a ", value, 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 %s can't be converted to a File", value);
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 %s can't be converted to a InetAddress", value);
504 }
505 try {
506 return InetAddress.getByName((String) value);
507 } catch (final UnknownHostException e) {
508 throw new ConversionException(e, "The value %s can't be converted to a InetAddress", value);
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 %s can't be converted to an InternetAddress", value);
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(e, "The value %s can't be converted to an InternetAddress", value);
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 %s can't be converted to a Locale", value);
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 %s can't be converted to a Locale", value);
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 (Strings.CS.startsWithAny(str, HEX_PREFIX)) {
610 try {
611 return new BigInteger(str.substring(HEX_PREFIX.length()), HEX_RADIX);
612 } catch (final NumberFormatException e) {
613 throw new ConversionException(e, "Could not convert %s to %s! Invalid hex number.", str, targetClass.getName());
614 }
615 }
616 if (Strings.CS.startsWithAny(str, BIN_PREFIX)) {
617 try {
618 return new BigInteger(str.substring(BIN_PREFIX.length()), BIN_RADIX);
619 } catch (final NumberFormatException e) {
620 throw new ConversionException(e, "Could not convert %s to %s! Invalid binary number.", str, targetClass.getName());
621 }
622 }
623 try {
624 final Constructor<?> constr = targetClass.getConstructor(CONSTR_ARGS);
625 return (Number) constr.newInstance(str);
626 } catch (final InvocationTargetException e) {
627 throw new ConversionException(e.getTargetException(), "Could not convert %s to %s", str, targetClass.getName());
628 } catch (final Exception e) {
629
630 throw new ConversionException(e, "Conversion error when trying to convert %s to %s", str, targetClass.getName());
631 }
632 }
633
634
635
636
637
638
639
640
641
642 public static Path toPath(final Object value) throws ConversionException {
643 if (value instanceof File) {
644 return ((File) value).toPath();
645 }
646 if (value instanceof Path) {
647 return (Path) value;
648 }
649 if (value instanceof String) {
650 return Paths.get((String) value);
651 }
652 throw new ConversionException("The value %s can't be converted to a Path", value);
653 }
654
655
656
657
658
659
660
661
662 public static Pattern toPattern(final Object value) throws ConversionException {
663 if (value instanceof Pattern) {
664 return (Pattern) value;
665 }
666 if (!(value instanceof String)) {
667 throw new ConversionException("The value %s can't be converted to a Pattern", value);
668 }
669 try {
670 return Pattern.compile((String) value);
671 } catch (final PatternSyntaxException e) {
672 throw new ConversionException(e, "The value %s can't be converted to a Pattern", value);
673 }
674 }
675
676
677
678
679
680
681
682
683 public static Short toShort(final Object value) throws ConversionException {
684 final Number n = toNumber(value, Short.class);
685 if (n instanceof Short) {
686 return (Short) n;
687 }
688 return n.shortValue();
689 }
690
691
692
693
694
695
696
697
698 public static URI toURI(final Object value) throws ConversionException {
699 if (value instanceof URI) {
700 return (URI) value;
701 }
702 if (!(value instanceof String)) {
703 throw new ConversionException("The value %s can't be converted to an URI", value);
704 }
705 try {
706 return new URI((String) value);
707 } catch (final URISyntaxException e) {
708 throw new ConversionException(e, "The value %s can't be converted to an URI", value);
709 }
710 }
711
712
713
714
715
716
717
718
719 public static URL toURL(final Object value) throws ConversionException {
720 if (value instanceof URL) {
721 return (URL) value;
722 }
723 if (!(value instanceof String)) {
724 throw new ConversionException("The value %s can't be converted to an URL", value);
725 }
726 try {
727 return new URL((String) value);
728 } catch (final MalformedURLException e) {
729 throw new ConversionException(e, "The value %s can't be converted to an URL", value);
730 }
731 }
732
733
734
735
736 private PropertyConverter() {
737
738 }
739 }