Coverage Report - org.apache.commons.beanutils.ConvertUtilsBean
 
Classes in this File Line Coverage Branch Coverage Complexity
ConvertUtilsBean
73%
164/222
63%
89/140
3.294
 
 1  
 /*
 2  
  * Licensed to the Apache Software Foundation (ASF) under one or more
 3  
  * contributor license agreements.  See the NOTICE file distributed with
 4  
  * this work for additional information regarding copyright ownership.
 5  
  * The ASF licenses this file to You under the Apache License, Version 2.0
 6  
  * (the "License"); you may not use this file except in compliance with
 7  
  * the License.  You may obtain a copy of the License at
 8  
  *
 9  
  *      http://www.apache.org/licenses/LICENSE-2.0
 10  
  *
 11  
  * Unless required by applicable law or agreed to in writing, software
 12  
  * distributed under the License is distributed on an "AS IS" BASIS,
 13  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 14  
  * See the License for the specific language governing permissions and
 15  
  * limitations under the License.
 16  
  */
 17  
 
 18  
 
 19  
 package org.apache.commons.beanutils;
 20  
 
 21  
 
 22  
 import java.io.File;
 23  
 import java.lang.reflect.Array;
 24  
 import java.math.BigDecimal;
 25  
 import java.math.BigInteger;
 26  
 import java.net.URL;
 27  
 import java.sql.Timestamp;
 28  
 import java.util.Calendar;
 29  
 import java.util.Collection;
 30  
 
 31  
 import org.apache.commons.beanutils.converters.ArrayConverter;
 32  
 import org.apache.commons.beanutils.converters.BigDecimalConverter;
 33  
 import org.apache.commons.beanutils.converters.BigIntegerConverter;
 34  
 import org.apache.commons.beanutils.converters.BooleanConverter;
 35  
 import org.apache.commons.beanutils.converters.ByteConverter;
 36  
 import org.apache.commons.beanutils.converters.CalendarConverter;
 37  
 import org.apache.commons.beanutils.converters.CharacterConverter;
 38  
 import org.apache.commons.beanutils.converters.ClassConverter;
 39  
 import org.apache.commons.beanutils.converters.ConverterFacade;
 40  
 import org.apache.commons.beanutils.converters.DateConverter;
 41  
 import org.apache.commons.beanutils.converters.DoubleConverter;
 42  
 import org.apache.commons.beanutils.converters.FileConverter;
 43  
 import org.apache.commons.beanutils.converters.FloatConverter;
 44  
 import org.apache.commons.beanutils.converters.IntegerConverter;
 45  
 import org.apache.commons.beanutils.converters.LongConverter;
 46  
 import org.apache.commons.beanutils.converters.ShortConverter;
 47  
 import org.apache.commons.beanutils.converters.SqlDateConverter;
 48  
 import org.apache.commons.beanutils.converters.SqlTimeConverter;
 49  
 import org.apache.commons.beanutils.converters.SqlTimestampConverter;
 50  
 import org.apache.commons.beanutils.converters.StringConverter;
 51  
 import org.apache.commons.beanutils.converters.URLConverter;
 52  
 import org.apache.commons.logging.Log;
 53  
 import org.apache.commons.logging.LogFactory;
 54  
 
 55  
 
 56  
 /**
 57  
  * <p>Utility methods for converting String scalar values to objects of the
 58  
  * specified Class, String arrays to arrays of the specified Class.  The
 59  
  * actual {@link Converter} instance to be used can be registered for each
 60  
  * possible destination Class.  Unless you override them, standard
 61  
  * {@link Converter} instances are provided for all of the following
 62  
  * destination Classes:</p>
 63  
  * <ul>
 64  
  * <li>java.lang.BigDecimal (no default value)</li>
 65  
  * <li>java.lang.BigInteger (no default value)</li>
 66  
  * <li>boolean and java.lang.Boolean (default to false)</li>
 67  
  * <li>byte and java.lang.Byte (default to zero)</li>
 68  
  * <li>char and java.lang.Character (default to a space)</li>
 69  
  * <li>java.lang.Class (no default value)</li>
 70  
  * <li>double and java.lang.Double (default to zero)</li>
 71  
  * <li>float and java.lang.Float (default to zero)</li>
 72  
  * <li>int and java.lang.Integer (default to zero)</li>
 73  
  * <li>long and java.lang.Long (default to zero)</li>
 74  
  * <li>short and java.lang.Short (default to zero)</li>
 75  
  * <li>java.lang.String (default to null)</li>
 76  
  * <li>java.io.File (no default value)</li>
 77  
  * <li>java.net.URL (no default value)</li>
 78  
  * <li>java.sql.Date (no default value)</li>
 79  
  * <li>java.sql.Time (no default value)</li>
 80  
  * <li>java.sql.Timestamp (no default value)</li>
 81  
  * </ul>
 82  
  *
 83  
  * <p>For backwards compatibility, the standard Converters for primitive
 84  
  * types (and the corresponding wrapper classes) return a defined
 85  
  * default value when a conversion error occurs.  If you prefer to have a
 86  
  * {@link ConversionException} thrown instead, replace the standard Converter
 87  
  * instances with instances created with the zero-arguments constructor.  For
 88  
  * example, to cause the Converters for integers to throw an exception on
 89  
  * conversion errors, you could do this:</p>
 90  
  * <pre>
 91  
  *   // No-args constructor gets the version that throws exceptions
 92  
  *   Converter myConverter =
 93  
  *    new org.apache.commons.beanutils.converter.IntegerConverter();
 94  
  *   ConvertUtils.register(myConverter, Integer.TYPE);    // Native type
 95  
  *   ConvertUtils.register(myConverter, Integer.class);   // Wrapper class
 96  
  * </pre>
 97  
  * 
 98  
  * <p>
 99  
  * Converters generally treat null input as if it were invalid
 100  
  * input, ie they return their default value if one was specified when the
 101  
  * converter was constructed, and throw an exception otherwise. If you prefer
 102  
  * nulls to be preserved for converters that are converting to objects (not
 103  
  * primitives) then register a converter as above, passing a default value of
 104  
  * null to the converter constructor (and of course registering that converter
 105  
  * only for the .class target).
 106  
  * </p>
 107  
  *
 108  
  * <p>
 109  
  * When a converter is listed above as having no default value, then that
 110  
  * converter will throw an exception when passed null or an invalid value
 111  
  * as its input. In particular, by default the BigInteger and BigDecimal
 112  
  * converters have no default (and are therefore somewhat inconsistent
 113  
  * with the other numerical converters which all have zero as their default).
 114  
  * </p>
 115  
  * 
 116  
  * <p>
 117  
  * Converters that generate <i>arrays</i> of each of the primitive types are
 118  
  * also automatically configured (including String[]). When passed null
 119  
  * or invalid input, these return an empty array (not null). See class
 120  
  * AbstractArrayConverter for the supported input formats for these converters.
 121  
  * </p>
 122  
  * 
 123  
  * @author Craig R. McClanahan
 124  
  * @author Ralph Schaer
 125  
  * @author Chris Audley
 126  
  * @author James Strachan
 127  
  * @version $Revision: 745079 $ $Date: 2009-02-17 09:04:10 -0500 (Tue, 17 Feb 2009) $
 128  
  * @since 1.7
 129  
  */
 130  
 
 131  
 public class ConvertUtilsBean {
 132  
     
 133  1
     private static final Integer ZERO = new Integer(0);
 134  1
     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  498
         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  195
     private WeakFastHashMap converters = new WeakFastHashMap();
 153  
 
 154  
     /**
 155  
      * The <code>Log</code> instance for this class.
 156  
      */
 157  195
     private Log log = LogFactory.getLog(ConvertUtils.class);
 158  
 
 159  
     // ------------------------------------------------------- Constructors
 160  
 
 161  
     /** Construct a bean with standard converters registered */
 162  195
     public ConvertUtilsBean() {
 163  195
         converters.setFast(false);   
 164  195
         deregister();
 165  195
         converters.setFast(true);
 166  195
     }
 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  195
     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  0
         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  0
         defaultBoolean = (newDefaultBoolean ? Boolean.TRUE : Boolean.FALSE);
 195  0
         register(new BooleanConverter(defaultBoolean), Boolean.TYPE);
 196  0
         register(new BooleanConverter(defaultBoolean), Boolean.class);
 197  0
     }
 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  195
     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  0
         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  0
         defaultByte = new Byte(newDefaultByte);
 225  0
         register(new ByteConverter(defaultByte), Byte.TYPE);
 226  0
         register(new ByteConverter(defaultByte), Byte.class);
 227  0
     }
 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  195
     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  0
         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  0
         defaultCharacter = new Character(newDefaultCharacter);
 255  0
         register(new CharacterConverter(defaultCharacter),
 256  
                     Character.TYPE);
 257  0
         register(new CharacterConverter(defaultCharacter),
 258  
                     Character.class);
 259  0
     }
 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  195
     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  0
         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  0
         defaultDouble = new Double(newDefaultDouble);
 287  0
         register(new DoubleConverter(defaultDouble), Double.TYPE);
 288  0
         register(new DoubleConverter(defaultDouble), Double.class);
 289  0
     }
 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  195
     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  0
         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  0
         defaultFloat = new Float(newDefaultFloat);
 317  0
         register(new FloatConverter(defaultFloat), Float.TYPE);
 318  0
         register(new FloatConverter(defaultFloat), Float.class);
 319  0
     }
 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  195
     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  0
         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  0
         defaultInteger = new Integer(newDefaultInteger);
 347  0
         register(new IntegerConverter(defaultInteger), Integer.TYPE);
 348  0
         register(new IntegerConverter(defaultInteger), Integer.class);
 349  0
     }
 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  195
     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  0
         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  0
         defaultLong = new Long(newDefaultLong);
 377  0
         register(new LongConverter(defaultLong), Long.TYPE);
 378  0
         register(new LongConverter(defaultLong), Long.class);
 379  0
     }
 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  1
     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  0
         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  0
         defaultShort = new Short(newDefaultShort);
 407  0
         register(new ShortConverter(defaultShort), Short.TYPE);
 408  0
         register(new ShortConverter(defaultShort), Short.class);
 409  0
     }
 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  107
         if (value == null) {
 427  8
             return null;
 428  99
         } else if (value.getClass().isArray()) {
 429  10
             if (Array.getLength(value) < 1) {
 430  2
                 return (null);
 431  
             }
 432  8
             value = Array.get(value, 0);
 433  8
             if (value == null) {
 434  0
                 return null;
 435  
             } else {
 436  8
                 Converter converter = lookup(String.class);
 437  8
                 return ((String) converter.convert(String.class, value));
 438  
             }
 439  
         } else {
 440  89
             Converter converter = lookup(String.class);
 441  89
             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  148
         if (log.isDebugEnabled()) {
 460  0
             log.debug("Convert string '" + value + "' to class '" +
 461  
                       clazz.getName() + "'");
 462  
         }
 463  148
         Converter converter = lookup(clazz);
 464  148
         if (converter == null) {
 465  3
             converter = lookup(String.class);
 466  
         }
 467  148
         if (log.isTraceEnabled()) {
 468  0
             log.trace("  Using converter " + converter);
 469  
         }
 470  148
         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  8
         Class type = clazz;
 491  8
         if (clazz.isArray()) {
 492  7
             type = clazz.getComponentType();
 493  
         }
 494  8
         if (log.isDebugEnabled()) {
 495  0
             log.debug("Convert String[" + values.length + "] to class '" +
 496  
                       type.getName() + "[]'");
 497  
         }
 498  8
         Converter converter = lookup(type);
 499  8
         if (converter == null) {
 500  0
             converter = lookup(String.class);
 501  
         }
 502  8
         if (log.isTraceEnabled()) {
 503  0
             log.trace("  Using converter " + converter);
 504  
         }
 505  8
         Object array = Array.newInstance(type, values.length);
 506  26
         for (int i = 0; i < values.length; i++) {
 507  18
             Array.set(array, i, converter.convert(type, values[i]));
 508  
         }
 509  8
         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  221
         Class sourceType = value == null ? null : value.getClass();
 527  
 
 528  221
         if (log.isDebugEnabled()) {
 529  0
             if (value == null) {
 530  0
                 log.debug("Convert null value to type '" +
 531  
                         targetType.getName() + "'");
 532  
             } else {
 533  0
                 log.debug("Convert type '" + sourceType.getName() + "' value '" + value +
 534  
                       "' to type '" + targetType.getName() + "'");
 535  
             }
 536  
         }
 537  
 
 538  221
         Object converted = value;
 539  221
         Converter converter = lookup(sourceType, targetType);
 540  221
         if (converter != null) {
 541  215
             if (log.isTraceEnabled()) {
 542  0
                 log.trace("  Using converter " + converter);
 543  
             }
 544  215
             converted = converter.convert(targetType, value);
 545  
         }
 546  221
         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  3
             converter = lookup(String.class);
 553  3
             if (converter != null) {
 554  2
                 if (log.isTraceEnabled()) {
 555  0
                     log.trace("  Using converter " + converter);
 556  
                 }
 557  2
                 converted = converter.convert(String.class, converted);
 558  
             }
 559  
 
 560  
             // If the object still isn't a String, use toString() method
 561  3
             if (converted != null && !(converted instanceof String)) {
 562  2
                 converted = converted.toString();
 563  
             }
 564  
 
 565  
         }
 566  221
         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  356
         converters.clear();
 577  
         
 578  356
         registerPrimitives(false);
 579  356
         registerStandard(false, false);
 580  356
         registerOther(true);
 581  356
         registerArrays(false, 0);
 582  356
         register(BigDecimal.class, new BigDecimalConverter());
 583  356
         register(BigInteger.class, new BigIntegerConverter());
 584  356
     }
 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  0
         registerPrimitives(throwException);
 603  0
         registerStandard(throwException, defaultNull);
 604  0
         registerOther(throwException);
 605  0
         registerArrays(throwException, defaultArraySize);
 606  0
     }
 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  356
         register(Boolean.TYPE,   throwException ? new BooleanConverter()    : new BooleanConverter(Boolean.FALSE));
 628  356
         register(Byte.TYPE,      throwException ? new ByteConverter()       : new ByteConverter(ZERO));
 629  356
         register(Character.TYPE, throwException ? new CharacterConverter()  : new CharacterConverter(SPACE));
 630  356
         register(Double.TYPE,    throwException ? new DoubleConverter()     : new DoubleConverter(ZERO));
 631  356
         register(Float.TYPE,     throwException ? new FloatConverter()      : new FloatConverter(ZERO));
 632  356
         register(Integer.TYPE,   throwException ? new IntegerConverter()    : new IntegerConverter(ZERO));
 633  356
         register(Long.TYPE,      throwException ? new LongConverter()       : new LongConverter(ZERO));
 634  356
         register(Short.TYPE,     throwException ? new ShortConverter()      : new ShortConverter(ZERO));
 635  356
     }
 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  356
         Number     defaultNumber     = defaultNull ? null : ZERO;
 665  356
         BigDecimal bigDecDeflt       = defaultNull ? null : new BigDecimal("0.0");
 666  356
         BigInteger bigIntDeflt       = defaultNull ? null : new BigInteger("0");
 667  356
         Boolean    booleanDefault    = defaultNull ? null : Boolean.FALSE;
 668  356
         Character  charDefault       = defaultNull ? null : SPACE;
 669  356
         String     stringDefault     = defaultNull ? null : "";
 670  
 
 671  356
         register(BigDecimal.class, throwException ? new BigDecimalConverter() : new BigDecimalConverter(bigDecDeflt));
 672  356
         register(BigInteger.class, throwException ? new BigIntegerConverter() : new BigIntegerConverter(bigIntDeflt));
 673  356
         register(Boolean.class,    throwException ? new BooleanConverter()    : new BooleanConverter(booleanDefault));
 674  356
         register(Byte.class,       throwException ? new ByteConverter()       : new ByteConverter(defaultNumber));
 675  356
         register(Character.class,  throwException ? new CharacterConverter()  : new CharacterConverter(charDefault));
 676  356
         register(Double.class,     throwException ? new DoubleConverter()     : new DoubleConverter(defaultNumber));
 677  356
         register(Float.class,      throwException ? new FloatConverter()      : new FloatConverter(defaultNumber));
 678  356
         register(Integer.class,    throwException ? new IntegerConverter()    : new IntegerConverter(defaultNumber));
 679  356
         register(Long.class,       throwException ? new LongConverter()       : new LongConverter(defaultNumber));
 680  356
         register(Short.class,      throwException ? new ShortConverter()      : new ShortConverter(defaultNumber));
 681  356
         register(String.class,     throwException ? new StringConverter()     : new StringConverter(stringDefault));
 682  
         
 683  356
     }
 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  356
         register(Class.class,         throwException ? new ClassConverter()        : new ClassConverter(null));
 705  356
         register(java.util.Date.class, throwException ? new DateConverter()        : new DateConverter(null));
 706  356
         register(Calendar.class,      throwException ? new CalendarConverter()     : new CalendarConverter(null));
 707  356
         register(File.class,          throwException ? new FileConverter()         : new FileConverter(null));
 708  356
         register(java.sql.Date.class, throwException ? new SqlDateConverter()      : new SqlDateConverter(null));
 709  356
         register(java.sql.Time.class, throwException ? new SqlTimeConverter()      : new SqlTimeConverter(null));
 710  356
         register(Timestamp.class,     throwException ? new SqlTimestampConverter() : new SqlTimestampConverter(null));
 711  356
         register(URL.class,           throwException ? new URLConverter()          : new URLConverter(null));
 712  356
     }
 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  356
         registerArrayConverter(Boolean.TYPE,   new BooleanConverter(),   throwException, defaultArraySize);
 729  356
         registerArrayConverter(Byte.TYPE,      new ByteConverter(),      throwException, defaultArraySize);
 730  356
         registerArrayConverter(Character.TYPE, new CharacterConverter(), throwException, defaultArraySize);
 731  356
         registerArrayConverter(Double.TYPE,    new DoubleConverter(),    throwException, defaultArraySize);
 732  356
         registerArrayConverter(Float.TYPE,     new FloatConverter(),     throwException, defaultArraySize);
 733  356
         registerArrayConverter(Integer.TYPE,   new IntegerConverter(),   throwException, defaultArraySize);
 734  356
         registerArrayConverter(Long.TYPE,      new LongConverter(),      throwException, defaultArraySize);
 735  356
         registerArrayConverter(Short.TYPE,     new ShortConverter(),     throwException, defaultArraySize);
 736  
 
 737  
         // Standard
 738  356
         registerArrayConverter(BigDecimal.class, new BigDecimalConverter(), throwException, defaultArraySize);
 739  356
         registerArrayConverter(BigInteger.class, new BigIntegerConverter(), throwException, defaultArraySize);
 740  356
         registerArrayConverter(Boolean.class,    new BooleanConverter(),    throwException, defaultArraySize);
 741  356
         registerArrayConverter(Byte.class,       new ByteConverter(),       throwException, defaultArraySize);
 742  356
         registerArrayConverter(Character.class,  new CharacterConverter(),  throwException, defaultArraySize);
 743  356
         registerArrayConverter(Double.class,     new DoubleConverter(),     throwException, defaultArraySize);
 744  356
         registerArrayConverter(Float.class,      new FloatConverter(),      throwException, defaultArraySize);
 745  356
         registerArrayConverter(Integer.class,    new IntegerConverter(),    throwException, defaultArraySize);
 746  356
         registerArrayConverter(Long.class,       new LongConverter(),       throwException, defaultArraySize);
 747  356
         registerArrayConverter(Short.class,      new ShortConverter(),      throwException, defaultArraySize);
 748  356
         registerArrayConverter(String.class,     new StringConverter(),     throwException, defaultArraySize);
 749  
 
 750  
         // Other
 751  356
         registerArrayConverter(Class.class,          new ClassConverter(),        throwException, defaultArraySize);
 752  356
         registerArrayConverter(java.util.Date.class, new DateConverter(),         throwException, defaultArraySize);
 753  356
         registerArrayConverter(Calendar.class,       new DateConverter(),         throwException, defaultArraySize);
 754  356
         registerArrayConverter(File.class,           new FileConverter(),         throwException, defaultArraySize);
 755  356
         registerArrayConverter(java.sql.Date.class,  new SqlDateConverter(),      throwException, defaultArraySize);
 756  356
         registerArrayConverter(java.sql.Time.class,  new SqlTimeConverter(),      throwException, defaultArraySize);
 757  356
         registerArrayConverter(Timestamp.class,      new SqlTimestampConverter(), throwException, defaultArraySize);
 758  356
         registerArrayConverter(URL.class,            new URLConverter(),          throwException, defaultArraySize);
 759  
 
 760  356
     }
 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  9612
         Class arrayType = Array.newInstance(componentType, 0).getClass();
 775  9612
         Converter arrayConverter = null;
 776  9612
         if (throwException) {
 777  0
             arrayConverter = new ArrayConverter(arrayType, componentConverter);
 778  
         } else {
 779  9612
             arrayConverter = new ArrayConverter(arrayType, componentConverter, defaultArraySize);
 780  
         }
 781  9612
         register(arrayType, arrayConverter);
 782  9612
     }
 783  
 
 784  
     /** strictly for convenience since it has same parameter order as Map.put */
 785  
     private void register(Class clazz, Converter converter) {
 786  19936
         register(new ConverterFacade(converter), clazz);
 787  19936
     }
 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  3
         converters.remove(clazz);
 798  
 
 799  3
     }
 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  716
         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  221
         if (targetType == null) {
 828  0
             throw new IllegalArgumentException("Target type is missing");
 829  
         }
 830  221
         if (sourceType == null) {
 831  18
             return lookup(targetType);
 832  
         }
 833  
 
 834  203
         Converter converter = null;
 835  
         // Convert --> String 
 836  203
         if (targetType == String.class) {
 837  67
             converter = lookup(sourceType);
 838  67
             if (converter == null && (sourceType.isArray() ||
 839  
                         Collection.class.isAssignableFrom(sourceType))) {
 840  1
                 converter = lookup(String[].class);
 841  
             }
 842  67
             if (converter == null) {
 843  6
                 converter = lookup(String.class);
 844  
             }
 845  67
             return converter;
 846  
         }
 847  
 
 848  
         // Convert --> String array 
 849  136
         if (targetType == String[].class) {
 850  15
             if (sourceType.isArray() || Collection.class.isAssignableFrom(sourceType)) {
 851  9
                 converter = lookup(sourceType);
 852  
             }
 853  15
             if (converter == null) {
 854  6
                 converter = lookup(String[].class);
 855  
             }
 856  15
             return converter;
 857  
         }
 858  
 
 859  121
         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  20169
         converters.put(clazz, converter);
 874  
 
 875  20169
     }
 876  
 }