    1   /*
    2    * Licensed to the Apache Software Foundation (ASF) under one or more
    3    * contributor license agreements.  See the NOTICE file distributed with
    4    * this work for additional information regarding copyright ownership.
    5    * The ASF licenses this file to You under the Apache License, Version 2.0
    6    * (the "License"); you may not use this file except in compliance with
    7    * the License.  You may obtain a copy of the License at
    8    *
    9    *      http://www.apache.org/licenses/LICENSE-2.0
    10   *
    11   * Unless required by applicable law or agreed to in writing, software
    12   * distributed under the License is distributed on an "AS IS" BASIS,
    13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14   * See the License for the specific language governing permissions and
    15   * limitations under the License.
    16   */
    17
    18  package org.apache.commons.beanutils.converters;
    19
    20  import java.text.DateFormat;
    21  import java.text.SimpleDateFormat;
    22  import java.util.Calendar;
    23  import java.util.Date;
    24  import java.util.GregorianCalendar;
    25  import java.util.Locale;
    26
    27  import junit.framework.TestCase;
    28  import org.apache.commons.beanutils.Converter;
    29  import org.apache.commons.beanutils.ConversionException;
    30
    31  /***
    32   * Abstract base for &lt;Date&gt;Converter classes.
    33   *
    34   * @version $Revision: 471689 $ $Date: 2006-11-06 10:52:49 +0000 (Mon, 06 Nov 2006) $
    35   */
    36
    37  public abstract class DateConverterTestBase extends TestCase {
    38
    39      // ------------------------------------------------------------------------
    40
    41      /***
    42       * Construtc a new test case.
    43       * @param name Name of the test
    44       */
    45      public DateConverterTestBase(String name) {
    46          super(name);
    47      }
    48
    49      // ------------------------------------------------------------------------
    50
    51      /***
    52       * Create the Converter with no default value.
    53       * @return A new Converter
    54       */
    55      protected abstract DateTimeConverter makeConverter();
    56
    57      /***
    58       * Create the Converter with a default value.
    59       * @param defaultValue The default value
    60       * @return A new Converter
    61       */
    62      protected abstract DateTimeConverter makeConverter(Object defaultValue);
    63
    64      /***
    65       * Return the expected type
    66       * @return The expected type
    67       */
    68      protected abstract Class getExpectedType();
    69
    70      /***
    71       * Convert from a Calendar to the appropriate Date type
    72       *
    73       * @param value The Calendar value to convert
    74       * @return The converted value
    75       */
    76      protected abstract Object toType(Calendar value);
    77
    78      // ------------------------------------------------------------------------
    79
    80      /***
    81       * Assumes ConversionException in response to covert(getExpectedType(), null).
    82       */
    83      public void testConvertNull() {
    84          try {
    85              makeConverter().convert(getExpectedType(), null);
    86              fail("Expected ConversionException");
    87          } catch(ConversionException e) {
    88              // expected
    89          }
    90      }
    91
    92      /***
    93       * Assumes convert() returns some non-null
    94       * instance of getExpectedType().
    95       */
    96      public void testConvertDate() {
    97          String[] message= {
    98              "from Date",
    99              "from Calendar",
    100             "from SQL Date",
    101             "from SQL Time",
    102             "from SQL Timestamp"
    103         };
    104
    105         long now = System.currentTimeMillis();
    106
    107         Object[] date = {
    108             new Date(now),
    109             new java.util.GregorianCalendar(),
    110             new java.sql.Date(now),
    111             new java.sql.Time(now),
    112             new java.sql.Timestamp(now)
    113         };
    114
    115         // Initialize calendar also with same ms to avoid a failing test in a new time slice
    116         ((GregorianCalendar)date[1]).setTime(new Date(now));
    117
    118         for (int i = 0; i < date.length; i++) {
    119             Object val = makeConverter().convert(getExpectedType(), date[i]);
    120             assertNotNull("Convert " + message[i] + " should not be null", val);
    121             assertTrue("Convert " + message[i] + " should return a " + getExpectedType().getName(),
    122                        getExpectedType().isInstance(val));
    123             assertEquals("Convert " + message[i] + " should return a " + date[0],
    124                          now, getTimeInMillis(val));
    125         }
    126     }
    127
    128     /***
    129      * Test Default Type conversion (i.e. don't specify target type)
    130      */
    131     public void testDefaultType() {
    132         String pattern = "yyyy-MM-dd";
    133
    134         // Create & Configure the Converter
    135         DateTimeConverter converter = makeConverter();
    136         converter.setPattern(pattern);
    137
    138         // Valid String --> Type Conversion
    139         String testString = "2006-10-29";
    140         Calendar calendar = toCalendar(testString, pattern, null);
    141         Object expected   = toType(calendar);
    142
    143         Object result = converter.convert(null, testString);
    144         if (getExpectedType().equals(Calendar.class)) {
    145             assertTrue("TYPE ", getExpectedType().isAssignableFrom(result.getClass()));
    146         } else {
    147             assertEquals("TYPE ", getExpectedType(), result.getClass());
    148         }
    149         assertEquals("VALUE ", expected, result);
    150     }
    151
    152     /***
    153      * Test default String to type conversion
    154      *
    155      * N.B. This method is overriden by test case
    156      * implementations for java.sql.Date/Time/Timestamp
    157      */
    158     public void testDefaultStringToTypeConvert() {
    159
    160         // Create & Configure the Converter
    161         DateTimeConverter converter = makeConverter();
    162         converter.setUseLocaleFormat(false);
    163         try {
    164             converter.convert(getExpectedType(), "2006-10-23");
    165             fail("Expected Conversion exception");
    166         } catch (ConversionException e) {
    167             // expected result
    168         }
    169
    170     }
    171
    172     /***
    173      * Test Conversion to String
    174      */
    175     public void testStringConversion() {
    176
    177         String pattern = "yyyy-MM-dd";
    178
    179         // Create & Configure the Converter
    180         DateTimeConverter converter = makeConverter();
    181         converter.setPattern(pattern);
    182
    183         // Create Values
    184         String expected = "2006-10-29";
    185         Calendar calendar = toCalendar(expected, pattern, null);
    186
    187         // Type --> String Conversion
    188         stringConversion(converter, expected, toType(calendar));
    189
    190         // Calendar --> String Conversion
    191         stringConversion(converter, expected, calendar);
    192
    193         // java.util.Date --> String Conversion
    194         stringConversion(converter, expected, toDate(calendar));
    195
    196         // java.sql.Date --> String Conversion
    197         stringConversion(converter, expected, toSqlDate(calendar));
    198
    199         // java.sql.Timestamp --> String Conversion
    200         stringConversion(converter, expected, toSqlTimestamp(calendar));
    201
    202         // java.sql.Time --> String Conversion
    203         stringConversion(converter, expected, toSqlTime(calendar));
    204
    205         stringConversion(converter, null, null);
    206         stringConversion(converter, "", "");
    207
    208     }
    209
    210     /***
    211      * Test Converter with no default value
    212      */
    213     public void testPatternNoDefault() {
    214
    215         String pattern = "yyyy-MM-dd";
    216
    217         // Create & Configure the Converter
    218         DateTimeConverter converter = makeConverter();
    219         converter.setPattern(pattern);
    220
    221         // Valid String --> Type Conversion
    222         String testString = "2006-10-29";
    223         Calendar calendar = toCalendar(testString, pattern, null);
    224         Object expected   = toType(calendar);
    225         validConversion(converter, expected, testString);
    226
    227         // Valid java.util.Date --> Type Conversion
    228         validConversion(converter, expected, calendar);
    229
    230         // Valid Calendar --> Type Conversion
    231         validConversion(converter, expected, toDate(calendar));
    232
    233         // Test java.sql.Date --> Type Conversion
    234         validConversion(converter, expected, toSqlDate(calendar));
    235
    236         // java.sql.Timestamp --> String Conversion
    237         validConversion(converter, expected, toSqlTimestamp(calendar));
    238
    239         // java.sql.Time --> String Conversion
    240         validConversion(converter, expected, toSqlTime(calendar));
    241
    242         // Invalid Conversions
    243         invalidConversion(converter, null);
    244         invalidConversion(converter, "");
    245         invalidConversion(converter, "2006-10-2X");
    246         invalidConversion(converter, "2006/10/01");
    247         invalidConversion(converter, "02/10/2006");
    248         invalidConversion(converter, "02/10/06");
    249         invalidConversion(converter, new Integer(2));
    250
    251     }
    252
    253     /***
    254      * Test Converter with no default value
    255      */
    256     public void testPatternDefault() {
    257
    258         String pattern = "yyyy-MM-dd";
    259
    260         // Create & Configure the Converter
    261         Object defaultValue = toType("2000-01-01", pattern, null);
    262         assertNotNull("Check default date", defaultValue);
    263         DateTimeConverter converter = makeConverter(defaultValue);
    264         converter.setPattern(pattern);
    265
    266         // Valid String --> Type Conversion
    267         String testString = "2006-10-29";
    268         Object expected = toType(testString, pattern, null);
    269         validConversion(converter, expected, testString);
    270
    271         // Invalid Values, expect default value
    272         validConversion(converter, defaultValue, null);
    273         validConversion(converter, defaultValue, "");
    274         validConversion(converter, defaultValue, "2006-10-2X");
    275         validConversion(converter, defaultValue, "2006/10/01");
    276         validConversion(converter, defaultValue, "02/10/06");
    277         validConversion(converter, defaultValue, new Integer(2));
    278
    279     }
    280
    281     /***
    282      * Test Converter with no default value
    283      */
    284     public void testPatternNullDefault() {
    285
    286         String pattern = "yyyy-MM-dd";
    287
    288         // Create & Configure the Converter
    289         Object defaultValue = null;
    290         DateTimeConverter converter = makeConverter(defaultValue);
    291         converter.setPattern(pattern);
    292
    293         // Valid String --> Type Conversion
    294         String testString = "2006-10-29";
    295         Object expected = toType(testString, pattern, null);
    296         validConversion(converter, expected, testString);
    297
    298         // Invalid Values, expect default --> null
    299         validConversion(converter, defaultValue, null);
    300         validConversion(converter, defaultValue, "");
    301         validConversion(converter, defaultValue, "2006-10-2X");
    302         validConversion(converter, defaultValue, "2006/10/01");
    303         validConversion(converter, defaultValue, "02/10/06");
    304         validConversion(converter, defaultValue, new Integer(2));
    305
    306     }
    307
    308     /***
    309      * Test Converter with multiple patterns
    310      */
    311     public void testMultiplePatterns() {
    312         String testString = null;
    313         Object expected = null;
    314
    315         // Create & Configure the Converter
    316         String[] patterns = new String[] {"yyyy-MM-dd", "yyyy/MM/dd"};
    317         DateTimeConverter converter = makeConverter();
    318         converter.setPatterns(patterns);
    319
    320         // First Pattern
    321         testString = "2006-10-28";
    322         expected = toType(testString, patterns[0], null);
    323         validConversion(converter, expected, testString);
    324
    325         // Second pattern
    326         testString = "2006/10/18";
    327         expected = toType(testString, patterns[1], null);
    328         validConversion(converter, expected, testString);
    329
    330         // Invalid Conversion
    331         invalidConversion(converter, "17/03/2006");
    332         invalidConversion(converter, "17.03.2006");
    333
    334     }
    335
    336     /***
    337      * Test Date Converter with no default value
    338      */
    339     public void testLocale() {
    340
    341         // Re-set the default Locale to Locale.US
    342         Locale defaultLocale = Locale.getDefault();
    343         Locale.setDefault(Locale.US);
    344
    345         String pattern = "M/d/yy"; // SHORT style date format for US Locale
    346
    347         // Create & Configure the Converter
    348         DateTimeConverter converter = makeConverter();
    349         converter.setUseLocaleFormat(true);
    350
    351         // Valid String --> Type Conversion
    352         String testString = "10/28/06";
    353         Object expected = toType(testString, pattern, null);
    354         validConversion(converter, expected, testString);
    355
    356         // Invalid Conversions
    357         invalidConversion(converter, null);
    358         invalidConversion(converter, "");
    359         invalidConversion(converter, "2006-10-2X");
    360         invalidConversion(converter, "10.28.06");
    361         invalidConversion(converter, "10-28-06");
    362         invalidConversion(converter, new Integer(2));
    363
    364         // Restore the default Locale
    365         Locale.setDefault(defaultLocale);
    366
    367     }
    368
    369     /***
    370      * Test Converter with types it can't handle
    371      */
    372     public void testInvalidType() {
    373
    374         // Create & Configure the Converter
    375         DateTimeConverter converter = makeConverter();
    376
    377         // Invalid Class Type
    378         try {
    379             converter.convert(Character.class, new Date());
    380             fail("Requested Character.class conversion, expected ConversionException");
    381         } catch (ConversionException e) {
    382             // Expected result
    383         }
    384     }
    385
    386     /***
    387      * Test Conversion to the required type
    388      * @param converter The converter to use
    389      * @param expected The expected result
    390      * @param value The value to convert
    391      */
    392     void validConversion(Converter converter, Object expected, Object value) {
    393         String valueType = (value == null ? "null" : value.getClass().getName());
    394         String msg = "Converting '" + valueType + "' value '" + value + "'";
    395         try {
    396             Object result = converter.convert(getExpectedType(), value);
    397             Class resultType = (result   == null ? null : result.getClass());
    398             Class expectType = (expected == null ? null : expected.getClass());
    399             assertEquals("TYPE "  + msg, expectType, resultType);
    400             assertEquals("VALUE " + msg, expected, result);
    401         } catch (Exception ex) {
    402             fail(msg + " threw " + ex.toString());
    403         }
    404     }
    405
    406     /***
    407      * Test Conversion to String
    408      * @param converter The converter to use
    409      * @param expected The expected result
    410      * @param value The value to convert
    411      */
    412     void stringConversion(Converter converter, String expected, Object value) {
    413         String valueType = (value == null ? "null" : value.getClass().getName());
    414         String msg = "Converting '" + valueType + "' value '" + value + "' to String";
    415         try {
    416             Object result = converter.convert(String.class, value);
    417             Class resultType = (result   == null ? null : result.getClass());
    418             Class expectType = (expected == null ? null : expected.getClass());
    419             assertEquals("TYPE "  + msg, expectType, resultType);
    420             assertEquals("VALUE " + msg, expected, result);
    421         } catch (Exception ex) {
    422             fail(msg + " threw " + ex.toString());
    423         }
    424     }
    425
    426     /***
    427      * Test Conversion Error
    428      * @param converter The converter to use
    429      * @param value The value to convert
    430      */
    431     void invalidConversion(Converter converter, Object value) {
    432         String valueType = (value == null ? "null" : value.getClass().getName());
    433         String msg = "Converting '" + valueType + "' value '" + value + "'";
    434         try {
    435             Object result = converter.convert(getExpectedType(), value);
    436             fail(msg + ", expected ConversionException, but result = '" + result + "'");
    437         } catch (ConversionException ex) {
    438             // Expected Result
    439         }
    440     }
    441
    442     /***
    443      * Parse a String value to the required type
    444      * @param value The String value to parse
    445      * @param pattern The date pattern
    446      * @param locale The locale to use (or null)
    447      * @return parsed Calendar value
    448      */
    449     Object toType(String value, String pattern, Locale locale) {
    450         Calendar calendar = toCalendar(value, pattern, locale);
    451         return toType(calendar);
    452     }
    453
    454     /***
    455      * Parse a String value to a Calendar
    456      * @param value The String value to parse
    457      * @param pattern The date pattern
    458      * @param locale The locale to use (or null)
    459      * @return parsed Calendar value
    460      */
    461     Calendar toCalendar(String value, String pattern, Locale locale) {
    462         Calendar calendar = null;
    463         try {
    464             DateFormat format = (locale == null)
    465                            ? new SimpleDateFormat(pattern)
    466                            : new SimpleDateFormat(pattern, locale);
    467             format.setLenient(false);
    468             format.parse(value);
    469             calendar = format.getCalendar();
    470         } catch (Exception e) {
    471             fail("Error creating Calendar value ='"
    472                     + value + ", pattern='" + pattern + "' " + e.toString());
    473         }
    474         return calendar;
    475     }
    476
    477     /***
    478      * Convert a Calendar to a java.util.Date
    479      * @param calendar The calendar object to convert
    480      * @return The converted java.util.Date
    481      */
    482     Date toDate(Calendar calendar) {
    483         return calendar.getTime();
    484     }
    485
    486     /***
    487      * Convert a Calendar to a java.sql.Date
    488      * @param calendar The calendar object to convert
    489      * @return The converted java.sql.Date
    490      */
    491     java.sql.Date toSqlDate(Calendar calendar) {
    492         return new java.sql.Date(getTimeInMillis(calendar));
    493     }
    494
    495     /***
    496      * Convert a Calendar to a java.sql.Time
    497      * @param calendar The calendar object to convert
    498      * @return The converted java.sql.Time
    499      */
    500     java.sql.Time toSqlTime(Calendar calendar) {
    501         return new java.sql.Time(getTimeInMillis(calendar));
    502     }
    503
    504     /***
    505      * Convert a Calendar to a java.sql.Timestamp
    506      * @param calendar The calendar object to convert
    507      * @return The converted java.sql.Timestamp
    508      */
    509     java.sql.Timestamp toSqlTimestamp(Calendar calendar) {
    510         return new java.sql.Timestamp(getTimeInMillis(calendar));
    511     }
    512
    513     /***
    514      * Convert a Date or Calendar objects to the time in millisconds
    515      * @param date The date or calendar object
    516      * @return The time in milliseconds
    517      */
    518     long getTimeInMillis(Object date) {
    519
    520         if (date instanceof java.sql.Timestamp) {
    521             // ---------------------- JDK 1.3 Fix ----------------------
    522             // N.B. Prior to JDK 1.4 the Timestamp's getTime() method
    523             //      didn't include the milliseconds. The following code
    524             //      ensures it works consistently accross JDK versions
    525             java.sql.Timestamp timestamp = (java.sql.Timestamp)date;
    526             long timeInMillis = ((timestamp.getTime() / 1000) * 1000);
    527             timeInMillis += timestamp.getNanos() / 1000000;
    528             return timeInMillis;
    529         }
    530
    531         if (date instanceof Calendar) {
    532             return ((Calendar)date).getTime().getTime();
    533         } else {
    534             return ((Date)date).getTime();
    535         }
    536     }
    537 }