View Javadoc
1   /*
2    * Copyright 2016 Andrew Rucker Jones.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package com.opencsv.bean;
17  
18  import com.opencsv.*;
19  import com.opencsv.bean.mocks.*;
20  import com.opencsv.exceptions.CsvDataTypeMismatchException;
21  import com.opencsv.exceptions.CsvException;
22  import com.opencsv.exceptions.CsvRequiredFieldEmptyException;
23  import org.apache.commons.lang3.tuple.ImmutablePair;
24  import org.junit.jupiter.api.AfterEach;
25  import org.junit.jupiter.api.BeforeAll;
26  import org.junit.jupiter.api.BeforeEach;
27  import org.junit.jupiter.api.Test;
28  
29  import java.io.IOException;
30  import java.io.StringWriter;
31  import java.util.ArrayList;
32  import java.util.Arrays;
33  import java.util.List;
34  import java.util.Locale;
35  import java.util.regex.Pattern;
36  
37  import static org.junit.jupiter.api.Assertions.*;
38  
39  /**
40   * Tests {@link StatefulBeanToCsv}.
41   *
42   * @author Andrew Rucker Jones (modified by Scott Conway to use CSVWriter for 4.2)
43   */
44  public class StatefulBeanToCsvWithCSVWriterTest {
45  
46      private static Locale systemLocale;
47  
48      // If alternatives in these regular expressions are always related to different
49      // handling of the same locale in different Java versions. There was a break from
50      // Java 8 to Java 9, and another from Java 12 to Java 13.
51      private static final String EXTRA_STRING_FOR_WRITING = "extrastringforwritinghowcreative";
52      private static final String GOOD_DATA_1 = "test string;value: true;false;1;2;3;4;123,101.101;123.202,202;123303.303;123[\u00A0\u202F]404,404;123101.1;1.000,2;2000.3;3.000,4;5000;6.000;2147476647;8.000;9000;10.000;11000;12.000;13000;14.000;15000;16.000;a;b;123101.101;123.102,102;101;102;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;01/15/1978;13. Dezember 2018;19780115T063209;19780115T063209;1.01;TEST1;EUR;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;";
53      private static final String GOOD_DATA_2 = "test string;;false;1;2;3;4;123,101.101;123.202,202;123303.303;123[\u00A0\u202F]404,404;123101.1;1.000,2;2000.3;3.000,4;5000;6.000;2147476647;8.000;9000;10.000;11000;12.000;13000;14.000;15000;16.000;a;b;123101.101;123.102,102;101;102;19780115T063209;19780115T163209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;01/15/1978;13. Dezember 2018;19780115T063209;19780115T063209;2.02;Test2;CHF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;";
54      private static final String GOOD_DATA_OPTIONALS_NULL = "test string;value: true;false;1;2;3;4;123,101.101;123.202,202;123303.303;123[\u00A0\u202F]404,404;;1.000,2;2000.3;3.000,4;5000;6.000;2147476647;8.000;9000;10.000;11000;12.000;13000;14.000;15000;16.000;a;b;123101.101;123.102,102;101;102;19780115T063209;19780115T063209;;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;01/15/1978;13. Dezember 2018;19780115T063209;19780115T063209;1.01;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;";
55      private static final String GOOD_DATA_CUSTOM_1 = "inside custom converter;wahr;falsch;127;127;127;;1.7976931348623157E308;1.7976931348623157E308;1.7976931348623157E308;1.7976931348623157E308;3.4028235E38;3.4028235E38;3.4028235E38;3.4028235E38;2147483647;2147483647;2147483647;2147483647;9223372036854775807;9223372036854775807;9223372036854775807;9223372036854775807;32767;32767;32767;32767;\uFFFF;\uFFFF;10;10;10;10;;;;;;;;;;;;;falsch;wahr;really long test string, yeah!;1.a.long,long.string1;2147483645.z.Inserted in setter methodlong,long.string2;3.c.long,long.derived.string3;inside custom converter";
56      private static final String HEADER_NAME_FULL = "BIGDECIMAL1;BIGDECIMAL2;BIGINTEGER1;BIGINTEGER2;BOOL1;BOOLPRIMITIVE;BYTE1;BYTE2;BYTE3;BYTE4;CHAR1;CHAR2;CURRENCY1;DATE1;DATE10;DATE11;DATE12;DATE13;DATE14;DATE15;DATE16;DATE2;DATE3;DATE4;DATE5;DATE6;DATE7;DATE8;DATE9;DOUBLE1;DOUBLE2;DOUBLE3;DOUBLE4;ENUM1;FLOAT1;FLOAT2;FLOAT3;FLOAT4;FLOAT5;INTEGER1;INTEGER2;INTEGER3;INTEGER4;ITNOGOODCOLUMNITVERYBAD;LONG1;LONG2;LONG3;LONG4;SHORT1;SHORT2;SHORT3;SHORT4;STRING1";
57      private static final String GOOD_DATA_NAME_1 = "123101.101;123.102,102;101;102;value: true;false;1;2;3;4;a;b;EUR;19780115T063209;19780115T063209;19780115T063209;19780115T063209;01/15/1978;13. Dezember 2018;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;123,101.101;123.202,202;123303.303;123[\u00A0\u202F]404,404;TEST1;123101.1;1.000,2;2000.3;3.000,4;1.01;5000;6.000;2147476647;8.000;;9000;10.000;11000;12.000;13000;14.000;15000;16.000;test string";
58      private static final String HEADER_NAME_FULL_CUSTOM = "BIGDECIMAL1;BIGDECIMAL2;BIGINTEGER1;BIGINTEGER2;BOOL1;BOOL2;BOOL3;BOOLPRIMITIVE;BYTE1;BYTE2;BYTE3;CHAR1;CHAR2;COMPLEX1;COMPLEX2;COMPLEX3;DOUBLE1;DOUBLE2;DOUBLE3;DOUBLE4;FLOAT1;FLOAT2;FLOAT3;FLOAT4;INTEGER1;INTEGER2;INTEGER3;INTEGER4;LONG1;LONG2;LONG3;LONG4;REQUIREDWITHCUSTOM;SHORT1;SHORT2;SHORT3;SHORT4;STRING1;STRING2";
59      private static final String GOOD_DATA_NAME_CUSTOM_1 = "10;10;10;10;wahr;falsch;wahr;falsch;127;127;127;\uFFFF;\uFFFF;1.a.long,long.string1;2147483645.z.Inserted in setter methodlong,long.string2;3.c.long,long.derived.string3;1.7976931348623157E308;1.7976931348623157E308;1.7976931348623157E308;1.7976931348623157E308;3.4028235E38;3.4028235E38;3.4028235E38;3.4028235E38;2147483647;2147483647;2147483647;2147483647;9223372036854775807;9223372036854775807;9223372036854775807;9223372036854775807;inside custom converter;32767;32767;32767;32767;inside custom converter;really long test string, yeah!";
60      private static final String GOOD_DATA_NAME_CUSTOM_2 = "10;10;10;10;wahr;falsch;wahr;falsch;127;127;127;\uFFFF;\uFFFF;4.d.long,long.string4;2147483642.z.Inserted in setter methodlong,long.derived.string5;6.f.long,long.string6;1.7976931348623157E308;1.7976931348623157E308;1.7976931348623157E308;1.7976931348623157E308;3.4028235E38;3.4028235E38;3.4028235E38;3.4028235E38;2147483647;2147483647;2147483647;2147483647;9223372036854775807;9223372036854775807;9223372036854775807;9223372036854775807;inside custom converter;32767;32767;32767;32767;inside custom converter;really";
61      private static final String HEADER_NAME_FULL_DERIVED = "BIGDECIMAL1;BIGDECIMAL2;BIGINTEGER1;BIGINTEGER2;BOOL1;BOOLPRIMITIVE;BYTE1;BYTE2;BYTE3;BYTE4;CHAR1;CHAR2;CURRENCY1;DATE1;DATE10;DATE11;DATE12;DATE13;DATE14;DATE15;DATE16;DATE2;DATE3;DATE4;DATE5;DATE6;DATE7;DATE8;DATE9;DOUBLE1;DOUBLE2;DOUBLE3;DOUBLE4;ENUM1;FLOAT1;FLOAT2;FLOAT3;FLOAT4;FLOAT5;INT IN SUBCLASS;INTEGER1;INTEGER2;INTEGER3;INTEGER4;ITNOGOODCOLUMNITVERYBAD;LONG1;LONG2;LONG3;LONG4;SHORT1;SHORT2;SHORT3;SHORT4;STRING1";
62      private static final String GOOD_DATA_NAME_DERIVED_1 = "123101.101;123.102,102;101;102;value: true;false;1;2;3;4;a;b;EUR;19780115T063209;19780115T063209;19780115T063209;19780115T063209;01/15/1978;13. Dezember 2018;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;123,101.101;123.202,202;123303.303;123[\u00A0\u202F]404,404;TEST1;123101.1;123.202,203;123303.305;123.404,406;1.01;7;5000;6.000;2147476647;8.000;;9000;10.000;11000;12.000;13000;14.000;15000;16.000;test string";
63      private static final String GOOD_DATA_NAME_DERIVED_SUB_1 = "123101.101;123.102,102;101;102;value: true;false;1;2;3;4;a;b;EUR;19780115T063209;19780115T063209;19780115T063209;19780115T063209;01/15/1978;13. Dezember 2018;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;123,101.101;123.202,202;123303.303;123[\u00A0\u202F]404,404;TEST1;123101.1;123.202,203;123303.305;123.404,406;1.01;5000;6.000;2147476647;8.000;;9000;10.000;11000;12.000;13000;14.000;15000;16.000;test string";
64  
65  
66      private StringWriter writer;
67      private CSVWriterBuilder csvWriterBuilder;
68  
69      @BeforeAll
70      public static void storeSystemLocale() {
71          systemLocale = Locale.getDefault();
72      }
73  
74      @BeforeEach
75      public void setSystemLocaleToValueNotGerman() {
76          Locale.setDefault(Locale.US);
77      }
78  
79      @BeforeEach
80      public void createWriters() {
81          writer = new StringWriter(ICSVWriter.INITIAL_STRING_SIZE);
82          csvWriterBuilder = new CSVWriterBuilder(writer);
83      }
84  
85      @AfterEach
86      public void setSystemLocaleBackToDefault() {
87          Locale.setDefault(systemLocale);
88      }
89  
90      /**
91       * Test of writing a single bean.
92       * This also incidentally covers the following conditions because of the
93       * data types and annotations in the bean used in testing:<ul>
94       * <li>Writing every primitive data type</li>
95       * <li>Writing every wrapped primitive data type</li>
96       * <li>Writing String, BigDecimal and BigInteger</li>
97       * <li>Writing all locale-sensitive data without locales</li>
98       * <li>Writing all locale-sensitive data with locales</li>
99       * <li>Writing a date type without an explicit format string</li>
100      * <li>Writing a date type with an explicit format string</li>
101      * <li>Writing with mixed @CsvBindByName and @CsvBindByPosition annotation
102      * types (expected behavior: The column position mapping strategy is
103      * automatically selected)</li></ul>
104      *
105      * @throws IOException  Never
106      * @throws CsvException Never
107      */
108     @Test
109     public void writeSingleBeanNoQuotes() throws IOException, CsvException {
110         ImmutablePair<AnnotatedMockBeanFull, AnnotatedMockBeanFull> beans = TestUtils.createTwoGoodBeans();
111         ICSVWriter csvWriter = csvWriterBuilder
112                 .withQuoteChar(ICSVWriter.NO_QUOTE_CHARACTER)
113                 .withSeparator(';')
114                 .build();
115         StatefulBeanToCsv<AnnotatedMockBeanFull> btcsv = new StatefulBeanToCsvBuilder<AnnotatedMockBeanFull>(csvWriter)
116                 .build();
117         btcsv.write(beans.left);
118         assertTrue(Pattern.matches(GOOD_DATA_1 + "\n", writer.toString()));
119     }
120 
121     @Test
122     public void writeSingleOptionallyQuotedBean() throws IOException, CsvException {
123         ImmutablePair<AnnotatedMockBeanFull, AnnotatedMockBeanFull> beans = TestUtils.createTwoGoodBeans();
124         RFC4180ParserBuilder parserBuilder = new RFC4180ParserBuilder();
125         RFC4180Parser parser = parserBuilder.withSeparator(';').build();
126         ICSVWriter csvWriter = csvWriterBuilder
127                 .withParser(parser)
128                 .build();
129         StatefulBeanToCsv<AnnotatedMockBeanFull> btcsv = new StatefulBeanToCsvBuilder<AnnotatedMockBeanFull>(csvWriter)
130                 .build();
131         beans.left.setStringClass("Quoted \"air quotes\" string");
132         btcsv.write(beans.left);
133         String output = writer.toString();
134         assertTrue(Pattern.matches(
135                 "\"Quoted \"\"air quotes\"\" string\";\"value: true\";\"false\";\"1\";\"2\";\"3\";\"4\";\"123,101\\.101\";\"123\\.202,202\";\"123303\\.303\";\"123[\u00A0\u202F]404,404\";\"123101\\.1\";\"1\\.000,2\";\"2000\\.3\";\"3\\.000,4\";\"5000\";\"6\\.000\";\"2147476647\";\"8\\.000\";\"9000\";\"10\\.000\";\"11000\";\"12\\.000\";\"13000\";\"14\\.000\";\"15000\";\"16\\.000\";\"a\";\"b\";\"123101\\.101\";\"123\\.102,102\";\"101\";\"102\";\"19780115T063209\";\"19780115T063209\";\"19780115T063209\";\"19780115T063209\";\"19780115T063209\";\"19780115T063209\";\"19780115T063209\";\"19780115T063209\";\"19780115T063209\";\"19780115T063209\";\"19780115T063209\";\"19780115T063209\";\"01/15/1978\";\"13\\. Dezember 2018\";\"19780115T063209\";\"19780115T063209\";\"1\\.01\";\"TEST1\";\"EUR\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\"\n",
136                 output));
137     }
138 
139     @Test
140     public void writeSingleOptionallyQuotedBeanWithCSVParser() throws IOException, CsvException {
141         ImmutablePair<AnnotatedMockBeanFull, AnnotatedMockBeanFull> beans = TestUtils.createTwoGoodBeans();
142         CSVParserBuilder parserBuilder = new CSVParserBuilder();
143         CSVParser parser = parserBuilder.withSeparator(';').build();
144         ICSVWriter csvWriter = csvWriterBuilder
145                 .withParser(parser)
146                 .build();
147         StatefulBeanToCsv<AnnotatedMockBeanFull> btcsv = new StatefulBeanToCsvBuilder<AnnotatedMockBeanFull>(csvWriter)
148                 .build();
149         beans.left.setStringClass("Quoted \"air quotes\" string");
150         btcsv.write(beans.left);
151         String output = writer.toString();
152         assertTrue(Pattern.matches(
153                 "\"Quoted \"\"air quotes\"\" string\";\"value: true\";\"false\";\"1\";\"2\";\"3\";\"4\";\"123,101\\.101\";\"123\\.202,202\";\"123303\\.303\";\"123[\u00A0\u202F]404,404\";\"123101\\.1\";\"1\\.000,2\";\"2000\\.3\";\"3\\.000,4\";\"5000\";\"6\\.000\";\"2147476647\";\"8\\.000\";\"9000\";\"10\\.000\";\"11000\";\"12\\.000\";\"13000\";\"14\\.000\";\"15000\";\"16\\.000\";\"a\";\"b\";\"123101\\.101\";\"123\\.102,102\";\"101\";\"102\";\"19780115T063209\";\"19780115T063209\";\"19780115T063209\";\"19780115T063209\";\"19780115T063209\";\"19780115T063209\";\"19780115T063209\";\"19780115T063209\";\"19780115T063209\";\"19780115T063209\";\"19780115T063209\";\"19780115T063209\";\"01/15/1978\";\"13\\. Dezember 2018\";\"19780115T063209\";\"19780115T063209\";\"1\\.01\";\"TEST1\";\"EUR\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\"\n",
154                 output));
155     }
156 
157     @Test
158     public void writeSingleOptionallyQuotedBeanWithPlainCSVWriter() throws IOException, CsvException {
159         ImmutablePair<AnnotatedMockBeanFull, AnnotatedMockBeanFull> beans = TestUtils.createTwoGoodBeans();
160         ICSVWriter csvWriter = csvWriterBuilder
161                 .withSeparator(';')
162                 .build();
163         StatefulBeanToCsv<AnnotatedMockBeanFull> btcsv = new StatefulBeanToCsvBuilder<AnnotatedMockBeanFull>(csvWriter)
164                 .withApplyQuotesToAll(true)
165                 .build();
166         beans.left.setStringClass("Quoted \"air quotes\" string");
167         btcsv.write(beans.left);
168         String output = writer.toString();
169         assertTrue(Pattern.matches(
170                 "\"Quoted \"\"air quotes\"\" string\";\"value: true\";\"false\";\"1\";\"2\";\"3\";\"4\";\"123,101\\.101\";\"123\\.202,202\";\"123303\\.303\";\"123[\u00A0\u202F]404,404\";\"123101\\.1\";\"1\\.000,2\";\"2000\\.3\";\"3\\.000,4\";\"5000\";\"6\\.000\";\"2147476647\";\"8\\.000\";\"9000\";\"10\\.000\";\"11000\";\"12\\.000\";\"13000\";\"14\\.000\";\"15000\";\"16\\.000\";\"a\";\"b\";\"123101\\.101\";\"123\\.102,102\";\"101\";\"102\";\"19780115T063209\";\"19780115T063209\";\"19780115T063209\";\"19780115T063209\";\"19780115T063209\";\"19780115T063209\";\"19780115T063209\";\"19780115T063209\";\"19780115T063209\";\"19780115T063209\";\"19780115T063209\";\"19780115T063209\";\"01/15/1978\";\"13\\. Dezember 2018\";\"19780115T063209\";\"19780115T063209\";\"1\\.01\";\"TEST1\";\"EUR\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\"\n",
171                 output));
172     }
173 
174     @Test
175     public void writeSingleQuotedBean() throws IOException, CsvException {
176         ImmutablePair<AnnotatedMockBeanFull, AnnotatedMockBeanFull> beans = TestUtils.createTwoGoodBeans();
177         ICSVWriter csvWriter = csvWriterBuilder
178                 .withSeparator(';')
179                 .build();
180         StatefulBeanToCsv<AnnotatedMockBeanFull> btcsv = new StatefulBeanToCsvBuilder<AnnotatedMockBeanFull>(csvWriter)
181                 .withApplyQuotesToAll(false)
182                 .build();
183         beans.left.setStringClass("Quoted \"air quotes\" string");
184         btcsv.write(beans.left);
185         assertTrue(Pattern.matches(
186                 "\"Quoted \"\"air quotes\"\" string\";value: true;false;1;2;3;4;123,101\\.101;123\\.202,202;123303\\.303;123[\u00A0\u202F]404,404;123101\\.1;1\\.000,2;2000\\.3;3\\.000,4;5000;6\\.000;2147476647;8\\.000;9000;10\\.000;11000;12\\.000;13000;14\\.000;15000;16\\.000;a;b;123101\\.101;123\\.102,102;101;102;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;01/15/1978;13\\. Dezember 2018;19780115T063209;19780115T063209;1\\.01;TEST1;EUR;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n",
187                 writer.toString()));
188     }
189 
190     /**
191      * Test of writing multiple beans at once when order counts.
192      *
193      * @throws IOException  Never
194      * @throws CsvException Never
195      */
196     @Test
197     public void writeMultipleBeansOrdered() throws IOException, CsvException {
198         ImmutablePair<AnnotatedMockBeanFull, AnnotatedMockBeanFull> beans = TestUtils.createTwoGoodBeans();
199         List<AnnotatedMockBeanFull> beanList = new ArrayList<>();
200         beanList.add(beans.left);
201         beanList.add(beans.right);
202         ICSVWriter csvWriter = csvWriterBuilder
203                 .withQuoteChar(ICSVWriter.NO_QUOTE_CHARACTER)
204                 .withSeparator(';')
205                 .build();
206         StatefulBeanToCsv<AnnotatedMockBeanFull> btcsv = new StatefulBeanToCsvBuilder<AnnotatedMockBeanFull>(csvWriter)
207                 .withApplyQuotesToAll(true)
208                 .build();
209         btcsv.write(beanList);
210         assertTrue(Pattern.matches(GOOD_DATA_1 + "\n" + GOOD_DATA_2 + "\n", writer.toString()));
211     }
212 
213     /**
214      * Test of writing multiple beans at once when order doesn't matter.
215      *
216      * @throws IOException  Never
217      * @throws CsvException Never
218      */
219     @Test
220     public void writeMultipleBeansUnordered() throws IOException, CsvException {
221         ImmutablePair<AnnotatedMockBeanFull, AnnotatedMockBeanFull> beans = TestUtils.createTwoGoodBeans();
222         List<AnnotatedMockBeanFull> beanList = new ArrayList<>();
223         beanList.add(beans.left);
224         beanList.add(beans.right);
225         ICSVWriter csvWriter = csvWriterBuilder
226                 .withQuoteChar(ICSVWriter.NO_QUOTE_CHARACTER)
227                 .withSeparator(';')
228                 .build();
229         StatefulBeanToCsv<AnnotatedMockBeanFull> btcsv = new StatefulBeanToCsvBuilder<AnnotatedMockBeanFull>(csvWriter)
230                 .withApplyQuotesToAll(true)
231                 .build();
232         btcsv.write(beanList);
233         String r = writer.toString();
234         assertTrue(Pattern.matches(GOOD_DATA_1 + "\n" + GOOD_DATA_2 + "\n", r) || Pattern.matches(GOOD_DATA_2 + "\n" + GOOD_DATA_1 + "\n", r));
235     }
236 
237     /**
238      * Test of writing a mixture of single beans and multiple beans.
239      *
240      * @throws IOException  Never
241      * @throws CsvException Never
242      */
243     @Test
244     public void writeMixedSingleMultipleBeans() throws IOException, CsvException {
245         ImmutablePair<AnnotatedMockBeanFull, AnnotatedMockBeanFull> beans = TestUtils.createTwoGoodBeans();
246         List<AnnotatedMockBeanFull> beanList = new ArrayList<>();
247         beanList.add(beans.left);
248         beanList.add(beans.right);
249         ICSVWriter csvWriter = csvWriterBuilder
250                 .withQuoteChar(ICSVWriter.NO_QUOTE_CHARACTER)
251                 .withLineEnd("arj\n")
252                 .withSeparator(';')
253                 .build();
254         StatefulBeanToCsv<AnnotatedMockBeanFull> btcsv = new StatefulBeanToCsvBuilder<AnnotatedMockBeanFull>(csvWriter)
255                 .withApplyQuotesToAll(true)
256                 .build();
257         btcsv.write(beanList);
258         btcsv.write(beans.left);
259         assertTrue(Pattern.matches(GOOD_DATA_1 + "arj\n" + GOOD_DATA_2 + "arj\n" + GOOD_DATA_1 + "arj\n", writer.toString()));
260     }
261 
262     /**
263      * Test of writing optional fields whose values are null.
264      * We test:<ul>
265      * <li>A wrapped primitive, and</li>
266      * <li>A date</li></ul>
267      *
268      * @throws IOException  Never
269      * @throws CsvException Never
270      */
271     @Test
272     public void writeOptionalFieldsWithNull() throws IOException, CsvException {
273         ImmutablePair<AnnotatedMockBeanFull, AnnotatedMockBeanFull> beans = TestUtils.createTwoGoodBeans();
274         beans.left.setFloatWrappedDefaultLocale(null);
275         beans.left.setCalDefaultLocale(null);
276         beans.left.setTestEnum(null);
277         beans.left.setTestCurrency(null);
278         ICSVWriter csvWriter = csvWriterBuilder
279                 .withQuoteChar(ICSVWriter.NO_QUOTE_CHARACTER)
280                 .withEscapeChar('|')
281                 .withSeparator(';')
282                 .build();
283         StatefulBeanToCsv<AnnotatedMockBeanFull> btcsv = new StatefulBeanToCsvBuilder<AnnotatedMockBeanFull>(csvWriter)
284                 .withApplyQuotesToAll(true)
285                 .build();
286         btcsv.write(beans.left);
287         assertTrue(Pattern.matches(GOOD_DATA_OPTIONALS_NULL + "\n", writer.toString()));
288     }
289 
290     /**
291      * Test of writing an optional field with a column position not adjacent
292      * to the other column positions.
293      *
294      * @throws IOException  Never
295      * @throws CsvException Never
296      */
297     @Test
298     public void writeOptionalNonContiguousField() throws IOException, CsvException {
299         ImmutablePair<AnnotatedMockBeanFull, AnnotatedMockBeanFull> beans = TestUtils.createTwoGoodBeans();
300         beans.left.setColumnDoesntExist(EXTRA_STRING_FOR_WRITING);
301         ICSVWriter csvWriter = csvWriterBuilder
302                 .withQuoteChar(ICSVWriter.NO_QUOTE_CHARACTER)
303                 .withSeparator(';')
304                 .build();
305         StatefulBeanToCsv<AnnotatedMockBeanFull> btcsv = new StatefulBeanToCsvBuilder<AnnotatedMockBeanFull>(csvWriter)
306                 .withApplyQuotesToAll(true)
307                 .build();
308         btcsv.write(beans.left);
309         assertTrue(Pattern.matches(GOOD_DATA_1 + EXTRA_STRING_FOR_WRITING + "\n", writer.toString()));
310     }
311 
312     /**
313      * Test of writing using a specified mapping strategy.
314      *
315      * @throws IOException  Never
316      * @throws CsvException Never
317      */
318     @Test
319     public void writeSpecifiedStrategy() throws IOException, CsvException {
320         ImmutablePair<AnnotatedMockBeanFull, AnnotatedMockBeanFull> beans = TestUtils.createTwoGoodBeans();
321         HeaderColumnNameMappingStrategy<AnnotatedMockBeanFull> strat = new HeaderColumnNameMappingStrategy<>();
322         strat.setType(AnnotatedMockBeanFull.class);
323         ICSVWriter csvWriter = csvWriterBuilder
324                 .withQuoteChar(ICSVWriter.NO_QUOTE_CHARACTER)
325                 .withSeparator(';')
326                 .build();
327         StatefulBeanToCsv<AnnotatedMockBeanFull> btcsv = new StatefulBeanToCsvBuilder<AnnotatedMockBeanFull>(csvWriter)
328                 .withMappingStrategy(strat)
329                 .build();
330         btcsv.write(beans.left);
331         assertTrue(Pattern.matches(HEADER_NAME_FULL + "\n" + GOOD_DATA_NAME_1 + "\n", writer.toString()));
332     }
333 
334     /**
335      * Test of writing with @CsvBindByPosition attached to unknown type.
336      * Expected behavior: Data are written with toString().
337      *
338      * @throws CsvException Never
339      */
340     @Test
341     public void writeBindByPositionUnknownType() throws CsvException {
342         BindUnknownType byNameUnsupported = new BindUnknownType();
343         ICSVWriter csvWriter = csvWriterBuilder
344                 .withQuoteChar(ICSVWriter.NO_QUOTE_CHARACTER)
345                 .build();
346         StatefulBeanToCsv<BindUnknownType> btcsv = new StatefulBeanToCsvBuilder<BindUnknownType>(csvWriter)
347                 .build();
348         btcsv.write(byNameUnsupported);
349         assertEquals(BindUnknownType.TOSTRING + "\n", writer.toString());
350     }
351 
352     /**
353      * Test of writing with @CsvBindByName attached to unknown type.
354      * Expected behavior: Data are written with toString().
355      *
356      * @throws CsvException Never
357      */
358     @Test
359     public void writeBindByNameUnknownType() throws CsvException {
360         BindUnknownType byNameUnsupported = new BindUnknownType();
361         HeaderColumnNameMappingStrategy<BindUnknownType> strat = new HeaderColumnNameMappingStrategy<>();
362         strat.setType(BindUnknownType.class);
363         ICSVWriter csvWriter = csvWriterBuilder
364                 .withQuoteChar(ICSVWriter.NO_QUOTE_CHARACTER)
365                 .build();
366         StatefulBeanToCsv<BindUnknownType> btcsv = new StatefulBeanToCsvBuilder<BindUnknownType>(csvWriter)
367                 .withMappingStrategy(strat)
368                 .build();
369         btcsv.write(byNameUnsupported);
370         assertEquals("TEST\n" + BindUnknownType.TOSTRING + "\n", writer.toString());
371     }
372 
373     /**
374      * Test writing with no annotations.
375      *
376      * @throws CsvException Never
377      */
378     @Test
379     public void writeWithoutAnnotations() throws CsvException {
380         ComplexClassForCustomAnnotation cc = new ComplexClassForCustomAnnotation();
381         cc.c = 'A';
382         cc.i = 1;
383         cc.s = "String";
384         ICSVWriter csvWriter = csvWriterBuilder
385                 .withQuoteChar(ICSVWriter.NO_QUOTE_CHARACTER)
386                 .withSeparator(';')
387                 .build();
388         StatefulBeanToCsv<ComplexClassForCustomAnnotation> btcsv = new StatefulBeanToCsvBuilder<ComplexClassForCustomAnnotation>(csvWriter)
389                 .build();
390         btcsv.write(cc);
391         assertEquals("C;I;S\nA;1;String\n", writer.toString());
392     }
393 
394     /**
395      * Writing a subclass with annotations in the subclass and the superclass.
396      *
397      * @throws IOException  Never
398      * @throws CsvException Never
399      */
400     @Test
401     public void writeDerivedSubclass() throws IOException, CsvException {
402         ImmutablePair<AnnotatedMockBeanFullDerived, AnnotatedMockBeanFullDerived> derivedList = TestUtils.createTwoGoodDerivedBeans();
403         HeaderColumnNameMappingStrategy<AnnotatedMockBeanFullDerived> strat = new HeaderColumnNameMappingStrategy<>();
404         strat.setType(AnnotatedMockBeanFullDerived.class);
405         ICSVWriter csvWriter = csvWriterBuilder
406                 .withQuoteChar(ICSVWriter.NO_QUOTE_CHARACTER)
407                 .withSeparator(';')
408                 .build();
409         StatefulBeanToCsv<AnnotatedMockBeanFullDerived> btcsv = new StatefulBeanToCsvBuilder<AnnotatedMockBeanFullDerived>(csvWriter)
410                 .withMappingStrategy(strat)
411                 .build();
412         btcsv.write(derivedList.left);
413         assertTrue(Pattern.matches(HEADER_NAME_FULL_DERIVED + "\n" + GOOD_DATA_NAME_DERIVED_1 + "\n", writer.toString()));
414     }
415 
416     /**
417      * Specifying a superclass, but writing a subclass.
418      * Expected behavior: Data from superclass are written.
419      *
420      * @throws IOException  Never
421      * @throws CsvException Never
422      */
423     @Test
424     public void writeDerivedSuperclass() throws IOException, CsvException {
425         ImmutablePair<AnnotatedMockBeanFullDerived, AnnotatedMockBeanFullDerived> derivedList = TestUtils.createTwoGoodDerivedBeans();
426         HeaderColumnNameMappingStrategy<AnnotatedMockBeanFull> strat = new HeaderColumnNameMappingStrategy<>();
427         strat.setType(AnnotatedMockBeanFull.class);
428         ICSVWriter csvWriter = csvWriterBuilder
429                 .withQuoteChar(ICSVWriter.NO_QUOTE_CHARACTER)
430                 .withSeparator(';')
431                 .build();
432         StatefulBeanToCsv<AnnotatedMockBeanFull> btcsv = new StatefulBeanToCsvBuilder<AnnotatedMockBeanFull>(csvWriter)
433                 .withMappingStrategy(strat)
434                 .build();
435         btcsv.write(derivedList.left);
436         assertTrue(Pattern.matches(HEADER_NAME_FULL + "\n" + GOOD_DATA_NAME_DERIVED_SUB_1 + "\n", writer.toString()));
437     }
438 
439     /**
440      * Tests of writing when getter is missing.
441      * Also tests incidentally:<ul>
442      * <li>Writing bad data without exceptions captured</li></ul>
443      *
444      * @throws CsvException         Never
445      */
446     @Test
447     public void writeGetterMissing() throws CsvException {
448         GetterMissing getterMissing = new GetterMissing();
449         ICSVWriter csvWriter = csvWriterBuilder
450                 .withQuoteChar(ICSVWriter.NO_QUOTE_CHARACTER)
451                 .withSeparator(';')
452                 .build();
453         StatefulBeanToCsv<GetterMissing> sbtcsv = new StatefulBeanToCsvBuilder<GetterMissing>(csvWriter)
454                 .build();
455         sbtcsv.write(getterMissing);
456         assertEquals("TEST\n123\n", writer.toString());
457     }
458 
459     /**
460      * Tests writing when getter is private.
461      *
462      * @throws CsvException         Never
463      */
464     @Test
465     public void writeGetterPrivate() throws CsvException {
466         GetterPrivate getterPrivate = new GetterPrivate();
467         ICSVWriter csvWriter = csvWriterBuilder
468                 .withQuoteChar(ICSVWriter.NO_QUOTE_CHARACTER)
469                 .build();
470         StatefulBeanToCsv<GetterPrivate> sbtcsv = new StatefulBeanToCsvBuilder<GetterPrivate>(csvWriter)
471                 .build();
472         sbtcsv.write(getterPrivate);
473         assertEquals("TEST\n123\n", writer.toString());
474     }
475 
476     /**
477      * Writing a required wrapped primitive field that is null.
478      * Also tests incidentally:<ul>
479      * <li>Writing bad data with exceptions captured</li></ul>
480      *
481      * @throws IOException          Never
482      * @throws CsvException         Never
483      * @throws NoSuchFieldException Never
484      */
485     @Test
486     public void writeNullRequiredWrappedPrimitive() throws IOException, CsvException, NoSuchFieldException {
487         ImmutablePair<AnnotatedMockBeanFull, AnnotatedMockBeanFull> beans = TestUtils.createTwoGoodBeans();
488         ICSVWriter csvWriter = csvWriterBuilder
489                 .withQuoteChar(ICSVWriter.NO_QUOTE_CHARACTER)
490                 .build();
491         StatefulBeanToCsv<AnnotatedMockBeanFull> sbtcsv = new StatefulBeanToCsvBuilder<AnnotatedMockBeanFull>(csvWriter)
492                 .withThrowExceptions(false)
493                 .build();
494         beans.left.setByteWrappedSetLocale(null); // required
495         sbtcsv.write(beans.left);
496         List<CsvException> csves = sbtcsv.getCapturedExceptions();
497         assertNotNull(csves);
498         assertEquals(1, csves.size());
499         CsvException csve = csves.get(0);
500         assertTrue(csve instanceof CsvRequiredFieldEmptyException);
501         CsvRequiredFieldEmptyException rfe = (CsvRequiredFieldEmptyException) csve;
502         assertEquals(1L, rfe.getLineNumber());
503         assertEquals(AnnotatedMockBeanFull.class, rfe.getBeanClass());
504         assertEquals(beans.left.getClass().getDeclaredField("byteWrappedSetLocale"),
505                 rfe.getDestinationField());
506     }
507 
508     /**
509      * Writing a required field with a custom converter that is null.
510      *
511      * @throws IOException          Never
512      * @throws CsvException         Never
513      * @throws NoSuchFieldException Never
514      */
515     @Test
516     public void writeNullRequiredCustom() throws IOException, CsvException, NoSuchFieldException {
517         ImmutablePair<AnnotatedMockBeanCustom, AnnotatedMockBeanCustom> beans = TestUtils.createTwoGoodCustomBeans();
518         ICSVWriter csvWriter = csvWriterBuilder
519                 .withQuoteChar(ICSVWriter.NO_QUOTE_CHARACTER)
520                 .build();
521         StatefulBeanToCsv<AnnotatedMockBeanCustom> sbtcsv = new StatefulBeanToCsvBuilder<AnnotatedMockBeanCustom>(csvWriter)
522                 .withThrowExceptions(false)
523                 .build();
524         beans.left.setRequiredWithCustom(null); // required
525         sbtcsv.write(beans.left);
526         List<CsvException> csves = sbtcsv.getCapturedExceptions();
527         assertNotNull(csves);
528         assertEquals(1, csves.size());
529         CsvException csve = csves.get(0);
530         assertTrue(csve instanceof CsvRequiredFieldEmptyException);
531         CsvRequiredFieldEmptyException rfe = (CsvRequiredFieldEmptyException) csve;
532         assertEquals(1L, rfe.getLineNumber());
533         assertEquals(AnnotatedMockBeanCustom.class, rfe.getBeanClass());
534         assertEquals(beans.left.getClass().getDeclaredField("requiredWithCustom"),
535                 rfe.getDestinationField());
536     }
537 
538     /**
539      * Writing a bad bean at the beginning of a long list to trigger shutting
540      * down the ExecutorService.
541      *
542      * @throws IOException          Never
543      * @throws CsvException         Never
544      * @throws NoSuchFieldException Never
545      */
546     @Test
547     public void writeManyFirstBeanIsBad() throws IOException, CsvException, NoSuchFieldException {
548         ImmutablePair<AnnotatedMockBeanCustom, AnnotatedMockBeanCustom> beans = TestUtils.createTwoGoodCustomBeans();
549         ICSVWriter csvWriter = csvWriterBuilder
550                 .withQuoteChar(ICSVWriter.NO_QUOTE_CHARACTER)
551                 .build();
552         StatefulBeanToCsv<AnnotatedMockBeanCustom> sbtcsv = new StatefulBeanToCsvBuilder<AnnotatedMockBeanCustom>(csvWriter)
553                 .withThrowExceptions(true)
554                 .build();
555         beans.left.setRequiredWithCustom(null); // required
556         List<AnnotatedMockBeanCustom> beanList = new ArrayList<>(1000);
557         beanList.add(beans.left);
558         for (int i = 0; i < 999; i++) {
559             beanList.add(beans.right);
560         }
561         try {
562             sbtcsv.write(beanList);
563         } catch (CsvRequiredFieldEmptyException rfe) {
564             assertEquals(1L, rfe.getLineNumber());
565             assertEquals(AnnotatedMockBeanCustom.class, rfe.getBeanClass());
566             assertEquals(beans.left.getClass().getDeclaredField("requiredWithCustom"),
567                     rfe.getDestinationField());
568         }
569     }
570 
571     /**
572      * Writing a bad bean when exceptions are not thrown and the results are
573      * unordered.
574      *
575      * @throws IOException          Never
576      * @throws CsvException         Never
577      * @throws NoSuchFieldException Never
578      */
579     @Test
580     public void writeBadBeanUnorderedCaptureExceptions() throws IOException, CsvException, NoSuchFieldException {
581         ImmutablePair<AnnotatedMockBeanCustom, AnnotatedMockBeanCustom> beans = TestUtils.createTwoGoodCustomBeans();
582         ICSVWriter csvWriter = csvWriterBuilder
583                 .withQuoteChar(ICSVWriter.NO_QUOTE_CHARACTER)
584                 .build();
585         StatefulBeanToCsv<AnnotatedMockBeanCustom> sbtcsv = new StatefulBeanToCsvBuilder<AnnotatedMockBeanCustom>(csvWriter)
586                 .withThrowExceptions(false)
587                 .withOrderedResults(false)
588                 .build();
589         beans.left.setRequiredWithCustom(null); // required
590         List<AnnotatedMockBeanCustom> beanList = new ArrayList<>(10);
591         beanList.add(beans.left);
592         for (int i = 0; i < 9; i++) {
593             beanList.add(beans.right);
594         }
595         sbtcsv.write(beanList);
596         List<CsvException> exceptionList = sbtcsv.getCapturedExceptions();
597         assertNotNull(exceptionList);
598         assertEquals(1, exceptionList.size());
599         CsvException csve = exceptionList.get(0);
600         assertTrue(csve instanceof CsvRequiredFieldEmptyException);
601         CsvRequiredFieldEmptyException rfe = (CsvRequiredFieldEmptyException) csve;
602         assertEquals(1L, rfe.getLineNumber());
603         assertEquals(AnnotatedMockBeanCustom.class, rfe.getBeanClass());
604         assertEquals(beans.left.getClass().getDeclaredField("requiredWithCustom"),
605                 rfe.getDestinationField());
606     }
607 
608     /**
609      * Writing a required date field that is null.
610      *
611      * @throws IOException          Never
612      * @throws CsvException         Never
613      * @throws NoSuchFieldException Never
614      */
615     @Test
616     public void writeNullRequiredDate() throws IOException, CsvException, NoSuchFieldException {
617         ImmutablePair<AnnotatedMockBeanFull, AnnotatedMockBeanFull> beans = TestUtils.createTwoGoodBeans();
618         beans.right.setDateDefaultLocale(null); // required
619         ICSVWriter csvWriter = csvWriterBuilder
620                 .withQuoteChar(ICSVWriter.NO_QUOTE_CHARACTER)
621                 .build();
622         StatefulBeanToCsv<AnnotatedMockBeanFull> sbtcsv = new StatefulBeanToCsvBuilder<AnnotatedMockBeanFull>(csvWriter)
623                 .withThrowExceptions(false)
624                 .build();
625         sbtcsv.write(beans.left);
626         sbtcsv.write(beans.right);
627         List<CsvException> csves = sbtcsv.getCapturedExceptions();
628         assertNotNull(csves);
629         assertEquals(1, csves.size());
630         CsvException csve = csves.get(0);
631         assertTrue(csve instanceof CsvRequiredFieldEmptyException);
632         CsvRequiredFieldEmptyException rfe = (CsvRequiredFieldEmptyException) csve;
633         assertEquals(2L, rfe.getLineNumber());
634         assertEquals(AnnotatedMockBeanFull.class, rfe.getBeanClass());
635         assertEquals(beans.right.getClass().getDeclaredField("dateDefaultLocale"),
636                 rfe.getDestinationField());
637     }
638 
639     /**
640      * Reading captured exceptions twice in a row.
641      *
642      * @throws IOException          Never
643      * @throws CsvException         Never
644      */
645     @Test
646     public void readCapturedExceptionsIsDestructive() throws IOException, CsvException {
647         ImmutablePair<AnnotatedMockBeanFull, AnnotatedMockBeanFull> beans = TestUtils.createTwoGoodBeans();
648         beans.left.setByteWrappedSetLocale(null); // required
649         beans.right.setDateDefaultLocale(null); // required
650         ICSVWriter csvWriter = csvWriterBuilder
651                 .withQuoteChar(ICSVWriter.NO_QUOTE_CHARACTER)
652                 .build();
653         StatefulBeanToCsv<AnnotatedMockBeanFull> sbtcsv = new StatefulBeanToCsvBuilder<AnnotatedMockBeanFull>(csvWriter)
654                 .withThrowExceptions(false)
655                 .build();
656         sbtcsv.write(beans.left);
657         sbtcsv.write(beans.right);
658         sbtcsv.getCapturedExceptions(); // First call
659         List<CsvException> csves = sbtcsv.getCapturedExceptions(); // Second call
660         assertTrue(csves.isEmpty());
661     }
662 
663     /**
664      * Tests writing multiple times with exceptions from each write.
665      *
666      * @throws IOException          Never
667      * @throws CsvException         Never
668      */
669     @Test
670     public void multipleWritesCapturedExceptions() throws IOException, CsvException {
671         ImmutablePair<AnnotatedMockBeanFull, AnnotatedMockBeanFull> beans = TestUtils.createTwoGoodBeans();
672         beans.left.setByteWrappedSetLocale(null); // required
673         beans.right.setDateDefaultLocale(null); // required
674         ICSVWriter csvWriter = csvWriterBuilder
675                 .withQuoteChar(ICSVWriter.NO_QUOTE_CHARACTER)
676                 .build();
677         StatefulBeanToCsv<AnnotatedMockBeanFull> sbtcsv = new StatefulBeanToCsvBuilder<AnnotatedMockBeanFull>(csvWriter)
678                 .withThrowExceptions(false)
679                 .build();
680         sbtcsv.write(beans.left);
681         sbtcsv.write(beans.right);
682         List<CsvException> csves = sbtcsv.getCapturedExceptions();
683         assertEquals(2, csves.size());
684     }
685 
686     /**
687      * Tests binding a custom converter to the wrong data type.
688      * Also incidentally tests that the error locale works.
689      *
690      * @throws CsvException         Never
691      */
692     @Test
693     public void bindCustomConverterToWrongDataType() throws CsvException {
694         BindCustomToWrongDataType wrongTypeBean = new BindCustomToWrongDataType();
695         wrongTypeBean.setWrongType(GOOD_DATA_1);
696         ICSVWriter csvWriter = csvWriterBuilder
697                 .withQuoteChar(ICSVWriter.NO_QUOTE_CHARACTER)
698                 .build();
699         StatefulBeanToCsv<BindCustomToWrongDataType> sbtcsv = new StatefulBeanToCsvBuilder<BindCustomToWrongDataType>(csvWriter)
700                 .withThrowExceptions(false)
701                 .build();
702         sbtcsv.write(wrongTypeBean);
703         List<CsvException> csves = sbtcsv.getCapturedExceptions();
704         assertNotNull(csves);
705         assertEquals(1, csves.size());
706         CsvException csve = csves.get(0);
707         assertTrue(csve instanceof CsvDataTypeMismatchException);
708         CsvDataTypeMismatchException dtm = (CsvDataTypeMismatchException) csve;
709         assertEquals(1L, dtm.getLineNumber());
710         assertTrue(dtm.getSourceObject() instanceof BindCustomToWrongDataType);
711         assertEquals(String.class, dtm.getDestinationClass());
712         String englishErrorMessage = dtm.getLocalizedMessage();
713 
714         // Now with another locale
715         writer = new StringWriter();
716         sbtcsv = new StatefulBeanToCsvBuilder<BindCustomToWrongDataType>(csvWriter)
717                 .withThrowExceptions(false)
718                 .withErrorLocale(Locale.GERMAN)
719                 .build();
720         sbtcsv.write(wrongTypeBean);
721         csves = sbtcsv.getCapturedExceptions();
722         assertNotNull(csves);
723         assertEquals(1, csves.size());
724         csve = csves.get(0);
725         assertTrue(csve instanceof CsvDataTypeMismatchException);
726         dtm = (CsvDataTypeMismatchException) csve;
727         assertEquals(1L, dtm.getLineNumber());
728         assertTrue(dtm.getSourceObject() instanceof BindCustomToWrongDataType);
729         assertEquals(String.class, dtm.getDestinationClass());
730         assertNotSame(englishErrorMessage, dtm.getLocalizedMessage());
731     }
732 
733     /**
734      * Test of good data with custom converters and a column position mapping
735      * strategy.
736      * Incidentally covers the following behavior by virtue of the beans
737      * written:<ul>
738      * <li>Writing with ConvertGermanToBoolean</li>
739      * <li>Writing with ConvertSplitOnWhitespace</li>
740      * </ul>
741      *
742      * @throws IOException  Never
743      * @throws CsvException Never
744      */
745     @Test
746     public void writeCustomByPosition() throws IOException, CsvException {
747         ImmutablePair<AnnotatedMockBeanCustom, AnnotatedMockBeanCustom> beans = TestUtils.createTwoGoodCustomBeans();
748         ICSVWriter csvWriter = csvWriterBuilder
749                 .withQuoteChar(ICSVWriter.NO_QUOTE_CHARACTER)
750                 .withSeparator(';')
751                 .build();
752         StatefulBeanToCsv<AnnotatedMockBeanCustom> btcsv = new StatefulBeanToCsvBuilder<AnnotatedMockBeanCustom>(csvWriter)
753                 .build();
754         btcsv.write(beans.left);
755         assertEquals(GOOD_DATA_CUSTOM_1 + "\n", writer.toString());
756     }
757 
758     /**
759      * Test of good data with custom converters and a header name mapping
760      * strategy.
761      * Incidentally test writing a mixture of single and multiple beans with
762      * custom converters.
763      *
764      * @throws IOException  Never
765      * @throws CsvException Never
766      */
767     @Test
768     public void writeCustomByName() throws IOException, CsvException {
769         ImmutablePair<AnnotatedMockBeanCustom, AnnotatedMockBeanCustom> beans = TestUtils.createTwoGoodCustomBeans();
770         HeaderColumnNameMappingStrategy<AnnotatedMockBeanCustom> strat = new HeaderColumnNameMappingStrategy<>();
771         strat.setType(AnnotatedMockBeanCustom.class);
772         ICSVWriter csvWriter = csvWriterBuilder
773                 .withQuoteChar(ICSVWriter.NO_QUOTE_CHARACTER)
774                 .withSeparator(';')
775                 .build();
776         StatefulBeanToCsv<AnnotatedMockBeanCustom> btcsv = new StatefulBeanToCsvBuilder<AnnotatedMockBeanCustom>(csvWriter)
777                 .withMappingStrategy(strat)
778                 .build();
779         btcsv.write(beans.right);
780         btcsv.write(Arrays.asList(beans.left, beans.right));
781         assertEquals(
782                 HEADER_NAME_FULL_CUSTOM + "\n" + GOOD_DATA_NAME_CUSTOM_2 + "\n" + GOOD_DATA_NAME_CUSTOM_1 + "\n" + GOOD_DATA_NAME_CUSTOM_2 + "\n",
783                 writer.toString());
784     }
785 
786     /**
787      * Tests writing an empty field annotated with the custom converter
788      * {@link com.opencsv.bean.customconverter.ConvertGermanToBoolean} with
789      * required set to true.
790      *
791      * @throws IOException          Never
792      * @throws CsvException         Never
793      */
794     @Test
795     public void writeEmptyFieldWithConvertGermanToBooleanRequired() throws IOException, CsvException {
796         ImmutablePair<AnnotatedMockBeanCustom, AnnotatedMockBeanCustom> beans = TestUtils.createTwoGoodCustomBeans();
797         ICSVWriter csvWriter = csvWriterBuilder
798                 .withQuoteChar(ICSVWriter.NO_QUOTE_CHARACTER)
799                 .withSeparator(';')
800                 .build();
801         StatefulBeanToCsv<AnnotatedMockBeanCustom> btcsv = new StatefulBeanToCsvBuilder<AnnotatedMockBeanCustom>(csvWriter)
802                 .build();
803         beans.left.setBoolWrapped(null);
804         try {
805             btcsv.write(beans.left);
806             fail("Exception should have been thrown!");
807         } catch (CsvRequiredFieldEmptyException e) {
808             assertEquals(1, e.getLineNumber());
809             assertEquals(AnnotatedMockBeanCustom.class, e.getBeanClass());
810             assertEquals("boolWrapped", e.getDestinationField().getName());
811         }
812     }
813 }