    001    /*
    002     * Licensed to the Apache Software Foundation (ASF) under one or more
    003     * contributor license agreements.  See the NOTICE file distributed with
    004     * this work for additional information regarding copyright ownership.
    005     * The ASF licenses this file to You under the Apache License, Version 2.0
    006     * (the "License"); you may not use this file except in compliance with
    007     * the License.  You may obtain a copy of the License at
    008     *
    009     *      http://www.apache.org/licenses/LICENSE-2.0
    010     *
    011     * Unless required by applicable law or agreed to in writing, software
    012     * distributed under the License is distributed on an "AS IS" BASIS,
    013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    014     * See the License for the specific language governing permissions and
    015     * limitations under the License.
    016     */
    017
    018
    019    package org.apache.commons.beanutils;
    020
    021
    022    import java.io.File;
    023    import java.lang.reflect.Array;
    024    import java.math.BigDecimal;
    025    import java.math.BigInteger;
    026    import java.net.URL;
    027    import java.sql.Timestamp;
    028    import java.util.Calendar;
    029    import java.util.Collection;
    030
    031    import org.apache.commons.beanutils.converters.ArrayConverter;
    032    import org.apache.commons.beanutils.converters.BigDecimalConverter;
    033    import org.apache.commons.beanutils.converters.BigIntegerConverter;
    034    import org.apache.commons.beanutils.converters.BooleanConverter;
    035    import org.apache.commons.beanutils.converters.ByteConverter;
    036    import org.apache.commons.beanutils.converters.CalendarConverter;
    037    import org.apache.commons.beanutils.converters.CharacterConverter;
    038    import org.apache.commons.beanutils.converters.ClassConverter;
    039    import org.apache.commons.beanutils.converters.ConverterFacade;
    040    import org.apache.commons.beanutils.converters.DateConverter;
    041    import org.apache.commons.beanutils.converters.DoubleConverter;
    042    import org.apache.commons.beanutils.converters.FileConverter;
    043    import org.apache.commons.beanutils.converters.FloatConverter;
    044    import org.apache.commons.beanutils.converters.IntegerConverter;
    045    import org.apache.commons.beanutils.converters.LongConverter;
    046    import org.apache.commons.beanutils.converters.ShortConverter;
    047    import org.apache.commons.beanutils.converters.SqlDateConverter;
    048    import org.apache.commons.beanutils.converters.SqlTimeConverter;
    049    import org.apache.commons.beanutils.converters.SqlTimestampConverter;
    050    import org.apache.commons.beanutils.converters.StringConverter;
    051    import org.apache.commons.beanutils.converters.URLConverter;
    052    import org.apache.commons.logging.Log;
    053    import org.apache.commons.logging.LogFactory;
    054
    055
    056    /**
    057     * <p>Utility methods for converting String scalar values to objects of the
    058     * specified Class, String arrays to arrays of the specified Class.  The
    059     * actual {@link Converter} instance to be used can be registered for each
    060     * possible destination Class.  Unless you override them, standard
    061     * {@link Converter} instances are provided for all of the following
    062     * destination Classes:</p>
    063     * <ul>
    064     * <li>java.lang.BigDecimal (no default value)</li>
    065     * <li>java.lang.BigInteger (no default value)</li>
    066     * <li>boolean and java.lang.Boolean (default to false)</li>
    067     * <li>byte and java.lang.Byte (default to zero)</li>
    068     * <li>char and java.lang.Character (default to a space)</li>
    069     * <li>java.lang.Class (no default value)</li>
    070     * <li>double and java.lang.Double (default to zero)</li>
    071     * <li>float and java.lang.Float (default to zero)</li>
    072     * <li>int and java.lang.Integer (default to zero)</li>
    073     * <li>long and java.lang.Long (default to zero)</li>
    074     * <li>short and java.lang.Short (default to zero)</li>
    075     * <li>java.lang.String (default to null)</li>
    076     * <li>java.io.File (no default value)</li>
    077     * <li>java.net.URL (no default value)</li>
    078     * <li>java.sql.Date (no default value)</li>
    079     * <li>java.sql.Time (no default value)</li>
    080     * <li>java.sql.Timestamp (no default value)</li>
    081     * </ul>
    082     *
    083     * <p>For backwards compatibility, the standard Converters for primitive
    084     * types (and the corresponding wrapper classes) return a defined
    085     * default value when a conversion error occurs.  If you prefer to have a
    086     * {@link ConversionException} thrown instead, replace the standard Converter
    087     * instances with instances created with the zero-arguments constructor.  For
    088     * example, to cause the Converters for integers to throw an exception on
    089     * conversion errors, you could do this:</p>
    090     * <pre>
    091     *   // No-args constructor gets the version that throws exceptions
    092     *   Converter myConverter =
    093     *    new org.apache.commons.beanutils.converter.IntegerConverter();
    094     *   ConvertUtils.register(myConverter, Integer.TYPE);    // Native type
    095     *   ConvertUtils.register(myConverter, Integer.class);   // Wrapper class
    096     * </pre>
    097     *
    098     * <p>
    099     * 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     * @author Craig R. McClanahan
    124     * @author Ralph Schaer
    125     * @author Chris Audley
    126     * @author James Strachan
    127     * @version $Revision: 745079 $ $Date: 2009-02-17 14:04:10 +0000 (Tue, 17 Feb 2009) $
    128     * @since 1.7
    129     */
    130
    131    public class ConvertUtilsBean {
    132
    133        private static final Integer ZERO = new Integer(0);
    134        private static final Character SPACE = new Character(' ');
    135
    136        // ------------------------------------------------------- Class Methods
    137        /**
    138         * Get singleton instance
    139         * @return The singleton instance
    140         */
    141        protected static ConvertUtilsBean getInstance() {
    142            return BeanUtilsBean.getInstance().getConvertUtils();
    143        }
    144
    145        // ------------------------------------------------------- Variables
    146
    147
    148        /**
    149         * The set of {@link Converter}s that can be used to convert Strings
    150         * into objects of a specified Class, keyed by the destination Class.
    151         */
    152        private WeakFastHashMap converters = new WeakFastHashMap();
    153
    154        /**
    155         * The <code>Log</code> instance for this class.
    156         */
    157        private Log log = LogFactory.getLog(ConvertUtils.class);
    158
    159        // ------------------------------------------------------- Constructors
    160
    161        /** Construct a bean with standard converters registered */
    162        public ConvertUtilsBean() {
    163            converters.setFast(false);
    164            deregister();
    165            converters.setFast(true);
    166        }
    167
    168        // --------------------------------------------------------- Public Methods
    169
    170        /**
    171         * The default value for Boolean conversions.
    172         * @deprecated Register replacement converters for Boolean.TYPE and
    173         *  Boolean.class instead
    174         */
    175        private Boolean defaultBoolean = Boolean.FALSE;
    176
    177        /**
    178         * Gets the default value for Boolean conversions.
    179         * @return The default Boolean value
    180         * @deprecated Register replacement converters for Boolean.TYPE and
    181         *  Boolean.class instead
    182         */
    183        public boolean getDefaultBoolean() {
    184            return (defaultBoolean.booleanValue());
    185        }
    186
    187        /**
    188         * Sets the default value for Boolean conversions.
    189         * @param newDefaultBoolean The default Boolean value
    190         * @deprecated Register replacement converters for Boolean.TYPE and
    191         *  Boolean.class instead
    192         */
    193        public void setDefaultBoolean(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        private Byte defaultByte = new Byte((byte) 0);
    206
    207        /**
    208         * Gets the default value for Byte conversions.
    209         * @return The default Byte value
    210         * @deprecated Register replacement converters for Byte.TYPE and
    211         *  Byte.class instead
    212         */
    213        public byte getDefaultByte() {
    214            return (defaultByte.byteValue());
    215        }
    216
    217        /**
    218         * Sets the default value for Byte conversions.
    219         * @param newDefaultByte The default Byte value
    220         * @deprecated Register replacement converters for Byte.TYPE and
    221         *  Byte.class instead
    222         */
    223        public void setDefaultByte(byte newDefaultByte) {
    224            defaultByte = new Byte(newDefaultByte);
    225            register(new ByteConverter(defaultByte), Byte.TYPE);
    226            register(new ByteConverter(defaultByte), Byte.class);
    227        }
    228
    229
    230        /**
    231         * The default value for Character conversions.
    232         * @deprecated Register replacement converters for Character.TYPE and
    233         *  Character.class instead
    234         */
    235        private Character defaultCharacter = new Character(' ');
    236
    237        /**
    238         * Gets the default value for Character conversions.
    239         * @return The default Character value
    240         * @deprecated Register replacement converters for Character.TYPE and
    241         *  Character.class instead
    242         */
    243        public char getDefaultCharacter() {
    244            return (defaultCharacter.charValue());
    245        }
    246
    247        /**
    248         * Sets the default value for Character conversions.
    249         * @param newDefaultCharacter The default Character value
    250         * @deprecated Register replacement converters for Character.TYPE and
    251         *  Character.class instead
    252         */
    253        public void setDefaultCharacter(char newDefaultCharacter) {
    254            defaultCharacter = new Character(newDefaultCharacter);
    255            register(new CharacterConverter(defaultCharacter),
    256                        Character.TYPE);
    257            register(new CharacterConverter(defaultCharacter),
    258                        Character.class);
    259        }
    260
    261
    262        /**
    263         * The default value for Double conversions.
    264         * @deprecated Register replacement converters for Double.TYPE and
    265         *  Double.class instead
    266         */
    267        private Double defaultDouble = new Double(0.0);
    268
    269        /**
    270         * Gets the default value for Double conversions.
    271         * @return The default Double value
    272         * @deprecated Register replacement converters for Double.TYPE and
    273         *  Double.class instead
    274         */
    275        public double getDefaultDouble() {
    276            return (defaultDouble.doubleValue());
    277        }
    278
    279        /**
    280         * Sets the default value for Double conversions.
    281         * @param newDefaultDouble The default Double value
    282         * @deprecated Register replacement converters for Double.TYPE and
    283         *  Double.class instead
    284         */
    285        public void setDefaultDouble(double newDefaultDouble) {
    286            defaultDouble = new Double(newDefaultDouble);
    287            register(new DoubleConverter(defaultDouble), Double.TYPE);
    288            register(new DoubleConverter(defaultDouble), Double.class);
    289        }
    290
    291
    292        /**
    293         * The default value for Float conversions.
    294         * @deprecated Register replacement converters for Float.TYPE and
    295         *  Float.class instead
    296         */
    297        private Float defaultFloat = new Float((float) 0.0);
    298
    299        /**
    300         * Gets the default value for Float conversions.
    301         * @return The default Float value
    302         * @deprecated Register replacement converters for Float.TYPE and
    303         *  Float.class instead
    304         */
    305        public float getDefaultFloat() {
    306            return (defaultFloat.floatValue());
    307        }
    308
    309        /**
    310         * Sets the default value for Float conversions.
    311         * @param newDefaultFloat The default Float value
    312         * @deprecated Register replacement converters for Float.TYPE and
    313         *  Float.class instead
    314         */
    315        public void setDefaultFloat(float newDefaultFloat) {
    316            defaultFloat = new Float(newDefaultFloat);
    317            register(new FloatConverter(defaultFloat), Float.TYPE);
    318            register(new FloatConverter(defaultFloat), Float.class);
    319        }
    320
    321
    322        /**
    323         * The default value for Integer conversions.
    324         * @deprecated Register replacement converters for Integer.TYPE and
    325         *  Integer.class instead
    326         */
    327        private Integer defaultInteger = new Integer(0);
    328
    329        /**
    330         * Gets the default value for Integer conversions.
    331         * @return The default Integer value
    332         * @deprecated Register replacement converters for Integer.TYPE and
    333         *  Integer.class instead
    334         */
    335        public int getDefaultInteger() {
    336            return (defaultInteger.intValue());
    337        }
    338
    339        /**
    340         * Sets the default value for Integer conversions.
    341         * @param newDefaultInteger The default Integer value
    342         * @deprecated Register replacement converters for Integer.TYPE and
    343         *  Integer.class instead
    344         */
    345        public void setDefaultInteger(int newDefaultInteger) {
    346            defaultInteger = new Integer(newDefaultInteger);
    347            register(new IntegerConverter(defaultInteger), Integer.TYPE);
    348            register(new IntegerConverter(defaultInteger), Integer.class);
    349        }
    350
    351
    352        /**
    353         * The default value for Long conversions.
    354         * @deprecated Register replacement converters for Long.TYPE and
    355         *  Long.class instead
    356         */
    357        private Long defaultLong = new Long(0);
    358
    359        /**
    360         * Gets the default value for Long conversions.
    361         * @return The default Long value
    362         * @deprecated Register replacement converters for Long.TYPE and
    363         *  Long.class instead
    364         */
    365        public long getDefaultLong() {
    366            return (defaultLong.longValue());
    367        }
    368
    369        /**
    370         * Sets the default value for Long conversions.
    371         * @param newDefaultLong The default Long value
    372         * @deprecated Register replacement converters for Long.TYPE and
    373         *  Long.class instead
    374         */
    375        public void setDefaultLong(long newDefaultLong) {
    376            defaultLong = new Long(newDefaultLong);
    377            register(new LongConverter(defaultLong), Long.TYPE);
    378            register(new LongConverter(defaultLong), Long.class);
    379        }
    380
    381
    382        /**
    383         * The default value for Short conversions.
    384         * @deprecated Register replacement converters for Short.TYPE and
    385         *  Short.class instead
    386         */
    387        private static Short defaultShort = new Short((short) 0);
    388
    389        /**
    390         * Gets the default value for Short conversions.
    391         * @return The default Short value
    392         * @deprecated Register replacement converters for Short.TYPE and
    393         *  Short.class instead
    394         */
    395        public short getDefaultShort() {
    396            return (defaultShort.shortValue());
    397        }
    398
    399        /**
    400         * Sets the default value for Short conversions.
    401         * @param newDefaultShort The default Short value
    402         * @deprecated Register replacement converters for Short.TYPE and
    403         *  Short.class instead
    404         */
    405        public void setDefaultShort(short newDefaultShort) {
    406            defaultShort = new Short(newDefaultShort);
    407            register(new ShortConverter(defaultShort), Short.TYPE);
    408            register(new ShortConverter(defaultShort), Short.class);
    409        }
    410
    411
    412
    413        /**
    414         * Convert the specified value into a String.  If the specified value
    415         * is an array, the first element (converted to a String) will be
    416         * returned.  The registered {@link Converter} for the
    417         * <code>java.lang.String</code> class will be used, which allows
    418         * applications to customize Object->String conversions (the default
    419         * implementation simply uses toString()).
    420         *
    421         * @param value Value to be converted (may be null)
    422         * @return The converted String value
    423         */
    424        public String convert(Object value) {
    425
    426            if (value == null) {
    427                return null;
    428            } else if (value.getClass().isArray()) {
    429                if (Array.getLength(value) < 1) {
    430                    return (null);
    431                }
    432                value = Array.get(value, 0);
    433                if (value == null) {
    434                    return null;
    435                } else {
    436                    Converter converter = lookup(String.class);
    437                    return ((String) converter.convert(String.class, value));
    438                }
    439            } else {
    440                Converter converter = lookup(String.class);
    441                return ((String) converter.convert(String.class, value));
    442            }
    443
    444        }
    445
    446
    447        /**
    448         * Convert the specified value to an object of the specified class (if
    449         * possible).  Otherwise, return a String representation of the value.
    450         *
    451         * @param value Value to be converted (may be null)
    452         * @param clazz Java class to be converted to
    453         * @return The converted value
    454         *
    455         * @exception ConversionException if thrown by an underlying Converter
    456         */
    457        public Object convert(String value, Class clazz) {
    458
    459            if (log.isDebugEnabled()) {
    460                log.debug("Convert string '" + value + "' to class '" +
    461                          clazz.getName() + "'");
    462            }
    463            Converter converter = lookup(clazz);
    464            if (converter == null) {
    465                converter = lookup(String.class);
    466            }
    467            if (log.isTraceEnabled()) {
    468                log.trace("  Using converter " + converter);
    469            }
    470            return (converter.convert(clazz, value));
    471
    472        }
    473
    474
    475        /**
    476         * Convert an array of specified values to an array of objects of the
    477         * specified class (if possible).  If the specified Java class is itself
    478         * an array class, this class will be the type of the returned value.
    479         * Otherwise, an array will be constructed whose component type is the
    480         * specified class.
    481         *
    482         * @param values Array of values to be converted
    483         * @param clazz Java array or element class to be converted to
    484         * @return The converted value
    485         *
    486         * @exception ConversionException if thrown by an underlying Converter
    487         */
    488        public Object convert(String[] values, Class clazz) {
    489
    490            Class type = clazz;
    491            if (clazz.isArray()) {
    492                type = clazz.getComponentType();
    493            }
    494            if (log.isDebugEnabled()) {
    495                log.debug("Convert String[" + values.length + "] to class '" +
    496                          type.getName() + "[]'");
    497            }
    498            Converter converter = lookup(type);
    499            if (converter == null) {
    500                converter = lookup(String.class);
    501            }
    502            if (log.isTraceEnabled()) {
    503                log.trace("  Using converter " + converter);
    504            }
    505            Object array = Array.newInstance(type, values.length);
    506            for (int i = 0; i < values.length; i++) {
    507                Array.set(array, i, converter.convert(type, values[i]));
    508            }
    509            return (array);
    510
    511        }
    512
    513
    514        /**
    515         * <p>Convert the value to an object of the specified class (if
    516         * possible).</p>
    517         *
    518         * @param value Value to be converted (may be null)
    519         * @param targetType Class of the value to be converted to
    520         * @return The converted value
    521         *
    522         * @exception ConversionException if thrown by an underlying Converter
    523         */
    524        public Object convert(Object value, Class targetType) {
    525
    526            Class sourceType = value == null ? null : value.getClass();
    527
    528            if (log.isDebugEnabled()) {
    529                if (value == null) {
    530                    log.debug("Convert null value to type '" +
    531                            targetType.getName() + "'");
    532                } else {
    533                    log.debug("Convert type '" + sourceType.getName() + "' value '" + value +
    534                          "' to type '" + targetType.getName() + "'");
    535                }
    536            }
    537
    538            Object converted = value;
    539            Converter converter = lookup(sourceType, targetType);
    540            if (converter != null) {
    541                if (log.isTraceEnabled()) {
    542                    log.trace("  Using converter " + converter);
    543                }
    544                converted = converter.convert(targetType, value);
    545            }
    546            if (targetType == String.class && converted != null &&
    547                    !(converted instanceof String)) {
    548
    549                // NOTE: For backwards compatibility, if the Converter
    550                //       doesn't handle  conversion-->String then
    551                //       use the registered String Converter
    552                converter = lookup(String.class);
    553                if (converter != null) {
    554                    if (log.isTraceEnabled()) {
    555                        log.trace("  Using converter " + converter);
    556                    }
    557                    converted = converter.convert(String.class, converted);
    558                }
    559
    560                // If the object still isn't a String, use toString() method
    561                if (converted != null && !(converted instanceof String)) {
    562                    converted = converted.toString();
    563                }
    564
    565            }
    566            return converted;
    567
    568        }
    569
    570        /**
    571         * Remove all registered {@link Converter}s, and re-establish the
    572         * standard Converters.
    573         */
    574        public void deregister() {
    575
    576            converters.clear();
    577
    578            registerPrimitives(false);
    579            registerStandard(false, false);
    580            registerOther(true);
    581            registerArrays(false, 0);
    582            register(BigDecimal.class, new BigDecimalConverter());
    583            register(BigInteger.class, new BigIntegerConverter());
    584        }
    585
    586        /**
    587         * Register the provided converters with the specified defaults.
    588         *
    589         * @param throwException <code>true</code> if the converters should
    590         * throw an exception when a conversion error occurs, otherwise <code>
    591         * <code>false</code> if a default value should be used.
    592         * @param defaultNull <code>true</code>if the <i>standard</i> converters
    593         * (see {@link ConvertUtilsBean#registerStandard(boolean, boolean)})
    594         * should use a default value of <code>null</code>, otherwise <code>false</code>.
    595         * N.B. This values is ignored if <code>throwException</code> is <code>true</code>
    596         * @param defaultArraySize The size of the default array value for array converters
    597         * (N.B. This values is ignored if <code>throwException</code> is <code>true</code>).
    598         * Specifying a value less than zero causes a <code>null<code> value to be used for
    599         * the default.
    600         */
    601        public void register(boolean throwException, boolean defaultNull, int defaultArraySize) {
    602            registerPrimitives(throwException);
    603            registerStandard(throwException, defaultNull);
    604            registerOther(throwException);
    605            registerArrays(throwException, defaultArraySize);
    606        }
    607
    608        /**
    609         * Register the converters for primitive types.
    610         * </p>
    611         * This method registers the following converters:
    612         * <ul>
    613         *     <li><code>Boolean.TYPE</code> - {@link BooleanConverter}</li>
    614         *     <li><code>Byte.TYPE</code> - {@link ByteConverter}</li>
    615         *     <li><code>Character.TYPE</code> - {@link CharacterConverter}</li>
    616         *     <li><code>Double.TYPE</code> - {@link DoubleConverter}</li>
    617         *     <li><code>Float.TYPE</code> - {@link FloatConverter}</li>
    618         *     <li><code>Integer.TYPE</code> - {@link IntegerConverter}</li>
    619         *     <li><code>Long.TYPE</code> - {@link LongConverter}</li>
    620         *     <li><code>Short.TYPE</code> - {@link ShortConverter}</li>
    621         * </ul>
    622         * @param throwException <code>true</code> if the converters should
    623         * throw an exception when a conversion error occurs, otherwise <code>
    624         * <code>false</code> if a default value should be used.
    625         */
    626        private void registerPrimitives(boolean throwException) {
    627            register(Boolean.TYPE,   throwException ? new BooleanConverter()    : new BooleanConverter(Boolean.FALSE));
    628            register(Byte.TYPE,      throwException ? new ByteConverter()       : new ByteConverter(ZERO));
    629            register(Character.TYPE, throwException ? new CharacterConverter()  : new CharacterConverter(SPACE));
    630            register(Double.TYPE,    throwException ? new DoubleConverter()     : new DoubleConverter(ZERO));
    631            register(Float.TYPE,     throwException ? new FloatConverter()      : new FloatConverter(ZERO));
    632            register(Integer.TYPE,   throwException ? new IntegerConverter()    : new IntegerConverter(ZERO));
    633            register(Long.TYPE,      throwException ? new LongConverter()       : new LongConverter(ZERO));
    634            register(Short.TYPE,     throwException ? new ShortConverter()      : new ShortConverter(ZERO));
    635        }
    636
    637        /**
    638         * Register the converters for standard types.
    639         * </p>
    640         * This method registers the following converters:
    641         * <ul>
    642         *     <li><code>BigDecimal.class</code> - {@link BigDecimalConverter}</li>
    643         *     <li><code>BigInteger.class</code> - {@link BigIntegerConverter}</li>
    644         *     <li><code>Boolean.class</code> - {@link BooleanConverter}</li>
    645         *     <li><code>Byte.class</code> - {@link ByteConverter}</li>
    646         *     <li><code>Character.class</code> - {@link CharacterConverter}</li>
    647         *     <li><code>Double.class</code> - {@link DoubleConverter}</li>
    648         *     <li><code>Float.class</code> - {@link FloatConverter}</li>
    649         *     <li><code>Integer.class</code> - {@link IntegerConverter}</li>
    650         *     <li><code>Long.class</code> - {@link LongConverter}</li>
    651         *     <li><code>Short.class</code> - {@link ShortConverter}</li>
    652         *     <li><code>String.class</code> - {@link StringConverter}</li>
    653         * </ul>
    654         * @param throwException <code>true</code> if the converters should
    655         * throw an exception when a conversion error occurs, otherwise <code>
    656         * <code>false</code> if a default value should be used.
    657         * @param defaultNull <code>true</code>if the <i>standard</i> converters
    658         * (see {@link ConvertUtilsBean#registerStandard(boolean, boolean)})
    659         * should use a default value of <code>null</code>, otherwise <code>false</code>.
    660         * N.B. This values is ignored if <code>throwException</code> is <code>true</code>
    661         */
    662        private void registerStandard(boolean throwException, boolean defaultNull) {
    663
    664            Number     defaultNumber     = defaultNull ? null : ZERO;
    665            BigDecimal bigDecDeflt       = defaultNull ? null : new BigDecimal("0.0");
    666            BigInteger bigIntDeflt       = defaultNull ? null : new BigInteger("0");
    667            Boolean    booleanDefault    = defaultNull ? null : Boolean.FALSE;
    668            Character  charDefault       = defaultNull ? null : SPACE;
    669            String     stringDefault     = defaultNull ? null : "";
    670
    671            register(BigDecimal.class, throwException ? new BigDecimalConverter() : new BigDecimalConverter(bigDecDeflt));
    672            register(BigInteger.class, throwException ? new BigIntegerConverter() : new BigIntegerConverter(bigIntDeflt));
    673            register(Boolean.class,    throwException ? new BooleanConverter()    : new BooleanConverter(booleanDefault));
    674            register(Byte.class,       throwException ? new ByteConverter()       : new ByteConverter(defaultNumber));
    675            register(Character.class,  throwException ? new CharacterConverter()  : new CharacterConverter(charDefault));
    676            register(Double.class,     throwException ? new DoubleConverter()     : new DoubleConverter(defaultNumber));
    677            register(Float.class,      throwException ? new FloatConverter()      : new FloatConverter(defaultNumber));
    678            register(Integer.class,    throwException ? new IntegerConverter()    : new IntegerConverter(defaultNumber));
    679            register(Long.class,       throwException ? new LongConverter()       : new LongConverter(defaultNumber));
    680            register(Short.class,      throwException ? new ShortConverter()      : new ShortConverter(defaultNumber));
    681            register(String.class,     throwException ? new StringConverter()     : new StringConverter(stringDefault));
    682
    683        }
    684
    685        /**
    686         * Register the converters for other types.
    687         * </p>
    688         * This method registers the following converters:
    689         * <ul>
    690         *     <li><code>Class.class</code> - {@link ClassConverter}</li>
    691         *     <li><code>java.util.Date.class</code> - {@link DateConverter}</li>
    692         *     <li><code>java.util.Calendar.class</code> - {@link CalendarConverter}</li>
    693         *     <li><code>File.class</code> - {@link FileConverter}</li>
    694         *     <li><code>java.sql.Date.class</code> - {@link SqlDateConverter}</li>
    695         *     <li><code>java.sql.Time.class</code> - {@link SqlTimeConverter}</li>
    696         *     <li><code>java.sql.Timestamp.class</code> - {@link SqlTimestampConverter}</li>
    697         *     <li><code>URL.class</code> - {@link URLConverter}</li>
    698         * </ul>
    699         * @param throwException <code>true</code> if the converters should
    700         * throw an exception when a conversion error occurs, otherwise <code>
    701         * <code>false</code> if a default value should be used.
    702         */
    703        private void registerOther(boolean throwException) {
    704            register(Class.class,         throwException ? new ClassConverter()        : new ClassConverter(null));
    705            register(java.util.Date.class, throwException ? new DateConverter()        : new DateConverter(null));
    706            register(Calendar.class,      throwException ? new CalendarConverter()     : new CalendarConverter(null));
    707            register(File.class,          throwException ? new FileConverter()         : new FileConverter(null));
    708            register(java.sql.Date.class, throwException ? new SqlDateConverter()      : new SqlDateConverter(null));
    709            register(java.sql.Time.class, throwException ? new SqlTimeConverter()      : new SqlTimeConverter(null));
    710            register(Timestamp.class,     throwException ? new SqlTimestampConverter() : new SqlTimestampConverter(null));
    711            register(URL.class,           throwException ? new URLConverter()          : new URLConverter(null));
    712        }
    713
    714        /**
    715         * Register array converters.
    716         *
    717         * @param throwException <code>true</code> if the converters should
    718         * throw an exception when a conversion error occurs, otherwise <code>
    719         * <code>false</code> if a default value should be used.
    720         * @param defaultArraySize The size of the default array value for array converters
    721         * (N.B. This values is ignored if <code>throwException</code> is <code>true</code>).
    722         * Specifying a value less than zero causes a <code>null<code> value to be used for
    723         * the default.
    724         */
    725        private void registerArrays(boolean throwException, int defaultArraySize) {
    726
    727            // Primitives
    728            registerArrayConverter(Boolean.TYPE,   new BooleanConverter(),   throwException, defaultArraySize);
    729            registerArrayConverter(Byte.TYPE,      new ByteConverter(),      throwException, defaultArraySize);
    730            registerArrayConverter(Character.TYPE, new CharacterConverter(), throwException, defaultArraySize);
    731            registerArrayConverter(Double.TYPE,    new DoubleConverter(),    throwException, defaultArraySize);
    732            registerArrayConverter(Float.TYPE,     new FloatConverter(),     throwException, defaultArraySize);
    733            registerArrayConverter(Integer.TYPE,   new IntegerConverter(),   throwException, defaultArraySize);
    734            registerArrayConverter(Long.TYPE,      new LongConverter(),      throwException, defaultArraySize);
    735            registerArrayConverter(Short.TYPE,     new ShortConverter(),     throwException, defaultArraySize);
    736
    737            // Standard
    738            registerArrayConverter(BigDecimal.class, new BigDecimalConverter(), throwException, defaultArraySize);
    739            registerArrayConverter(BigInteger.class, new BigIntegerConverter(), throwException, defaultArraySize);
    740            registerArrayConverter(Boolean.class,    new BooleanConverter(),    throwException, defaultArraySize);
    741            registerArrayConverter(Byte.class,       new ByteConverter(),       throwException, defaultArraySize);
    742            registerArrayConverter(Character.class,  new CharacterConverter(),  throwException, defaultArraySize);
    743            registerArrayConverter(Double.class,     new DoubleConverter(),     throwException, defaultArraySize);
    744            registerArrayConverter(Float.class,      new FloatConverter(),      throwException, defaultArraySize);
    745            registerArrayConverter(Integer.class,    new IntegerConverter(),    throwException, defaultArraySize);
    746            registerArrayConverter(Long.class,       new LongConverter(),       throwException, defaultArraySize);
    747            registerArrayConverter(Short.class,      new ShortConverter(),      throwException, defaultArraySize);
    748            registerArrayConverter(String.class,     new StringConverter(),     throwException, defaultArraySize);
    749
    750            // Other
    751            registerArrayConverter(Class.class,          new ClassConverter(),        throwException, defaultArraySize);
    752            registerArrayConverter(java.util.Date.class, new DateConverter(),         throwException, defaultArraySize);
    753            registerArrayConverter(Calendar.class,       new DateConverter(),         throwException, defaultArraySize);
    754            registerArrayConverter(File.class,           new FileConverter(),         throwException, defaultArraySize);
    755            registerArrayConverter(java.sql.Date.class,  new SqlDateConverter(),      throwException, defaultArraySize);
    756            registerArrayConverter(java.sql.Time.class,  new SqlTimeConverter(),      throwException, defaultArraySize);
    757            registerArrayConverter(Timestamp.class,      new SqlTimestampConverter(), throwException, defaultArraySize);
    758            registerArrayConverter(URL.class,            new URLConverter(),          throwException, defaultArraySize);
    759
    760        }
    761
    762        /**
    763         * Register a new ArrayConverter with the specified element delegate converter
    764         * that returns a default array of the specified size in the event of conversion errors.
    765         *
    766         * @param componentType The component type of the array
    767         * @param componentConverter The converter to delegate to for the array elements
    768         * @param throwException Whether a conversion exception should be thrown or a default
    769         * value used in the event of a conversion error
    770         * @param defaultArraySize The size of the default array
    771         */
    772        private void registerArrayConverter(Class componentType, Converter componentConverter,
    773                boolean throwException, int defaultArraySize) {
    774            Class arrayType = Array.newInstance(componentType, 0).getClass();
    775            Converter arrayConverter = null;
    776            if (throwException) {
    777                arrayConverter = new ArrayConverter(arrayType, componentConverter);
    778            } else {
    779                arrayConverter = new ArrayConverter(arrayType, componentConverter, defaultArraySize);
    780            }
    781            register(arrayType, arrayConverter);
    782        }
    783
    784        /** strictly for convenience since it has same parameter order as Map.put */
    785        private void register(Class clazz, Converter converter) {
    786            register(new ConverterFacade(converter), clazz);
    787        }
    788
    789        /**
    790         * Remove any registered {@link Converter} for the specified destination
    791         * <code>Class</code>.
    792         *
    793         * @param clazz Class for which to remove a registered Converter
    794         */
    795        public void deregister(Class clazz) {
    796
    797            converters.remove(clazz);
    798
    799        }
    800
    801
    802        /**
    803         * Look up and return any registered {@link Converter} for the specified
    804         * destination class; if there is no registered Converter, return
    805         * <code>null</code>.
    806         *
    807         * @param clazz Class for which to return a registered Converter
    808         * @return The registered {@link Converter} or <code>null</code> if not found
    809         */
    810        public Converter lookup(Class clazz) {
    811
    812            return ((Converter) converters.get(clazz));
    813
    814        }
    815
    816        /**
    817         * Look up and return any registered {@link Converter} for the specified
    818         * source and destination class; if there is no registered Converter,
    819         * return <code>null</code>.
    820         *
    821         * @param sourceType Class of the value being converted
    822         * @param targetType Class of the value to be converted to
    823         * @return The registered {@link Converter} or <code>null</code> if not found
    824         */
    825        public Converter lookup(Class sourceType, Class targetType) {
    826
    827            if (targetType == null) {
    828                throw new IllegalArgumentException("Target type is missing");
    829            }
    830            if (sourceType == null) {
    831                return lookup(targetType);
    832            }
    833
    834            Converter converter = null;
    835            // Convert --> String
    836            if (targetType == String.class) {
    837                converter = lookup(sourceType);
    838                if (converter == null && (sourceType.isArray() ||
    839                            Collection.class.isAssignableFrom(sourceType))) {
    840                    converter = lookup(String[].class);
    841                }
    842                if (converter == null) {
    843                    converter = lookup(String.class);
    844                }
    845                return converter;
    846            }
    847
    848            // Convert --> String array
    849            if (targetType == String[].class) {
    850                if (sourceType.isArray() || Collection.class.isAssignableFrom(sourceType)) {
    851                    converter = lookup(sourceType);
    852                }
    853                if (converter == null) {
    854                    converter = lookup(String[].class);
    855                }
    856                return converter;
    857            }
    858
    859            return lookup(targetType);
    860
    861        }
    862
    863        /**
    864         * Register a custom {@link Converter} for the specified destination
    865         * <code>Class</code>, replacing any previously registered Converter.
    866         *
    867         * @param converter Converter to be registered
    868         * @param clazz Destination class for conversions performed by this
    869         *  Converter
    870         */
    871        public void register(Converter converter, Class clazz) {
    872
    873            converters.put(clazz, converter);
    874
    875        }
    876    }