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