View Javadoc

1   /*
2    * Copyright 2004-2005 The Apache Software Foundation.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License")
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
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          // to prevent instanciation...
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             // find the next delimiter
467             int index = s.indexOf(delimiter, end);
468 
469             // move the end index at the end of the string if the delimiter is not found
470             end = (index != -1) ? index : s.length();
471 
472             // extract the chunk
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                 // append the chunk to the token
483                 token.append(chunk);
484 
485                 // add the token to the list
486                 list.add(token.toString().trim());
487 
488                 // reset the token
489                 token = new StringBuffer();
490             }
491 
492             // move to the next chunk
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             // check the size of the string
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             // remove the leading #
551             if (color.startsWith("#"))
552             {
553                 color = color.substring(1);
554             }
555 
556             try
557             {
558                 // parse the components
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                 // parse the transparency
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 }