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 }