##### Coverage Report - org.apache.commons.configuration.PropertyConverter

Classes in this File | Line Coverage | Branch Coverage | Complexity
PropertyConverter |
98% | 180/184
100% | 59/59
| 6.0476190476190474;6,048

1 |  |
     /*
2 |  |
      * Licensed to the Apache Software Foundation (ASF) under one or more
3 |  |
      * contributor license agreements.  See the NOTICE file distributed with
4 |  |
      * this work for additional information regarding copyright ownership.
5 |  |
      * The ASF licenses this file to You under the Apache License, Version 2.0
6 |  |
      * (the "License"); you may not use this file except in compliance with
7 |  |
      * the License.  You may obtain a copy of the License at
8 |  |
      *
9 |  |
      *     http://www.apache.org/licenses/LICENSE-2.0
10 |  |
      *
11 |  |
      * Unless required by applicable law or agreed to in writing, software
12 |  |
      * distributed under the License is distributed on an "AS IS" BASIS,
13 |  |
      * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 |  |
      * See the License for the specific language governing permissions and
15 |  |
      * limitations under the License.
16 |  |
      */
17 |  |
18 |  |
     package org.apache.commons.configuration;
19 |  |
20 |  |
     import java.awt.Color;
21 |  |
     import java.lang.reflect.Constructor;
22 |  |
     import java.lang.reflect.InvocationTargetException;
23 |  |
     import java.math.BigDecimal;
24 |  |
     import java.math.BigInteger;
25 |  |
     import java.net.MalformedURLException;
26 |  |
     import java.net.URL;
27 |  |
     import java.text.ParseException;
28 |  |
     import java.text.SimpleDateFormat;
29 |  |
     import java.util.ArrayList;
30 |  |
     import java.util.Calendar;
31 |  |
     import java.util.Collection;
32 |  |
     import java.util.Date;
33 |  |
     import java.util.Iterator;
34 |  |
     import java.util.List;
35 |  |
     import java.util.Locale;
36 |  |
37 |  |
     import org.apache.commons.collections.IteratorUtils;
38 |  |
     import org.apache.commons.collections.iterators.IteratorChain;
39 |  |
     import org.apache.commons.collections.iterators.SingletonIterator;
40 |  |
     import org.apache.commons.lang.BooleanUtils;
41 |  |
     import org.apache.commons.lang.StringUtils;
42 |  |
43 |  |
     /**
44 |  |
      * A utility class to convert the configuration properties into any type.
45 |  |
      *
46 |  |
      * @author Emmanuel Bourg
47 |  |
      * @version $Revision: 442690 $, $Date: 2006-09-12 22:03:18 +0200 (Di, 12 Sep 2006) $
48 |  |
      * @since 1.1
49 |  |
      */
50 | 108 |
     public final class PropertyConverter
51 |  |
     {
52 |  |
         /** Constant for the list delimiter escaping character.*/
53 |  |
         static final String LIST_ESCAPE = "\\";
54 |  |
55 |  |
         /** Constant for the prefix of hex numbers.*/
56 |  |
         private static final String HEX_PREFIX = "0x";
57 |  |
58 |  |
         /** Constant for the radix of hex numbers.*/
59 |  |
         private static final int HEX_RADIX = 16;
60 |  |
61 |  |
         /** Constant for the argument classes of the Number constructor that takes
62 |  |
          * a String.
63 |  |
          */
64 | 53 |
         private static final Class[] CONSTR_ARGS = {String.class};
65 |  |
66 |  |
         /**
67 |  |
          * Private constructor prevents instances from being created.
68 |  |
          */
69 |  |
         private PropertyConverter()
70 | 0 |
         {
71 |  |
             // to prevent instanciation...
72 | 0 |
         }
73 |  |
74 |  |
         /**
75 |  |
          * Convert the specified object into a Boolean. Internally the
76 |  |
          * <code>org.apache.commons.lang.BooleanUtils</code> class from the
77 |  |
          * <a href="http://jakarta.apache.org/commons/lang/">Commons Lang</a>
78 |  |
          * project is used to perform this conversion. This class accepts some more
79 |  |
          * tokens for the boolean value of <b>true</b>, e.g. <code>yes</code> and
80 |  |
          * <code>on</code>. Please refer to the documentation of this class for more
81 |  |
          * details.
82 |  |
          *
83 |  |
          * @param value the value to convert
84 |  |
          * @return the converted value
85 |  |
          * @throws ConversionException thrown if the value cannot be converted to a boolean
86 |  |
          */
87 |  |
         public static Boolean toBoolean(Object value) throws ConversionException
88 |  |
         {
89 | 117 |
             if (value instanceof Boolean)
90 |  |
             {
91 | 42 |
                 return (Boolean) value;
92 |  |
             }
93 | 75 |
             else if (value instanceof String)
94 |  |
             {
95 | 73 |
                 Boolean b = BooleanUtils.toBooleanObject((String) value);
96 | 73 |
                 if (b == null)
97 |  |
                 {
98 | 5 |
                     throw new ConversionException("The value " + value + " can't be converted to a Boolean object");
99 |  |
                 }
100 | 68 |
                 return b;
101 |  |
             }
102 |  |
             else
103 |  |
             {
104 | 2 |
                 throw new ConversionException("The value " + value + " can't be converted to a Boolean object");
105 |  |
             }
106 |  |
         }
107 |  |
108 |  |
         /**
109 |  |
          * Convert the specified object into a Byte.
110 |  |
          *
111 |  |
          * @param value the value to convert
112 |  |
          * @return the converted value
113 |  |
          * @throws ConversionException thrown if the value cannot be converted to a byte
114 |  |
          */
115 |  |
         public static Byte toByte(Object value) throws ConversionException
116 |  |
         {
117 | 54 |
             Number n = toNumber(value, Byte.class);
118 | 48 |
             if (n instanceof Byte)
119 |  |
             {
120 | 46 |
                 return (Byte) n;
121 |  |
             }
122 |  |
             else
123 |  |
             {
124 | 2 |
                 return new Byte(n.byteValue());
125 |  |
             }
126 |  |
         }
127 |  |
128 |  |
         /**
129 |  |
          * Convert the specified object into a Short.
130 |  |
          *
131 |  |
          * @param value the value to convert
132 |  |
          * @return the converted value
133 |  |
          * @throws ConversionException thrown if the value cannot be converted to a short
134 |  |
          */
135 |  |
         public static Short toShort(Object value) throws ConversionException
136 |  |
         {
137 | 58 |
             Number n = toNumber(value, Short.class);
138 | 52 |
             if (n instanceof Short)
139 |  |
             {
140 | 51 |
                 return (Short) n;
141 |  |
             }
142 |  |
             else
143 |  |
             {
144 | 1 |
                 return new Short(n.shortValue());
145 |  |
             }
146 |  |
         }
147 |  |
148 |  |
         /**
149 |  |
          * Convert the specified object into an Integer.
150 |  |
          *
151 |  |
          * @param value the value to convert
152 |  |
          * @return the converted value
153 |  |
          * @throws ConversionException thrown if the value cannot be converted to an integer
154 |  |
          */
155 |  |
         public static Integer toInteger(Object value) throws ConversionException
156 |  |
         {
157 | 76 |
             Number n = toNumber(value, Integer.class);
158 | 72 |
             if (n instanceof Integer)
159 |  |
             {
160 | 71 |
                 return (Integer) n;
161 |  |
             }
162 |  |
             else
163 |  |
             {
164 | 1 |
                 return new Integer(n.intValue());
165 |  |
             }
166 |  |
         }
167 |  |
168 |  |
         /**
169 |  |
          * Convert the specified object into a Long.
170 |  |
          *
171 |  |
          * @param value the value to convert
172 |  |
          * @return the converted value
173 |  |
          * @throws ConversionException thrown if the value cannot be converted to a Long
174 |  |
          */
175 |  |
         public static Long toLong(Object value) throws ConversionException
176 |  |
         {
177 | 55 |
             Number n = toNumber(value, Long.class);
178 | 49 |
             if (n instanceof Long)
179 |  |
             {
180 | 46 |
                 return (Long) n;
181 |  |
             }
182 |  |
             else
183 |  |
             {
184 | 3 |
                 return new Long(n.longValue());
185 |  |
             }
186 |  |
         }
187 |  |
188 |  |
         /**
189 |  |
          * Convert the specified object into a Float.
190 |  |
          *
191 |  |
          * @param value the value to convert
192 |  |
          * @return the converted value
193 |  |
          * @throws ConversionException thrown if the value cannot be converted to a Float
194 |  |
          */
195 |  |
         public static Float toFloat(Object value) throws ConversionException
196 |  |
         {
197 | 53 |
             Number n = toNumber(value, Float.class);
198 | 47 |
             if (n instanceof Float)
199 |  |
             {
200 | 46 |
                 return (Float) n;
201 |  |
             }
202 |  |
             else
203 |  |
             {
204 | 1 |
                 return new Float(n.floatValue());
205 |  |
             }
206 |  |
         }
207 |  |
208 |  |
         /**
209 |  |
          * Convert the specified object into a Double.
210 |  |
          *
211 |  |
          * @param value the value to convert
212 |  |
          * @return the converted value
213 |  |
          * @throws ConversionException thrown if the value cannot be converted to a Double
214 |  |
          */
215 |  |
         public static Double toDouble(Object value) throws ConversionException
216 |  |
         {
217 | 54 |
             Number n = toNumber(value, Double.class);
218 | 48 |
             if (n instanceof Double)
219 |  |
             {
220 | 47 |
                 return (Double) n;
221 |  |
             }
222 |  |
             else
223 |  |
             {
224 | 1 |
                 return new Double(n.doubleValue());
225 |  |
             }
226 |  |
         }
227 |  |
228 |  |
         /**
229 |  |
          * Convert the specified object into a BigInteger.
230 |  |
          *
231 |  |
          * @param value the value to convert
232 |  |
          * @return the converted value
233 |  |
          * @throws ConversionException thrown if the value cannot be converted to a BigInteger
234 |  |
          */
235 |  |
         public static BigInteger toBigInteger(Object value) throws ConversionException
236 |  |
         {
237 | 41 |
             Number n = toNumber(value, BigInteger.class);
238 | 35 |
             if (n instanceof BigInteger)
239 |  |
             {
240 | 34 |
                 return (BigInteger) n;
241 |  |
             }
242 |  |
             else
243 |  |
             {
244 | 1 |
                 return BigInteger.valueOf(n.longValue());
245 |  |
             }
246 |  |
         }
247 |  |
248 |  |
         /**
249 |  |
          * Convert the specified object into a BigDecimal.
250 |  |
          *
251 |  |
          * @param value the value to convert
252 |  |
          * @return the converted value
253 |  |
          * @throws ConversionException thrown if the value cannot be converted to a BigDecimal
254 |  |
          */
255 |  |
         public static BigDecimal toBigDecimal(Object value) throws ConversionException
256 |  |
         {
257 | 40 |
             Number n = toNumber(value, BigDecimal.class);
258 | 34 |
             if (n instanceof BigDecimal)
259 |  |
             {
260 | 33 |
                 return (BigDecimal) n;
261 |  |
             }
262 |  |
             else
263 |  |
             {
264 | 1 |
                 return new BigDecimal(n.doubleValue());
265 |  |
             }
266 |  |
         }
267 |  |
268 |  |
         /**
269 |  |
          * Tries to convert the specified object into a number object. This method
270 |  |
          * is used by the conversion methods for number types. Note that the return
271 |  |
          * value is not in always of the specified target class, but only if a new
272 |  |
          * object has to be created.
273 |  |
          *
274 |  |
          * @param value the value to be converted (must not be <b>null</b>)
275 |  |
          * @param targetClass the target class of the conversion (must be derived
276 |  |
          * from <code>java.lang.Number</code>)
277 |  |
          * @return the converted number
278 |  |
          * @throws ConversionException if the object cannot be converted
279 |  |
          */
280 |  |
         static Number toNumber(Object value, Class targetClass)
281 |  |
                 throws ConversionException
282 |  |
         {
283 | 439 |
             if (value instanceof Number)
284 |  |
             {
285 | 157 |
                 return (Number) value;
286 |  |
             }
287 |  |
             else
288 |  |
             {
289 | 282 |
                 String str = value.toString();
290 | 282 |
                 if (str.startsWith(HEX_PREFIX))
291 |  |
                 {
292 |  |
                     try
293 |  |
                     {
294 | 7 |
                         return new BigInteger(str.substring(HEX_PREFIX.length()),
295 |  |
                                 HEX_RADIX);
296 |  |
                     }
297 |  |
                     catch (NumberFormatException nex)
298 |  |
                     {
299 | 1 |
                         throw new ConversionException("Could not convert " + str
300 |  |
                                 + " to " + targetClass.getName()
301 |  |
                                 + "! Invalid hex number.", nex);
302 |  |
                     }
303 |  |
                 }
304 |  |
305 |  |
                 try
306 |  |
                 {
307 | 275 |
                     Constructor constr = targetClass.getConstructor(CONSTR_ARGS);
308 | 274 |
                     return (Number) constr.newInstance(new Object[]{str});
309 |  |
                 }
310 |  |
                 catch (InvocationTargetException itex)
311 |  |
                 {
312 | 47 |
                     throw new ConversionException("Could not convert " + str
313 |  |
                             + " to " + targetClass.getName(), itex
314 |  |
                             .getTargetException());
315 |  |
                 }
316 |  |
                 catch (Exception ex)
317 |  |
                 {
318 |  |
                     // Treat all possible exceptions the same way
319 | 1 |
                     throw new ConversionException(
320 |  |
                             "Conversion error when trying to convert " + str
321 |  |
                                     + " to " + targetClass.getName(), ex);
322 |  |
                 }
323 |  |
             }
324 |  |
         }
325 |  |
326 |  |
         /**
327 |  |
          * Convert the specified object into an URL.
328 |  |
          *
329 |  |
          * @param value the value to convert
330 |  |
          * @return the converted value
331 |  |
          * @throws ConversionException thrown if the value cannot be converted to an URL
332 |  |
          */
333 |  |
         public static URL toURL(Object value) throws ConversionException
334 |  |
         {
335 | 35 |
             if (value instanceof URL)
336 |  |
             {
337 | 15 |
                 return (URL) value;
338 |  |
             }
339 | 20 |
             else if (value instanceof String)
340 |  |
             {
341 |  |
                 try
342 |  |
                 {
343 | 18 |
                     return new URL((String) value);
344 |  |
                 }
345 |  |
                 catch (MalformedURLException e)
346 |  |
                 {
347 | 2 |
                     throw new ConversionException("The value " + value + " can't be converted to an URL", e);
348 |  |
                 }
349 |  |
             }
350 |  |
             else
351 |  |
             {
352 | 2 |
                 throw new ConversionException("The value " + value + " can't be converted to an URL");
353 |  |
             }
354 |  |
         }
355 |  |
356 |  |
         /**
357 |  |
          * Convert the specified object into a Locale.
358 |  |
          *
359 |  |
          * @param value the value to convert
360 |  |
          * @return the converted value
361 |  |
          * @throws ConversionException thrown if the value cannot be converted to a Locale
362 |  |
          */
363 |  |
         public static Locale toLocale(Object value) throws ConversionException
364 |  |
         {
365 | 40 |
             if (value instanceof Locale)
366 |  |
             {
367 | 14 |
                 return (Locale) value;
368 |  |
             }
369 | 26 |
             else if (value instanceof String)
370 |  |
             {
371 | 24 |
                 List elements = split((String) value, '_');
372 | 24 |
                 int size = elements.size();
373 |  |
374 | 24 |
                 if (size >= 1 && (((String) elements.get(0)).length() == 2 || ((String) elements.get(0)).length() == 0))
375 |  |
                 {
376 | 22 |
                     String language = (String) elements.get(0);
377 | 22 |
                     String country = (String) ((size >= 2) ? elements.get(1) : "");
378 | 22 |
                     String variant = (String) ((size >= 3) ? elements.get(2) : "");
379 |  |
380 | 22 |
                     return new Locale(language, country, variant);
381 |  |
                 }
382 |  |
                 else
383 |  |
                 {
384 | 2 |
                     throw new ConversionException("The value " + value + " can't be converted to a Locale");
385 |  |
                 }
386 |  |
             }
387 |  |
             else
388 |  |
             {
389 | 2 |
                 throw new ConversionException("The value " + value + " can't be converted to a Locale");
390 |  |
             }
391 |  |
         }
392 |  |
393 |  |
         /**
394 |  |
          * Split a string on the specified delimiter. To be removed when
395 |  |
          * commons-lang has a better replacement available (Tokenizer?).
396 |  |
          *
397 |  |
          * todo: replace with a commons-lang equivalent
398 |  |
          *
399 |  |
          * @param s          the string to split
400 |  |
          * @param delimiter  the delimiter
401 |  |
          * @return a list with the single tokens
402 |  |
          */
403 |  |
         public static List split(String s, char delimiter)
404 |  |
         {
405 | 10079 |
             if (s == null)
406 |  |
             {
407 | 9 |
                 return new ArrayList();
408 |  |
             }
409 |  |
410 | 10070 |
             List list = new ArrayList();
411 |  |
412 | 10070 |
             StringBuffer token = new StringBuffer();
413 | 10070 |
             int begin = 0;
414 | 10070 |
             int end = 0;
415 | 33813 |
             while (begin <= s.length())
416 |  |
             {
417 |  |
                 // find the next delimiter
418 | 13673 |
                 int index = s.indexOf(delimiter, end);
419 |  |
420 |  |
                 // move the end index at the end of the string if the delimiter is not found
421 | 13673 |
                 end = (index != -1) ? index : s.length();
422 |  |
423 |  |
                 // extract the chunk
424 | 13673 |
                 String chunk = s.substring(begin , end);
425 |  |
426 | 13673 |
                 if (chunk.endsWith(LIST_ESCAPE) && end != s.length())
427 |  |
                 {
428 | 936 |
                     token.append(chunk.substring(0, chunk.length() - 1));
429 | 936 |
                     token.append(delimiter);
430 |  |
                 }
431 |  |
                 else
432 |  |
                 {
433 |  |
                     // append the chunk to the token
434 | 12737 |
                     token.append(chunk);
435 |  |
436 |  |
                     // add the token to the list
437 | 12737 |
                     list.add(token.toString().trim());
438 |  |
439 |  |
                     // reset the token
440 | 12737 |
                     token = new StringBuffer();
441 |  |
                 }
442 |  |
443 |  |
                 // move to the next chunk
444 | 13673 |
                 end = end + 1;
445 | 13673 |
                 begin = end;
446 |  |
             }
447 |  |
448 | 10070 |
             return list;
449 |  |
         }
450 |  |
451 |  |
         /**
452 |  |
          * Escapes the delimiters that might be contained in the given string. This
453 |  |
          * method ensures that list delimiter characters that are part of a
454 |  |
          * property's value are correctly escaped when a configuration is saved to a
455 |  |
          * file. Otherwise when loaded again the property will be treated as a list
456 |  |
          * property.
457 |  |
          *
458 |  |
          * @param s the string with the value
459 |  |
          * @param delimiter the list delimiter to use
460 |  |
          * @return the correctly esaped string
461 |  |
          */
462 |  |
         public static String escapeDelimiters(String s, char delimiter)
463 |  |
         {
464 | 105 |
             return StringUtils.replace(s, String.valueOf(delimiter), LIST_ESCAPE
465 |  |
                     + delimiter);
466 |  |
         }
467 |  |
468 |  |
         /**
469 |  |
          * Convert the specified object into a Color. If the value is a String,
470 |  |
          * the format allowed is (#)?[0-9A-F]{6}([0-9A-F]{2})?. Examples:
471 |  |
          * <ul>
472 |  |
          *   <li>FF0000 (red)</li>
473 |  |
          *   <li>0000FFA0 (semi transparent blue)</li>
474 |  |
          *   <li>#CCCCCC (gray)</li>
475 |  |
          *   <li>#00FF00A0 (semi transparent green)</li>
476 |  |
          * </ul>
477 |  |
          *
478 |  |
          * @param value the value to convert
479 |  |
          * @return the converted value
480 |  |
          * @throws ConversionException thrown if the value cannot be converted to a Color
481 |  |
          */
482 |  |
         public static Color toColor(Object value) throws ConversionException
483 |  |
         {
484 | 36 |
             if (value instanceof Color)
485 |  |
             {
486 | 14 |
                 return (Color) value;
487 |  |
             }
488 | 22 |
             else if (value instanceof String && !StringUtils.isBlank((String) value))
489 |  |
             {
490 | 20 |
                 String color = ((String) value).trim();
491 |  |
492 | 20 |
                 int[] components = new int[3];
493 |  |
494 |  |
                 // check the size of the string
495 | 20 |
                 int minlength = components.length * 2;
496 | 20 |
                 if (color.length() < minlength)
497 |  |
                 {
498 | 0 |
                     throw new ConversionException("The value " + value + " can't be converted to a Color");
499 |  |
                 }
500 |  |
501 |  |
                 // remove the leading #
502 | 20 |
                 if (color.startsWith("#"))
503 |  |
                 {
504 | 2 |
                     color = color.substring(1);
505 |  |
                 }
506 |  |
507 |  |
                 try
508 |  |
                 {
509 |  |
                     // parse the components
510 | 74 |
                     for (int i = 0; i < components.length; i++)
511 |  |
                     {
512 | 56 |
                         components[i] = Integer.parseInt(color.substring(2 * i, 2 * i + 2), HEX_RADIX);
513 |  |
                     }
514 |  |
515 |  |
                     // parse the transparency
516 |  |
                     int alpha;
517 | 18 |
                     if (color.length() >= minlength + 2)
518 |  |
                     {
519 | 1 |
                         alpha = Integer.parseInt(color.substring(minlength, minlength + 2), HEX_RADIX);
520 |  |
                     }
521 |  |
                     else
522 |  |
                     {
523 | 17 |
                         alpha = Color.black.getAlpha();
524 |  |
                     }
525 |  |
526 | 18 |
                     return new Color(components[0], components[1], components[2], alpha);
527 |  |
                 }
528 |  |
                 catch (Exception e)
529 |  |
                 {
530 | 2 |
                     throw new ConversionException("The value " + value + " can't be converted to a Color", e);
531 |  |
                 }
532 |  |
             }
533 |  |
             else
534 |  |
             {
535 | 2 |
                 throw new ConversionException("The value " + value + " can't be converted to a Color");
536 |  |
             }
537 |  |
         }
538 |  |
539 |  |
         /**
540 |  |
          * Convert the specified object into a Date.
541 |  |
          *
542 |  |
          * @param value  the value to convert
543 |  |
          * @param format the DateFormat pattern to parse String values
544 |  |
          * @return the converted value
545 |  |
          * @throws ConversionException thrown if the value cannot be converted to a Calendar
546 |  |
          */
547 |  |
         public static Date toDate(Object value, String format) throws ConversionException
548 |  |
         {
549 | 42 |
             if (value instanceof Date)
550 |  |
             {
551 | 15 |
                 return (Date) value;
552 |  |
             }
553 | 27 |
             else if (value instanceof Calendar)
554 |  |
             {
555 | 5 |
                 return ((Calendar) value).getTime();
556 |  |
             }
557 | 22 |
             else if (value instanceof String)
558 |  |
             {
559 |  |
                 try
560 |  |
                 {
561 | 20 |
                     return new SimpleDateFormat(format).parse((String) value);
562 |  |
                 }
563 |  |
                 catch (ParseException e)
564 |  |
                 {
565 | 2 |
                     throw new ConversionException("The value " + value + " can't be converted to a Date", e);
566 |  |
                 }
567 |  |
             }
568 |  |
             else
569 |  |
             {
570 | 2 |
                 throw new ConversionException("The value " + value + " can't be converted to a Date");
571 |  |
             }
572 |  |
         }
573 |  |
574 |  |
         /**
575 |  |
          * Convert the specified object into a Calendar.
576 |  |
          *
577 |  |
          * @param value  the value to convert
578 |  |
          * @param format the DateFormat pattern to parse String values
579 |  |
          * @return the converted value
580 |  |
          * @throws ConversionException thrown if the value cannot be converted to a Calendar
581 |  |
          */
582 |  |
         public static Calendar toCalendar(Object value, String format) throws ConversionException
583 |  |
         {
584 | 40 |
             if (value instanceof Calendar)
585 |  |
             {
586 | 10 |
                 return (Calendar) value;
587 |  |
             }
588 | 30 |
             else if (value instanceof Date)
589 |  |
             {
590 | 10 |
                 Calendar calendar = Calendar.getInstance();
591 | 10 |
                 calendar.setTime((Date) value);
592 | 10 |
                 return calendar;
593 |  |
             }
594 | 20 |
             else if (value instanceof String)
595 |  |
             {
596 |  |
                 try
597 |  |
                 {
598 | 18 |
                     Calendar calendar = Calendar.getInstance();
599 | 18 |
                     calendar.setTime(new SimpleDateFormat(format).parse((String) value));
600 | 16 |
                     return calendar;
601 |  |
                 }
602 |  |
                 catch (ParseException e)
603 |  |
                 {
604 | 2 |
                     throw new ConversionException("The value " + value + " can't be converted to a Calendar", e);
605 |  |
                 }
606 |  |
             }
607 |  |
             else
608 |  |
             {
609 | 2 |
                 throw new ConversionException("The value " + value + " can't be converted to a Calendar");
610 |  |
             }
611 |  |
         }
612 |  |
613 |  |
         /**
614 |  |
          * Return an iterator over the simple values of a composite value. The value
615 |  |
          * specified is handled depending on its type:
616 |  |
          * <ul>
617 |  |
          *   <li>Strings are checked for delimiter characters and splitted if necessary.</li>
618 |  |
          *   <li>For collections the single elements are checked.</li>
619 |  |
          *   <li>Arrays are treated like collections.</li>
620 |  |
          *   <li>All other types are directly inserted.</li>
621 |  |
          *   <li>Recursive combinations are supported, e.g. a collection containing array that contain strings.</li>
622 |  |
          * </ul>
623 |  |
          *
624 |  |
          * @param value     the value to "split"
625 |  |
          * @param delimiter the delimiter for String values
626 |  |
          * @return an iterator for accessing the single values
627 |  |
          */
628 |  |
         public static Iterator toIterator(Object value, char delimiter)
629 |  |
         {
630 | 42069 |
             if (value == null)
631 |  |
             {
632 | 3 |
                 return IteratorUtils.emptyIterator();
633 |  |
             }
634 | 42066 |
             if (value instanceof String)
635 |  |
             {
636 | 28736 |
                 String s = (String) value;
637 | 28736 |
                 if (s.indexOf(delimiter) > 0)
638 |  |
                 {
639 | 2057 |
                     return split((String) value, delimiter).iterator();
640 |  |
                 }
641 |  |
                 else
642 |  |
                 {
643 | 26679 |
                     return new SingletonIterator(value);
644 |  |
                 }
645 |  |
             }
646 | 13330 |
             else if (value instanceof Collection)
647 |  |
             {
648 | 698 |
                 return toIterator(((Collection) value).iterator(), delimiter);
649 |  |
             }
650 | 12632 |
             else if (value.getClass().isArray())
651 |  |
             {
652 | 810 |
                 return toIterator(IteratorUtils.arrayIterator(value), delimiter);
653 |  |
             }
654 | 11822 |
             else if (value instanceof Iterator)
655 |  |
             {
656 | 1508 |
                 Iterator iterator = (Iterator) value;
657 | 1508 |
                 IteratorChain chain = new IteratorChain();
658 | 6234 |
                 while (iterator.hasNext())
659 |  |
                 {
660 | 3218 |
                     chain.addIterator(toIterator(iterator.next(), delimiter));
661 |  |
                 }
662 | 1508 |
                 return chain;
663 |  |
             }
664 |  |
             else
665 |  |
             {
666 | 10314 |
                 return new SingletonIterator(value);
667 |  |
             }
668 |  |
         }
669 |  |
670 |  |
         /**
671 |  |
          * Performs interpolation of the specified value. This method checks if the
672 |  |
          * given value contains variables of the form <code>${...}</code>. If
673 |  |
          * this is the case, all occurrances will be substituted by their current
674 |  |
          * values.
675 |  |
          *
676 |  |
          * @param value the value to be interpolated
677 |  |
          * @param config the current configuration object
678 |  |
          * @return the interpolated value
679 |  |
          */
680 |  |
         public static Object interpolate(Object value, AbstractConfiguration config)
681 |  |
         {
682 | 2139 |
             if (value instanceof String)
683 |  |
             {
684 | 1424 |
                 return interpolateHelper((String) value, null, config);
685 |  |
             }
686 |  |
             else
687 |  |
             {
688 | 715 |
                 return value;
689 |  |
             }
690 |  |
         }
691 |  |
692 |  |
         /**
693 |  |
          * Recursive handler for multple levels of interpolation. This will be
694 |  |
          * replaced when Commons Lang provides an interpolation feature. When called
695 |  |
          * the first time, priorVariables should be null.
696 |  |
          *
697 |  |
          * @param base string with the ${key} variables
698 |  |
          * @param priorVariables serves two purposes: to allow checking for loops,
699 |  |
          * and creating a meaningful exception message should a loop occur. It's
700 |  |
          * 0'th element will be set to the value of base from the first call. All
701 |  |
          * subsequent interpolated variables are added afterward.
702 |  |
          * @param config the current configuration
703 |  |
          * @return the string with the interpolation taken care of
704 |  |
          */
705 |  |
         private static String interpolateHelper(String base, List priorVariables,
706 |  |
                 AbstractConfiguration config)
707 |  |
         {
708 | 1529 |
             if (base == null)
709 |  |
             {
710 | 0 |
                 return null;
711 |  |
             }
712 |  |
713 |  |
             // on the first call initialize priorVariables
714 |  |
             // and add base as the first element
715 | 1529 |
             if (priorVariables == null)
716 |  |
             {
717 | 1424 |
                 priorVariables = new ArrayList();
718 | 1424 |
                 priorVariables.add(base);
719 |  |
             }
720 |  |
721 | 1529 |
             int begin = -1;
722 | 1529 |
             int end = -1;
723 | 1529 |
             int prec = 0 - AbstractConfiguration.END_TOKEN.length();
724 | 1529 |
             StringBuffer result = new StringBuffer();
725 |  |
726 |  |
             // FIXME: we should probably allow the escaping of the start token
727 | 1529 |
             while (((begin = base.indexOf(AbstractConfiguration.START_TOKEN, prec
728 |  |
                     + AbstractConfiguration.END_TOKEN.length())) > -1)
729 | 1637 |
                     && ((end = base.indexOf(AbstractConfiguration.END_TOKEN, begin)) > -1))
730 |  |
             {
731 | 117 |
                 result.append(base.substring(prec
732 |  |
                         + AbstractConfiguration.END_TOKEN.length(), begin));
733 | 117 |
                 String variable = base.substring(begin
734 |  |
                         + AbstractConfiguration.START_TOKEN.length(), end);
735 |  |
736 |  |
                 // if we've got a loop, create a useful exception message and throw
737 | 117 |
                 if (priorVariables.contains(variable))
738 |  |
                 {
739 | 3 |
                     String initialBase = priorVariables.remove(0).toString();
740 | 3 |
                     priorVariables.add(variable);
741 | 3 |
                     StringBuffer priorVariableSb = new StringBuffer();
742 |  |
743 |  |
                     // create a nice trace of interpolated variables like so:
744 |  |
                     // var1->var2->var3
745 | 15 |
                     for (Iterator it = priorVariables.iterator(); it.hasNext();)
746 |  |
                     {
747 | 9 |
                         priorVariableSb.append(it.next());
748 | 9 |
                         if (it.hasNext())
749 |  |
                         {
750 | 6 |
                             priorVariableSb.append("->");
751 |  |
                         }
752 |  |
                     }
753 |  |
754 | 3 |
                     throw new IllegalStateException(
755 |  |
                             "infinite loop in property interpolation of "
756 |  |
                                     + initialBase + ": "
757 |  |
                                     + priorVariableSb.toString());
758 |  |
                 }
759 |  |
                 // otherwise, add this variable to the interpolation list.
760 |  |
                 else
761 |  |
                 {
762 | 114 |
                     priorVariables.add(variable);
763 |  |
                 }
764 |  |
765 | 114 |
                 Object value = config.resolveContainerStore(variable);
766 | 114 |
                 if (value != null)
767 |  |
                 {
768 | 105 |
                     result.append(interpolateHelper(value.toString(),
769 |  |
                             priorVariables, config));
770 |  |
771 |  |
                     // pop the interpolated variable off the stack
772 |  |
                     // this maintains priorVariables correctness for
773 |  |
                     // properties with multiple interpolations, e.g.
774 |  |
                     // prop.name=${some.other.prop1}/blahblah/${some.other.prop2}
775 | 99 |
                     priorVariables.remove(priorVariables.size() - 1);
776 |  |
                 }
777 |  |
                 else
778 |  |
                 {
779 |  |
                     // variable not defined - so put it back in the value
780 | 9 |
                     result.append(AbstractConfiguration.START_TOKEN);
781 | 9 |
                     result.append(variable);
782 | 9 |
                     result.append(AbstractConfiguration.END_TOKEN);
783 |  |
                 }
784 |  |
785 | 108 |
                 prec = end;
786 |  |
             }
787 | 1520 |
             result.append(base.substring(prec
788 |  |
                     + AbstractConfiguration.END_TOKEN.length(), base.length()));
789 | 1520 |
             return result.toString();
790 |  |
         }
791 |  |
     }