View Javadoc

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  
19  package org.apache.commons.beanutils;
20  
21  
22  import java.io.File;
23  import java.lang.reflect.Array;
24  import java.math.BigDecimal;
25  import java.math.BigInteger;
26  import java.net.URL;
27  import java.sql.Timestamp;
28  import java.util.Calendar;
29  import java.util.Collection;
30  import java.util.Map;
31  import java.util.HashMap;
32  import org.apache.commons.beanutils.converters.ArrayConverter;
33  import org.apache.commons.beanutils.converters.BigDecimalConverter;
34  import org.apache.commons.beanutils.converters.BigIntegerConverter;
35  import org.apache.commons.beanutils.converters.BooleanConverter;
36  import org.apache.commons.beanutils.converters.ByteConverter;
37  import org.apache.commons.beanutils.converters.CalendarConverter;
38  import org.apache.commons.beanutils.converters.CharacterConverter;
39  import org.apache.commons.beanutils.converters.ClassConverter;
40  import org.apache.commons.beanutils.converters.ConverterFacade;
41  import org.apache.commons.beanutils.converters.DateConverter;
42  import org.apache.commons.beanutils.converters.DoubleConverter;
43  import org.apache.commons.beanutils.converters.FileConverter;
44  import org.apache.commons.beanutils.converters.FloatConverter;
45  import org.apache.commons.beanutils.converters.IntegerConverter;
46  import org.apache.commons.beanutils.converters.LongConverter;
47  import org.apache.commons.beanutils.converters.ShortConverter;
48  import org.apache.commons.beanutils.converters.SqlDateConverter;
49  import org.apache.commons.beanutils.converters.SqlTimeConverter;
50  import org.apache.commons.beanutils.converters.SqlTimestampConverter;
51  import org.apache.commons.beanutils.converters.StringConverter;
52  import org.apache.commons.beanutils.converters.URLConverter;
53  import org.apache.commons.logging.Log;
54  import org.apache.commons.logging.LogFactory;
55  
56  
57  /***
58   * <p>Utility methods for converting String scalar values to objects of the
59   * specified Class, String arrays to arrays of the specified Class.  The
60   * actual {@link Converter} instance to be used can be registered for each
61   * possible destination Class.  Unless you override them, standard
62   * {@link Converter} instances are provided for all of the following
63   * destination Classes:</p>
64   * <ul>
65   * <li>java.lang.BigDecimal (no default value)</li>
66   * <li>java.lang.BigInteger (no default value)</li>
67   * <li>boolean and java.lang.Boolean (default to false)</li>
68   * <li>byte and java.lang.Byte (default to zero)</li>
69   * <li>char and java.lang.Character (default to a space)</li>
70   * <li>java.lang.Class (no default value)</li>
71   * <li>double and java.lang.Double (default to zero)</li>
72   * <li>float and java.lang.Float (default to zero)</li>
73   * <li>int and java.lang.Integer (default to zero)</li>
74   * <li>long and java.lang.Long (default to zero)</li>
75   * <li>short and java.lang.Short (default to zero)</li>
76   * <li>java.lang.String (default to null)</li>
77   * <li>java.io.File (no default value)</li>
78   * <li>java.net.URL (no default value)</li>
79   * <li>java.sql.Date (no default value)</li>
80   * <li>java.sql.Time (no default value)</li>
81   * <li>java.sql.Timestamp (no default value)</li>
82   * </ul>
83   *
84   * <p>For backwards compatibility, the standard Converters for primitive
85   * types (and the corresponding wrapper classes) return a defined
86   * default value when a conversion error occurs.  If you prefer to have a
87   * {@link ConversionException} thrown instead, replace the standard Converter
88   * instances with instances created with the zero-arguments constructor.  For
89   * example, to cause the Converters for integers to throw an exception on
90   * conversion errors, you could do this:</p>
91   * <pre>
92   *   // No-args constructor gets the version that throws exceptions
93   *   Converter myConverter =
94   *    new org.apache.commons.beanutils.converter.IntegerConverter();
95   *   ConvertUtils.register(myConverter, Integer.TYPE);    // Native type
96   *   ConvertUtils.register(myConverter, Integer.class);   // Wrapper class
97   * </pre>
98   * 
99   * <p>
100  * Converters generally treat null input as if it were invalid
101  * input, ie they return their default value if one was specified when the
102  * converter was constructed, and throw an exception otherwise. If you prefer
103  * nulls to be preserved for converters that are converting to objects (not
104  * primitives) then register a converter as above, passing a default value of
105  * null to the converter constructor (and of course registering that converter
106  * only for the .class target).
107  * </p>
108  *
109  * <p>
110  * When a converter is listed above as having no default value, then that
111  * converter will throw an exception when passed null or an invalid value
112  * as its input. In particular, by default the BigInteger and BigDecimal
113  * converters have no default (and are therefore somewhat inconsistent
114  * with the other numerical converters which all have zero as their default).
115  * </p>
116  * 
117  * <p>
118  * Converters that generate <i>arrays</i> of each of the primitive types are
119  * also automatically configured (including String[]). When passed null
120  * or invalid input, these return an empty array (not null). See class
121  * AbstractArrayConverter for the supported input formats for these converters.
122  * </p>
123  * 
124  * @author Craig R. McClanahan
125  * @author Ralph Schaer
126  * @author Chris Audley
127  * @author James Strachan
128  * @version $Revision: 557868 $ $Date: 2007-07-20 06:22:19 +0100 (Fri, 20 Jul 2007) $
129  * @since 1.7
130  */
131 
132 public class ConvertUtilsBean {
133     
134     private static final Integer ZERO = new Integer(0);
135     private static final Character SPACE = new Character(' ');
136 
137     // ------------------------------------------------------- Class Methods
138     /***
139      * Get singleton instance
140      * @return The singleton instance
141      */
142     protected static ConvertUtilsBean getInstance() {
143         return BeanUtilsBean.getInstance().getConvertUtils();
144     }
145 
146     // ------------------------------------------------------- Variables
147 
148 
149     /***
150      * The set of {@link Converter}s that can be used to convert Strings
151      * into objects of a specified Class, keyed by the destination Class.
152      */
153     private Map converters = new HashMap();
154 
155     /***
156      * The <code>Log</code> instance for this class.
157      */
158     private Log log = LogFactory.getLog(ConvertUtils.class);
159 
160     // ------------------------------------------------------- Constructors
161 
162     /*** Construct a bean with standard converters registered */
163     public ConvertUtilsBean() {
164         deregister();
165     }
166 
167     // --------------------------------------------------------- Public Methods
168     
169     /***
170      * The default value for Boolean conversions.
171      * @deprecated Register replacement converters for Boolean.TYPE and
172      *  Boolean.class instead
173      */
174     private Boolean defaultBoolean = Boolean.FALSE;
175 
176     /***
177      * Gets the default value for Boolean conversions.
178      * @return The default Boolean value
179      * @deprecated Register replacement converters for Boolean.TYPE and
180      *  Boolean.class instead
181      */
182     public boolean getDefaultBoolean() {
183         return (defaultBoolean.booleanValue());
184     }
185 
186     /***
187      * Sets the default value for Boolean conversions.
188      * @param newDefaultBoolean The default Boolean value
189      * @deprecated Register replacement converters for Boolean.TYPE and
190      *  Boolean.class instead
191      */
192     public void setDefaultBoolean(boolean newDefaultBoolean) {
193         defaultBoolean = (newDefaultBoolean ? Boolean.TRUE : Boolean.FALSE);
194         register(new BooleanConverter(defaultBoolean), Boolean.TYPE);
195         register(new BooleanConverter(defaultBoolean), Boolean.class);
196     }
197 
198 
199     /***
200      * The default value for Byte conversions.
201      * @deprecated Register replacement converters for Byte.TYPE and
202      *  Byte.class instead
203      */
204     private Byte defaultByte = new Byte((byte) 0);
205 
206     /***
207      * Gets the default value for Byte conversions.
208      * @return The default Byte value
209      * @deprecated Register replacement converters for Byte.TYPE and
210      *  Byte.class instead
211      */
212     public byte getDefaultByte() {
213         return (defaultByte.byteValue());
214     }
215 
216     /***
217      * Sets the default value for Byte conversions.
218      * @param newDefaultByte The default Byte value
219      * @deprecated Register replacement converters for Byte.TYPE and
220      *  Byte.class instead
221      */
222     public void setDefaultByte(byte newDefaultByte) {
223         defaultByte = new Byte(newDefaultByte);
224         register(new ByteConverter(defaultByte), Byte.TYPE);
225         register(new ByteConverter(defaultByte), Byte.class);
226     }
227 
228 
229     /***
230      * The default value for Character conversions.
231      * @deprecated Register replacement converters for Character.TYPE and
232      *  Character.class instead
233      */
234     private Character defaultCharacter = new Character(' ');
235 
236     /***
237      * Gets the default value for Character conversions.
238      * @return The default Character value
239      * @deprecated Register replacement converters for Character.TYPE and
240      *  Character.class instead
241      */
242     public char getDefaultCharacter() {
243         return (defaultCharacter.charValue());
244     }
245 
246     /***
247      * Sets the default value for Character conversions.
248      * @param newDefaultCharacter The default Character value
249      * @deprecated Register replacement converters for Character.TYPE and
250      *  Character.class instead
251      */
252     public void setDefaultCharacter(char newDefaultCharacter) {
253         defaultCharacter = new Character(newDefaultCharacter);
254         register(new CharacterConverter(defaultCharacter),
255                     Character.TYPE);
256         register(new CharacterConverter(defaultCharacter),
257                     Character.class);
258     }
259 
260 
261     /***
262      * The default value for Double conversions.
263      * @deprecated Register replacement converters for Double.TYPE and
264      *  Double.class instead
265      */
266     private Double defaultDouble = new Double(0.0);
267 
268     /***
269      * Gets the default value for Double conversions.
270      * @return The default Double value
271      * @deprecated Register replacement converters for Double.TYPE and
272      *  Double.class instead
273      */
274     public double getDefaultDouble() {
275         return (defaultDouble.doubleValue());
276     }
277 
278     /***
279      * Sets the default value for Double conversions.
280      * @param newDefaultDouble The default Double value
281      * @deprecated Register replacement converters for Double.TYPE and
282      *  Double.class instead
283      */
284     public void setDefaultDouble(double newDefaultDouble) {
285         defaultDouble = new Double(newDefaultDouble);
286         register(new DoubleConverter(defaultDouble), Double.TYPE);
287         register(new DoubleConverter(defaultDouble), Double.class);
288     }
289 
290 
291     /***
292      * The default value for Float conversions.
293      * @deprecated Register replacement converters for Float.TYPE and
294      *  Float.class instead
295      */
296     private Float defaultFloat = new Float((float) 0.0);
297 
298     /***
299      * Gets the default value for Float conversions.
300      * @return The default Float value
301      * @deprecated Register replacement converters for Float.TYPE and
302      *  Float.class instead
303      */
304     public float getDefaultFloat() {
305         return (defaultFloat.floatValue());
306     }
307 
308     /***
309      * Sets the default value for Float conversions.
310      * @param newDefaultFloat The default Float value
311      * @deprecated Register replacement converters for Float.TYPE and
312      *  Float.class instead
313      */
314     public void setDefaultFloat(float newDefaultFloat) {
315         defaultFloat = new Float(newDefaultFloat);
316         register(new FloatConverter(defaultFloat), Float.TYPE);
317         register(new FloatConverter(defaultFloat), Float.class);
318     }
319 
320 
321     /***
322      * The default value for Integer conversions.
323      * @deprecated Register replacement converters for Integer.TYPE and
324      *  Integer.class instead
325      */
326     private Integer defaultInteger = new Integer(0);
327 
328     /***
329      * Gets the default value for Integer conversions.
330      * @return The default Integer value
331      * @deprecated Register replacement converters for Integer.TYPE and
332      *  Integer.class instead
333      */
334     public int getDefaultInteger() {
335         return (defaultInteger.intValue());
336     }
337     
338     /***
339      * Sets the default value for Integer conversions.
340      * @param newDefaultInteger The default Integer value
341      * @deprecated Register replacement converters for Integer.TYPE and
342      *  Integer.class instead
343      */
344     public void setDefaultInteger(int newDefaultInteger) {
345         defaultInteger = new Integer(newDefaultInteger);
346         register(new IntegerConverter(defaultInteger), Integer.TYPE);
347         register(new IntegerConverter(defaultInteger), Integer.class);
348     }
349 
350 
351     /***
352      * The default value for Long conversions.
353      * @deprecated Register replacement converters for Long.TYPE and
354      *  Long.class instead
355      */
356     private Long defaultLong = new Long(0);
357 
358     /***
359      * Gets the default value for Long conversions.
360      * @return The default Long value
361      * @deprecated Register replacement converters for Long.TYPE and
362      *  Long.class instead
363      */
364     public long getDefaultLong() {
365         return (defaultLong.longValue());
366     }
367 
368     /***
369      * Sets the default value for Long conversions.
370      * @param newDefaultLong The default Long value
371      * @deprecated Register replacement converters for Long.TYPE and
372      *  Long.class instead
373      */
374     public void setDefaultLong(long newDefaultLong) {
375         defaultLong = new Long(newDefaultLong);
376         register(new LongConverter(defaultLong), Long.TYPE);
377         register(new LongConverter(defaultLong), Long.class);
378     }
379 
380 
381     /***
382      * The default value for Short conversions.
383      * @deprecated Register replacement converters for Short.TYPE and
384      *  Short.class instead
385      */
386     private static Short defaultShort = new Short((short) 0);
387 
388     /***
389      * Gets the default value for Short conversions.
390      * @return The default Short value
391      * @deprecated Register replacement converters for Short.TYPE and
392      *  Short.class instead
393      */
394     public short getDefaultShort() {
395         return (defaultShort.shortValue());
396     }
397 
398     /***
399      * Sets the default value for Short conversions.
400      * @param newDefaultShort The default Short value
401      * @deprecated Register replacement converters for Short.TYPE and
402      *  Short.class instead
403      */
404     public void setDefaultShort(short newDefaultShort) {
405         defaultShort = new Short(newDefaultShort);
406         register(new ShortConverter(defaultShort), Short.TYPE);
407         register(new ShortConverter(defaultShort), Short.class);
408     }
409 
410 
411 
412     /***
413      * Convert the specified value into a String.  If the specified value
414      * is an array, the first element (converted to a String) will be
415      * returned.  The registered {@link Converter} for the
416      * <code>java.lang.String</code> class will be used, which allows
417      * applications to customize Object->String conversions (the default
418      * implementation simply uses toString()).
419      *
420      * @param value Value to be converted (may be null)
421      * @return The converted String value
422      */
423     public String convert(Object value) {
424 
425         if (value == null) {
426             return ((String) null);
427         } else if (value.getClass().isArray()) {
428             if (Array.getLength(value) < 1) {
429                 return (null);
430             }
431             value = Array.get(value, 0);
432             if (value == null) {
433                 return ((String) null);
434             } else {
435                 Converter converter = lookup(String.class);
436                 return ((String) converter.convert(String.class, value));
437             }
438         } else {
439             Converter converter = lookup(String.class);
440             return ((String) converter.convert(String.class, value));
441         }
442 
443     }
444 
445 
446     /***
447      * Convert the specified value to an object of the specified class (if
448      * possible).  Otherwise, return a String representation of the value.
449      *
450      * @param value Value to be converted (may be null)
451      * @param clazz Java class to be converted to
452      * @return The converted value
453      *
454      * @exception ConversionException if thrown by an underlying Converter
455      */
456     public Object convert(String value, Class clazz) {
457 
458         if (log.isDebugEnabled()) {
459             log.debug("Convert string '" + value + "' to class '" +
460                       clazz.getName() + "'");
461         }
462         Converter converter = lookup(clazz);
463         if (converter == null) {
464             converter = lookup(String.class);
465         }
466         if (log.isTraceEnabled()) {
467             log.trace("  Using converter " + converter);
468         }
469         return (converter.convert(clazz, value));
470 
471     }
472 
473 
474     /***
475      * Convert an array of specified values to an array of objects of the
476      * specified class (if possible).  If the specified Java class is itself
477      * an array class, this class will be the type of the returned value.
478      * Otherwise, an array will be constructed whose component type is the
479      * specified class.
480      *
481      * @param values Array of values to be converted
482      * @param clazz Java array or element class to be converted to
483      * @return The converted value
484      *
485      * @exception ConversionException if thrown by an underlying Converter
486      */
487     public Object convert(String[] values, Class clazz) {
488 
489         Class type = clazz;
490         if (clazz.isArray()) {
491             type = clazz.getComponentType();
492         }
493         if (log.isDebugEnabled()) {
494             log.debug("Convert String[" + values.length + "] to class '" +
495                       type.getName() + "[]'");
496         }
497         Converter converter = lookup(type);
498         if (converter == null) {
499             converter = lookup(String.class);
500         }
501         if (log.isTraceEnabled()) {
502             log.trace("  Using converter " + converter);
503         }
504         Object array = Array.newInstance(type, values.length);
505         for (int i = 0; i < values.length; i++) {
506             Array.set(array, i, converter.convert(type, values[i]));
507         }
508         return (array);
509 
510     }
511 
512 
513     /***
514      * <p>Convert the value to an object of the specified class (if
515      * possible).</p>
516      *
517      * @param value Value to be converted (may be null)
518      * @param targetType Class of the value to be converted to
519      * @return The converted value
520      *
521      * @exception ConversionException if thrown by an underlying Converter
522      */
523     public Object convert(Object value, Class targetType) {
524 
525         Class sourceType = value == null ? null : value.getClass();
526 
527         if (log.isDebugEnabled()) {
528             if (value == null) {
529                 log.debug("Convert null value to type '" +
530                         targetType.getName() + "'");
531             } else {
532                 log.debug("Convert type '" + sourceType.getName() + "' value '" + value +
533                       "' to type '" + targetType.getName() + "'");
534             }
535         }
536 
537         Object converted = value;
538         Converter converter = lookup(sourceType, targetType);
539         if (converter != null) {
540             if (log.isTraceEnabled()) {
541                 log.trace("  Using converter " + converter);
542             }
543             converted = converter.convert(targetType, value);
544         }
545         if (targetType == String.class && converted != null && 
546                 !(converted instanceof String)) {
547 
548             // NOTE: For backwards compatibility, if the Converter
549             //       doesn't handle  conversion-->String then
550             //       use the registered String Converter
551             converter = lookup(String.class);
552             if (converter != null) {
553                 if (log.isTraceEnabled()) {
554                     log.trace("  Using converter " + converter);
555                 }
556                 converted = converter.convert(String.class, converted);
557             }
558 
559             // If the object still isn't a String, use toString() method
560             if (converted != null && !(converted instanceof String)) {
561                 converted = converted.toString();
562             }
563 
564         }
565         return converted;
566 
567     }
568 
569     /***
570      * Remove all registered {@link Converter}s, and re-establish the
571      * standard Converters.
572      */
573     public void deregister() {
574 
575         converters.clear();
576         
577         registerPrimitives(false);
578         registerStandard(false, false);
579         registerOther(true);
580         registerArrays(false, 0);
581         register(BigDecimal.class, new BigDecimalConverter());
582         register(BigInteger.class, new BigIntegerConverter());
583     }
584 
585     /***
586      * Register the provided converters with the specified defaults.
587      *
588      * @param throwException <code>true</code> if the converters should
589      * throw an exception when a conversion error occurs, otherwise <code>
590      * <code>false</code> if a default value should be used.
591      * @param defaultNull <code>true</code>if the <i>standard</i> converters
592      * (see {@link ConvertUtilsBean#registerStandard(boolean, boolean)})
593      * should use a default value of <code>null</code>, otherwise <code>false</code>.
594      * N.B. This values is ignored if <code>throwException</code> is <code>true</code>
595      * @param defaultArraySize The size of the default array value for array converters
596      * (N.B. This values is ignored if <code>throwException</code> is <code>true</code>).
597      * Specifying a value less than zero causes a <code>null<code> value to be used for
598      * the default.
599      */
600     public void register(boolean throwException, boolean defaultNull, int defaultArraySize) {
601         registerPrimitives(throwException);
602         registerStandard(throwException, defaultNull);
603         registerOther(throwException);
604         registerArrays(throwException, defaultArraySize);
605     }
606 
607     /***
608      * Register the converters for primitive types.
609      * </p>
610      * This method registers the following converters:
611      * <ul>
612      *     <li><code>Boolean.TYPE</code> - {@link BooleanConverter}</li>
613      *     <li><code>Byte.TYPE</code> - {@link ByteConverter}</li>
614      *     <li><code>Character.TYPE</code> - {@link CharacterConverter}</li>
615      *     <li><code>Double.TYPE</code> - {@link DoubleConverter}</li>
616      *     <li><code>Float.TYPE</code> - {@link FloatConverter}</li>
617      *     <li><code>Integer.TYPE</code> - {@link IntegerConverter}</li>
618      *     <li><code>Long.TYPE</code> - {@link LongConverter}</li>
619      *     <li><code>Short.TYPE</code> - {@link ShortConverter}</li>
620      * </ul>
621      * @param throwException <code>true</code> if the converters should
622      * throw an exception when a conversion error occurs, otherwise <code>
623      * <code>false</code> if a default value should be used.
624      */
625     private void registerPrimitives(boolean throwException) {
626         register(Boolean.TYPE,   throwException ? new BooleanConverter()    : new BooleanConverter(Boolean.FALSE));
627         register(Byte.TYPE,      throwException ? new ByteConverter()       : new ByteConverter(ZERO));
628         register(Character.TYPE, throwException ? new CharacterConverter()  : new CharacterConverter(SPACE));
629         register(Double.TYPE,    throwException ? new DoubleConverter()     : new DoubleConverter(ZERO));
630         register(Float.TYPE,     throwException ? new FloatConverter()      : new FloatConverter(ZERO));
631         register(Integer.TYPE,   throwException ? new IntegerConverter()    : new IntegerConverter(ZERO));
632         register(Long.TYPE,      throwException ? new LongConverter()       : new LongConverter(ZERO));
633         register(Short.TYPE,     throwException ? new ShortConverter()      : new ShortConverter(ZERO));
634     }
635 
636     /***
637      * Register the converters for standard types.
638      * </p>
639      * This method registers the following converters:
640      * <ul>
641      *     <li><code>BigDecimal.class</code> - {@link BigDecimalConverter}</li>
642      *     <li><code>BigInteger.class</code> - {@link BigIntegerConverter}</li>
643      *     <li><code>Boolean.class</code> - {@link BooleanConverter}</li>
644      *     <li><code>Byte.class</code> - {@link ByteConverter}</li>
645      *     <li><code>Character.class</code> - {@link CharacterConverter}</li>
646      *     <li><code>Double.class</code> - {@link DoubleConverter}</li>
647      *     <li><code>Float.class</code> - {@link FloatConverter}</li>
648      *     <li><code>Integer.class</code> - {@link IntegerConverter}</li>
649      *     <li><code>Long.class</code> - {@link LongConverter}</li>
650      *     <li><code>Short.class</code> - {@link ShortConverter}</li>
651      *     <li><code>String.class</code> - {@link StringConverter}</li>
652      * </ul>
653      * @param throwException <code>true</code> if the converters should
654      * throw an exception when a conversion error occurs, otherwise <code>
655      * <code>false</code> if a default value should be used.
656      * @param defaultNull <code>true</code>if the <i>standard</i> converters
657      * (see {@link ConvertUtilsBean#registerStandard(boolean, boolean)})
658      * should use a default value of <code>null</code>, otherwise <code>false</code>.
659      * N.B. This values is ignored if <code>throwException</code> is <code>true</code>
660      */
661     private void registerStandard(boolean throwException, boolean defaultNull) {
662 
663         Number     defaultNumber     = defaultNull ? null : ZERO;
664         BigDecimal bigDecDeflt       = defaultNull ? null : new BigDecimal("0.0");
665         BigInteger bigIntDeflt       = defaultNull ? null : new BigInteger("0");
666         Boolean    booleanDefault    = defaultNull ? null : Boolean.FALSE;
667         Character  charDefault       = defaultNull ? null : SPACE;
668         String     stringDefault     = defaultNull ? null : "";
669 
670         register(BigDecimal.class, throwException ? new BigDecimalConverter() : new BigDecimalConverter(bigDecDeflt));
671         register(BigInteger.class, throwException ? new BigIntegerConverter() : new BigIntegerConverter(bigIntDeflt));
672         register(Boolean.class,    throwException ? new BooleanConverter()    : new BooleanConverter(booleanDefault));
673         register(Byte.class,       throwException ? new ByteConverter()       : new ByteConverter(defaultNumber));
674         register(Character.class,  throwException ? new CharacterConverter()  : new CharacterConverter(charDefault));
675         register(Double.class,     throwException ? new DoubleConverter()     : new DoubleConverter(defaultNumber));
676         register(Float.class,      throwException ? new FloatConverter()      : new FloatConverter(defaultNumber));
677         register(Integer.class,    throwException ? new IntegerConverter()    : new IntegerConverter(defaultNumber));
678         register(Long.class,       throwException ? new LongConverter()       : new LongConverter(defaultNumber));
679         register(Short.class,      throwException ? new ShortConverter()      : new ShortConverter(defaultNumber));
680         register(String.class,     throwException ? new StringConverter()     : new StringConverter(stringDefault));
681         
682     }
683 
684     /***
685      * Register the converters for other types.
686      * </p>
687      * This method registers the following converters:
688      * <ul>
689      *     <li><code>Class.class</code> - {@link ClassConverter}</li>
690      *     <li><code>java.util.Date.class</code> - {@link DateConverter}</li>
691      *     <li><code>java.util.Calendar.class</code> - {@link CalendarConverter}</li>
692      *     <li><code>File.class</code> - {@link FileConverter}</li>
693      *     <li><code>java.sql.Date.class</code> - {@link SqlDateConverter}</li>
694      *     <li><code>java.sql.Time.class</code> - {@link SqlTimeConverter}</li>
695      *     <li><code>java.sql.Timestamp.class</code> - {@link SqlTimestampConverter}</li>
696      *     <li><code>URL.class</code> - {@link URLConverter}</li>
697      * </ul>
698      * @param throwException <code>true</code> if the converters should
699      * throw an exception when a conversion error occurs, otherwise <code>
700      * <code>false</code> if a default value should be used.
701      */
702     private void registerOther(boolean throwException) {
703         register(Class.class,         throwException ? new ClassConverter()        : new ClassConverter(null));
704         register(java.util.Date.class, throwException ? new DateConverter()        : new DateConverter(null));
705         register(Calendar.class,      throwException ? new CalendarConverter()     : new CalendarConverter(null));
706         register(File.class,          throwException ? new FileConverter()         : new FileConverter(null));
707         register(java.sql.Date.class, throwException ? new SqlDateConverter()      : new SqlDateConverter(null));
708         register(java.sql.Time.class, throwException ? new SqlTimeConverter()      : new SqlTimeConverter(null));
709         register(Timestamp.class,     throwException ? new SqlTimestampConverter() : new SqlTimestampConverter(null));
710         register(URL.class,           throwException ? new URLConverter()          : new URLConverter(null));
711     }
712 
713     /***
714      * Register array converters.
715      *
716      * @param throwException <code>true</code> if the converters should
717      * throw an exception when a conversion error occurs, otherwise <code>
718      * <code>false</code> if a default value should be used.
719      * @param defaultArraySize The size of the default array value for array converters
720      * (N.B. This values is ignored if <code>throwException</code> is <code>true</code>).
721      * Specifying a value less than zero causes a <code>null<code> value to be used for
722      * the default.
723      */
724     private void registerArrays(boolean throwException, int defaultArraySize) {
725 
726         // Primitives
727         registerArrayConverter(Boolean.TYPE,   new BooleanConverter(),   throwException, defaultArraySize);
728         registerArrayConverter(Byte.TYPE,      new ByteConverter(),      throwException, defaultArraySize);
729         registerArrayConverter(Character.TYPE, new CharacterConverter(), throwException, defaultArraySize);
730         registerArrayConverter(Double.TYPE,    new DoubleConverter(),    throwException, defaultArraySize);
731         registerArrayConverter(Float.TYPE,     new FloatConverter(),     throwException, defaultArraySize);
732         registerArrayConverter(Integer.TYPE,   new IntegerConverter(),   throwException, defaultArraySize);
733         registerArrayConverter(Long.TYPE,      new LongConverter(),      throwException, defaultArraySize);
734         registerArrayConverter(Short.TYPE,     new ShortConverter(),     throwException, defaultArraySize);
735 
736         // Standard
737         registerArrayConverter(BigDecimal.class, new BigDecimalConverter(), throwException, defaultArraySize);
738         registerArrayConverter(BigInteger.class, new BigIntegerConverter(), throwException, defaultArraySize);
739         registerArrayConverter(Boolean.class,    new BooleanConverter(),    throwException, defaultArraySize);
740         registerArrayConverter(Byte.class,       new ByteConverter(),       throwException, defaultArraySize);
741         registerArrayConverter(Character.class,  new CharacterConverter(),  throwException, defaultArraySize);
742         registerArrayConverter(Double.class,     new DoubleConverter(),     throwException, defaultArraySize);
743         registerArrayConverter(Float.class,      new FloatConverter(),      throwException, defaultArraySize);
744         registerArrayConverter(Integer.class,    new IntegerConverter(),    throwException, defaultArraySize);
745         registerArrayConverter(Long.class,       new LongConverter(),       throwException, defaultArraySize);
746         registerArrayConverter(Short.class,      new ShortConverter(),      throwException, defaultArraySize);
747         registerArrayConverter(String.class,     new StringConverter(),     throwException, defaultArraySize);
748 
749         // Other
750         registerArrayConverter(Class.class,          new ClassConverter(),        throwException, defaultArraySize);
751         registerArrayConverter(java.util.Date.class, new DateConverter(),         throwException, defaultArraySize);
752         registerArrayConverter(Calendar.class,       new DateConverter(),         throwException, defaultArraySize);
753         registerArrayConverter(File.class,           new FileConverter(),         throwException, defaultArraySize);
754         registerArrayConverter(java.sql.Date.class,  new SqlDateConverter(),      throwException, defaultArraySize);
755         registerArrayConverter(java.sql.Time.class,  new SqlTimeConverter(),      throwException, defaultArraySize);
756         registerArrayConverter(Timestamp.class,      new SqlTimestampConverter(), throwException, defaultArraySize);
757         registerArrayConverter(URL.class,            new URLConverter(),          throwException, defaultArraySize);
758 
759     }
760 
761     /***
762      * Register a new ArrayConverter with the specified element delegate converter
763      * that returns a default array of the specified size in the event of conversion errors.
764      *
765      * @param componentType The component type of the array
766      * @param componentConverter The converter to delegate to for the array elements
767      * @param throwException Whether a conversion exception should be thrown or a default
768      * value used in the event of a conversion error
769      * @param defaultArraySize The size of the default array
770      */
771     private void registerArrayConverter(Class componentType, Converter componentConverter,
772             boolean throwException, int defaultArraySize) {
773         Class arrayType = Array.newInstance(componentType, 0).getClass();
774         Converter arrayConverter = null;
775         if (throwException) {
776             arrayConverter = new ArrayConverter(arrayType, componentConverter);
777         } else {
778             arrayConverter = new ArrayConverter(arrayType, componentConverter, defaultArraySize);
779         }
780         register(arrayType, arrayConverter);
781     }
782 
783     /*** strictly for convenience since it has same parameter order as Map.put */
784     private void register(Class clazz, Converter converter) {
785         register(new ConverterFacade(converter), clazz);
786     }
787 
788     /***
789      * Remove any registered {@link Converter} for the specified destination
790      * <code>Class</code>.
791      *
792      * @param clazz Class for which to remove a registered Converter
793      */
794     public void deregister(Class clazz) {
795 
796         converters.remove(clazz);
797 
798     }
799 
800 
801     /***
802      * Look up and return any registered {@link Converter} for the specified
803      * destination class; if there is no registered Converter, return
804      * <code>null</code>.
805      *
806      * @param clazz Class for which to return a registered Converter
807      * @return The registered {@link Converter} or <code>null</code> if not found
808      */
809     public Converter lookup(Class clazz) {
810 
811         return ((Converter) converters.get(clazz));
812 
813     }
814 
815     /***
816      * Look up and return any registered {@link Converter} for the specified
817      * source and destination class; if there is no registered Converter,
818      * return <code>null</code>.
819      *
820      * @param sourceType Class of the value being converted
821      * @param targetType Class of the value to be converted to
822      * @return The registered {@link Converter} or <code>null</code> if not found
823      */
824     public Converter lookup(Class sourceType, Class targetType) {
825 
826         if (targetType == null) {
827             throw new IllegalArgumentException("Target type is missing");
828         }
829         if (sourceType == null) {
830             return lookup(targetType);
831         }
832 
833         Converter converter = null;
834         // Convert --> String 
835         if (targetType == String.class) {
836             converter = lookup(sourceType);
837             if (converter == null && (sourceType.isArray() ||
838                         Collection.class.isAssignableFrom(sourceType))) {
839                 converter = lookup(String[].class);
840             }
841             if (converter == null) {
842                 converter = lookup(String.class);
843             }
844             return converter;
845         }
846 
847         // Convert --> String array 
848         if (targetType == String[].class) {
849             if (sourceType.isArray() || Collection.class.isAssignableFrom(sourceType)) {
850                 converter = lookup(sourceType);
851             }
852             if (converter == null) {
853                 converter = lookup(String[].class);
854             }
855             return converter;
856         }
857 
858         return lookup(targetType);
859 
860     }
861 
862     /***
863      * Register a custom {@link Converter} for the specified destination
864      * <code>Class</code>, replacing any previously registered Converter.
865      *
866      * @param converter Converter to be registered
867      * @param clazz Destination class for conversions performed by this
868      *  Converter
869      */
870     public void register(Converter converter, Class clazz) {
871 
872         converters.put(clazz, converter);
873 
874     }
875 }