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
019package org.apache.commons.beanutils;
020
021
022import java.io.File;
023import java.lang.reflect.Array;
024import java.math.BigDecimal;
025import java.math.BigInteger;
026import java.net.URL;
027import java.sql.Timestamp;
028import java.util.Calendar;
029import java.util.Collection;
030
031import org.apache.commons.beanutils.converters.ArrayConverter;
032import org.apache.commons.beanutils.converters.BigDecimalConverter;
033import org.apache.commons.beanutils.converters.BigIntegerConverter;
034import org.apache.commons.beanutils.converters.BooleanConverter;
035import org.apache.commons.beanutils.converters.ByteConverter;
036import org.apache.commons.beanutils.converters.CalendarConverter;
037import org.apache.commons.beanutils.converters.CharacterConverter;
038import org.apache.commons.beanutils.converters.ClassConverter;
039import org.apache.commons.beanutils.converters.ConverterFacade;
040import org.apache.commons.beanutils.converters.DateConverter;
041import org.apache.commons.beanutils.converters.DoubleConverter;
042import org.apache.commons.beanutils.converters.FileConverter;
043import org.apache.commons.beanutils.converters.FloatConverter;
044import org.apache.commons.beanutils.converters.IntegerConverter;
045import org.apache.commons.beanutils.converters.LongConverter;
046import org.apache.commons.beanutils.converters.ShortConverter;
047import org.apache.commons.beanutils.converters.SqlDateConverter;
048import org.apache.commons.beanutils.converters.SqlTimeConverter;
049import org.apache.commons.beanutils.converters.SqlTimestampConverter;
050import org.apache.commons.beanutils.converters.StringConverter;
051import org.apache.commons.beanutils.converters.URLConverter;
052import org.apache.commons.logging.Log;
053import 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
131public 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}