View Javadoc
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    *      https://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.beanutils2.converters;
19  
20  import static org.junit.jupiter.api.Assertions.assertEquals;
21  import static org.junit.jupiter.api.Assertions.fail;
22  
23  import java.text.DateFormatSymbols;
24  import java.text.ParseException;
25  import java.text.SimpleDateFormat;
26  import java.util.Calendar;
27  import java.util.Date;
28  import java.util.Locale;
29  
30  import org.apache.commons.beanutils2.ConversionException;
31  import org.apache.commons.beanutils2.locale.converters.DateLocaleConverter;
32  import org.apache.commons.beanutils2.locale.converters.DateLocaleConverter.Builder;
33  import org.apache.commons.lang3.SystemProperties;
34  import org.apache.commons.logging.Log;
35  import org.apache.commons.logging.LogFactory;
36  import org.junit.jupiter.api.BeforeEach;
37  import org.junit.jupiter.api.Test;
38  
39  /**
40   * Test Case for the DateLocaleConverter class.
41   */
42  class DateLocaleConverterTest extends AbstractLocaleConverterTest<Date> {
43  
44      /** All logging goes through this logger */
45      private static final Log LOG = LogFactory.getLog(DateLocaleConverterTest.class);
46  
47      protected String localizedDatePattern;
48      protected String localizedDateValue;
49      protected String localizedShortDateValue;
50      protected String defaultDatePattern;
51      protected String defaultDateValue;
52      protected String defaultShortDateValue;
53      protected boolean validLocalDateSymbols;
54  
55      /**
56       * Sets up instance variables required by this test case.
57       */
58      @Override
59      @BeforeEach
60      public void setUp() throws Exception {
61  
62          super.setUp();
63  
64          final String version = SystemProperties.getJavaSpecificationVersion();
65          LOG.debug("JDK Version " + version);
66  
67          try {
68              final SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd");
69              expectedValue = format.parse("20041001");
70              defaultValue = format.parse("19670316");
71          } catch (final Exception ex) {
72              LOG.error("Error creating expected/default dates", ex);
73          }
74  
75          // Default Locale (Use US)
76          defaultLocale = Locale.US;
77          defaultDatePattern = "d MMMM yyyy";
78          defaultDateValue = "1 October 2004";
79          defaultShortDateValue = "10/01/04";
80  
81          // Use German Locale
82  //        localizedLocale         = Locale.GERMAN;  // doesn't work for dates
83  //        localizedLocale         = Locale.GERMANY; // doesn't work for dates
84          localizedLocale = new Locale("de", "AT"); // Austria/German works
85          localizedDatePattern = "t MMMM uuuu";
86          localizedDateValue = "1 Oktober 2004";
87          localizedShortDateValue = "01.10.04";
88  
89          // Test whether the "local pattern characters" are what we
90          // are expecting - Locale.GERMAN and Locale.GERMANY, Locale.FRENCH all
91          // returned the standard "English" pattern characters on my machine
92          // for JDK 1.4 (JDK 1.3 was OK). The Austria/German locale was OK though
93          final String expectedChars = "GuMtkHmsSEDFwWahKzZ";
94          final DateFormatSymbols localizedSymbols = new DateFormatSymbols(localizedLocale);
95          final String localChars = localizedSymbols.getLocalPatternChars();
96  
97          // different JDK versions seem to have different numbers of pattern characters
98          final int lth = localChars.length() > expectedChars.length() ? expectedChars.length() : Math.min(localChars.length(), expectedChars.length());
99          validLocalDateSymbols = expectedChars.substring(0, lth).equals(localChars.substring(0, lth));
100 
101     }
102 
103     /**
104      * Test Calendar
105      */
106     @Test
107     public void testCalendarObject() {
108         converter = DateLocaleConverter.builder().setLocale(defaultLocale).get();
109         final Calendar calendar = Calendar.getInstance();
110         calendar.setTime((java.util.Date) expectedValue);
111         assertEquals(expectedValue, converter.convert(calendar), "java.util.Calendar");
112     }
113 
114     /**
115      * Test Converter() constructor
116      *
117      * Uses the default locale, no default value
118      */
119     @Test
120     public void testConstructor_2() {
121 
122         // Construct using default pattern & default locale
123         converter = DateLocaleConverter.builder().get();
124 
125         // Perform Tests
126         convertValueNoPattern(converter, defaultShortDateValue, expectedValue);
127         convertValueWithPattern(converter, defaultDateValue, defaultDatePattern, expectedValue);
128         convertInvalid(converter, null);
129         convertNull(converter, null);
130 
131         converter = DateLocaleConverter.builder().get();
132 
133         // Perform Tests
134         convertValueNoPattern(converter, defaultShortDateValue, expectedValue);
135         convertValueWithPattern(converter, defaultDateValue, defaultDatePattern, expectedValue);
136         convertInvalid(converter, null);
137         convertNull(converter, null);
138 
139     }
140 
141     /**
142      * Test Converter(locPattern) constructor
143      *
144      * Uses the default locale, no default value
145      */
146     @Test
147     public void testConstructor_3() {
148 
149         // Construct using default pattern & default locale
150         converter = DateLocaleConverter.builder().setLocalizedPattern(true).get();
151 
152         // Perform Tests
153         convertValueNoPattern(converter, defaultShortDateValue, expectedValue);
154         convertValueWithPattern(converter, defaultDateValue, defaultDatePattern, expectedValue);
155         convertInvalid(converter, null);
156         convertNull(converter, null);
157 
158     }
159 
160     /**
161      * Test Converter(Locale) constructor
162      */
163     @Test
164     public void testConstructor_4() {
165 
166         // Construct using specified Locale
167         converter = DateLocaleConverter.builder().setLocale(localizedLocale).get();
168 
169         // Perform Tests
170         convertValueNoPattern(converter, localizedShortDateValue, expectedValue);
171         convertValueWithPattern(converter, localizedDateValue, defaultDatePattern, expectedValue);
172         convertInvalid(converter, null);
173         convertNull(converter, null);
174 
175     }
176 
177     /**
178      * Test Converter(Locale, locPattern) constructor
179      */
180     @Test
181     public void testConstructor_5() {
182 
183         // Skip this test if no valid symbols for the locale
184         if (!validLocalDateSymbols) {
185             LOG.error("Invalid locale symbols *** skipping testConstructor_5() **");
186             return;
187         }
188 
189         // Construct using specified Locale
190         converter = DateLocaleConverter.builder().setLocale(localizedLocale).setLocalizedPattern(true).get();
191 
192         // Perform Tests
193         convertValueNoPattern(converter, localizedShortDateValue, expectedValue);
194         convertValueWithPattern(converter, localizedDateValue, localizedDatePattern, expectedValue);
195         convertInvalid(converter, null);
196         convertNull(converter, null);
197 
198     }
199 
200     /**
201      * Test Converter(Locale, pattern) constructor
202      */
203     @Test
204     public void testConstructor_6() {
205 
206         // Construct using specified Locale
207         converter = DateLocaleConverter.builder().setLocale(localizedLocale).setPattern(defaultDatePattern).get();
208 
209         // Perform Tests
210         convertValueNoPattern(converter, localizedDateValue, expectedValue);
211         convertValueWithPattern(converter, localizedDateValue, defaultDatePattern, expectedValue);
212         convertInvalid(converter, null);
213         convertNull(converter, null);
214 
215     }
216 
217     /**
218      * Test Converter(Locale, pattern, locPattern) constructor
219      */
220     @Test
221     public void testConstructor_7() {
222 
223         // Skip this test if no valid symbols for the locale
224         if (!validLocalDateSymbols) {
225             LOG.error("Invalid locale symbols *** skipping testConstructor_7() **");
226             return;
227         }
228 
229         // Construct using specified Locale
230         // @formatter:off
231         converter = DateLocaleConverter.builder()
232                 .setLocale(localizedLocale)
233                 .setPattern(localizedDatePattern)
234                 .setLocalizedPattern(true)
235                 .get();
236         // @formatter:on
237 
238         // Perform Tests
239         convertValueNoPattern(converter, localizedDateValue, expectedValue);
240         convertValueWithPattern(converter, localizedDateValue, localizedDatePattern, expectedValue);
241         convertInvalid(converter, null);
242         convertNull(converter, null);
243 
244     }
245 
246     /**
247      * Test Converter(defaultValue) constructor
248      */
249     @Test
250     public void testConstructor_8() {
251 
252         // Construct using specified Locale
253         converter = DateLocaleConverter.builder().setDefault(defaultValue).get();
254 
255         // Perform Tests
256         convertValueNoPattern(converter, defaultShortDateValue, expectedValue);
257         convertValueWithPattern(converter, defaultDateValue, defaultDatePattern, expectedValue);
258         convertInvalid(converter, defaultValue);
259         convertNull(converter, defaultValue);
260 
261     }
262 
263     /**
264      * Test Converter(defaultValue, locPattern) constructor
265      */
266     @Test
267     public void testConstructor_9() {
268 
269         // Construct using specified Locale
270         converter = DateLocaleConverter.builder().setDefault(defaultValue).setLocalizedPattern(true).get();
271 
272         // @formatter:off
273         converter = DateLocaleConverter.builder()
274                 .setDefault(defaultValue)
275                 .setLocalizedPattern(true)
276                 .get();
277         // @formatter:on
278         converter = DateLocaleConverter.builder().setDefault(defaultValue).setLocalizedPattern(true).get();
279 
280         // Perform Tests
281         convertValueNoPattern(converter, defaultShortDateValue, expectedValue);
282         convertValueWithPattern(converter, defaultDateValue, defaultDatePattern, expectedValue);
283         convertInvalid(converter, defaultValue);
284         convertNull(converter, defaultValue);
285 
286     }
287 
288     /**
289      * Test Converter(defaultValue, locale, pattern, localizedPattern) constructor
290      */
291     @Test
292     public void testConstructorMain() {
293 
294         // Skip this test if no valid symbols for the locale
295         if (!validLocalDateSymbols) {
296             LOG.error("Invalid locale symbols *** skipping testConstructorMain() **");
297             return;
298         }
299 
300         // Construct with localized pattern
301         // @formatter:off
302         converter = DateLocaleConverter.builder()
303                 .setDefault(defaultValue)
304                 .setLocale(localizedLocale)
305                 .setPattern(localizedDatePattern)
306                 .setLocalizedPattern(true)
307                 .get();
308         // @formatter:on
309 
310         convertValueNoPattern(converter, "(A)", localizedDateValue, expectedValue);
311         convertValueWithPattern(converter, "(A)", localizedDateValue, localizedDatePattern, expectedValue);
312         convertInvalid(converter, "(A)", defaultValue);
313         convertNull(converter, "(A)", defaultValue);
314 
315         // Convert value in the wrong format - should return default value
316         convertValueNoPattern(converter, "(B)", defaultDateValue, defaultValue);
317 
318         // Convert with non-localized pattern - should return default value
319         convertValueWithPattern(converter, "(B)", localizedDateValue, defaultDatePattern, defaultValue);
320 
321         // **************************************************************************
322         // Convert with specified type
323         //
324         // BaseLocaleConverter completely ignores the type - so even if we specify
325         // Double.class here it still returns a Date.
326         // **** This has been changed due to BEANUTILS-449 ****
327         // **************************************************************************
328         // convertValueToType(converter, "(B)", String.class, localizedDateValue, localizedDatePattern, expectedValue);
329 
330         // Construct with non-localized pattern
331         // @formatter:off
332         converter = DateLocaleConverter.builder()
333                 .setDefault(defaultValue)
334                 .setLocale(localizedLocale)
335                 .setPattern(defaultDatePattern)
336                 .setLocalizedPattern(false)
337                 .get();
338         // @formatter:on
339 
340         convertValueNoPattern(converter, "(C)", localizedDateValue, expectedValue);
341         convertValueWithPattern(converter, "(C)", localizedDateValue, defaultDatePattern, expectedValue);
342         convertInvalid(converter, "(C)", defaultValue);
343         convertNull(converter, "(C)", defaultValue);
344 
345     }
346 
347     /**
348      * Test java.util.Date
349      */
350     @Test
351     public void testDateObject() {
352         converter = DateLocaleConverter.builder().setLocale(defaultLocale).get();
353         assertEquals(expectedValue, converter.convert(expectedValue), "java.util.Date");
354     }
355 
356     /**
357      * Test invalid date
358      */
359     @Test
360     public void testInvalidDate() {
361 
362         converter = DateLocaleConverter.builder().setLocale(defaultLocale).get();
363 
364         try {
365             converter.convert("01/10/2004", "dd-MM-yyyy");
366         } catch (final ConversionException e) {
367             assertEquals("Error parsing date '01/10/2004' at position = 2", e.getMessage(), "Parse Error");
368         }
369 
370         try {
371             converter.convert("01-10-2004X", "dd-MM-yyyy");
372         } catch (final ConversionException e) {
373             assertEquals("Date '01-10-2004X' contains unparsed characters from position = 10", e.getMessage(), "Parse Length");
374         }
375 
376     }
377 
378     @Test
379     public void testSetLenient() {
380         // make sure that date format works as expected
381         final SimpleDateFormat dateFormat = new SimpleDateFormat("MMM dd, yyyy", Locale.UK);
382 
383         // test with no leniency
384         dateFormat.setLenient(false);
385 
386         try {
387 
388             dateFormat.parse("Feb 10, 2001");
389 
390         } catch (final ParseException e) {
391             fail("Could not parse date (1) - " + e.getMessage());
392         }
393 
394         try {
395 
396             dateFormat.parse("Feb 31, 2001");
397             fail("Parsed illegal date (1)");
398 
399         } catch (final ParseException e) {
400             // that's what we expected
401         }
402 
403         // test with leniency
404         dateFormat.setLenient(true);
405 
406         try {
407 
408             dateFormat.parse("Feb 10, 2001");
409 
410         } catch (final ParseException e) {
411             fail("Could not parse date (2) - " + e.getMessage());
412         }
413 
414         try {
415 
416             dateFormat.parse("Feb 31, 2001");
417 
418         } catch (final ParseException e) {
419             fail("Could not parse date (3) - " + e.getMessage());
420         }
421 
422         // now repeat tests for converter
423         // test with no leniency
424         final Builder<?, Date> builder = DateLocaleConverter.builder().setLocale(Locale.UK).setLenient(false).setPattern("MMM dd, yyyy");
425         DateLocaleConverter<Date> converter = builder.get();
426 
427         assertEquals(converter.isLenient(), false, "Set lenient failed");
428 
429         try {
430 
431             converter.convert("Feb 10, 2001");
432 
433         } catch (final ConversionException e) {
434             fail("Could not parse date (4) - " + e.getMessage());
435         }
436 
437         try {
438 
439             converter.convert("Feb 31, 2001");
440             assertEquals(converter.isLenient(), false, "Set lenient failed");
441             fail("Parsed illegal date (2)");
442 
443         } catch (final ConversionException e) {
444             // that's what we expected
445         }
446 
447         // test with leniency
448         converter = builder.setLenient(true).get();
449         assertEquals(converter.isLenient(), true, "Set lenient failed");
450 
451         try {
452 
453             converter.convert("Feb 10, 2001");
454 
455         } catch (final ConversionException e) {
456             fail("Could not parse date (5) - " + e.getMessage());
457         }
458 
459         try {
460 
461             converter.convert("Feb 31, 2001");
462 
463         } catch (final ConversionException e) {
464             fail("Could not parse date (6) - " + e.getMessage());
465         }
466     }
467 
468 }