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