View Javadoc
1   package com.opencsv.bean;
2   
3   import com.opencsv.bean.mocks.recurse.*;
4   import com.opencsv.exceptions.*;
5   import org.apache.commons.lang3.StringUtils;
6   import org.junit.jupiter.api.Test;
7   
8   import java.io.StringReader;
9   import java.io.StringWriter;
10  import java.util.List;
11  
12  import static org.junit.jupiter.api.Assertions.*;
13  
14  public class RecursionTest {
15  
16      private static final String HEADER = "intLevelZero,stringLevelOne,charLevelTwo,floatLevelThree,booleanLevelThree,shortLevelThree\n";
17      private static final String DATA = "10,11,c,4.0,true,32\n";
18  
19      private RecursionMockLevelZero oneGoodMock() {
20          RecursionMockLevelZero bean = new RecursionMockLevelZero();
21          bean.setIntLevelZero(10);
22          RecursionMockLevelOne l1 = new RecursionMockLevelOne();
23          l1.setStringLevelOne("11");
24          bean.setLevelOne(l1);
25          RecursionMockLevelTwo l2 = new RecursionMockLevelTwo();
26          l2.setCharLevelTwo('c');
27          l1.setLevelTwo(l2);
28          l2.procureTheThirdLevelPointZero().setFloatLevelThree(4.0f);
29          RecursionMockLevelThreePointOne l3point1 = new RecursionMockLevelThreePointOne();
30          l3point1.setBooleanLevelThree(true);
31          l2.setLevelThreePointOne(l3point1);
32          RecursionMockLevelThreePointTwo l3point2 = new RecursionMockLevelThreePointTwo();
33          l3point2.setShortLevelThree((short)32);
34          l2.setLevelThreePointTwo(l3point2);
35          return bean;
36      }
37  
38      private RecursionMockLevelZeroNoAnnotations oneGoodMockNoAnnotations() {
39          RecursionMockLevelZeroNoAnnotations bean = new RecursionMockLevelZeroNoAnnotations();
40          bean.setIntLevelZero(10);
41          RecursionMockLevelOneNoAnnotations l1 = new RecursionMockLevelOneNoAnnotations();
42          l1.setStringLevelOne("11");
43          bean.setLevelOne(l1);
44          RecursionMockLevelTwoNoAnnotations l2 = new RecursionMockLevelTwoNoAnnotations();
45          l2.setCharLevelTwo('c');
46          l1.setLevelTwo(l2);
47          l2.procureTheThirdLevelPointZero().setFloatLevelThree(4.0f);
48          RecursionMockLevelThreePointOneNoAnnotations l3point1 = new RecursionMockLevelThreePointOneNoAnnotations();
49          l3point1.setBooleanLevelThree(true);
50          l2.setLevelThreePointOne(l3point1);
51          RecursionMockLevelThreePointTwoNoAnnotations l3point2 = new RecursionMockLevelThreePointTwoNoAnnotations();
52          l3point2.setShortLevelThree((short)32);
53          l2.setLevelThreePointTwo(l3point2);
54          return bean;
55      }
56  
57      private void checkReadingResults(List<RecursionMockLevelZero> beans) {
58          assertNotNull(beans);
59          assertEquals(1, beans.size());
60  
61          RecursionMockLevelZero b0 = beans.get(0);
62          assertEquals(10, b0.getIntLevelZero());
63  
64          RecursionMockLevelOne b1 = b0.getLevelOne();
65          assertEquals("11", b1.getStringLevelOne());
66  
67          RecursionMockLevelTwo b2 = b1.getLevelTwo();
68          assertEquals('c', b2.getCharLevelTwo());
69  
70          RecursionMockLevelThreePointZero b3point0 = b2.procureTheThirdLevelPointZero();
71          assertEquals(4.0f, b3point0.getFloatLevelThree());
72  
73          RecursionMockLevelThreePointOne b3point1 = b2.getLevelThreePointOne();
74          assertTrue(b3point1.isBooleanLevelThree());
75  
76          RecursionMockLevelThreePointTwo b3point2 = b2.getLevelThreePointTwo();
77          assertEquals((short)32, b3point2.getShortLevelThree());
78      }
79  
80      private void checkReadingResultsNoAnnotations(List<RecursionMockLevelZeroNoAnnotations> beans) {
81          assertNotNull(beans);
82          assertEquals(1, beans.size());
83  
84          RecursionMockLevelZeroNoAnnotations b0 = beans.get(0);
85          assertEquals(10, b0.getIntLevelZero());
86  
87          RecursionMockLevelOneNoAnnotations b1 = b0.getLevelOne();
88          assertEquals("11", b1.getStringLevelOne());
89  
90          RecursionMockLevelTwoNoAnnotations b2 = b1.getLevelTwo();
91          assertEquals('c', b2.getCharLevelTwo());
92  
93          RecursionMockLevelThreePointZeroNoAnnotations b3point0 = b2.procureTheThirdLevelPointZero();
94          assertEquals(4.0f, b3point0.getFloatLevelThree());
95  
96          RecursionMockLevelThreePointOneNoAnnotations b3point1 = b2.getLevelThreePointOne();
97          assertTrue(b3point1.isBooleanLevelThree());
98  
99          RecursionMockLevelThreePointTwoNoAnnotations b3point2 = b2.getLevelThreePointTwo();
100         assertEquals((short)32, b3point2.getShortLevelThree());
101     }
102 
103     @Test
104     public void testPrimitives() {
105 
106         // boolean
107         try {
108             new CsvToBeanBuilder<BooleanRecurse>(new StringReader(StringUtils.EMPTY))
109                     .withType(BooleanRecurse.class)
110                     .build();
111             fail("Exception should have been thrown.");
112         }
113         catch(CsvRecursionException e) {
114             assertFalse(StringUtils.isBlank(e.getMessage()));
115             assertEquals(Boolean.TYPE, e.getOffendingType());
116         }
117 
118         // byte
119         try {
120             new CsvToBeanBuilder<ByteRecurse>(new StringReader(StringUtils.EMPTY))
121                     .withType(ByteRecurse.class)
122                     .build();
123             fail("Exception should have been thrown.");
124         }
125         catch(CsvRecursionException e) {
126             assertFalse(StringUtils.isBlank(e.getMessage()));
127             assertEquals(Byte.TYPE, e.getOffendingType());
128         }
129 
130         // char
131         try {
132             new CsvToBeanBuilder<CharacterRecurse>(new StringReader(StringUtils.EMPTY))
133                     .withType(CharacterRecurse.class)
134                     .build();
135             fail("Exception should have been thrown.");
136         }
137         catch(CsvRecursionException e) {
138             assertFalse(StringUtils.isBlank(e.getMessage()));
139             assertEquals(Character.TYPE, e.getOffendingType());
140         }
141 
142         // double
143         try {
144             new CsvToBeanBuilder<DoubleRecurse>(new StringReader(StringUtils.EMPTY))
145                     .withType(DoubleRecurse.class)
146                     .build();
147             fail("Exception should have been thrown.");
148         }
149         catch(CsvRecursionException e) {
150             assertFalse(StringUtils.isBlank(e.getMessage()));
151             assertEquals(Double.TYPE, e.getOffendingType());
152         }
153 
154         // float
155         try {
156             new CsvToBeanBuilder<FloatRecurse>(new StringReader(StringUtils.EMPTY))
157                     .withType(FloatRecurse.class)
158                     .build();
159             fail("Exception should have been thrown.");
160         }
161         catch(CsvRecursionException e) {
162             assertFalse(StringUtils.isBlank(e.getMessage()));
163             assertEquals(Float.TYPE, e.getOffendingType());
164         }
165 
166         // int
167         try {
168             new CsvToBeanBuilder<IntegerRecurse>(new StringReader(StringUtils.EMPTY))
169                     .withType(IntegerRecurse.class)
170                     .build();
171             fail("Exception should have been thrown.");
172         }
173         catch(CsvRecursionException e) {
174             assertFalse(StringUtils.isBlank(e.getMessage()));
175             assertEquals(Integer.TYPE, e.getOffendingType());
176         }
177 
178         // long
179         try {
180             new CsvToBeanBuilder<LongRecurse>(new StringReader(StringUtils.EMPTY))
181                     .withType(LongRecurse.class)
182                     .build();
183             fail("Exception should have been thrown.");
184         }
185         catch(CsvRecursionException e) {
186             assertFalse(StringUtils.isBlank(e.getMessage()));
187             assertEquals(Long.TYPE, e.getOffendingType());
188         }
189 
190         // short
191         try {
192             new CsvToBeanBuilder<ShortRecurse>(new StringReader(StringUtils.EMPTY))
193                     .withType(ShortRecurse.class)
194                     .build();
195             fail("Exception should have been thrown.");
196         }
197         catch(CsvRecursionException e) {
198             assertFalse(StringUtils.isBlank(e.getMessage()));
199             assertEquals(Short.TYPE, e.getOffendingType());
200         }
201     }
202 
203     @Test
204     public void testDuplicateRecursion() {
205         try {
206             new CsvToBeanBuilder<DuplicateRecurse>(new StringReader(StringUtils.EMPTY))
207                     .withType(DuplicateRecurse.class)
208                     .build();
209             fail("Exception should have been thrown.");
210         }
211         catch(CsvRecursionException e) {
212             assertFalse(StringUtils.isBlank(e.getMessage()));
213             assertEquals(RecursionMockLevelZero.class, e.getOffendingType());
214         }
215     }
216 
217     @Test
218     public void testBindAndRecurse() {
219         try {
220             new CsvToBeanBuilder<BindAndRecurse>(new StringReader(StringUtils.EMPTY))
221                     .withType(BindAndRecurse.class)
222                     .build();
223             fail("Exception should have been thrown.");
224         }
225         catch(CsvRecursionException e) {
226             assertFalse(StringUtils.isBlank(e.getMessage()));
227             assertEquals(RecursionMockLevelZero.class, e.getOffendingType());
228         }
229     }
230 
231     /**
232      * Tests that reading data into beans with recursion in use and using the
233      * header name mapping strategy functions as expected.
234      * <p>Also incidentally tests:<ul>
235      *     <li>Creation of subordinate beans</li>
236      *     <li>Access to subordinate beans using standard accessor methods</li>
237      *     <li>Access to subordinate beans using reflection</li>
238      *     <li>Bean creates subordinate bean (enforced by no nullary constructor)</li>
239      *     <li>Multiple levels of recursion</li>
240      *     <li>Multiple recursion directives in one enclosing bean</li>
241      * </ul></p>
242      */
243     @Test
244     public void testReadingHeaderNames() {
245         MappingStrategy<RecursionMockLevelZero> strategy = new HeaderColumnNameMappingStrategy<>();
246         strategy.setType(RecursionMockLevelZero.class);
247         CsvToBean<RecursionMockLevelZero> csvToBean =
248                 new CsvToBeanBuilder<RecursionMockLevelZero>(new StringReader(
249                         HEADER + DATA))
250                         .withMappingStrategy(strategy)
251                         .build();
252         List<RecursionMockLevelZero> beans = csvToBean.parse();
253         List<CsvException> exceptions = csvToBean.getCapturedExceptions();
254         assertNotNull(exceptions);
255         assertTrue(exceptions.isEmpty());
256         checkReadingResults(beans);
257     }
258 
259     @Test
260     public void testReadingHeaderNamesNoAnnotations() {
261         CsvToBean<RecursionMockLevelZeroNoAnnotations> csvToBean =
262                 new CsvToBeanBuilder<RecursionMockLevelZeroNoAnnotations>(new StringReader(
263                         HEADER + DATA))
264                         .withType(RecursionMockLevelZeroNoAnnotations.class)
265                         .build();
266         List<RecursionMockLevelZeroNoAnnotations> beans = csvToBean.parse();
267         List<CsvException> exceptions = csvToBean.getCapturedExceptions();
268         assertNotNull(exceptions);
269         assertTrue(exceptions.isEmpty());
270         checkReadingResultsNoAnnotations(beans);
271     }
272 
273     @Test
274     public void testReadingColumnPositions() {
275         CsvToBean<RecursionMockLevelZero> csvToBean =
276                 new CsvToBeanBuilder<RecursionMockLevelZero>(new StringReader(DATA))
277                         .withType(RecursionMockLevelZero.class)
278                         .build();
279         List<RecursionMockLevelZero> beans = csvToBean.parse();
280         List<CsvException> exceptions = csvToBean.getCapturedExceptions();
281         assertNotNull(exceptions);
282         assertTrue(exceptions.isEmpty());
283         checkReadingResults(beans);
284     }
285 
286     @Test
287     public void testEmbeddedBeanNoNullaryConstructor() {
288         try {
289             CsvToBean<EmbeddedBeanNoNullaryConstructor> csvToBean = new CsvToBeanBuilder<EmbeddedBeanNoNullaryConstructor>(new StringReader(DATA))
290                     .withType(EmbeddedBeanNoNullaryConstructor.class)
291                     .build();
292             csvToBean.parse();
293             fail("Exception should have been thrown.");
294         } catch (RuntimeException e) {
295             assertFalse(StringUtils.isBlank(e.getMessage()));
296             assertTrue(e.getCause() instanceof CsvBeanIntrospectionException);
297             CsvBeanIntrospectionException csve = (CsvBeanIntrospectionException)e.getCause();
298             assertFalse(StringUtils.isBlank(csve.getMessage()));
299             assertNull(csve.getField());
300             assertNull(csve.getBean());
301             assertNotNull(csve.getCause());
302         }
303     }
304 
305     @Test
306     public void testWritingHeaderNames() throws CsvDataTypeMismatchException, CsvRequiredFieldEmptyException {
307         StringWriter w = new StringWriter();
308         MappingStrategy<RecursionMockLevelZero> strategy = new HeaderColumnNameMappingStrategy<>();
309         strategy.setType(RecursionMockLevelZero.class);
310         StatefulBeanToCsv<RecursionMockLevelZero> b2c = new StatefulBeanToCsvBuilder<RecursionMockLevelZero>(w)
311                 .withMappingStrategy(strategy)
312                 .withApplyQuotesToAll(false)
313                 .build();
314         b2c.write(oneGoodMock());
315         assertEquals("BOOLEANLEVELTHREE,CHARLEVELTWO,FLOATLEVELTHREE,INTLEVELZERO,SHORTLEVELTHREE,STRINGLEVELONE\n" +
316                 "true,c,4.0,10,32,11\n", w.toString());
317     }
318 
319     @Test
320     public void testWritingHeaderNamesNoAnnotations() throws CsvDataTypeMismatchException, CsvRequiredFieldEmptyException {
321         StringWriter w = new StringWriter();
322         StatefulBeanToCsv<RecursionMockLevelZeroNoAnnotations> b2c = new StatefulBeanToCsvBuilder<RecursionMockLevelZeroNoAnnotations>(w)
323                 .withApplyQuotesToAll(false)
324                 .build();
325         b2c.write(oneGoodMockNoAnnotations());
326         assertEquals("BOOLEANLEVELTHREE,CHARLEVELTWO,FLOATLEVELTHREE,INTLEVELZERO,SHORTLEVELTHREE,STRINGLEVELONE\n" +
327                 "true,c,4.0,10,32,11\n", w.toString());
328     }
329 
330     @Test
331     public void testWritingColumnPositions() throws CsvDataTypeMismatchException, CsvRequiredFieldEmptyException {
332         StringWriter w = new StringWriter();
333         StatefulBeanToCsv<RecursionMockLevelZero> b2c = new StatefulBeanToCsvBuilder<RecursionMockLevelZero>(w)
334                 .withApplyQuotesToAll(false)
335                 .build();
336         b2c.write(oneGoodMock());
337         assertEquals(DATA, w.toString());
338     }
339 
340     @Test
341     public void testNullMemberVariableOptional() throws CsvDataTypeMismatchException, CsvRequiredFieldEmptyException {
342         StringWriter w = new StringWriter();
343         StatefulBeanToCsv<RecursionMockLevelZero> b2c = new StatefulBeanToCsvBuilder<RecursionMockLevelZero>(w)
344                 .withApplyQuotesToAll(false)
345                 .build();
346         RecursionMockLevelZero bean = oneGoodMock();
347         bean.getLevelOne().setLevelTwo(null);
348         b2c.write(bean);
349         assertEquals("10,11,,,,\n", w.toString());
350     }
351 
352     @Test
353     public void testNullMemberVariableRequired() throws CsvDataTypeMismatchException {
354         StringWriter w = new StringWriter();
355         StatefulBeanToCsv<RecursionMockLevelZero> b2c = new StatefulBeanToCsvBuilder<RecursionMockLevelZero>(w)
356                 .withApplyQuotesToAll(false)
357                 .build();
358         RecursionMockLevelZero bean = oneGoodMock();
359         bean.setLevelOne(null);
360         try {
361             b2c.write(bean);
362             fail("Exception should have been thrown.");
363         } catch (CsvRequiredFieldEmptyException e) {
364             assertEquals(RecursionMockLevelOne.class, e.getBeanClass());
365             assertNotNull(e.getDestinationFields());
366             assertFalse(e.getDestinationFields().isEmpty());
367             assertEquals("stringLevelOne", e.getDestinationFields().get(0).getName());
368         }
369     }
370 }