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 }