Clover Coverage Report - Commons BeanUtils 1.8.3-kuali-SNAPSHOT
Coverage timestamp: Thu Jun 9 2011 14:31:17 EDT
../../../../../img/srcFileCovDistChart8.png 43% of files have more coverage
182   628   78   9.1
94   304   0.43   20
20     3.9  
1    
 
  DateTimeConverter       Line # 79 182 0% 78 70 76.4% 0.7635135
 
  (138)
 
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    package org.apache.commons.beanutils.converters;
18   
19    import java.util.Date;
20    import java.util.Locale;
21    import java.util.Calendar;
22    import java.util.TimeZone;
23    import java.text.DateFormat;
24    import java.text.SimpleDateFormat;
25    import java.text.ParsePosition;
26    import org.apache.commons.beanutils.ConversionException;
27   
28    /**
29    * {@link org.apache.commons.beanutils.Converter} implementaion
30    * that handles conversion to and from <b>date/time</b> objects.
31    * <p>
32    * This implementation handles conversion for the following
33    * <i>date/time</i> types.
34    * <ul>
35    * <li><code>java.util.Date</code></li>
36    * <li><code>java.util.Calendar</code></li>
37    * <li><code>java.sql.Date</code></li>
38    * <li><code>java.sql.Time</code></li>
39    * <li><code>java.sql.Timestamp</code></li>
40    * </ul>
41    *
42    * <h3>String Conversions (to and from)</h3>
43    * This class provides a number of ways in which date/time
44    * conversions to/from Strings can be achieved:
45    * <ul>
46    * <li>Using the SHORT date format for the default Locale, configure using:</li>
47    * <ul>
48    * <li><code>setUseLocaleFormat(true)</code></li>
49    * </ul>
50    * <li>Using the SHORT date format for a specified Locale, configure using:</li>
51    * <ul>
52    * <li><code>setLocale(Locale)</code></li>
53    * </ul>
54    * <li>Using the specified date pattern(s) for the default Locale, configure using:</li>
55    * <ul>
56    * <li>Either <code>setPattern(String)</code> or
57    * <code>setPatterns(String[])</code></li>
58    * </ul>
59    * <li>Using the specified date pattern(s) for a specified Locale, configure using:</li>
60    * <ul>
61    * <li><code>setPattern(String)</code> or
62    * <code>setPatterns(String[]) and...</code></li>
63    * <li><code>setLocale(Locale)</code></li>
64    * </ul>
65    * <li>If none of the above are configured the
66    * <code>toDate(String)</code> method is used to convert
67    * from String to Date and the Dates's
68    * <code>toString()</code> method used to convert from
69    * Date to String.</li>
70    * </ul>
71    *
72    * <p>
73    * The <b>Time Zone</b> to use with the date format can be specified
74    * using the <code>setTimeZone()</code> method.
75    *
76    * @version $Revision: 640131 $ $Date: 2008-03-22 22:10:31 -0400 (Sat, 22 Mar 2008) $
77    * @since 1.8.0
78    */
 
79    public abstract class DateTimeConverter extends AbstractConverter {
80   
81    private String[] patterns;
82    private String displayPatterns;
83    private Locale locale;
84    private TimeZone timeZone;
85    private boolean useLocaleFormat;
86   
87   
88    // ----------------------------------------------------------- Constructors
89   
90    /**
91    * Construct a Date/Time <i>Converter</i> that throws a
92    * <code>ConversionException</code> if an error occurs.
93    */
 
94  3969 toggle public DateTimeConverter() {
95  3969 super();
96    }
97   
98    /**
99    * Construct a Date/Time <i>Converter</i> that returns a default
100    * value if an error occurs.
101    *
102    * @param defaultValue The default value to be returned
103    * if the value to be converted is missing or an error
104    * occurs converting the value.
105    */
 
106  120 toggle public DateTimeConverter(Object defaultValue) {
107  120 super(defaultValue);
108    }
109   
110   
111    // --------------------------------------------------------- Public Methods
112   
113    /**
114    * Indicate whether conversion should use a format/pattern or not.
115    *
116    * @param useLocaleFormat <code>true</code> if the format
117    * for the locale should be used, otherwise <code>false</code>
118    */
 
119  263 toggle public void setUseLocaleFormat(boolean useLocaleFormat) {
120  263 this.useLocaleFormat = useLocaleFormat;
121    }
122   
123    /**
124    * Return the Time Zone to use when converting dates
125    * (or <code>null</code> if none specified.
126    *
127    * @return The Time Zone.
128    */
 
129  0 toggle public TimeZone getTimeZone() {
130  0 return timeZone;
131    }
132   
133    /**
134    * Set the Time Zone to use when converting dates.
135    *
136    * @param timeZone The Time Zone.
137    */
 
138  0 toggle public void setTimeZone(TimeZone timeZone) {
139  0 this.timeZone = timeZone;
140    }
141   
142    /**
143    * Return the Locale for the <i>Converter</i>
144    * (or <code>null</code> if none specified).
145    *
146    * @return The locale to use for conversion
147    */
 
148  0 toggle public Locale getLocale() {
149  0 return locale;
150    }
151   
152    /**
153    * Set the Locale for the <i>Converter</i>.
154    *
155    * @param locale The Locale.
156    */
 
157  112 toggle public void setLocale(Locale locale) {
158  112 this.locale = locale;
159  112 setUseLocaleFormat(true);
160    }
161   
162    /**
163    * Set a date format pattern to use to convert
164    * dates to/from a <code>java.lang.String</code>.
165    *
166    * @see SimpleDateFormat
167    * @param pattern The format pattern.
168    */
 
169  135 toggle public void setPattern(String pattern) {
170  135 setPatterns(new String[] {pattern});
171    }
172   
173    /**
174    * Return the date format patterns used to convert
175    * dates to/from a <code>java.lang.String</code>
176    * (or <code>null</code> if none specified).
177    *
178    * @see SimpleDateFormat
179    * @return Array of format patterns.
180    */
 
181  0 toggle public String[] getPatterns() {
182  0 return patterns;
183    }
184   
185    /**
186    * Set the date format patterns to use to convert
187    * dates to/from a <code>java.lang.String</code>.
188    *
189    * @see SimpleDateFormat
190    * @param patterns Array of format patterns.
191    */
 
192  140 toggle public void setPatterns(String[] patterns) {
193  140 this.patterns = patterns;
194  140 if (patterns != null && patterns.length > 1) {
195  5 StringBuffer buffer = new StringBuffer();
196  15 for (int i = 0; i < patterns.length; i++) {
197  10 if (i > 0) {
198  5 buffer.append(", ");
199    }
200  10 buffer.append(patterns[i]);
201    }
202  5 displayPatterns = buffer.toString();
203    }
204  140 setUseLocaleFormat(true);
205    }
206   
207    // ------------------------------------------------------ Protected Methods
208   
209    /**
210    * Convert an input Date/Calendar object into a String.
211    * <p>
212    * <b>N.B.</b>If the converter has been configured to with
213    * one or more patterns (using <code>setPatterns()</code>), then
214    * the first pattern will be used to format the date into a String.
215    * Otherwise the default <code>DateFormat</code> for the default locale
216    * (and <i>style</i> if configured) will be used.
217    *
218    * @param value The input value to be converted
219    * @return the converted String value.
220    * @throws Throwable if an error occurs converting to a String
221    */
 
222  47 toggle protected String convertToString(Object value) throws Throwable {
223   
224  47 Date date = null;
225  47 if (value instanceof Date) {
226  35 date = (Date)value;
227  12 } else if (value instanceof Calendar) {
228  6 date = ((Calendar)value).getTime();
229  6 } else if (value instanceof Long) {
230  0 date = new Date(((Long)value).longValue());
231    }
232   
233  47 String result = null;
234  47 if (useLocaleFormat && date != null) {
235  40 DateFormat format = null;
236  40 if (patterns != null && patterns.length > 0) {
237  39 format = getFormat(patterns[0]);
238    } else {
239  1 format = getFormat(locale, timeZone);
240    }
241  40 logFormat("Formatting", format);
242  40 result = format.format(date);
243  40 if (log().isDebugEnabled()) {
244  0 log().debug(" Converted to String using format '" + result + "'");
245    }
246    } else {
247  7 result = value.toString();
248  7 if (log().isDebugEnabled()) {
249  0 log().debug(" Converted to String using toString() '" + result + "'");
250    }
251    }
252  47 return result;
253    }
254   
255    /**
256    * Convert the input object into a Date object of the
257    * specified type.
258    * <p>
259    * This method handles conversions between the following
260    * types:
261    * <ul>
262    * <li><code>java.util.Date</code></li>
263    * <li><code>java.util.Calendar</code></li>
264    * <li><code>java.sql.Date</code></li>
265    * <li><code>java.sql.Time</code></li>
266    * <li><code>java.sql.Timestamp</code></li>
267    * </ul>
268    *
269    * It also handles conversion from a <code>String</code> to
270    * any of the above types.
271    * <p>
272    *
273    * For <code>String</code> conversion, if the converter has been configured
274    * with one or more patterns (using <code>setPatterns()</code>), then
275    * the conversion is attempted with each of the specified patterns.
276    * Otherwise the default <code>DateFormat</code> for the default locale
277    * (and <i>style</i> if configured) will be used.
278    *
279    * @param targetType Data type to which this value should be converted.
280    * @param value The input value to be converted.
281    * @return The converted value.
282    * @throws Exception if conversion cannot be performed successfully
283    */
 
284  227 toggle protected Object convertToType(Class targetType, Object value) throws Exception {
285   
286  227 Class sourceType = value.getClass();
287   
288    // Handle java.sql.Timestamp
289  227 if (value instanceof java.sql.Timestamp) {
290   
291    // ---------------------- JDK 1.3 Fix ----------------------
292    // N.B. Prior to JDK 1.4 the Timestamp's getTime() method
293    // didn't include the milliseconds. The following code
294    // ensures it works consistently accross JDK versions
295  8 java.sql.Timestamp timestamp = (java.sql.Timestamp)value;
296  8 long timeInMillis = ((timestamp.getTime() / 1000) * 1000);
297  8 timeInMillis += timestamp.getNanos() / 1000000;
298    // ---------------------- JDK 1.3 Fix ----------------------
299  8 return toDate(targetType, timeInMillis);
300    }
301   
302    // Handle Date (includes java.sql.Date & java.sql.Time)
303  219 if (value instanceof Date) {
304  29 Date date = (Date)value;
305  29 return toDate(targetType, date.getTime());
306    }
307   
308    // Handle Calendar
309  190 if (value instanceof Calendar) {
310  21 Calendar calendar = (Calendar)value;
311  21 return toDate(targetType, calendar.getTime().getTime());
312    }
313   
314    // Handle Long
315  169 if (value instanceof Long) {
316  0 Long longObj = (Long)value;
317  0 return toDate(targetType, longObj.longValue());
318    }
319   
320    // Convert all other types to String & handle
321  169 String stringValue = value.toString().trim();
322  169 if (stringValue.length() == 0) {
323  20 return handleMissing(targetType);
324    }
325   
326    // Parse the Date/Time
327  149 if (useLocaleFormat) {
328  136 Calendar calendar = null;
329  136 if (patterns != null && patterns.length > 0) {
330  109 calendar = parse(sourceType, targetType, stringValue);
331    } else {
332  27 DateFormat format = getFormat(locale, timeZone);
333  27 calendar = parse(sourceType, targetType, stringValue, format);
334    }
335  40 if (Calendar.class.isAssignableFrom(targetType)) {
336  7 return calendar;
337    } else {
338  33 return toDate(targetType, calendar.getTime().getTime());
339    }
340    }
341   
342    // Default String conversion
343  13 return toDate(targetType, stringValue);
344   
345    }
346   
347    /**
348    * Convert a long value to the specified Date type for this
349    * <i>Converter</i>.
350    * <p>
351    *
352    * This method handles conversion to the following types:
353    * <ul>
354    * <li><code>java.util.Date</code></li>
355    * <li><code>java.util.Calendar</code></li>
356    * <li><code>java.sql.Date</code></li>
357    * <li><code>java.sql.Time</code></li>
358    * <li><code>java.sql.Timestamp</code></li>
359    * </ul>
360    *
361    * @param type The Date type to convert to
362    * @param value The long value to convert.
363    * @return The converted date value.
364    */
 
365  91 toggle private Object toDate(Class type, long value) {
366   
367    // java.util.Date
368  91 if (type.equals(Date.class)) {
369  23 return new Date(value);
370    }
371   
372    // java.sql.Date
373  68 if (type.equals(java.sql.Date.class)) {
374  15 return new java.sql.Date(value);
375    }
376   
377    // java.sql.Time
378  53 if (type.equals(java.sql.Time.class)) {
379  16 return new java.sql.Time(value);
380    }
381   
382    // java.sql.Timestamp
383  37 if (type.equals(java.sql.Timestamp.class)) {
384  15 return new java.sql.Timestamp(value);
385    }
386   
387    // java.util.Calendar
388  22 if (type.equals(Calendar.class)) {
389  17 Calendar calendar = null;
390  17 if (locale == null && timeZone == null) {
391  17 calendar = Calendar.getInstance();
392  0 } else if (locale == null) {
393  0 calendar = Calendar.getInstance(timeZone);
394  0 } else if (timeZone == null) {
395  0 calendar = Calendar.getInstance(locale);
396    } else {
397  0 calendar = Calendar.getInstance(timeZone, locale);
398    }
399  17 calendar.setTime(new Date(value));
400  17 calendar.setLenient(false);
401  17 return calendar;
402    }
403   
404  5 String msg = toString(getClass()) + " cannot handle conversion to '"
405    + toString(type) + "'";
406  5 if (log().isWarnEnabled()) {
407  5 log().warn(" " + msg);
408    }
409  5 throw new ConversionException(msg);
410    }
411   
412    /**
413    * Default String to Date conversion.
414    * <p>
415    * This method handles conversion from a String to the following types:
416    * <ul>
417    * <li><code>java.sql.Date</code></li>
418    * <li><code>java.sql.Time</code></li>
419    * <li><code>java.sql.Timestamp</code></li>
420    * </ul>
421    * <p>
422    * <strong>N.B.</strong> No default String conversion
423    * mechanism is provided for <code>java.util.Date</code>
424    * and <code>java.util.Calendar</code> type.
425    *
426    * @param type The Number type to convert to
427    * @param value The String value to convert.
428    * @return The converted Number value.
429    */
 
430  13 toggle private Object toDate(Class type, String value) {
431    // java.sql.Date
432  13 if (type.equals(java.sql.Date.class)) {
433  3 try {
434  3 return java.sql.Date.valueOf(value);
435    } catch (IllegalArgumentException e) {
436  1 throw new ConversionException(
437    "String must be in JDBC format [yyyy-MM-dd] to create a java.sql.Date");
438    }
439    }
440   
441    // java.sql.Time
442  10 if (type.equals(java.sql.Time.class)) {
443  3 try {
444  3 return java.sql.Time.valueOf(value);
445    } catch (IllegalArgumentException e) {
446  1 throw new ConversionException(
447    "String must be in JDBC format [HH:mm:ss] to create a java.sql.Time");
448    }
449    }
450   
451    // java.sql.Timestamp
452  7 if (type.equals(java.sql.Timestamp.class)) {
453  5 try {
454  5 return java.sql.Timestamp.valueOf(value);
455    } catch (IllegalArgumentException e) {
456  3 throw new ConversionException(
457    "String must be in JDBC format [yyyy-MM-dd HH:mm:ss.fffffffff] " +
458    "to create a java.sql.Timestamp");
459    }
460    }
461   
462  2 String msg = toString(getClass()) + " does not support default String to '"
463    + toString(type) + "' conversion.";
464  2 if (log().isWarnEnabled()) {
465  2 log().warn(" " + msg);
466  2 log().warn(" (N.B. Re-configure Converter or use alternative implementation)");
467    }
468  2 throw new ConversionException(msg);
469    }
470   
471    /**
472    * Return a <code>DateFormat<code> for the Locale.
473    * @param locale The Locale to create the Format with (may be null)
474    * @param timeZone The Time Zone create the Format with (may be null)
475    *
476    * @return A Date Format.
477    */
 
478  16 toggle protected DateFormat getFormat(Locale locale, TimeZone timeZone) {
479  16 DateFormat format = null;
480  16 if (locale == null) {
481  15 format = DateFormat.getDateInstance(DateFormat.SHORT);
482    } else {
483  1 format = DateFormat.getDateInstance(DateFormat.SHORT, locale);
484    }
485  16 if (timeZone != null) {
486  0 format.setTimeZone(timeZone);
487    }
488  16 return format;
489    }
490   
491    /**
492    * Create a date format for the specified pattern.
493    *
494    * @param pattern The date pattern
495    * @return The DateFormat
496    */
 
497  163 toggle private DateFormat getFormat(String pattern) {
498  163 DateFormat format = new SimpleDateFormat(pattern);
499  163 if (timeZone != null) {
500  0 format.setTimeZone(timeZone);
501    }
502  163 return format;
503    }
504   
505    /**
506    * Parse a String date value using the set of patterns.
507    *
508    * @param sourceType The type of the value being converted
509    * @param targetType The type to convert the value to.
510    * @param value The String date value.
511    *
512    * @return The converted Date object.
513    * @throws Exception if an error occurs parsing the date.
514    */
 
515  109 toggle private Calendar parse(Class sourceType, Class targetType, String value) throws Exception {
516  109 Exception firstEx = null;
517  199 for (int i = 0; i < patterns.length; i++) {
518  124 try {
519  124 DateFormat format = getFormat(patterns[i]);
520  124 Calendar calendar = parse(sourceType, targetType, value, format);
521  34 return calendar;
522    } catch (Exception ex) {
523  90 if (firstEx == null) {
524  80 firstEx = ex;
525    }
526    }
527    }
528  75 if (patterns.length > 1) {
529  10 throw new ConversionException("Error converting '" + toString(sourceType) + "' to '" + toString(targetType)
530    + "' using patterns '" + displayPatterns + "'");
531    } else {
532  65 throw firstEx;
533    }
534    }
535   
536    /**
537    * Parse a String into a <code>Calendar</code> object
538    * using the specified <code>DateFormat</code>.
539    *
540    * @param sourceType The type of the value being converted
541    * @param targetType The type to convert the value to
542    * @param value The String date value.
543    * @param format The DateFormat to parse the String value.
544    *
545    * @return The converted Calendar object.
546    * @throws ConversionException if the String cannot be converted.
547    */
 
548  151 toggle private Calendar parse(Class sourceType, Class targetType, String value, DateFormat format) {
549  151 logFormat("Parsing", format);
550  151 format.setLenient(false);
551  151 ParsePosition pos = new ParsePosition(0);
552  151 Date parsedDate = format.parse(value, pos); // ignore the result (use the Calendar)
553  151 if (pos.getErrorIndex() >= 0 || pos.getIndex() != value.length() || parsedDate == null) {
554  111 String msg = "Error converting '" + toString(sourceType) + "' to '" + toString(targetType) + "'";
555  111 if (format instanceof SimpleDateFormat) {
556  111 msg += " using pattern '" + ((SimpleDateFormat)format).toPattern() + "'";
557    }
558  111 if (log().isDebugEnabled()) {
559  0 log().debug(" " + msg);
560    }
561  111 throw new ConversionException(msg);
562    }
563  40 Calendar calendar = format.getCalendar();
564  40 return calendar;
565    }
566   
567    /**
568    * Provide a String representation of this date/time converter.
569    *
570    * @return A String representation of this date/time converter
571    */
 
572  5 toggle public String toString() {
573  5 StringBuffer buffer = new StringBuffer();
574  5 buffer.append(toString(getClass()));
575  5 buffer.append("[UseDefault=");
576  5 buffer.append(isUseDefault());
577  5 buffer.append(", UseLocaleFormat=");
578  5 buffer.append(useLocaleFormat);
579  5 if (displayPatterns != null) {
580  0 buffer.append(", Patterns={");
581  0 buffer.append(displayPatterns);
582  0 buffer.append('}');
583    }
584  5 if (locale != null) {
585  5 buffer.append(", Locale=");
586  5 buffer.append(locale);
587    }
588  5 if (timeZone != null) {
589  0 buffer.append(", TimeZone=");
590  0 buffer.append(timeZone);
591    }
592  5 buffer.append(']');
593  5 return buffer.toString();
594    }
595   
596    /**
597    * Log the <code>DateFormat<code> creation.
598    * @param action The action the format is being used for
599    * @param format The Date format
600    */
 
601  191 toggle private void logFormat(String action, DateFormat format) {
602  191 if (log().isDebugEnabled()) {
603  0 StringBuffer buffer = new StringBuffer(45);
604  0 buffer.append(" ");
605  0 buffer.append(action);
606  0 buffer.append(" with Format");
607  0 if (format instanceof SimpleDateFormat) {
608  0 buffer.append("[");
609  0 buffer.append(((SimpleDateFormat)format).toPattern());
610  0 buffer.append("]");
611    }
612  0 buffer.append(" for ");
613  0 if (locale == null) {
614  0 buffer.append("default locale");
615    } else {
616  0 buffer.append("locale[");
617  0 buffer.append(locale);
618  0 buffer.append("]");
619    }
620  0 if (timeZone != null) {
621  0 buffer.append(", TimeZone[");
622  0 buffer.append(timeZone);
623  0 buffer.append("]");
624    }
625  0 log().debug(buffer.toString());
626    }
627    }
628    }