1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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 import java.util.Map;
31 import java.util.HashMap;
32 import org.apache.commons.beanutils.converters.ArrayConverter;
33 import org.apache.commons.beanutils.converters.BigDecimalConverter;
34 import org.apache.commons.beanutils.converters.BigIntegerConverter;
35 import org.apache.commons.beanutils.converters.BooleanConverter;
36 import org.apache.commons.beanutils.converters.ByteConverter;
37 import org.apache.commons.beanutils.converters.CalendarConverter;
38 import org.apache.commons.beanutils.converters.CharacterConverter;
39 import org.apache.commons.beanutils.converters.ClassConverter;
40 import org.apache.commons.beanutils.converters.ConverterFacade;
41 import org.apache.commons.beanutils.converters.DateConverter;
42 import org.apache.commons.beanutils.converters.DoubleConverter;
43 import org.apache.commons.beanutils.converters.FileConverter;
44 import org.apache.commons.beanutils.converters.FloatConverter;
45 import org.apache.commons.beanutils.converters.IntegerConverter;
46 import org.apache.commons.beanutils.converters.LongConverter;
47 import org.apache.commons.beanutils.converters.ShortConverter;
48 import org.apache.commons.beanutils.converters.SqlDateConverter;
49 import org.apache.commons.beanutils.converters.SqlTimeConverter;
50 import org.apache.commons.beanutils.converters.SqlTimestampConverter;
51 import org.apache.commons.beanutils.converters.StringConverter;
52 import org.apache.commons.beanutils.converters.URLConverter;
53 import org.apache.commons.logging.Log;
54 import org.apache.commons.logging.LogFactory;
55
56
57 /***
58 * <p>Utility methods for converting String scalar values to objects of the
59 * specified Class, String arrays to arrays of the specified Class. The
60 * actual {@link Converter} instance to be used can be registered for each
61 * possible destination Class. Unless you override them, standard
62 * {@link Converter} instances are provided for all of the following
63 * destination Classes:</p>
64 * <ul>
65 * <li>java.lang.BigDecimal (no default value)</li>
66 * <li>java.lang.BigInteger (no default value)</li>
67 * <li>boolean and java.lang.Boolean (default to false)</li>
68 * <li>byte and java.lang.Byte (default to zero)</li>
69 * <li>char and java.lang.Character (default to a space)</li>
70 * <li>java.lang.Class (no default value)</li>
71 * <li>double and java.lang.Double (default to zero)</li>
72 * <li>float and java.lang.Float (default to zero)</li>
73 * <li>int and java.lang.Integer (default to zero)</li>
74 * <li>long and java.lang.Long (default to zero)</li>
75 * <li>short and java.lang.Short (default to zero)</li>
76 * <li>java.lang.String (default to null)</li>
77 * <li>java.io.File (no default value)</li>
78 * <li>java.net.URL (no default value)</li>
79 * <li>java.sql.Date (no default value)</li>
80 * <li>java.sql.Time (no default value)</li>
81 * <li>java.sql.Timestamp (no default value)</li>
82 * </ul>
83 *
84 * <p>For backwards compatibility, the standard Converters for primitive
85 * types (and the corresponding wrapper classes) return a defined
86 * default value when a conversion error occurs. If you prefer to have a
87 * {@link ConversionException} thrown instead, replace the standard Converter
88 * instances with instances created with the zero-arguments constructor. For
89 * example, to cause the Converters for integers to throw an exception on
90 * conversion errors, you could do this:</p>
91 * <pre>
92 * // No-args constructor gets the version that throws exceptions
93 * Converter myConverter =
94 * new org.apache.commons.beanutils.converter.IntegerConverter();
95 * ConvertUtils.register(myConverter, Integer.TYPE); // Native type
96 * ConvertUtils.register(myConverter, Integer.class); // Wrapper class
97 * </pre>
98 *
99 * <p>
100 * Converters generally treat null input as if it were invalid
101 * input, ie they return their default value if one was specified when the
102 * converter was constructed, and throw an exception otherwise. If you prefer
103 * nulls to be preserved for converters that are converting to objects (not
104 * primitives) then register a converter as above, passing a default value of
105 * null to the converter constructor (and of course registering that converter
106 * only for the .class target).
107 * </p>
108 *
109 * <p>
110 * When a converter is listed above as having no default value, then that
111 * converter will throw an exception when passed null or an invalid value
112 * as its input. In particular, by default the BigInteger and BigDecimal
113 * converters have no default (and are therefore somewhat inconsistent
114 * with the other numerical converters which all have zero as their default).
115 * </p>
116 *
117 * <p>
118 * Converters that generate <i>arrays</i> of each of the primitive types are
119 * also automatically configured (including String[]). When passed null
120 * or invalid input, these return an empty array (not null). See class
121 * AbstractArrayConverter for the supported input formats for these converters.
122 * </p>
123 *
124 * @author Craig R. McClanahan
125 * @author Ralph Schaer
126 * @author Chris Audley
127 * @author James Strachan
128 * @version $Revision: 557868 $ $Date: 2007-07-20 06:22:19 +0100 (Fri, 20 Jul 2007) $
129 * @since 1.7
130 */
131
132 public class ConvertUtilsBean {
133
134 private static final Integer ZERO = new Integer(0);
135 private static final Character SPACE = new Character(' ');
136
137
138 /***
139 * Get singleton instance
140 * @return The singleton instance
141 */
142 protected static ConvertUtilsBean getInstance() {
143 return BeanUtilsBean.getInstance().getConvertUtils();
144 }
145
146
147
148
149 /***
150 * The set of {@link Converter}s that can be used to convert Strings
151 * into objects of a specified Class, keyed by the destination Class.
152 */
153 private Map converters = new HashMap();
154
155 /***
156 * The <code>Log</code> instance for this class.
157 */
158 private Log log = LogFactory.getLog(ConvertUtils.class);
159
160
161
162 /*** Construct a bean with standard converters registered */
163 public ConvertUtilsBean() {
164 deregister();
165 }
166
167
168
169 /***
170 * The default value for Boolean conversions.
171 * @deprecated Register replacement converters for Boolean.TYPE and
172 * Boolean.class instead
173 */
174 private Boolean defaultBoolean = Boolean.FALSE;
175
176 /***
177 * Gets the default value for Boolean conversions.
178 * @return The default Boolean value
179 * @deprecated Register replacement converters for Boolean.TYPE and
180 * Boolean.class instead
181 */
182 public boolean getDefaultBoolean() {
183 return (defaultBoolean.booleanValue());
184 }
185
186 /***
187 * Sets the default value for Boolean conversions.
188 * @param newDefaultBoolean The default Boolean value
189 * @deprecated Register replacement converters for Boolean.TYPE and
190 * Boolean.class instead
191 */
192 public void setDefaultBoolean(boolean newDefaultBoolean) {
193 defaultBoolean = (newDefaultBoolean ? Boolean.TRUE : Boolean.FALSE);
194 register(new BooleanConverter(defaultBoolean), Boolean.TYPE);
195 register(new BooleanConverter(defaultBoolean), Boolean.class);
196 }
197
198
199 /***
200 * The default value for Byte conversions.
201 * @deprecated Register replacement converters for Byte.TYPE and
202 * Byte.class instead
203 */
204 private Byte defaultByte = new Byte((byte) 0);
205
206 /***
207 * Gets the default value for Byte conversions.
208 * @return The default Byte value
209 * @deprecated Register replacement converters for Byte.TYPE and
210 * Byte.class instead
211 */
212 public byte getDefaultByte() {
213 return (defaultByte.byteValue());
214 }
215
216 /***
217 * Sets the default value for Byte conversions.
218 * @param newDefaultByte The default Byte value
219 * @deprecated Register replacement converters for Byte.TYPE and
220 * Byte.class instead
221 */
222 public void setDefaultByte(byte newDefaultByte) {
223 defaultByte = new Byte(newDefaultByte);
224 register(new ByteConverter(defaultByte), Byte.TYPE);
225 register(new ByteConverter(defaultByte), Byte.class);
226 }
227
228
229 /***
230 * The default value for Character conversions.
231 * @deprecated Register replacement converters for Character.TYPE and
232 * Character.class instead
233 */
234 private Character defaultCharacter = new Character(' ');
235
236 /***
237 * Gets the default value for Character conversions.
238 * @return The default Character value
239 * @deprecated Register replacement converters for Character.TYPE and
240 * Character.class instead
241 */
242 public char getDefaultCharacter() {
243 return (defaultCharacter.charValue());
244 }
245
246 /***
247 * Sets the default value for Character conversions.
248 * @param newDefaultCharacter The default Character value
249 * @deprecated Register replacement converters for Character.TYPE and
250 * Character.class instead
251 */
252 public void setDefaultCharacter(char newDefaultCharacter) {
253 defaultCharacter = new Character(newDefaultCharacter);
254 register(new CharacterConverter(defaultCharacter),
255 Character.TYPE);
256 register(new CharacterConverter(defaultCharacter),
257 Character.class);
258 }
259
260
261 /***
262 * The default value for Double conversions.
263 * @deprecated Register replacement converters for Double.TYPE and
264 * Double.class instead
265 */
266 private Double defaultDouble = new Double(0.0);
267
268 /***
269 * Gets the default value for Double conversions.
270 * @return The default Double value
271 * @deprecated Register replacement converters for Double.TYPE and
272 * Double.class instead
273 */
274 public double getDefaultDouble() {
275 return (defaultDouble.doubleValue());
276 }
277
278 /***
279 * Sets the default value for Double conversions.
280 * @param newDefaultDouble The default Double value
281 * @deprecated Register replacement converters for Double.TYPE and
282 * Double.class instead
283 */
284 public void setDefaultDouble(double newDefaultDouble) {
285 defaultDouble = new Double(newDefaultDouble);
286 register(new DoubleConverter(defaultDouble), Double.TYPE);
287 register(new DoubleConverter(defaultDouble), Double.class);
288 }
289
290
291 /***
292 * The default value for Float conversions.
293 * @deprecated Register replacement converters for Float.TYPE and
294 * Float.class instead
295 */
296 private Float defaultFloat = new Float((float) 0.0);
297
298 /***
299 * Gets the default value for Float conversions.
300 * @return The default Float value
301 * @deprecated Register replacement converters for Float.TYPE and
302 * Float.class instead
303 */
304 public float getDefaultFloat() {
305 return (defaultFloat.floatValue());
306 }
307
308 /***
309 * Sets the default value for Float conversions.
310 * @param newDefaultFloat The default Float value
311 * @deprecated Register replacement converters for Float.TYPE and
312 * Float.class instead
313 */
314 public void setDefaultFloat(float newDefaultFloat) {
315 defaultFloat = new Float(newDefaultFloat);
316 register(new FloatConverter(defaultFloat), Float.TYPE);
317 register(new FloatConverter(defaultFloat), Float.class);
318 }
319
320
321 /***
322 * The default value for Integer conversions.
323 * @deprecated Register replacement converters for Integer.TYPE and
324 * Integer.class instead
325 */
326 private Integer defaultInteger = new Integer(0);
327
328 /***
329 * Gets the default value for Integer conversions.
330 * @return The default Integer value
331 * @deprecated Register replacement converters for Integer.TYPE and
332 * Integer.class instead
333 */
334 public int getDefaultInteger() {
335 return (defaultInteger.intValue());
336 }
337
338 /***
339 * Sets the default value for Integer conversions.
340 * @param newDefaultInteger The default Integer value
341 * @deprecated Register replacement converters for Integer.TYPE and
342 * Integer.class instead
343 */
344 public void setDefaultInteger(int newDefaultInteger) {
345 defaultInteger = new Integer(newDefaultInteger);
346 register(new IntegerConverter(defaultInteger), Integer.TYPE);
347 register(new IntegerConverter(defaultInteger), Integer.class);
348 }
349
350
351 /***
352 * The default value for Long conversions.
353 * @deprecated Register replacement converters for Long.TYPE and
354 * Long.class instead
355 */
356 private Long defaultLong = new Long(0);
357
358 /***
359 * Gets the default value for Long conversions.
360 * @return The default Long value
361 * @deprecated Register replacement converters for Long.TYPE and
362 * Long.class instead
363 */
364 public long getDefaultLong() {
365 return (defaultLong.longValue());
366 }
367
368 /***
369 * Sets the default value for Long conversions.
370 * @param newDefaultLong The default Long value
371 * @deprecated Register replacement converters for Long.TYPE and
372 * Long.class instead
373 */
374 public void setDefaultLong(long newDefaultLong) {
375 defaultLong = new Long(newDefaultLong);
376 register(new LongConverter(defaultLong), Long.TYPE);
377 register(new LongConverter(defaultLong), Long.class);
378 }
379
380
381 /***
382 * The default value for Short conversions.
383 * @deprecated Register replacement converters for Short.TYPE and
384 * Short.class instead
385 */
386 private static Short defaultShort = new Short((short) 0);
387
388 /***
389 * Gets the default value for Short conversions.
390 * @return The default Short value
391 * @deprecated Register replacement converters for Short.TYPE and
392 * Short.class instead
393 */
394 public short getDefaultShort() {
395 return (defaultShort.shortValue());
396 }
397
398 /***
399 * Sets the default value for Short conversions.
400 * @param newDefaultShort The default Short value
401 * @deprecated Register replacement converters for Short.TYPE and
402 * Short.class instead
403 */
404 public void setDefaultShort(short newDefaultShort) {
405 defaultShort = new Short(newDefaultShort);
406 register(new ShortConverter(defaultShort), Short.TYPE);
407 register(new ShortConverter(defaultShort), Short.class);
408 }
409
410
411
412 /***
413 * Convert the specified value into a String. If the specified value
414 * is an array, the first element (converted to a String) will be
415 * returned. The registered {@link Converter} for the
416 * <code>java.lang.String</code> class will be used, which allows
417 * applications to customize Object->String conversions (the default
418 * implementation simply uses toString()).
419 *
420 * @param value Value to be converted (may be null)
421 * @return The converted String value
422 */
423 public String convert(Object value) {
424
425 if (value == null) {
426 return ((String) null);
427 } else if (value.getClass().isArray()) {
428 if (Array.getLength(value) < 1) {
429 return (null);
430 }
431 value = Array.get(value, 0);
432 if (value == null) {
433 return ((String) null);
434 } else {
435 Converter converter = lookup(String.class);
436 return ((String) converter.convert(String.class, value));
437 }
438 } else {
439 Converter converter = lookup(String.class);
440 return ((String) converter.convert(String.class, value));
441 }
442
443 }
444
445
446 /***
447 * Convert the specified value to an object of the specified class (if
448 * possible). Otherwise, return a String representation of the value.
449 *
450 * @param value Value to be converted (may be null)
451 * @param clazz Java class to be converted to
452 * @return The converted value
453 *
454 * @exception ConversionException if thrown by an underlying Converter
455 */
456 public Object convert(String value, Class clazz) {
457
458 if (log.isDebugEnabled()) {
459 log.debug("Convert string '" + value + "' to class '" +
460 clazz.getName() + "'");
461 }
462 Converter converter = lookup(clazz);
463 if (converter == null) {
464 converter = lookup(String.class);
465 }
466 if (log.isTraceEnabled()) {
467 log.trace(" Using converter " + converter);
468 }
469 return (converter.convert(clazz, value));
470
471 }
472
473
474 /***
475 * Convert an array of specified values to an array of objects of the
476 * specified class (if possible). If the specified Java class is itself
477 * an array class, this class will be the type of the returned value.
478 * Otherwise, an array will be constructed whose component type is the
479 * specified class.
480 *
481 * @param values Array of values to be converted
482 * @param clazz Java array or element class to be converted to
483 * @return The converted value
484 *
485 * @exception ConversionException if thrown by an underlying Converter
486 */
487 public Object convert(String[] values, Class clazz) {
488
489 Class type = clazz;
490 if (clazz.isArray()) {
491 type = clazz.getComponentType();
492 }
493 if (log.isDebugEnabled()) {
494 log.debug("Convert String[" + values.length + "] to class '" +
495 type.getName() + "[]'");
496 }
497 Converter converter = lookup(type);
498 if (converter == null) {
499 converter = lookup(String.class);
500 }
501 if (log.isTraceEnabled()) {
502 log.trace(" Using converter " + converter);
503 }
504 Object array = Array.newInstance(type, values.length);
505 for (int i = 0; i < values.length; i++) {
506 Array.set(array, i, converter.convert(type, values[i]));
507 }
508 return (array);
509
510 }
511
512
513 /***
514 * <p>Convert the value to an object of the specified class (if
515 * possible).</p>
516 *
517 * @param value Value to be converted (may be null)
518 * @param targetType Class of the value to be converted to
519 * @return The converted value
520 *
521 * @exception ConversionException if thrown by an underlying Converter
522 */
523 public Object convert(Object value, Class targetType) {
524
525 Class sourceType = value == null ? null : value.getClass();
526
527 if (log.isDebugEnabled()) {
528 if (value == null) {
529 log.debug("Convert null value to type '" +
530 targetType.getName() + "'");
531 } else {
532 log.debug("Convert type '" + sourceType.getName() + "' value '" + value +
533 "' to type '" + targetType.getName() + "'");
534 }
535 }
536
537 Object converted = value;
538 Converter converter = lookup(sourceType, targetType);
539 if (converter != null) {
540 if (log.isTraceEnabled()) {
541 log.trace(" Using converter " + converter);
542 }
543 converted = converter.convert(targetType, value);
544 }
545 if (targetType == String.class && converted != null &&
546 !(converted instanceof String)) {
547
548
549
550
551 converter = lookup(String.class);
552 if (converter != null) {
553 if (log.isTraceEnabled()) {
554 log.trace(" Using converter " + converter);
555 }
556 converted = converter.convert(String.class, converted);
557 }
558
559
560 if (converted != null && !(converted instanceof String)) {
561 converted = converted.toString();
562 }
563
564 }
565 return converted;
566
567 }
568
569 /***
570 * Remove all registered {@link Converter}s, and re-establish the
571 * standard Converters.
572 */
573 public void deregister() {
574
575 converters.clear();
576
577 registerPrimitives(false);
578 registerStandard(false, false);
579 registerOther(true);
580 registerArrays(false, 0);
581 register(BigDecimal.class, new BigDecimalConverter());
582 register(BigInteger.class, new BigIntegerConverter());
583 }
584
585 /***
586 * Register the provided converters with the specified defaults.
587 *
588 * @param throwException <code>true</code> if the converters should
589 * throw an exception when a conversion error occurs, otherwise <code>
590 * <code>false</code> if a default value should be used.
591 * @param defaultNull <code>true</code>if the <i>standard</i> converters
592 * (see {@link ConvertUtilsBean#registerStandard(boolean, boolean)})
593 * should use a default value of <code>null</code>, otherwise <code>false</code>.
594 * N.B. This values is ignored if <code>throwException</code> is <code>true</code>
595 * @param defaultArraySize The size of the default array value for array converters
596 * (N.B. This values is ignored if <code>throwException</code> is <code>true</code>).
597 * Specifying a value less than zero causes a <code>null<code> value to be used for
598 * the default.
599 */
600 public void register(boolean throwException, boolean defaultNull, int defaultArraySize) {
601 registerPrimitives(throwException);
602 registerStandard(throwException, defaultNull);
603 registerOther(throwException);
604 registerArrays(throwException, defaultArraySize);
605 }
606
607 /***
608 * Register the converters for primitive types.
609 * </p>
610 * This method registers the following converters:
611 * <ul>
612 * <li><code>Boolean.TYPE</code> - {@link BooleanConverter}</li>
613 * <li><code>Byte.TYPE</code> - {@link ByteConverter}</li>
614 * <li><code>Character.TYPE</code> - {@link CharacterConverter}</li>
615 * <li><code>Double.TYPE</code> - {@link DoubleConverter}</li>
616 * <li><code>Float.TYPE</code> - {@link FloatConverter}</li>
617 * <li><code>Integer.TYPE</code> - {@link IntegerConverter}</li>
618 * <li><code>Long.TYPE</code> - {@link LongConverter}</li>
619 * <li><code>Short.TYPE</code> - {@link ShortConverter}</li>
620 * </ul>
621 * @param throwException <code>true</code> if the converters should
622 * throw an exception when a conversion error occurs, otherwise <code>
623 * <code>false</code> if a default value should be used.
624 */
625 private void registerPrimitives(boolean throwException) {
626 register(Boolean.TYPE, throwException ? new BooleanConverter() : new BooleanConverter(Boolean.FALSE));
627 register(Byte.TYPE, throwException ? new ByteConverter() : new ByteConverter(ZERO));
628 register(Character.TYPE, throwException ? new CharacterConverter() : new CharacterConverter(SPACE));
629 register(Double.TYPE, throwException ? new DoubleConverter() : new DoubleConverter(ZERO));
630 register(Float.TYPE, throwException ? new FloatConverter() : new FloatConverter(ZERO));
631 register(Integer.TYPE, throwException ? new IntegerConverter() : new IntegerConverter(ZERO));
632 register(Long.TYPE, throwException ? new LongConverter() : new LongConverter(ZERO));
633 register(Short.TYPE, throwException ? new ShortConverter() : new ShortConverter(ZERO));
634 }
635
636 /***
637 * Register the converters for standard types.
638 * </p>
639 * This method registers the following converters:
640 * <ul>
641 * <li><code>BigDecimal.class</code> - {@link BigDecimalConverter}</li>
642 * <li><code>BigInteger.class</code> - {@link BigIntegerConverter}</li>
643 * <li><code>Boolean.class</code> - {@link BooleanConverter}</li>
644 * <li><code>Byte.class</code> - {@link ByteConverter}</li>
645 * <li><code>Character.class</code> - {@link CharacterConverter}</li>
646 * <li><code>Double.class</code> - {@link DoubleConverter}</li>
647 * <li><code>Float.class</code> - {@link FloatConverter}</li>
648 * <li><code>Integer.class</code> - {@link IntegerConverter}</li>
649 * <li><code>Long.class</code> - {@link LongConverter}</li>
650 * <li><code>Short.class</code> - {@link ShortConverter}</li>
651 * <li><code>String.class</code> - {@link StringConverter}</li>
652 * </ul>
653 * @param throwException <code>true</code> if the converters should
654 * throw an exception when a conversion error occurs, otherwise <code>
655 * <code>false</code> if a default value should be used.
656 * @param defaultNull <code>true</code>if the <i>standard</i> converters
657 * (see {@link ConvertUtilsBean#registerStandard(boolean, boolean)})
658 * should use a default value of <code>null</code>, otherwise <code>false</code>.
659 * N.B. This values is ignored if <code>throwException</code> is <code>true</code>
660 */
661 private void registerStandard(boolean throwException, boolean defaultNull) {
662
663 Number defaultNumber = defaultNull ? null : ZERO;
664 BigDecimal bigDecDeflt = defaultNull ? null : new BigDecimal("0.0");
665 BigInteger bigIntDeflt = defaultNull ? null : new BigInteger("0");
666 Boolean booleanDefault = defaultNull ? null : Boolean.FALSE;
667 Character charDefault = defaultNull ? null : SPACE;
668 String stringDefault = defaultNull ? null : "";
669
670 register(BigDecimal.class, throwException ? new BigDecimalConverter() : new BigDecimalConverter(bigDecDeflt));
671 register(BigInteger.class, throwException ? new BigIntegerConverter() : new BigIntegerConverter(bigIntDeflt));
672 register(Boolean.class, throwException ? new BooleanConverter() : new BooleanConverter(booleanDefault));
673 register(Byte.class, throwException ? new ByteConverter() : new ByteConverter(defaultNumber));
674 register(Character.class, throwException ? new CharacterConverter() : new CharacterConverter(charDefault));
675 register(Double.class, throwException ? new DoubleConverter() : new DoubleConverter(defaultNumber));
676 register(Float.class, throwException ? new FloatConverter() : new FloatConverter(defaultNumber));
677 register(Integer.class, throwException ? new IntegerConverter() : new IntegerConverter(defaultNumber));
678 register(Long.class, throwException ? new LongConverter() : new LongConverter(defaultNumber));
679 register(Short.class, throwException ? new ShortConverter() : new ShortConverter(defaultNumber));
680 register(String.class, throwException ? new StringConverter() : new StringConverter(stringDefault));
681
682 }
683
684 /***
685 * Register the converters for other types.
686 * </p>
687 * This method registers the following converters:
688 * <ul>
689 * <li><code>Class.class</code> - {@link ClassConverter}</li>
690 * <li><code>java.util.Date.class</code> - {@link DateConverter}</li>
691 * <li><code>java.util.Calendar.class</code> - {@link CalendarConverter}</li>
692 * <li><code>File.class</code> - {@link FileConverter}</li>
693 * <li><code>java.sql.Date.class</code> - {@link SqlDateConverter}</li>
694 * <li><code>java.sql.Time.class</code> - {@link SqlTimeConverter}</li>
695 * <li><code>java.sql.Timestamp.class</code> - {@link SqlTimestampConverter}</li>
696 * <li><code>URL.class</code> - {@link URLConverter}</li>
697 * </ul>
698 * @param throwException <code>true</code> if the converters should
699 * throw an exception when a conversion error occurs, otherwise <code>
700 * <code>false</code> if a default value should be used.
701 */
702 private void registerOther(boolean throwException) {
703 register(Class.class, throwException ? new ClassConverter() : new ClassConverter(null));
704 register(java.util.Date.class, throwException ? new DateConverter() : new DateConverter(null));
705 register(Calendar.class, throwException ? new CalendarConverter() : new CalendarConverter(null));
706 register(File.class, throwException ? new FileConverter() : new FileConverter(null));
707 register(java.sql.Date.class, throwException ? new SqlDateConverter() : new SqlDateConverter(null));
708 register(java.sql.Time.class, throwException ? new SqlTimeConverter() : new SqlTimeConverter(null));
709 register(Timestamp.class, throwException ? new SqlTimestampConverter() : new SqlTimestampConverter(null));
710 register(URL.class, throwException ? new URLConverter() : new URLConverter(null));
711 }
712
713 /***
714 * Register array converters.
715 *
716 * @param throwException <code>true</code> if the converters should
717 * throw an exception when a conversion error occurs, otherwise <code>
718 * <code>false</code> if a default value should be used.
719 * @param defaultArraySize The size of the default array value for array converters
720 * (N.B. This values is ignored if <code>throwException</code> is <code>true</code>).
721 * Specifying a value less than zero causes a <code>null<code> value to be used for
722 * the default.
723 */
724 private void registerArrays(boolean throwException, int defaultArraySize) {
725
726
727 registerArrayConverter(Boolean.TYPE, new BooleanConverter(), throwException, defaultArraySize);
728 registerArrayConverter(Byte.TYPE, new ByteConverter(), throwException, defaultArraySize);
729 registerArrayConverter(Character.TYPE, new CharacterConverter(), throwException, defaultArraySize);
730 registerArrayConverter(Double.TYPE, new DoubleConverter(), throwException, defaultArraySize);
731 registerArrayConverter(Float.TYPE, new FloatConverter(), throwException, defaultArraySize);
732 registerArrayConverter(Integer.TYPE, new IntegerConverter(), throwException, defaultArraySize);
733 registerArrayConverter(Long.TYPE, new LongConverter(), throwException, defaultArraySize);
734 registerArrayConverter(Short.TYPE, new ShortConverter(), throwException, defaultArraySize);
735
736
737 registerArrayConverter(BigDecimal.class, new BigDecimalConverter(), throwException, defaultArraySize);
738 registerArrayConverter(BigInteger.class, new BigIntegerConverter(), throwException, defaultArraySize);
739 registerArrayConverter(Boolean.class, new BooleanConverter(), throwException, defaultArraySize);
740 registerArrayConverter(Byte.class, new ByteConverter(), throwException, defaultArraySize);
741 registerArrayConverter(Character.class, new CharacterConverter(), throwException, defaultArraySize);
742 registerArrayConverter(Double.class, new DoubleConverter(), throwException, defaultArraySize);
743 registerArrayConverter(Float.class, new FloatConverter(), throwException, defaultArraySize);
744 registerArrayConverter(Integer.class, new IntegerConverter(), throwException, defaultArraySize);
745 registerArrayConverter(Long.class, new LongConverter(), throwException, defaultArraySize);
746 registerArrayConverter(Short.class, new ShortConverter(), throwException, defaultArraySize);
747 registerArrayConverter(String.class, new StringConverter(), throwException, defaultArraySize);
748
749
750 registerArrayConverter(Class.class, new ClassConverter(), throwException, defaultArraySize);
751 registerArrayConverter(java.util.Date.class, new DateConverter(), throwException, defaultArraySize);
752 registerArrayConverter(Calendar.class, new DateConverter(), throwException, defaultArraySize);
753 registerArrayConverter(File.class, new FileConverter(), throwException, defaultArraySize);
754 registerArrayConverter(java.sql.Date.class, new SqlDateConverter(), throwException, defaultArraySize);
755 registerArrayConverter(java.sql.Time.class, new SqlTimeConverter(), throwException, defaultArraySize);
756 registerArrayConverter(Timestamp.class, new SqlTimestampConverter(), throwException, defaultArraySize);
757 registerArrayConverter(URL.class, new URLConverter(), throwException, defaultArraySize);
758
759 }
760
761 /***
762 * Register a new ArrayConverter with the specified element delegate converter
763 * that returns a default array of the specified size in the event of conversion errors.
764 *
765 * @param componentType The component type of the array
766 * @param componentConverter The converter to delegate to for the array elements
767 * @param throwException Whether a conversion exception should be thrown or a default
768 * value used in the event of a conversion error
769 * @param defaultArraySize The size of the default array
770 */
771 private void registerArrayConverter(Class componentType, Converter componentConverter,
772 boolean throwException, int defaultArraySize) {
773 Class arrayType = Array.newInstance(componentType, 0).getClass();
774 Converter arrayConverter = null;
775 if (throwException) {
776 arrayConverter = new ArrayConverter(arrayType, componentConverter);
777 } else {
778 arrayConverter = new ArrayConverter(arrayType, componentConverter, defaultArraySize);
779 }
780 register(arrayType, arrayConverter);
781 }
782
783 /*** strictly for convenience since it has same parameter order as Map.put */
784 private void register(Class clazz, Converter converter) {
785 register(new ConverterFacade(converter), clazz);
786 }
787
788 /***
789 * Remove any registered {@link Converter} for the specified destination
790 * <code>Class</code>.
791 *
792 * @param clazz Class for which to remove a registered Converter
793 */
794 public void deregister(Class clazz) {
795
796 converters.remove(clazz);
797
798 }
799
800
801 /***
802 * Look up and return any registered {@link Converter} for the specified
803 * destination class; if there is no registered Converter, return
804 * <code>null</code>.
805 *
806 * @param clazz Class for which to return a registered Converter
807 * @return The registered {@link Converter} or <code>null</code> if not found
808 */
809 public Converter lookup(Class clazz) {
810
811 return ((Converter) converters.get(clazz));
812
813 }
814
815 /***
816 * Look up and return any registered {@link Converter} for the specified
817 * source and destination class; if there is no registered Converter,
818 * return <code>null</code>.
819 *
820 * @param sourceType Class of the value being converted
821 * @param targetType Class of the value to be converted to
822 * @return The registered {@link Converter} or <code>null</code> if not found
823 */
824 public Converter lookup(Class sourceType, Class targetType) {
825
826 if (targetType == null) {
827 throw new IllegalArgumentException("Target type is missing");
828 }
829 if (sourceType == null) {
830 return lookup(targetType);
831 }
832
833 Converter converter = null;
834
835 if (targetType == String.class) {
836 converter = lookup(sourceType);
837 if (converter == null && (sourceType.isArray() ||
838 Collection.class.isAssignableFrom(sourceType))) {
839 converter = lookup(String[].class);
840 }
841 if (converter == null) {
842 converter = lookup(String.class);
843 }
844 return converter;
845 }
846
847
848 if (targetType == String[].class) {
849 if (sourceType.isArray() || Collection.class.isAssignableFrom(sourceType)) {
850 converter = lookup(sourceType);
851 }
852 if (converter == null) {
853 converter = lookup(String[].class);
854 }
855 return converter;
856 }
857
858 return lookup(targetType);
859
860 }
861
862 /***
863 * Register a custom {@link Converter} for the specified destination
864 * <code>Class</code>, replacing any previously registered Converter.
865 *
866 * @param converter Converter to be registered
867 * @param clazz Destination class for conversions performed by this
868 * Converter
869 */
870 public void register(Converter converter, Class clazz) {
871
872 converters.put(clazz, converter);
873
874 }
875 }