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.bean;
12  
13  import static org.junit.jupiter.api.Assertions.assertEquals;
14  import static org.junit.jupiter.api.Assertions.assertNull;
15  import static org.junit.jupiter.api.Assertions.assertThrows;
16  
17  import java.io.Reader;
18  import java.io.StringReader;
19  import java.text.SimpleDateFormat;
20  import java.util.List;
21  
22  import org.csveed.report.CsvException;
23  import org.csveed.test.model.BeanCommodity;
24  import org.csveed.test.model.BeanLotsOfIgnores;
25  import org.csveed.test.model.BeanSimple;
26  import org.csveed.test.model.BeanWithAlienSettings;
27  import org.csveed.test.model.BeanWithConverter;
28  import org.csveed.test.model.BeanWithCustomIndexes;
29  import org.csveed.test.model.BeanWithCustomNumberAnnotated;
30  import org.csveed.test.model.BeanWithEnum;
31  import org.csveed.test.model.BeanWithEnumAndMore;
32  import org.csveed.test.model.BeanWithMultipleStrings;
33  import org.csveed.test.model.BeanWithNameMatching;
34  import org.csveed.test.model.BeanWithNonStandardObject;
35  import org.csveed.test.model.BeanWithVariousTypes;
36  import org.csveed.test.model.BeanWithoutHeader;
37  import org.csveed.test.model.BeanWithoutNoArgPublicConstructor;
38  import org.csveed.token.ParseState;
39  import org.junit.jupiter.api.Test;
40  
41  /**
42   * The Class BeanReaderTest.
43   */
44  class BeanReaderTest {
45  
46      /**
47       * Dynamic columns.
48       */
49      @Test
50      void dynamicColumns() {
51          Reader reader = new StringReader(
52                  "commodity;language;14-01;14-02;14-03\n" + "corn;NL;1;2;3\n" + "corn;BE;4;5;6\n");
53          BeanReader<BeanCommodity> beanReader = new BeanReaderImpl<>(reader, BeanCommodity.class);
54          List<BeanCommodity> commodities = beanReader.readBeans();
55          assertEquals(6, commodities.size());
56          assertBeanCommodity(commodities.get(0), "corn", "NL", "14-01", 1);
57          assertBeanCommodity(commodities.get(1), "corn", "NL", "14-02", 2);
58          assertBeanCommodity(commodities.get(2), "corn", "NL", "14-03", 3);
59          assertBeanCommodity(commodities.get(3), "corn", "BE", "14-01", 4);
60          assertBeanCommodity(commodities.get(4), "corn", "BE", "14-02", 5);
61          assertBeanCommodity(commodities.get(5), "corn", "BE", "14-03", 6);
62      }
63  
64      /**
65       * Assert bean commodity.
66       *
67       * @param beanCommodity
68       *            the bean commodity
69       * @param expectedCommodity
70       *            the expected commodity
71       * @param expectedLanguage
72       *            the expected language
73       * @param expectedDay
74       *            the expected day
75       * @param expectedAmount
76       *            the expected amount
77       */
78      protected void assertBeanCommodity(BeanCommodity beanCommodity, String expectedCommodity, String expectedLanguage,
79              String expectedDay, int expectedAmount) {
80          assertEquals(expectedCommodity, beanCommodity.getCommodity());
81          assertEquals(expectedLanguage, beanCommodity.getLanguage());
82          assertEquals(expectedDay, beanCommodity.getDay());
83          assertEquals(expectedAmount, beanCommodity.getAmount());
84      }
85  
86      /**
87       * Enum may be null.
88       */
89      @Test
90      void enumMayBeNull() {
91          Reader reader = new StringReader(
92                  "name;parseState\n" + "alpha;\"FIRST_CHAR_INSIDE_QUOTED_FIELD\"\n" + "beta;\n");
93          BeanReader<BeanWithEnumAndMore> beanReader = new BeanReaderImpl<>(reader, BeanWithEnumAndMore.class);
94          List<BeanWithEnumAndMore> beans = beanReader.readBeans();
95          assertEquals(2, beans.size());
96          assertEquals(null, beans.get(1).getParseState());
97      }
98  
99      /**
100      * Convert to enum.
101      */
102     @Test
103     void convertToEnum() {
104         Reader reader = new StringReader("parseState\n" + "\"FIRST_CHAR_INSIDE_QUOTED_FIELD\"");
105         BeanReader<BeanWithEnum> beanReader = new BeanReaderImpl<>(reader, BeanWithEnum.class);
106         BeanWithEnum bean = beanReader.readBean();
107         assertEquals(ParseState.FIRST_CHAR_INSIDE_QUOTED_FIELD, bean.getParseState());
108     }
109 
110     /**
111      * Missing converter.
112      */
113     @Test
114     void missingConverter() {
115         Reader reader = new StringReader("alpha\n" + "\"row 1, cell 1\"");
116         BeanReader<BeanWithNonStandardObject> beanReader = new BeanReaderImpl<>(reader,
117                 BeanWithNonStandardObject.class);
118         assertThrows(CsvException.class, () -> beanReader.readBean());
119     }
120 
121     /**
122      * Illegal column index mapping too low.
123      */
124     @Test
125     void illegalColumnIndexMappingTooLow() {
126         assertThrows(CsvException.class, () -> new BeanInstructionsImpl(BeanWithMultipleStrings.class)
127                 .setMapper(ColumnIndexMapper.class).mapColumnIndexToProperty(-1, "alpha"));
128     }
129 
130     /**
131      * Illegal column index mapping too high.
132      */
133     @Test
134     void illegalColumnIndexMappingTooHigh() {
135         checkIllegalMapping(new BeanInstructionsImpl(BeanWithMultipleStrings.class).setMapper(ColumnIndexMapper.class)
136                 .mapColumnIndexToProperty(99, "alpha"));
137     }
138 
139     /**
140      * Illegal column name.
141      */
142     @Test
143     void illegalColumnName() {
144         checkIllegalMapping(new BeanInstructionsImpl(BeanWithMultipleStrings.class).setMapper(ColumnNameMapper.class)
145                 .mapColumnNameToProperty("Alphabetical", "alpha"));
146     }
147 
148     /**
149      * Check illegal mapping.
150      *
151      * @param beanInstructions
152      *            the bean instructions
153      */
154     protected void checkIllegalMapping(BeanInstructions beanInstructions) {
155         Reader reader = new StringReader(
156                 "alpha;beta;gamma\n" + "\"row 1, cell 1\";\"row 1, cell 2\";\"row 1, cell 3\"");
157         BeanReader<BeanWithMultipleStrings> beanReader = new BeanReaderImpl<>(reader, beanInstructions);
158         assertThrows(CsvException.class, () -> beanReader.readBean());
159     }
160 
161     /**
162      * Custom number conversion.
163      */
164     @Test
165     void customNumberConversion() {
166         Reader reader = new StringReader("money\n" + "11.398,22");
167         BeanReader<BeanWithCustomNumberAnnotated> beanReader = new BeanReaderImpl<>(reader,
168                 BeanWithCustomNumberAnnotated.class);
169         BeanWithCustomNumberAnnotated bean = beanReader.readBean();
170         assertEquals(Double.valueOf(11398.22), bean.getNumber());
171     }
172 
173     /**
174      * Gets the beans manual mapping.
175      */
176     @Test
177     void getBeansManualMapping() {
178         Reader reader = new StringReader("a;c;b\n" + "\"row 1, cell 1\";\"row 1, cell 2\";\"row 1, cell 3\"\n"
179                 + "\"row 2, cell 1\";\"row 2, cell 2\";\"row 2, cell 3\"\n"
180                 + "\"row 3, cell 1\";\"row 3, cell 2\";\"row 3, cell 3\"");
181         BeanReader<BeanWithMultipleStrings> beanReader = new BeanReaderImpl<>(reader,
182                 new BeanInstructionsImpl(BeanWithMultipleStrings.class).setMapper(ColumnNameMapper.class)
183                         .mapColumnNameToProperty("a", "alpha").mapColumnNameToProperty("b", "beta")
184                         .mapColumnNameToProperty("c", "gamma"));
185         List<BeanWithMultipleStrings> beans = beanReader.readBeans();
186         assertEquals(3, beans.size());
187         BeanWithMultipleStrings bean = beans.get(0);
188         assertEquals("row 1, cell 1", bean.getAlpha());
189         assertEquals("row 1, cell 2", bean.getGamma());
190         assertEquals("row 1, cell 3", bean.getBeta());
191     }
192 
193     /**
194      * Gets the beans.
195      */
196     @Test
197     void getBeans() {
198         Reader reader = new StringReader(
199                 "alpha;beta;gamma\n" + "\"row 1, cell 1\";\"row 1, cell 2\";\"row 1, cell 3\"\n"
200                         + "\"row 2, cell 1\";\"row 2, cell 2\";\"row 2, cell 3\"\n"
201                         + "\"row 3, cell 1\";\"row 3, cell 2\";\"row 3, cell 3\"");
202         BeanReader<BeanWithMultipleStrings> beanReader = new BeanReaderImpl<>(reader, BeanWithMultipleStrings.class);
203         List<BeanWithMultipleStrings> beans = beanReader.readBeans();
204         assertEquals(3, beans.size());
205         BeanWithMultipleStrings bean = beans.get(0);
206         assertEquals("row 1, cell 1", bean.getGamma());
207         assertEquals("row 1, cell 2", bean.getBeta());
208         assertEquals("row 1, cell 3", bean.getAlpha());
209     }
210 
211     /**
212      * Tab separated.
213      */
214     @Test
215     void tabSeparated() {
216         Reader reader = new StringReader(
217                 "alpha\tbeta\tgamma\r" + "'\\'row\\' 1, cell 1'\t'row 1, cell 2'\t'row 1, cell 3'\r"
218                         + "'\\'row\\' 2, cell 1'\t'row 2, cell 2'\t'row 2, cell 3'\r"
219                         + "'\\'row\\' 3, cell 1'\t'row 3, cell 2'\t'row 3, cell 3'");
220         BeanReader<BeanWithAlienSettings> beanReader = new BeanReaderImpl<>(reader, BeanWithAlienSettings.class);
221         List<BeanWithAlienSettings> beans = beanReader.readBeans();
222         assertEquals(3, beans.size());
223         BeanWithAlienSettings bean = beans.get(0);
224         assertEquals("'row' 1, cell 1", bean.getGamma());
225         assertEquals("row 1, cell 2", bean.getBeta());
226         assertEquals("row 1, cell 3", bean.getAlpha());
227     }
228 
229     /**
230      * Error in date.
231      */
232     @Test
233     void errorInDate() {
234         // Month and day in reverse order
235         Reader reader = new StringReader(
236                 "text;year;number;date;year and month\n\"a bit of text\";1984;42.42;1972-13-01;2013-04\n");
237         BeanReader<BeanWithVariousTypes> beanReader = new BeanReaderImpl<>(reader, BeanWithVariousTypes.class);
238         assertThrows(CsvException.class, () -> beanReader.readBeans());
239     }
240 
241     /**
242      * Various data types.
243      */
244     @Test
245     void variousDataTypes() {
246         Reader reader = new StringReader(
247                 "text;year;number;date;year and month\n" + "\"a bit of text\";1984;42.42;1972-01-13;2013-04\n");
248         BeanReader<BeanWithVariousTypes> beanReader = new BeanReaderImpl<>(reader, BeanWithVariousTypes.class);
249         List<BeanWithVariousTypes> beans = beanReader.readBeans();
250         assertEquals(1, beans.size());
251         BeanWithVariousTypes bean = beans.get(0);
252         assertEquals("a bit of text", bean.getText());
253         assertEquals((Integer) 1984, bean.getYear());
254         assertEquals(Double.valueOf(42.42), bean.getNumber());
255         SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
256         assertEquals("1972-01-13", formatter.format(bean.getDate()));
257         formatter = new SimpleDateFormat("yyyy-MM");
258         assertEquals("2013-04", formatter.format(bean.getYearMonth()));
259     }
260 
261     /**
262      * No header.
263      */
264     @Test
265     void noHeader() {
266         Reader reader = new StringReader("\"a bit of text\";1984;42.42;1972-01-13;2013-04\n");
267         BeanReader<BeanWithoutHeader> beanReader = new BeanReaderImpl<>(reader, BeanWithoutHeader.class);
268         List<BeanWithoutHeader> beans = beanReader.readBeans();
269         assertEquals(1, beans.size());
270     }
271 
272     /**
273      * Name matching.
274      */
275     @Test
276     void nameMatching() {
277         Reader reader = new StringReader("street;CITY;postal code;ignore this\n"
278                 + "\"Some street\";\"Some city\";\"Some postal code\";\"Some ignoring\"");
279         BeanReader<BeanWithNameMatching> beanReader = new BeanReaderImpl<>(reader, BeanWithNameMatching.class);
280         List<BeanWithNameMatching> beans = beanReader.readBeans();
281         assertEquals(1, beans.size());
282         BeanWithNameMatching bean = beans.get(0);
283         assertEquals("Some street", bean.getLine1());
284         assertEquals("Some city", bean.getLine2());
285         assertEquals("Some postal code", bean.getLine3());
286     }
287 
288     /**
289      * Name matching with bom.
290      */
291     @Test
292     void nameMatchingWithBom() {
293         Reader reader = new StringReader("\uFEFFstreet;CITY;postal code;ignore this\n"
294                 + "\"Some street\";\"Some city\";\"Some postal code\";\"Some ignoring\"");
295         BeanReader<BeanWithNameMatching> beanReader = new BeanReaderImpl<>(reader, BeanWithNameMatching.class);
296         List<BeanWithNameMatching> beans = beanReader.readBeans();
297         assertEquals(1, beans.size());
298         BeanWithNameMatching bean = beans.get(0);
299         assertEquals("Some street", bean.getLine1());
300         assertEquals("Some city", bean.getLine2());
301         assertEquals("Some postal code", bean.getLine3());
302     }
303 
304     /**
305      * Index matching.
306      */
307     @Test
308     void indexMatching() {
309         Reader reader = new StringReader("\"line-1\";\"line0\";\"line1\";\"line2\";\"line3\"");
310         BeanReader<BeanWithCustomIndexes> beanReader = new BeanReaderImpl<>(reader, BeanWithCustomIndexes.class);
311         BeanWithCustomIndexes bean = beanReader.readBean();
312         assertEquals("line0", bean.getLine0());
313         assertEquals("line1", bean.getLine1());
314         assertEquals("line2", bean.getLine2());
315         assertEquals("line3", bean.getLine3());
316     }
317 
318     /**
319      * Number of ignores.
320      */
321     @Test
322     void numberOfIgnores() {
323         Reader reader = new StringReader("14;28;42");
324         BeanReader<BeanLotsOfIgnores> beanReader = new BeanReaderImpl<>(reader, BeanLotsOfIgnores.class);
325         BeanLotsOfIgnores bean = beanReader.readBean();
326         assertEquals((Integer) 14, bean.getTakeThis1());
327         assertEquals((Integer) 28, bean.getPickThis1());
328         assertEquals((Integer) 42, bean.getChooseThis1());
329         assertNull(bean.getDitchThat1());
330         assertNull(bean.getLeaveThat1());
331     }
332 
333     /**
334      * Custom property editor.
335      */
336     @Test
337     void customPropertyEditor() {
338         Reader reader = new StringReader("\"some text\"");
339         BeanReader<BeanWithConverter> beanReader = new BeanReaderImpl<>(reader, BeanWithConverter.class);
340         BeanWithConverter bean = beanReader.readBean();
341         assertEquals("some text", bean.getBean().getName());
342     }
343 
344     /**
345      * Illegal token.
346      */
347     @Test
348     void illegalToken() {
349         Reader reader = new StringReader("\"alpha\";\"beta\";\"gamma\"a\n");
350         BeanReader<BeanSimple> beanReader = new BeanReaderImpl<>(reader, BeanSimple.class);
351         assertThrows(CsvException.class, () -> beanReader.readBeans());
352     }
353 
354     /**
355      * Bean mapping error.
356      */
357     @Test
358     void beanMappingError() {
359         Reader reader = new StringReader("text;year;number;date;year and month\n"
360                 + "\"a bit of text\";UNEXPECTED TEXT!!!;42.42;1972-01-13;2013-04\n");
361         BeanReader<BeanWithVariousTypes> beanReader = new BeanReaderImpl<>(reader, BeanWithVariousTypes.class);
362         assertThrows(CsvException.class, () -> beanReader.readBeans());
363     }
364 
365     /**
366      * Cannot convert to non standard object.
367      */
368     @Test
369     void cannotConvertToNonStandardObject() {
370         Reader reader = new StringReader("\"can I convert this to a simple bean?\"");
371         BeanReader<BeanWithNonStandardObject> beanReader = new BeanReaderImpl<>(reader,
372                 BeanWithNonStandardObject.class);
373         assertThrows(CsvException.class, () -> beanReader.readBeans());
374     }
375 
376     /**
377      * Non instantiable bean.
378      */
379     @Test
380     void nonInstantiableBean() {
381         Reader reader = new StringReader("\"can I convert this to a simple bean?\"");
382         BeanReader<BeanWithoutNoArgPublicConstructor> beanReader = new BeanReaderImpl<>(reader,
383                 BeanWithoutNoArgPublicConstructor.class);
384         assertThrows(CsvException.class, () -> beanReader.readBeans());
385     }
386 
387 }