View Javadoc
1   package com.opencsv.bean;
2   
3   import com.opencsv.bean.mocks.AnnotatedMockBeanTemporal;
4   import com.opencsv.bean.mocks.temporal.*;
5   import com.opencsv.exceptions.CsvBadConverterException;
6   import com.opencsv.exceptions.CsvDataTypeMismatchException;
7   import com.opencsv.exceptions.CsvException;
8   import org.apache.commons.lang3.StringUtils;
9   import org.apache.commons.lang3.tuple.ImmutablePair;
10  import org.junit.After;
11  import org.junit.Before;
12  import org.junit.BeforeClass;
13  import org.junit.Test;
14  
15  import java.io.FileNotFoundException;
16  import java.io.FileReader;
17  import java.io.StringReader;
18  import java.io.StringWriter;
19  import java.time.*;
20  import java.time.chrono.*;
21  import java.time.temporal.Temporal;
22  import java.util.*;
23  import java.util.regex.Pattern;
24  import java.util.stream.Collectors;
25  
26  import static org.junit.jupiter.api.Assertions.*;
27  
28  public class TemporalTest {
29  
30      private static Locale systemLocale;
31  
32      @BeforeClass
33      public static void storeSystemLocale() {
34          systemLocale = Locale.getDefault();
35      }
36  
37      @Before
38      public void setSystemLocaleToValueNotGerman() {
39          Locale.setDefault(Locale.US);
40      }
41  
42      @After
43      public void setSystemLocaleBackToDefault() {
44          Locale.setDefault(systemLocale);
45      }
46  
47      private ImmutablePair<AnnotatedMockBeanTemporal, AnnotatedMockBeanTemporal> getTwoBeans() throws FileNotFoundException {
48          List<AnnotatedMockBeanTemporal> beans = new CsvToBeanBuilder<AnnotatedMockBeanTemporal>(
49                  new FileReader("src/test/resources/testInputTemporalByPosition.csv"))
50                  .withType(AnnotatedMockBeanTemporal.class)
51                  // Because of the inconsistencies in era designations between
52                  // Java versions, there will always be three good beans and
53                  // multiple bad beans.
54                  .withThrowExceptions(false)
55                  .build().parse();
56          return new ImmutablePair<>(beans.get(0), beans.get(1));
57      }
58  
59      private void verifyBeans(List<AnnotatedMockBeanTemporal> beans) {
60          assertNotNull(beans);
61          assertEquals(3, beans.size());
62          for(int i = 1; i < 4; i++) {
63              int month = i == 2 ? 5 : i;
64              AnnotatedMockBeanTemporal b = beans.get(i-1);
65              assertEquals(
66                      ZonedDateTime.of(1978, month, 15, 6, 2, 35, 0, ZoneId.of("America/New_York")),
67                      ZonedDateTime.from(b.getTemporalAccessor()));
68              assertEquals(
69                      ZonedDateTime.of(1978, month, 15, 6, 2, 35, 0, ZoneId.of("America/New_York")),
70                      ZonedDateTime.from(b.getTemporalAccessorLocale()));
71              assertEquals(
72                      LocalDate.of(1978, month, 16),
73                      b.getChronoLocalDate());
74              assertEquals(
75                      LocalDate.of(1978, month, 16),
76                      b.getChronoLocalDateLocale());
77              assertEquals(
78                      LocalDate.of(1978, month, 17),
79                      b.getLocalDate());
80              assertEquals(
81                      LocalDate.of(1978, month, 17),
82                      b.getLocalDateLocale());
83              assertEquals(
84                      LocalDateTime.of(1978, month, 18, 6, 2, 35),
85                      b.getChronoLocalDateTime());
86              assertEquals(
87                      LocalDateTime.of(1978, month, 18, 6, 2, 35),
88                      b.getChronoLocalDateTimeLocale());
89              assertEquals(
90                      LocalDateTime.of(1978, month, 19, 6, 2, 35),
91                      b.getLocalDateTime());
92              assertEquals(
93                      LocalDateTime.of(1978, month, 19, 6, 2, 35),
94                      b.getLocalDateTimeLocale());
95              assertEquals(
96                      ZonedDateTime.of(1978, month, 20, 6, 2, 35, 0, ZoneId.of("America/New_York")),
97                      b.getChronoZonedDateTime());
98              assertEquals(
99                      ZonedDateTime.of(1978, month, 20, 6, 2, 35, 0, ZoneId.of("America/New_York")),
100                     b.getChronoZonedDateTimeLocale());
101             assertEquals(
102                     ZonedDateTime.of(1978, month, 3, 6, 2, 35, 0, ZoneOffset.UTC),
103                     b.getZonedDateTime());
104             assertEquals(
105                     ZonedDateTime.of(1978, month, 3, 6, 2, 35, 0, ZoneOffset.UTC),
106                     b.getZonedDateTimeLocale());
107             assertEquals(
108                     ZonedDateTime.of(1978, month, 21, 6, 2, 35, 0, ZoneId.of("America/New_York")),
109                     b.getTemporal());
110             assertEquals(
111                     ZonedDateTime.of(1978, month, 21, 6, 2, 35, 0, ZoneId.of("America/New_York")),
112                     b.getTemporalLocale());
113             assertEquals(IsoEra.CE, b.getEra());
114             assertEquals(IsoEra.CE, b.getEraLocale());
115             assertEquals(IsoEra.CE, b.getIsoEra());
116             assertEquals(IsoEra.CE, b.getIsoEraLocale());
117             switch(month) {
118                 case 1: assertEquals(DayOfWeek.TUESDAY, b.getDayOfWeek()); break;
119                 case 3: assertEquals(DayOfWeek.FRIDAY, b.getDayOfWeek()); break;
120                 case 5: assertEquals(DayOfWeek.WEDNESDAY, b.getDayOfWeek()); break;
121                 default: throw new IllegalArgumentException();
122             }
123             switch(month) {
124                 case 1: assertEquals(DayOfWeek.TUESDAY, b.getDayOfWeekLocale()); break;
125                 case 3: assertEquals(DayOfWeek.FRIDAY, b.getDayOfWeekLocale()); break;
126                 case 5: assertEquals(DayOfWeek.WEDNESDAY, b.getDayOfWeekLocale()); break;
127                 default: throw new IllegalArgumentException();
128             }
129             assertEquals(
130                     HijrahDate.of(1398, 2, 6),
131                     b.getHijrahDate());
132             assertEquals(
133                     HijrahDate.of(1398, 2, 6),
134                     b.getHijrahDateLocale());
135             assertEquals(HijrahEra.AH, b.getHijrahEra());
136             assertEquals(HijrahEra.AH, b.getHijrahEraLocale());
137             Instant inst;
138             if(month == 5) {
139                 inst = Instant.parse(String.format("1978-%02d-25T10:02:35.00Z", month));
140             }
141             else {
142                 // Daylight savings time.
143                 inst = Instant.parse(String.format("1978-%02d-25T11:02:35.00Z", month));
144             }
145             assertEquals(inst, b.getInstant());
146             assertEquals(inst, b.getInstantLocale());
147             assertEquals(
148                     JapaneseDate.of(JapaneseEra.SHOWA, 53, month, 15),
149                     b.getJapaneseDate());
150             assertEquals(
151                     JapaneseDate.of(JapaneseEra.SHOWA, 53, month, 15),
152                     b.getJapaneseDateLocale());
153             assertEquals(JapaneseEra.SHOWA, b.getJapaneseEra());
154             assertEquals(JapaneseEra.SHOWA, b.getJapaneseEraLocale());
155             assertEquals(LocalTime.of(6, 2, 35), b.getLocalTime());
156             assertEquals(LocalTime.of(6, 2, 35), b.getLocalTimeLocale());
157             assertEquals(
158                     MinguoDate.of(-166, month, 15),
159                     b.getMinguoDate());
160             assertEquals(
161                     MinguoDate.of(-166, month, 15),
162                     b.getMinguoDateLocale());
163             assertEquals(MinguoEra.BEFORE_ROC, b.getMinguoEra());
164             assertEquals(MinguoEra.BEFORE_ROC, b.getMinguoEraLocale());
165             assertEquals(month, b.getMonth().getValue());
166             assertEquals(month, b.getMonthLocale().getValue());
167             assertEquals(MonthDay.of(month, 28), b.getMonthDay());
168             assertEquals(MonthDay.of(month, 28), b.getMonthDayLocale());
169             assertEquals(
170                     OffsetDateTime.of(1978, month, 29, 6, 2, 35, 0, ZoneOffset.ofHoursMinutes(0, 30)),
171                     b.getOffsetDateTime());
172             assertEquals(
173                     OffsetDateTime.of(1978, month, 29, 6, 2, 35, 0, ZoneOffset.ofHoursMinutes(0, 30)),
174                     b.getOffsetDateTimeLocale());
175             assertEquals(
176                     OffsetTime.of(6, 2, 35, 0, ZoneOffset.ofHoursMinutes(0, 30)),
177                     b.getOffsetTime());
178             assertEquals(
179                     OffsetTime.of(6, 2, 35, 0, ZoneOffset.ofHoursMinutes(0, 30)),
180                     b.getOffsetTimeLocale());
181             assertEquals(
182                     ThaiBuddhistDate.of(2521, month, 15),
183                     b.getThaiBuddhistDate());
184             assertEquals(
185                     ThaiBuddhistDate.of(2521, month, 15),
186                     b.getThaiBuddhistDateLocale());
187             assertEquals(ThaiBuddhistEra.BE, b.getThaiBuddhistEra());
188             assertEquals(ThaiBuddhistEra.BE, b.getThaiBuddhistEraLocale());
189             assertEquals(1978, b.getYear().getValue());
190             assertEquals(1978, b.getYearLocale().getValue());
191             assertEquals(YearMonth.of(1978, month), b.getYearMonth());
192             assertEquals(YearMonth.of(1978, month), b.getYearMonthLocale());
193             assertEquals(ZoneOffset.ofHours(1), b.getZoneOffset());
194             assertEquals(ZoneOffset.ofHours(1), b.getZoneOffsetLocale());
195             assertEquals(
196                     ZonedDateTime.of(1978, month, 3, 6, 2, 35, 0, ZoneId.of("+00:00")),
197                     b.getZonedDateTime());
198             assertEquals(
199                     ZonedDateTime.of(1978, month, 3, 6, 2, 35, 0, ZoneId.of("+00:00")),
200                     b.getZonedDateTimeLocale());
201         }
202     }
203 
204     private void verifyEras(String input, Class<? extends Era> era) {
205         CsvToBean<EraMock> csvToBean = new CsvToBeanBuilder<EraMock>(new StringReader(input))
206                 .withType(EraMock.class)
207                 .withThrowExceptions(false)
208                 .build();
209 
210         List<EraMock> beans = csvToBean.parse();
211         assertNotNull(beans);
212         assertEquals(1, beans.size());
213         EraMock bean = beans.get(0);
214         assertEquals(IsoEra.BCE, bean.getIsoEra());
215         assertEquals(HijrahEra.AH, bean.getHijrahEra());
216         assertEquals(JapaneseEra.TAISHO, bean.getJapaneseEra());
217         assertEquals(MinguoEra.BEFORE_ROC, bean.getMinguoEra());
218         assertEquals(ThaiBuddhistEra.BE, bean.getThaiBuddhistEra());
219 
220         List<CsvException> exceptions = csvToBean.getCapturedExceptions();
221         assertNotNull(exceptions);
222 
223         // Seven lines must show errors
224         assertEquals(7, exceptions.stream()
225                 .map(CsvException::getLineNumber)
226                 .collect(Collectors.toSet()).size());
227 
228         // The exception we're looking for could be in one of three lines
229         Set<Long> possibleLines = new HashSet<>();
230         possibleLines.add(4L); possibleLines.add(5L); possibleLines.add(6L);
231         assertTrue(exceptions.stream()
232                 .filter(e -> e.getCause() != null)
233                 .filter(e -> e instanceof CsvDataTypeMismatchException)
234                 .filter(e -> era.equals(((CsvDataTypeMismatchException)e).getDestinationClass()))
235                 .filter(e -> ((CsvDataTypeMismatchException) e).getSourceObject() != null)
236                 .anyMatch(e -> possibleLines.contains(e.getLineNumber())));
237     }
238 
239     /**
240      * Reads good data using the header name mappings.
241      * <p>Also incidentally tests:
242      * <ul><li>Read multiple beans to ensure proper concurrency</li></ul></p>
243      *
244      * @throws FileNotFoundException Never
245      */
246     @Test
247     public void testReadGoodDataByName() throws FileNotFoundException {
248         HeaderColumnNameMappingStrategy<AnnotatedMockBeanTemporal> strategy = new HeaderColumnNameMappingStrategy<>();
249         strategy.setType(AnnotatedMockBeanTemporal.class);
250         CsvToBean<AnnotatedMockBeanTemporal> csvToBean = new CsvToBeanBuilder<AnnotatedMockBeanTemporal>(
251                 new FileReader("src/test/resources/testInputTemporalByName.csv"))
252                 .withMappingStrategy(strategy)
253                 .withThrowExceptions(false)
254                 .build();
255         List<AnnotatedMockBeanTemporal> beans = csvToBean.parse();
256         // Different Java versions disagree about names of eras.
257         // For Java 8:
258         //   Current Thai Buddhist era = "B.E."
259         //   Japanese eras = "Showa" and "Taisho"
260         //   Old Minguo era = "Before R.O.C."
261         // For Java 9 through 12:
262         //   Current Thai Buddhist era = "BE"
263         //   Japanese eras = "Showa" and "Taisho"
264         //   Old Minguo era = "Before R.O.C."
265         // For Java 13 through 18:
266         //   Current Thai Buddhist era = "BE"
267         //   Japanese eras = "Shōwa" and "Taishō"
268         //   Old Minguo era = "Before R.O.C."
269         // For Java 19+:
270         //   Current Thai Buddhist era = "BE"
271         //   Japanese eras = "Shōwa" and "Taishō"
272         //   Old Minguo era = "B.R.O.C."
273         // Thus we have three lines of input for every combination and any
274         // given version of Java will accept three and throw exceptions for the
275         // other nine. But there are either four or six errors per line, as well.
276         Set<Integer> numErrors = new HashSet<>();
277         numErrors.add(66); // Java 8
278         numErrors.add(42); // Java 9 through 18
279         numErrors.add(54); // Java 19
280         assertTrue(numErrors.contains(csvToBean.getCapturedExceptions().size()));
281         verifyBeans(beans);
282     }
283 
284     @Test
285     public void testReadGoodDataByPosition() throws FileNotFoundException {
286         CsvToBean<AnnotatedMockBeanTemporal> csvToBean = new CsvToBeanBuilder<AnnotatedMockBeanTemporal>(
287                 new FileReader("src/test/resources/testInputTemporalByPosition.csv"))
288                 .withType(AnnotatedMockBeanTemporal.class)
289                 .withThrowExceptions(false)
290                 .build();
291         List<AnnotatedMockBeanTemporal> beans = csvToBean.parse();
292         // Different Java versions disagree about names of eras.
293         // For Java 8:
294         //   Current Thai Buddhist era = "B.E."
295         //   Japanese eras = "Showa" and "Taisho"
296         //   Old Minguo era = "Before R.O.C."
297         // For Java 9 through 12:
298         //   Current Thai Buddhist era = "BE"
299         //   Japanese eras = "Showa" and "Taisho"
300         //   Old Minguo era = "Before R.O.C."
301         // For Java 13 through 18:
302         //   Current Thai Buddhist era = "BE"
303         //   Japanese eras = "Shōwa" and "Taishō"
304         //   Old Minguo era = "Before R.O.C."
305         // For Java 19+:
306         //   Current Thai Buddhist era = "BE"
307         //   Japanese eras = "Shōwa" and "Taishō"
308         //   Old Minguo era = "B.R.O.C."
309         // Thus we have three lines of input for every combination and any
310         // given version of Java will accept three and throw exceptions for the
311         // other nine. But there are either four or six errors per line, as well.
312         Set<Integer> numErrors = new HashSet<>();
313         numErrors.add(66); // Java 8
314         numErrors.add(42); // Java 9 through 18
315         numErrors.add(54); // Java 19
316         assertTrue(numErrors.contains(csvToBean.getCapturedExceptions().size()));
317         verifyBeans(beans);
318     }
319 
320     /**
321      * Writes beans to a CSV output using a header name mapping.
322      * <p>Also incidentally tests:
323      * <ul><li>Write multiple beans to ensure proper concurrency</li>
324      * <li>Writing with a different (and explicit) chronology</li>
325      * <li>Setting an explicit chronology but leaving writeChronologyEqualsReadChronology false</li></ul></p>
326      */
327     @Test
328     public void testWriteGoodDataByName() throws FileNotFoundException, CsvException {
329         ImmutablePair<AnnotatedMockBeanTemporal, AnnotatedMockBeanTemporal> pair = getTwoBeans();
330         HeaderColumnNameMappingStrategy<AnnotatedMockBeanTemporal> strategy = new HeaderColumnNameMappingStrategy<>();
331         strategy.setType(AnnotatedMockBeanTemporal.class);
332         StringWriter w = new StringWriter();
333         StatefulBeanToCsv<AnnotatedMockBeanTemporal> beanToCsv = new StatefulBeanToCsvBuilder<AnnotatedMockBeanTemporal>(w)
334                 .withMappingStrategy(strategy)
335                 .withApplyQuotesToAll(false)
336                 .build();
337         beanToCsv.write(Arrays.asList(pair.left, pair.right));
338         Pattern p = Pattern.compile("CHRONOLOCALDATE,CHRONOLOCALDATELOCALE,CHRONOLOCALDATETIME,CHRONOLOCALDATETIMELOCALE,CHRONOZONEDDATETIME,CHRONOZONEDDATETIMELOCALE,DAYOFWEEK,DAYOFWEEKLOCALE,ERA,ERALOCALE,HIJRAHDATE,HIJRAHDATELOCALE,HIJRAHERA,HIJRAHERALOCALE,INSTANT,INSTANTLOCALE,ISOERA,ISOERALOCALE,JAPANESEDATE,JAPANESEDATELOCALE,JAPANESEERA,JAPANESEERALOCALE,LOCALDATE,LOCALDATELOCALE,LOCALDATETIME,LOCALDATETIMELOCALE,LOCALTIME,LOCALTIMELOCALE,MINGUODATE,MINGUODATELOCALE,MINGUOERA,MINGUOERALOCALE,MONTH,MONTHDAY,MONTHDAYLOCALE,MONTHLOCALE,OFFSETDATETIME,OFFSETDATETIMELOCALE,OFFSETTIME,OFFSETTIMELOCALE,TEMPORAL,TEMPORALACCESSOR,TEMPORALACCESSORLOCALE,TEMPORALLOCALE,THAIBUDDHISTDATE,THAIBUDDHISTDATELOCALE,THAIBUDDHISTERA,THAIBUDDHISTERALOCALE,YEAR,YEARLOCALE,YEARMONTH,YEARMONTHLOCALE,ZONEDDATETIME,ZONEDDATETIMELOCALE,ZONEOFFSET,ZONEOFFSETLOCALE\n" +
339                 "AD 1978 January 16,n\\. Chr\\. 1978 Januar 16,AD 1978 January 18 06 02 35,n\\. Chr\\. 1978 Januar 18 06 02 35,AD 1978 January 20 06 02 35 EST,n\\. Chr\\. 1978 Januar 20 06 02 35 EST,Tue,Di\\.?,AD,n\\. Chr\\.,AH 1398 Safar 06,AH 1398 Safar 06,AD,n\\. Chr\\.,1978 January 25 11 02 35,1978 1月 25 11 02 35,AD,n\\. Chr\\.,Sh[oō]wa 53 January 15,Sh[oō]wa 53 Januar 15,AD,n\\. Chr\\.,AD 1978 January 17,n\\. Chr\\. 1978 Januar 17,AD 1978 January 19 06 02 35,n\\. Chr\\. 1978 Januar 19 06 02 35,06 02 35,06 02 35,B(efore |\\.)R\\.O\\.C\\. 67 January 15,Before R\\.O\\.C\\. 67 Januar 15,BC,v\\. Chr\\.,January,28,28,Januar,1978-January-29T06:02:35\\+00:30,1978-Januar-29T06:02:35\\+00:30,06:02:35\\+00:30,06:02:35\\+00:30,AD 1978 January 21 06 02 35 EST,Anno Domini 1978 January 15 06 02 35 EST,n\\. Chr\\. 1978 Januar 15 06 02 35 EST,n\\. Chr\\. 1978 Januar 21 06 02 35 EST,B\\.?E\\.? 2521 January 15,B\\.?E\\.? 2521 Januar 15,AD,n\\. Chr\\.,1978,1978,1978 January,1978 Januar,Sh[oō]wa 0053 January 03 06 02 35 Z,n\\. Chr\\. 1978 Januar 03 06 02 35 Z,\\+01:00,\\+01:00\n" +
340                 "AD 1978 May 16,n\\. Chr\\. 1978 Mai 16,AD 1978 May 18 06 02 35,n\\. Chr\\. 1978 Mai 18 06 02 35,AD 1978 May 20 06 02 35 EDT,n\\. Chr\\. 1978 Mai 20 06 02 35 EDT,Wed,Mi\\.?,AD,n\\. Chr\\.,AH 1398 Safar 06,AH 1398 Safar 06,AD,n\\. Chr\\.,1978 May 25 10 02 35,1978 5月 25 10 02 35,AD,n\\. Chr\\.,Sh[oō]wa 53 May 15,Sh[oō]wa 53 Mai 15,AD,n\\. Chr\\.,AD 1978 May 17,n\\. Chr\\. 1978 Mai 17,AD 1978 May 19 06 02 35,n\\. Chr\\. 1978 Mai 19 06 02 35,06 02 35,06 02 35,B(efore |\\.)R\\.O\\.C\\. 67 May 15,Before R\\.O\\.C\\. 67 Mai 15,BC,v\\. Chr\\.,May,28,28,Mai,1978-May-29T06:02:35\\+00:30,1978-Mai-29T06:02:35\\+00:30,06:02:35\\+00:30,06:02:35\\+00:30,AD 1978 May 21 06 02 35 EDT,Anno Domini 1978 May 15 06 02 35 EDT,n\\. Chr\\. 1978 Mai 15 06 02 35 EDT,n\\. Chr\\. 1978 Mai 21 06 02 35 EDT,B\\.?E\\.? 2521 May 15,B\\.?E\\.? 2521 Mai 15,AD,n\\. Chr\\.,1978,1978,1978 May,1978 Mai,Sh[oō]wa 0053 May 03 06 02 35 Z,n\\. Chr\\. 1978 Mai 03 06 02 35 Z,\\+01:00,\\+01:00\n");
341         assertTrue(p.matcher(w.toString()).matches());
342     }
343 
344     @Test
345     public void testWriteGoodDataByPosition() throws FileNotFoundException, CsvException {
346         ImmutablePair<AnnotatedMockBeanTemporal, AnnotatedMockBeanTemporal> pair = getTwoBeans();
347         StringWriter w = new StringWriter();
348         StatefulBeanToCsv<AnnotatedMockBeanTemporal> beanToCsv = new StatefulBeanToCsvBuilder<AnnotatedMockBeanTemporal>(w)
349                 .withApplyQuotesToAll(false)
350                 .build();
351         beanToCsv.write(Arrays.asList(pair.left, pair.right));
352         Pattern p = Pattern.compile("Anno Domini 1978 January 15 06 02 35 EST,n\\. Chr\\. 1978 Januar 15 06 02 35 EST,AD 1978 January 16,n\\. Chr\\. 1978 Januar 16,AD 1978 January 17,n\\. Chr\\. 1978 Januar 17,AD 1978 January 18 06 02 35,n\\. Chr\\. 1978 Januar 18 06 02 35,AD 1978 January 19 06 02 35,n\\. Chr\\. 1978 Januar 19 06 02 35,AD 1978 January 20 06 02 35 EST,n\\. Chr\\. 1978 Januar 20 06 02 35 EST,AD 1978 January 21 06 02 35 EST,n\\. Chr\\. 1978 Januar 21 06 02 35 EST,AD,n\\. Chr\\.,AD,n\\. Chr\\.,Tue,Di\\.?,AH 1398 Safar 06,AH 1398 Safar 06,AD,n\\. Chr\\.,1978 January 25 11 02 35,1978 1月 25 11 02 35,Sh[oō]wa 53 January 15,Sh[oō]wa 53 Januar 15,AD,n\\. Chr\\.,06 02 35,06 02 35,B(efore |\\.)R\\.O\\.C\\. 67 January 15,Before R\\.O\\.C\\. 67 Januar 15,BC,v\\. Chr\\.,January,Januar,28,28,1978-January-29T06:02:35\\+00:30,1978-Januar-29T06:02:35\\+00:30,06:02:35\\+00:30,06:02:35\\+00:30,B\\.?E\\.? 2521 January 15,B\\.?E\\.? 2521 Januar 15,AD,n\\. Chr\\.,1978,1978,1978 January,1978 Januar,\\+01:00,\\+01:00,Sh[oō]wa 0053 January 03 06 02 35 Z,n\\. Chr\\. 1978 Januar 03 06 02 35 Z\n" +
353                 "Anno Domini 1978 May 15 06 02 35 EDT,n\\. Chr\\. 1978 Mai 15 06 02 35 EDT,AD 1978 May 16,n\\. Chr\\. 1978 Mai 16,AD 1978 May 17,n\\. Chr\\. 1978 Mai 17,AD 1978 May 18 06 02 35,n\\. Chr\\. 1978 Mai 18 06 02 35,AD 1978 May 19 06 02 35,n\\. Chr\\. 1978 Mai 19 06 02 35,AD 1978 May 20 06 02 35 EDT,n\\. Chr\\. 1978 Mai 20 06 02 35 EDT,AD 1978 May 21 06 02 35 EDT,n\\. Chr\\. 1978 Mai 21 06 02 35 EDT,AD,n\\. Chr\\.,AD,n\\. Chr\\.,Wed,Mi\\.?,AH 1398 Safar 06,AH 1398 Safar 06,AD,n\\. Chr\\.,1978 May 25 10 02 35,1978 5月 25 10 02 35,Sh[oō]wa 53 May 15,Sh[oō]wa 53 Mai 15,AD,n\\. Chr\\.,06 02 35,06 02 35,B(efore |\\.)R\\.O\\.C\\. 67 May 15,Before R\\.O\\.C\\. 67 Mai 15,BC,v\\. Chr\\.,May,Mai,28,28,1978-May-29T06:02:35\\+00:30,1978-Mai-29T06:02:35\\+00:30,06:02:35\\+00:30,06:02:35\\+00:30,B\\.?E\\.? 2521 May 15,B\\.?E\\.? 2521 Mai 15,AD,n\\. Chr\\.,1978,1978,1978 May,1978 Mai,\\+01:00,\\+01:00,Sh[oō]wa 0053 May 03 06 02 35 Z,n\\. Chr\\. 1978 Mai 03 06 02 35 Z\n");
354         assertTrue(p.matcher(w.toString()).matches());
355     }
356 
357     @Test
358     public void testUnknownTemporalAccessor() {
359         final String input = "19780115T060323";
360         try {
361             new CsvToBeanBuilder<UnknownTemporalAccessor>(new StringReader(input))
362                     .withType(UnknownTemporalAccessor.class)
363                     .withThrowExceptions(false)
364                     .build();
365             fail("Exception should have been thrown.");
366         } catch (CsvBadConverterException e) {
367             assertNull(e.getCause());
368             assertEquals(ConverterDate.class, e.getConverterClass());
369             assertNotNull(e.getMessage());
370         }
371     }
372 
373     @Test
374     public void testInvalidFormatStringForReading() {
375         try {
376             new CsvToBeanBuilder<InvalidFormatStringReading>(new StringReader("1978/Jan/15"))
377                     .withType(InvalidFormatStringReading.class)
378                     .build().parse();
379             fail("Exception should have been thrown.");
380         }
381         catch(CsvBadConverterException e) {
382             assertEquals(ConverterDate.class, e.getConverterClass());
383             assertFalse(StringUtils.isBlank(e.getMessage()));
384             assertNotNull(e.getCause());
385         }
386     }
387 
388     @Test
389     public void testInvalidFormatStringForWriting() throws CsvException {
390         try {
391             new StatefulBeanToCsvBuilder<InvalidFormatStringWriting>(new StringWriter())
392                     .build().write(new InvalidFormatStringWriting(ZonedDateTime.now()));
393             fail("Exception should have been thrown.");
394         }
395         catch(CsvBadConverterException e) {
396             assertEquals(ConverterDate.class, e.getConverterClass());
397             assertFalse(StringUtils.isBlank(e.getMessage()));
398             assertNotNull(e.getCause());
399         }
400     }
401 
402     @Test
403     public void testCsvInputDoesNotMatchFormatString() {
404         String input = "19780115T060323,AH,Taisho,Before R.O.C.,B.E.\n" + // For Java 8
405                 "19780115T060323,AH,Taisho,Before R.O.C.,BE\n" + // For Java 9 through 18
406                 "19780115T060323,AH,Taisho,B.R.O.C.,BE"; // For Java 19 and beyond
407         CsvToBean<EraMock> csvToBean = new CsvToBeanBuilder<EraMock>(new StringReader(input))
408                 .withType(EraMock.class)
409                 .withThrowExceptions(false)
410                 .build();
411         csvToBean.parse();
412         List<CsvException> exceptions = csvToBean.getCapturedExceptions();
413         assertNotNull(exceptions);
414         assertEquals(3, exceptions.stream()
415                 .filter(e -> e.getCause() != null)
416                 .filter(e -> e.getLine() != null)
417                 .filter(e -> e instanceof CsvDataTypeMismatchException)
418                 .filter(e -> IsoEra.class.equals(((CsvDataTypeMismatchException)e).getDestinationClass()))
419                 .filter(e -> input.split(",", 2)[0].equals(((CsvDataTypeMismatchException)e).getSourceObject()))
420                 .count());
421     }
422 
423     @Test
424     public void testBeanInputDoesNotMatchFormatString() throws FileNotFoundException, CsvException {
425         AnnotatedMockBeanTemporal bean = getTwoBeans().right;
426         bean.setTemporal(bean.getLocalTime());
427         StringWriter w = new StringWriter();
428         StatefulBeanToCsv<AnnotatedMockBeanTemporal> beanToCsv = new StatefulBeanToCsvBuilder<AnnotatedMockBeanTemporal>(w)
429                 .withThrowExceptions(false)
430                 .build();
431         beanToCsv.write(bean);
432         List<CsvException> exceptions = beanToCsv.getCapturedExceptions();
433         assertNotNull(exceptions);
434         assertEquals(1, exceptions.size());
435         CsvException e = exceptions.get(0);
436         assertEquals(1, e.getLineNumber());
437         assertNotNull(e.getCause());
438         assertTrue(e instanceof CsvDataTypeMismatchException);
439         CsvDataTypeMismatchException csve = (CsvDataTypeMismatchException) e;
440         assertEquals(bean, csve.getSourceObject());
441         assertEquals(Temporal.class, csve.getDestinationClass());
442     }
443 
444     @Test
445     public void testIsoEraUnparseable() {
446         verifyEras(
447                 "BC,AH,Taisho,Before R.O.C.,B.E.\n" + // For Java 8
448                         "BC,AH,Taisho,Before R.O.C.,BE\n" + // For Java 9 through 12
449                         "BC,AH,Taishō,Before R.O.C.,BE\n" + // For Java 13 through 18
450                         "BC,AH,Taishō,B.R.O.C.,BE\n" + // For Java 19 and beyond
451                         "unparsable,AH,Taisho,Before R.O.C.,B.E.\n" + // Intentionally broken but otherwise parsable for Java 8
452                         "unparsable,AH,Taisho,Before R.O.C.,BE\n" + // Intentionally broken but otherwise parsable for Java 9 through 12
453                         "unparsable,AH,Taishō,Before R.O.C.,BE\n" + // Intentionally broken but otherwise parsable for Java 13 through 18
454                         "unparsable,AH,Taishō,B.R.O.C.,BE", // Intentionally broken but otherwise parsable for Java 19 and beyond
455                 IsoEra.class);
456     }
457 
458     @Test
459     public void testHijrahEraUnparseable() {
460         verifyEras(
461                 "BC,AH,Taisho,Before R.O.C.,B.E.\n" + // For Java 8
462                         "BC,AH,Taisho,Before R.O.C.,BE\n" + // For Java 9 through 12
463                         "BC,AH,Taishō,Before R.O.C.,BE\n" + // For Java 13 through 18
464                         "BC,AH,Taishō,B.R.O.C.,BE\n" + // For Java 19 and beyond
465                         "BC,unparsable,Taisho,Before R.O.C.,B.E.\n" + // Intentionally broken but otherwise parsable for Java 8
466                         "BC,unparsable,Taisho,Before R.O.C.,BE\n" + // Intentionally broken but otherwise parsable for Java 9 through 12
467                         "BC,unparsable,Taishō,Before R.O.C.,BE\n" + // Intentionally broken but otherwise parsable for Java 13 through 18
468                         "BC,unparsable,Taishō,B.R.O.C.,BE", // Intentionally broken but otherwise parsable for Java 19 and beyond
469                 HijrahEra.class);
470     }
471 
472     @Test
473     public void testJapaneseEraUnparseable() {
474         verifyEras(
475                 "BC,AH,Taisho,Before R.O.C.,B.E.\n" + // For Java 8
476                         "BC,AH,Taisho,Before R.O.C.,BE\n" + // For Java 9 through 12
477                         "BC,AH,Taishō,Before R.O.C.,BE\n" + // For Java 13 through Java 18
478                         "BC,AH,Taishō,B.R.O.C.,BE\n" + // For Java 19 and beyond
479                         "BC,AH,unparsable,Before R.O.C.,B.E.\n" + // Intentionally broken but otherwise parsable for Java 8
480                         "BC,AH,unparsable,Before R.O.C.,BE\n" + // Intentionally broken but otherwise parsable for Java 9 and beyond
481                         "BC,AH,unparsable,Before R.O.C.,BE\n" + // For symmetry
482                         "BC,AH,unparsable,B.R.O.C.,BE", // For symmetry
483                 JapaneseEra.class);
484     }
485 
486     @Test
487     public void testMinguoEraUnparseable() {
488         verifyEras(
489                 "BC,AH,Taisho,Before R.O.C.,B.E.\n" + // For Java 8
490                         "BC,AH,Taisho,Before R.O.C.,BE\n" + // For Java 9 through 12
491                         "BC,AH,Taishō,Before R.O.C.,BE\n" + // For Java 13 through Java 18
492                         "BC,AH,Taishō,B.R.O.C.,BE\n" + // For Java 19 and beyond
493                         "BC,AH,Taisho,unparsable,B.E.\n" + // Intentionally broken but otherwise parsable for Java 8
494                         "BC,AH,Taisho,unparsable,BE\n" + // Intentionally broken but otherwise parsable for Java 9 through 12
495                         "BC,AH,Taishō,unparsable,BE\n" + // Intentionally broken but otherwise parsable for Java 13 through 18
496                         "BC,AH,Taishō,unparsable,BE", // For symmetry
497                 MinguoEra.class);
498     }
499 
500     @Test
501     public void testThaiBuddhistEraUnparseable() {
502         verifyEras(
503                 "BC,AH,Taisho,Before R.O.C.,B.E.\n" + // For Java 8
504                         "BC,AH,Taisho,Before R.O.C.,BE\n" + // For Java 9 through 12
505                         "BC,AH,Taishō,Before R.O.C.,BE\n" + // For Java 13 through 18
506                         "BC,AH,Taishō,B.R.O.C.,BE\n" + // For Java 19 and beyond
507                         "BC,AH,Taisho,Before R.O.C.,unparsable\n" + // Intentionally broken but otherwise parseable for Java 8 through 12
508                         "BC,AH,Taishō,Before R.O.C.,unparsable\n" + // Intentionally broken but otherwise parseable for Java 13 and beyond
509                         "BC,AH,Taishō,Before R.O.C.,unparsable\n" + // For symmetry
510                         "BC,AH,Taishō,B.R.O.C.,unparsable", // For symmetry
511                 ThaiBuddhistEra.class);
512     }
513 
514     @Test
515     public void testInvalidChronologyForReading() {
516         try {
517             new CsvToBeanBuilder<InvalidChronologyReading>(new StringReader("19780115T060323"))
518                     .withType(InvalidChronologyReading.class)
519                     .build().parse();
520             fail("Exception should have been thrown.");
521         }
522         catch(CsvBadConverterException e) {
523             assertNotNull(e.getCause());
524             assertEquals(ConverterDate.class, e.getConverterClass());
525         }
526     }
527 
528     @Test
529     public void testInvalidChronologyForWriting() throws CsvException {
530         try {
531             new StatefulBeanToCsvBuilder<InvalidChronologyWriting>(new StringWriter())
532                     .build().write(new InvalidChronologyWriting(ZonedDateTime.now()));
533             fail("Exception should have been thrown.");
534         }
535         catch(CsvBadConverterException e) {
536             assertNotNull(e.getCause());
537             assertEquals(ConverterDate.class, e.getConverterClass());
538         }
539     }
540 }