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    }