1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.configuration;
18
19 import java.awt.Color;
20 import java.math.BigDecimal;
21 import java.math.BigInteger;
22 import java.net.MalformedURLException;
23 import java.net.URL;
24 import java.text.ParseException;
25 import java.text.SimpleDateFormat;
26 import java.util.ArrayList;
27 import java.util.Calendar;
28 import java.util.Collection;
29 import java.util.Date;
30 import java.util.Iterator;
31 import java.util.List;
32 import java.util.Locale;
33
34 import org.apache.commons.collections.IteratorUtils;
35 import org.apache.commons.collections.iterators.IteratorChain;
36 import org.apache.commons.collections.iterators.SingletonIterator;
37 import org.apache.commons.lang.BooleanUtils;
38 import org.apache.commons.lang.StringUtils;
39
40 /***
41 * A utility class to convert the configuration properties into any type.
42 *
43 * @author Emmanuel Bourg
44 * @version $Revision$, $Date: 2005-12-06 04:10:27 +0100 (Tue, 06 Dec 2005) $
45 * @since 1.1
46 */
47 public final class PropertyConverter
48 {
49 /*** Constant for the list delimiter escaping character.*/
50 static final String LIST_ESCAPE = "//";
51
52 /*** Constant for the prefix of hex numbers.*/
53 private static final String HEX_PREFIX = "0x";
54
55 /*** Constant for the radix of hex numbers.*/
56 private static final int HEX_RADIX = 16;
57
58 /***
59 * Private constructor prevents instances from being created.
60 */
61 private PropertyConverter()
62 {
63
64 }
65
66 /***
67 * Convert the specified object into a Boolean.
68 *
69 * @param value the value to convert
70 * @return the converted value
71 * @throws ConversionException thrown if the value cannot be converted to a boolean
72 */
73 public static Boolean toBoolean(Object value) throws ConversionException
74 {
75 if (value instanceof Boolean)
76 {
77 return (Boolean) value;
78 }
79 else if (value instanceof String)
80 {
81 Boolean b = BooleanUtils.toBooleanObject((String) value);
82 if (b == null)
83 {
84 throw new ConversionException("The value " + value + " can't be converted to a Boolean object");
85 }
86 return b;
87 }
88 else
89 {
90 throw new ConversionException("The value " + value + " can't be converted to a Boolean object");
91 }
92 }
93
94 /***
95 * Convert the specified object into a Byte.
96 *
97 * @param value the value to convert
98 * @return the converted value
99 * @throws ConversionException thrown if the value cannot be converted to a byte
100 */
101 public static Byte toByte(Object value) throws ConversionException
102 {
103 if (value instanceof Byte)
104 {
105 return (Byte) value;
106 }
107 else if (value instanceof String)
108 {
109 try
110 {
111 String string = (String) value;
112 if (string.startsWith(HEX_PREFIX))
113 {
114 return new Byte((byte) Integer.parseInt(string.substring(2), HEX_RADIX));
115 }
116 else
117 {
118 return new Byte(string);
119 }
120 }
121 catch (NumberFormatException e)
122 {
123 throw new ConversionException("The value " + value + " can't be converted to a Byte object", e);
124 }
125 }
126 else
127 {
128 throw new ConversionException("The value " + value + " can't be converted to a Byte object");
129 }
130 }
131
132 /***
133 * Convert the specified object into a Short.
134 *
135 * @param value the value to convert
136 * @return the converted value
137 * @throws ConversionException thrown if the value cannot be converted to a short
138 */
139 public static Short toShort(Object value) throws ConversionException
140 {
141 if (value instanceof Short)
142 {
143 return (Short) value;
144 }
145 else if (value instanceof String)
146 {
147 try
148 {
149 String string = (String) value;
150 if (string.startsWith(HEX_PREFIX))
151 {
152 return new Short((short) Integer.parseInt(string.substring(2), HEX_RADIX));
153 }
154 else
155 {
156 return new Short(string);
157 }
158
159 }
160 catch (NumberFormatException e)
161 {
162 throw new ConversionException("The value " + value + " can't be converted to a Short object", e);
163 }
164 }
165 else
166 {
167 throw new ConversionException("The value " + value + " can't be converted to a Short object");
168 }
169 }
170
171 /***
172 * Convert the specified object into an Integer.
173 *
174 * @param value the value to convert
175 * @return the converted value
176 * @throws ConversionException thrown if the value cannot be converted to an integer
177 */
178 public static Integer toInteger(Object value) throws ConversionException
179 {
180 if (value instanceof Integer)
181 {
182 return (Integer) value;
183 }
184 else if (value instanceof String)
185 {
186 try
187 {
188 String string = (String) value;
189 if (string.startsWith(HEX_PREFIX))
190 {
191 return new Integer((int) Long.parseLong(string.substring(2), HEX_RADIX));
192 }
193 else
194 {
195 return new Integer(string);
196 }
197 }
198 catch (NumberFormatException e)
199 {
200 throw new ConversionException("The value " + value + " can't be converted to an Integer object", e);
201 }
202 }
203 else
204 {
205 throw new ConversionException("The value " + value + " can't be converted to an Integer object");
206 }
207 }
208
209 /***
210 * Convert the specified object into a Long.
211 *
212 * @param value the value to convert
213 * @return the converted value
214 * @throws ConversionException thrown if the value cannot be converted to a Long
215 */
216 public static Long toLong(Object value) throws ConversionException
217 {
218 if (value instanceof Long)
219 {
220 return (Long) value;
221 }
222 else if (value instanceof String)
223 {
224 try
225 {
226 String string = (String) value;
227 if (string.startsWith(HEX_PREFIX))
228 {
229 return new Long(new BigInteger(string.substring(2), HEX_RADIX).longValue());
230 }
231 else
232 {
233 return new Long(string);
234 }
235 }
236 catch (NumberFormatException e)
237 {
238 throw new ConversionException("The value " + value + " can't be converted to a Long object", e);
239 }
240 }
241 else
242 {
243 throw new ConversionException("The value " + value + " can't be converted to a Long object");
244 }
245 }
246
247 /***
248 * Convert the specified object into a Float.
249 *
250 * @param value the value to convert
251 * @return the converted value
252 * @throws ConversionException thrown if the value cannot be converted to a Float
253 */
254 public static Float toFloat(Object value) throws ConversionException
255 {
256 if (value instanceof Float)
257 {
258 return (Float) value;
259 }
260 else if (value instanceof String)
261 {
262 try
263 {
264 return new Float((String) value);
265 }
266 catch (NumberFormatException e)
267 {
268 throw new ConversionException("The value " + value + " can't be converted to a Float object", e);
269 }
270 }
271 else
272 {
273 throw new ConversionException("The value " + value + " can't be converted to a Float object");
274 }
275 }
276
277 /***
278 * Convert the specified object into a Double.
279 *
280 * @param value the value to convert
281 * @return the converted value
282 * @throws ConversionException thrown if the value cannot be converted to a Double
283 */
284 public static Double toDouble(Object value) throws ConversionException
285 {
286 if (value instanceof Double)
287 {
288 return (Double) value;
289 }
290 else if (value instanceof String)
291 {
292 try
293 {
294 return new Double((String) value);
295 }
296 catch (NumberFormatException e)
297 {
298 throw new ConversionException("The value " + value + " can't be converted to a Double object", e);
299 }
300 }
301 else
302 {
303 throw new ConversionException("The value " + value + " can't be converted to a Double object");
304 }
305 }
306
307 /***
308 * Convert the specified object into a BigInteger.
309 *
310 * @param value the value to convert
311 * @return the converted value
312 * @throws ConversionException thrown if the value cannot be converted to a BigInteger
313 */
314 public static BigInteger toBigInteger(Object value) throws ConversionException
315 {
316 if (value instanceof BigInteger)
317 {
318 return (BigInteger) value;
319 }
320 else if (value instanceof String)
321 {
322 try
323 {
324 String string = (String) value;
325 if (string.startsWith(HEX_PREFIX))
326 {
327 return new BigInteger(string.substring(2), HEX_RADIX);
328 }
329 else
330 {
331 return new BigInteger(string);
332 }
333 }
334 catch (NumberFormatException e)
335 {
336 throw new ConversionException("The value " + value + " can't be converted to a BigInteger object", e);
337 }
338 }
339 else
340 {
341 throw new ConversionException("The value " + value + " can't be converted to a BigInteger object");
342 }
343 }
344
345 /***
346 * Convert the specified object into a BigDecimal.
347 *
348 * @param value the value to convert
349 * @return the converted value
350 * @throws ConversionException thrown if the value cannot be converted to a BigDecimal
351 */
352 public static BigDecimal toBigDecimal(Object value) throws ConversionException
353 {
354 if (value instanceof BigDecimal)
355 {
356 return (BigDecimal) value;
357 }
358 else if (value instanceof String)
359 {
360 try
361 {
362 return new BigDecimal((String) value);
363 }
364 catch (NumberFormatException e)
365 {
366 throw new ConversionException("The value " + value + " can't be converted to a BigDecimal object", e);
367 }
368 }
369 else
370 {
371 throw new ConversionException("The value " + value + " can't be converted to a BigDecimal object");
372 }
373 }
374
375 /***
376 * Convert the specified object into an URL.
377 *
378 * @param value the value to convert
379 * @return the converted value
380 * @throws ConversionException thrown if the value cannot be converted to an URL
381 */
382 public static URL toURL(Object value) throws ConversionException
383 {
384 if (value instanceof URL)
385 {
386 return (URL) value;
387 }
388 else if (value instanceof String)
389 {
390 try
391 {
392 return new URL((String) value);
393 }
394 catch (MalformedURLException e)
395 {
396 throw new ConversionException("The value " + value + " can't be converted to an URL", e);
397 }
398 }
399 else
400 {
401 throw new ConversionException("The value " + value + " can't be converted to an URL");
402 }
403 }
404
405 /***
406 * Convert the specified object into a Locale.
407 *
408 * @param value the value to convert
409 * @return the converted value
410 * @throws ConversionException thrown if the value cannot be converted to a Locale
411 */
412 public static Locale toLocale(Object value) throws ConversionException
413 {
414 if (value instanceof Locale)
415 {
416 return (Locale) value;
417 }
418 else if (value instanceof String)
419 {
420 List elements = split((String) value, '_');
421 int size = elements.size();
422
423 if (size >= 1 && (((String) elements.get(0)).length() == 2 || ((String) elements.get(0)).length() == 0))
424 {
425 String language = (String) elements.get(0);
426 String country = (String) ((size >= 2) ? elements.get(1) : "");
427 String variant = (String) ((size >= 3) ? elements.get(2) : "");
428
429 return new Locale(language, country, variant);
430 }
431 else
432 {
433 throw new ConversionException("The value " + value + " can't be converted to a Locale");
434 }
435 }
436 else
437 {
438 throw new ConversionException("The value " + value + " can't be converted to a Locale");
439 }
440 }
441
442 /***
443 * Split a string on the specified delimiter. To be removed when
444 * commons-lang has a better replacement available (Tokenizer?).
445 *
446 * todo: replace with a commons-lang equivalent
447 *
448 * @param s the string to split
449 * @param delimiter the delimiter
450 * @return a list with the single tokens
451 */
452 public static List split(String s, char delimiter)
453 {
454 if (s == null)
455 {
456 return new ArrayList();
457 }
458
459 List list = new ArrayList();
460
461 StringBuffer token = new StringBuffer();
462 int begin = 0;
463 int end = 0;
464 while (begin <= s.length())
465 {
466
467 int index = s.indexOf(delimiter, end);
468
469
470 end = (index != -1) ? index : s.length();
471
472
473 String chunk = s.substring(begin , end);
474
475 if (chunk.endsWith(LIST_ESCAPE) && end != s.length())
476 {
477 token.append(chunk.substring(0, chunk.length() - 1));
478 token.append(delimiter);
479 }
480 else
481 {
482
483 token.append(chunk);
484
485
486 list.add(token.toString().trim());
487
488
489 token = new StringBuffer();
490 }
491
492
493 end = end + 1;
494 begin = end;
495 }
496
497 return list;
498 }
499
500 /***
501 * Escapes the delimiters that might be contained in the given string. This
502 * method ensures that list delimiter characters that are part of a
503 * property's value are correctly escaped when a configuration is saved to a
504 * file. Otherwise when loaded again the property will be treated as a list
505 * property.
506 *
507 * @param s the string with the value
508 * @param delimiter the list delimiter to use
509 * @return the correctly esaped string
510 */
511 public static String escapeDelimiters(String s, char delimiter)
512 {
513 return StringUtils.replace(s, String.valueOf(delimiter), LIST_ESCAPE
514 + delimiter);
515 }
516
517 /***
518 * Convert the specified object into a Color. If the value is a String,
519 * the format allowed is (#)?[0-9A-F]{6}([0-9A-F]{2})?. Examples:
520 * <ul>
521 * <li>FF0000 (red)</li>
522 * <li>0000FFA0 (semi transparent blue)</li>
523 * <li>#CCCCCC (gray)</li>
524 * <li>#00FF00A0 (semi transparent green)</li>
525 * </ul>
526 *
527 * @param value the value to convert
528 * @return the converted value
529 * @throws ConversionException thrown if the value cannot be converted to a Color
530 */
531 public static Color toColor(Object value) throws ConversionException
532 {
533 if (value instanceof Color)
534 {
535 return (Color) value;
536 }
537 else if (value instanceof String && !StringUtils.isBlank((String) value))
538 {
539 String color = ((String) value).trim();
540
541 int[] components = new int[3];
542
543
544 int minlength = components.length * 2;
545 if (color.length() < minlength)
546 {
547 throw new ConversionException("The value " + value + " can't be converted to a Color");
548 }
549
550
551 if (color.startsWith("#"))
552 {
553 color = color.substring(1);
554 }
555
556 try
557 {
558
559 for (int i = 0; i < components.length; i++)
560 {
561 components[i] = Integer.parseInt(color.substring(2 * i, 2 * i + 2), HEX_RADIX);
562 }
563
564
565 int alpha;
566 if (color.length() >= minlength + 2)
567 {
568 alpha = Integer.parseInt(color.substring(minlength, minlength + 2), HEX_RADIX);
569 }
570 else
571 {
572 alpha = Color.black.getAlpha();
573 }
574
575 return new Color(components[0], components[1], components[2], alpha);
576 }
577 catch (Exception e)
578 {
579 throw new ConversionException("The value " + value + " can't be converted to a Color", e);
580 }
581 }
582 else
583 {
584 throw new ConversionException("The value " + value + " can't be converted to a Color");
585 }
586 }
587
588 /***
589 * Convert the specified object into a Date.
590 *
591 * @param value the value to convert
592 * @param format the DateFormat pattern to parse String values
593 * @return the converted value
594 * @throws ConversionException thrown if the value cannot be converted to a Calendar
595 */
596 public static Date toDate(Object value, String format) throws ConversionException
597 {
598 if (value instanceof Date)
599 {
600 return (Date) value;
601 }
602 else if (value instanceof Calendar)
603 {
604 return ((Calendar) value).getTime();
605 }
606 else if (value instanceof String)
607 {
608 try
609 {
610 return new SimpleDateFormat(format).parse((String) value);
611 }
612 catch (ParseException e)
613 {
614 throw new ConversionException("The value " + value + " can't be converted to a Date", e);
615 }
616 }
617 else
618 {
619 throw new ConversionException("The value " + value + " can't be converted to a Date");
620 }
621 }
622
623 /***
624 * Convert the specified object into a Calendar.
625 *
626 * @param value the value to convert
627 * @param format the DateFormat pattern to parse String values
628 * @return the converted value
629 * @throws ConversionException thrown if the value cannot be converted to a Calendar
630 */
631 public static Calendar toCalendar(Object value, String format) throws ConversionException
632 {
633 if (value instanceof Calendar)
634 {
635 return (Calendar) value;
636 }
637 else if (value instanceof Date)
638 {
639 Calendar calendar = Calendar.getInstance();
640 calendar.setTime((Date) value);
641 return calendar;
642 }
643 else if (value instanceof String)
644 {
645 try
646 {
647 Calendar calendar = Calendar.getInstance();
648 calendar.setTime(new SimpleDateFormat(format).parse((String) value));
649 return calendar;
650 }
651 catch (ParseException e)
652 {
653 throw new ConversionException("The value " + value + " can't be converted to a Calendar", e);
654 }
655 }
656 else
657 {
658 throw new ConversionException("The value " + value + " can't be converted to a Calendar");
659 }
660 }
661
662 /***
663 * Return an iterator over the simple values of a composite value. The value
664 * specified is handled depending on its type:
665 * <ul>
666 * <li>Strings are checked for delimiter characters and splitted if necessary.</li>
667 * <li>For collections the single elements are checked.</li>
668 * <li>Arrays are treated like collections.</li>
669 * <li>All other types are directly inserted.</li>
670 * <li>Recursive combinations are supported, e.g. a collection containing array that contain strings.</li>
671 * </ul>
672 *
673 * @param value the value to "split"
674 * @param delimiter the delimiter for String values
675 * @return an iterator for accessing the single values
676 */
677 public static Iterator toIterator(Object value, char delimiter)
678 {
679 if (value == null)
680 {
681 return IteratorUtils.emptyIterator();
682 }
683 if (value instanceof String)
684 {
685 String s = (String) value;
686 if (s.indexOf(delimiter) > 0)
687 {
688 return split((String) value, delimiter).iterator();
689 }
690 else
691 {
692 return new SingletonIterator(value);
693 }
694 }
695 else if (value instanceof Collection)
696 {
697 return toIterator(((Collection) value).iterator(), delimiter);
698 }
699 else if (value.getClass().isArray())
700 {
701 return toIterator(IteratorUtils.arrayIterator(value), delimiter);
702 }
703 else if (value instanceof Iterator)
704 {
705 Iterator iterator = (Iterator) value;
706 IteratorChain chain = new IteratorChain();
707 while (iterator.hasNext())
708 {
709 chain.addIterator(toIterator(iterator.next(), delimiter));
710 }
711 return chain;
712 }
713 else
714 {
715 return new SingletonIterator(value);
716 }
717 }
718 }