View Javadoc
1   /*
2    * CSVeed (https://github.com/42BV/CSVeed)
3    *
4    * Copyright 2013-2023 CSVeed.
5    *
6    * All rights reserved. This program and the accompanying materials
7    * are made available under the terms of The Apache Software License,
8    * Version 2.0 which accompanies this distribution, and is available at
9    * https://www.apache.org/licenses/LICENSE-2.0.txt
10   */
11  package org.csveed.api;
12  
13  import static org.junit.jupiter.api.Assertions.assertEquals;
14  import static org.junit.jupiter.api.Assertions.assertNotNull;
15  import static org.junit.jupiter.api.Assertions.assertThrows;
16  import static org.junit.jupiter.api.Assertions.assertTrue;
17  
18  import java.io.IOException;
19  import java.io.Reader;
20  import java.io.StringReader;
21  import java.io.StringWriter;
22  import java.util.ArrayList;
23  import java.util.List;
24  import java.util.Locale;
25  
26  import org.csveed.bean.BeanInstructionsImpl;
27  import org.csveed.bean.ColumnNameMapper;
28  import org.csveed.report.CsvException;
29  import org.csveed.test.converters.BeanSimpleConverter;
30  import org.csveed.test.model.BeanCustomComments;
31  import org.csveed.test.model.BeanSimple;
32  import org.csveed.test.model.BeanVariousNotAnnotated;
33  import org.csveed.test.model.BeanWithCustomNumber;
34  import org.csveed.test.model.BeanWithMultipleStrings;
35  import org.junit.jupiter.api.Test;
36  
37  /**
38   * The Class CsvClientTest.
39   */
40  class CsvClientTest {
41  
42      /**
43       * Write beans based on class.
44       *
45       * @throws IOException
46       *             Signals that an I/O exception has occurred.
47       */
48      @Test
49      void writeBeansBasedOnClass() throws IOException {
50          try (StringWriter writer = new StringWriter()) {
51              List<BeanWithMultipleStrings> beans = new ArrayList<>();
52              beans.add(createBean("row 1, cell 3", "row 1, cell 2", "row 1, cell 1"));
53              beans.add(createBean("row 2, cell 3", "row 2, cell 2", "row 2, cell 1"));
54              beans.add(createBean("row 3, cell 3", "row 3, cell 2", "row 3, cell 1"));
55              CsvClient<BeanWithMultipleStrings> client = new CsvClientImpl<>(writer, BeanWithMultipleStrings.class);
56              client.writeBeans(beans);
57  
58              assertEquals(
59                      "\"gamma\";\"beta\";\"alpha\"\r\n" + "\"row 1, cell 1\";\"row 1, cell 2\";\"row 1, cell 3\"\r\n"
60                              + "\"row 2, cell 1\";\"row 2, cell 2\";\"row 2, cell 3\"\r\n"
61                              + "\"row 3, cell 1\";\"row 3, cell 2\";\"row 3, cell 3\"\r\n",
62                      writer.getBuffer().toString());
63          }
64      }
65  
66      /**
67       * Write beans based on instructions.
68       *
69       * @throws IOException
70       *             Signals that an I/O exception has occurred.
71       */
72      @Test
73      void writeBeansBasedOnInstructions() throws IOException {
74          try (StringWriter writer = new StringWriter()) {
75              List<BeanWithMultipleStrings> beans = new ArrayList<>();
76              beans.add(createBean("row 1, cell 3", "row 1, cell 2", "row 1, cell 1"));
77              beans.add(createBean("row 2, cell 3", "row 2, cell 2", "row 2, cell 1"));
78              beans.add(createBean("row 3, cell 3", "row 3, cell 2", "row 3, cell 1"));
79              CsvClient<BeanWithMultipleStrings> client = new CsvClientImpl<>(writer,
80                      new BeanInstructionsImpl(BeanWithMultipleStrings.class).mapColumnNameToProperty("alpha", "alpha")
81                              .mapColumnNameToProperty("beta", "beta").ignoreProperty("gamma"));
82              client.writeBeans(beans);
83  
84              assertEquals(
85                      "\"beta\";\"alpha\"\r\n" + "\"row 1, cell 2\";\"row 1, cell 3\"\r\n"
86                              + "\"row 2, cell 2\";\"row 2, cell 3\"\r\n" + "\"row 3, cell 2\";\"row 3, cell 3\"\r\n",
87                      writer.getBuffer().toString());
88          }
89      }
90  
91      /**
92       * Creates the bean.
93       *
94       * @param alpha
95       *            the alpha
96       * @param beta
97       *            the beta
98       * @param gamma
99       *            the gamma
100      *
101      * @return the bean with multiple strings
102      */
103     private BeanWithMultipleStrings createBean(String alpha, String beta, String gamma) {
104         BeanWithMultipleStrings bean = new BeanWithMultipleStrings();
105         bean.setAlpha(alpha);
106         bean.setBeta(beta);
107         bean.setGamma(gamma);
108         return bean;
109     }
110 
111     /**
112      * Read and write rows.
113      *
114      * @throws IOException
115      *             Signals that an I/O exception has occurred.
116      */
117     @Test
118     void readAndWriteRows() throws IOException {
119         Reader reader = new StringReader(
120                 "alpha;beta;gamma\n" + "\"row 1, cell 1\";\"row 1, cell 2\";\"row 1, cell 3\"\n"
121                         + "\"row 2, cell 1\";\"row 2, cell 2\";\"row 2, cell 3\"\n");
122         CsvClient<Reader> csvReader = new CsvClientImpl<>(reader);
123         List<Row> rows = csvReader.readRows();
124 
125         try (StringWriter writer = new StringWriter()) {
126             CsvClient<StringWriter> csvWriter = new CsvClientImpl<>(writer);
127             csvWriter.writeHeader(rows.get(0).getHeader());
128             csvWriter.writeRows(rows);
129 
130             assertEquals(
131                     "\"alpha\";\"beta\";\"gamma\"\r\n" + "\"row 1, cell 1\";\"row 1, cell 2\";\"row 1, cell 3\"\r\n"
132                             + "\"row 2, cell 1\";\"row 2, cell 2\";\"row 2, cell 3\"\r\n",
133                     writer.getBuffer().toString());
134         }
135     }
136 
137     /**
138      * Write row.
139      *
140      * @throws IOException
141      *             Signals that an I/O exception has occurred.
142      */
143     @Test
144     void writeRow() throws IOException {
145         try (StringWriter writer = new StringWriter()) {
146             CsvClient<StringWriter> csvClient = new CsvClientImpl<StringWriter>(writer).setUseHeader(false);
147             csvClient.writeRow(new String[] { "alpha", "beta", "gamma" });
148 
149             assertEquals("\"alpha\";\"beta\";\"gamma\"\r\n", writer.getBuffer().toString());
150         }
151     }
152 
153     /**
154      * Write rows LF.
155      *
156      * @throws IOException
157      *             Signals that an I/O exception has occurred.
158      */
159     @Test
160     void writeRowsLF() throws IOException {
161         writeRows("\n");
162     }
163 
164     /**
165      * Write rows CRLF.
166      *
167      * @throws IOException
168      *             Signals that an I/O exception has occurred.
169      */
170     @Test
171     void writeRowsCRLF() throws IOException {
172         writeRows("\r\n");
173     }
174 
175     /**
176      * Write rows.
177      *
178      * @param lineTerminators
179      *            the line terminators
180      *
181      * @throws IOException
182      *             Signals that an I/O exception has occurred.
183      */
184     private void writeRows(String lineTerminators) throws IOException {
185         try (StringWriter writer = new StringWriter()) {
186             CsvClient<StringWriter> csvClient = new CsvClientImpl<StringWriter>(writer).setUseHeader(false)
187                     .setEndOfLine(lineTerminators.toCharArray());
188             csvClient.writeHeader(new String[] { "h1", "h2", "h3" });
189             csvClient.writeRows(new String[][] { { "l1c1", "l1c2", "l1c3" }, { "l2c1", "l2c2", "l2c3" },
190                     { "l3c1", "l3c2", "l3c3" } });
191 
192             assertEquals("\"h1\";\"h2\";\"h3\"" + lineTerminators + "\"l1c1\";\"l1c2\";\"l1c3\"" + lineTerminators
193                     + "\"l2c1\";\"l2c2\";\"l2c3\"" + lineTerminators + "\"l3c1\";\"l3c2\";\"l3c3\"" + lineTerminators,
194                     writer.getBuffer().toString());
195         }
196     }
197 
198     /**
199      * Windows CRLF 0 x 0 d 0 x 0 a.
200      */
201     @Test
202     void windowsCRLF0x0d0x0a() {
203         char[] file = { 'n', 'a', 'm', 'e', 0x0d, 0x0a, 'A', 'l', 'p', 'h', 'a', 0x0d, 0x0a, 'B', 'e', 't', 'a', 0x0d,
204                 0x0a, 'G', 'a', 'm', 'm', 'a' };
205         String fileText = new String(file);
206         Reader reader = new StringReader(fileText);
207         CsvClient<BeanSimple> csvClient = new CsvClientImpl<>(reader, BeanSimple.class);
208         final List<BeanSimple> beans = csvClient.readBeans();
209         assertEquals(3, beans.size());
210     }
211 
212     /**
213      * Do not skip comment line must cause column check to fail.
214      */
215     @Test
216     void doNotSkipCommentLineMustCauseColumnCheckToFail() {
217         Reader reader = new StringReader("name;name 2;name 3\n" + "# ignore me!\n");
218         CsvClient<StringWriter> csvClient = new CsvClientImpl<StringWriter>(reader).skipCommentLines(false);
219         assertThrows(CsvException.class, () -> csvClient.readRows());
220     }
221 
222     /**
223      * Custom comments.
224      */
225     @Test
226     void customComments() {
227         Reader reader = new StringReader("name\n" + "% ignore me!\n" + "some name\n");
228         CsvClient<BeanCustomComments> csvClient = new CsvClientImpl<>(reader, BeanCustomComments.class);
229         List<BeanCustomComments> beans = csvClient.readBeans();
230         assertEquals(1, beans.size());
231     }
232 
233     /**
234      * Call bean method on non bean reader facade.
235      */
236     @Test
237     void callBeanMethodOnNonBeanReaderFacade() {
238         Reader reader = new StringReader("");
239         CsvClient<StringWriter> csvClient = new CsvClientImpl<>(reader);
240         assertThrows(CsvException.class, () -> csvClient.readBean());
241     }
242 
243     /**
244      * Custom number conversion.
245      */
246     @Test
247     void customNumberConversion() {
248         Reader reader = new StringReader("money\n" + "11.398,22");
249         CsvClient<BeanWithCustomNumber> beanReader = new CsvClientImpl<>(reader, BeanWithCustomNumber.class)
250                 .setLocalizedNumber("number", Locale.GERMANY);
251         BeanWithCustomNumber bean = beanReader.readBean();
252         assertEquals(Double.valueOf(11398.22), bean.getNumber());
253     }
254 
255     /**
256      * Read lines LF.
257      */
258     @Test
259     void readLinesLF() {
260         readLines("\n");
261     }
262 
263     /**
264      * Read lines CRLF.
265      */
266     @Test
267     void readLinesCRLF() {
268         readLines("\r\n");
269     }
270 
271     /**
272      * Read lines.
273      *
274      * @param lineTerminators
275      *            the line terminators
276      */
277     private void readLines(String lineTerminators) {
278         Reader reader = new StringReader("text,year,number,date,lines,year and month" + lineTerminators
279                 + "'a bit of text',1983,42.42,1972-01-13,'line 1',2013-04" + lineTerminators
280                 + "'more text',1984,42.42,1972-01-14,'line 1" + lineTerminators + "line 2',2014-04" + lineTerminators
281                 + "# please ignore me" + lineTerminators + "'and yet more text',1985,42.42,1972-01-15,'line 1"
282                 + lineTerminators + "line 2" + lineTerminators + "line 3',2015-04" + lineTerminators);
283 
284         CsvClient<BeanVariousNotAnnotated> csvClient = new CsvClientImpl<>(reader, BeanVariousNotAnnotated.class)
285                 .setEscape('\\').setQuote('\'').setComment('#').setEndOfLine(new char[] { '\n' }).setSeparator(',')
286                 .setStartRow(1).setUseHeader(true).setMapper(ColumnNameMapper.class).ignoreProperty("ignoreMe")
287                 .mapColumnNameToProperty("text", "txt").setRequired("txt", true).mapColumnNameToProperty("year", "year")
288                 .mapColumnNameToProperty("number", "number").mapColumnNameToProperty("date", "date")
289                 .setDate("date", "yyyy-MM-dd").mapColumnNameToProperty("year and month", "yearMonth")
290                 .setDate("yearMonth", "yyyy-MM").mapColumnNameToProperty("lines", "simple")
291                 .setConverter("simple", new BeanSimpleConverter());
292 
293         List<BeanVariousNotAnnotated> beans = csvClient.readBeans();
294         assertTrue(csvClient.isFinished());
295         assertEquals(6, csvClient.getCurrentLine());
296         assertEquals(3, beans.size());
297     }
298 
299     /**
300      * Multiple header reads.
301      */
302     @Test
303     void multipleHeaderReads() {
304         Reader reader = new StringReader("text;year;number;date;lines;year and month\n"
305                 + "\"a bit of text\";1983;42.42;1972-01-13;\"line 1\";2013-04\n"
306                 + "\"more text\";1984;42.42;1972-01-14;\"line 1\nline 2\";2014-04\n"
307                 + "\"and yet more text\";1985;42.42;1972-01-15;\"line 1\nline 2\nline 3\";2015-04\n");
308         CsvClient<BeanVariousNotAnnotated> csvClient = new CsvClientImpl<>(reader, BeanVariousNotAnnotated.class);
309 
310         assertNotNull(csvClient.readHeader());
311         assertNotNull(csvClient.readHeader());
312     }
313 
314     /**
315      * Required field.
316      */
317     @Test
318     void requiredField() {
319         Reader reader = new StringReader("alpha;beta;gamma\n" + "\"l1c1\";\"l1c2\";\"l1c3\"\n"
320                 + "\"l2c1\";\"l2c2\";\"l2c3\"\n" + "\"l3c1\";\"l3c2\";");
321         CsvClient<BeanWithMultipleStrings> csvClient = new CsvClientImpl<>(reader, BeanWithMultipleStrings.class)
322                 .setMapper(ColumnNameMapper.class).setRequired("gamma", true);
323         assertThrows(CsvException.class, () -> csvClient.readBeans());
324     }
325 
326     /**
327      * Start at later line.
328      */
329     @Test
330     void startAtLaterLine() {
331         Reader reader = new StringReader("-- ignore line 1\n" + "-- ignore line 2\n" + "-- ignore line 3\n"
332                 + "text;year;number;date;lines;year and month\n"
333                 + "\"a bit of text\";1983;42.42;1972-01-13;\"line 1\";2013-04\n"
334                 + "\"more text\";1984;42.42;1972-01-14;\"line 1\nline 2\";2014-04\n"
335                 + "\"and yet more text\";1985;42.42;1972-01-15;\"line 1\nline 2\nline 3\";2015-04\n");
336         CsvClient<BeanVariousNotAnnotated> csvClient = new CsvClientImpl<>(reader, BeanVariousNotAnnotated.class)
337                 .setStartRow(4);
338         List<Row> rows = csvClient.readRows();
339         assertEquals(3, rows.size());
340         assertEquals(8, csvClient.getCurrentLine());
341     }
342 
343     /**
344      * Comment lines not skipped.
345      */
346     @Test
347     void commentLinesNotSkipped() {
348         Reader reader = new StringReader("Issue ID;Submitter\n" + "#1;Bill\n" + "#2;Mary\n" + "#3;Jane\n" + "#4;Will");
349         CsvClient<BeanSimple> csvClient = new CsvClientImpl<>(reader, BeanSimple.class).skipCommentLines(false);
350         List<Row> rows = csvClient.readRows();
351         assertEquals(4, rows.size());
352     }
353 
354     /**
355      * Header not written for otherwise empty csv.
356      *
357      * @throws IOException
358      *             Signals that an I/O exception has occurred.
359      */
360     @Test
361     void headerNotWrittenForOtherwiseEmptyCsv() throws IOException {
362         try (StringWriter writer = new StringWriter()) {
363             new CsvClientImpl<>(writer, BeanWithMultipleStrings.class);
364 
365             assertEquals("", writer.getBuffer().toString());
366         }
367     }
368 
369     /**
370      * Write header based on bean properties.
371      *
372      * @throws IOException
373      *             Signals that an I/O exception has occurred.
374      */
375     @Test
376     void writeHeaderBasedOnBeanProperties() throws IOException {
377         try (StringWriter writer = new StringWriter()) {
378             CsvClient<BeanWithMultipleStrings> client = new CsvClientImpl<>(writer, BeanWithMultipleStrings.class);
379             client.writeHeader();
380 
381             assertEquals("\"gamma\";\"beta\";\"alpha\"\r\n", writer.getBuffer().toString());
382         }
383     }
384 }