View Javadoc
1   /*
2    * Copyright 2007 Kasper B. Graversen
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 org.supercsv.io.dozer;
17  
18  import static org.junit.Assert.assertEquals;
19  import static org.junit.Assert.assertNotNull;
20  import static org.junit.Assert.assertNull;
21  import static org.junit.Assert.fail;
22  
23  import java.io.IOException;
24  import java.io.Reader;
25  import java.io.StringReader;
26  import java.util.ArrayList;
27  import java.util.Arrays;
28  
29  import java.util.List;
30  import org.dozer.DozerBeanMapper;
31  import org.junit.After;
32  import org.junit.Before;
33  import org.junit.Test;
34  import org.supercsv.cellprocessor.Optional;
35  import org.supercsv.cellprocessor.ParseBool;
36  import org.supercsv.cellprocessor.ParseInt;
37  import org.supercsv.cellprocessor.ift.CellProcessor;
38  import org.supercsv.exception.SuperCsvException;
39  import org.supercsv.io.ITokenizer;
40  import org.supercsv.io.Tokenizer;
41  import org.supercsv.mock.dozer.Answer;
42  import org.supercsv.mock.dozer.SurveyResponse;
43  import org.supercsv.prefs.CsvPreference;
44  import org.supercsv.util.CsvContext;
45  
46  /**
47   * Tests the CsvDozerBeanReader class.
48   * 
49   * @author James Bassett
50   */
51  public class CsvDozerBeanReaderTest {
52  	
53  	private static final CsvPreference PREFS = CsvPreference.STANDARD_PREFERENCE;
54  	private static final boolean EXISTING_BEAN = true;
55  	private static final boolean CREATE_NEW_BEAN = false;
56  	private static final boolean CONFIGURED = true;
57  	private static final boolean NOT_CONFIGURED = false;
58  	private static final boolean USE_PROCESSORS = true;
59  	private static final boolean NO_PROCESSORS = false;
60  	
61  	private Reader reader;
62  	
63  	private CsvDozerBeanReader beanReader;
64  	private CsvDozerBeanReader beanReaderWithMapper;
65  	private CsvDozerBeanReader beanReaderWithConfiguredMapper;
66  	private CsvDozerBeanReader tokenizerBeanReader;
67  	private CsvDozerBeanReader tokenizerBeanReaderWithMapper;
68  	private ITokenizer tokenizer;
69  	private DozerBeanMapper beanMapper;
70  	private DozerBeanMapper configuredBeanMapper;
71  	
72  	private static final String[] FIELD_MAPPING = new String[] { "age", "consentGiven", "answers[0].questionNo",
73  		"answers[0].answer", "answers[1].questionNo", "answers[1].answer", "answers[2].questionNo", "answers[2].answer" };
74  	
75  	private static final CellProcessor[] PROCESSORS = new CellProcessor[] { new ParseInt(), new ParseBool(),
76  		new ParseInt(), new Optional(), new ParseInt(), new Optional(), new ParseInt(), new Optional() };
77  	
78  	private static final String CSV = "age,consentGiven,questionNo1,answer1,questionNo2,answer2,questionNo3,answer3\n"
79  		+ "23,Y,1,Nikola Tesla,2,\"\"\"A brief history of time\"\" by Steven Hawking\",3,Theoretical physicist\n"
80  		+ "16,Y,1,Genghis Kahn,2,\"\"\"Monsoon\"\" by Wilbur Smith\",3,\n"
81  		+ "44,Y,1,,2,,3,\"I hate surveys, thanks for wasting my time!\"";
82  
83  	private static final String BROKEN_CSV = "age,consentGiven,questionNo1,answer1,questionNo2,answer2,questionNo3,answer3\n"
84  			+ "23,Y,1,Nikola Tesla\",2,A brief history of time by Steven Hawking,3,Theoretical physicist\n"
85  			+ "16,Y,1,Genghis Kahn,2,Monsoon by Wilbur Smith,3,\n"
86  			+ "44,Y,1,,2,,3,\"I hate surveys, thanks for wasting my time!\"";
87  
88  
89  	/**
90  	 * Sets up the readers for the tests (all four constructor varieties are tested!).
91  	 */
92  	@Before
93  	public void setUp() {
94  		reader = new StringReader(CSV);
95  		beanReader = new CsvDozerBeanReader(reader, PREFS);
96  		
97  		tokenizer = new Tokenizer(reader, PREFS);
98  		tokenizerBeanReader = new CsvDozerBeanReader(tokenizer, PREFS);
99  		
100 		beanMapper = new DozerBeanMapper();
101 		beanReaderWithMapper = new CsvDozerBeanReader(reader, PREFS, beanMapper);
102 		
103 		configuredBeanMapper = new DozerBeanMapper(Arrays.asList("reference.xml"));
104 		beanReaderWithConfiguredMapper = new CsvDozerBeanReader(reader, PREFS, configuredBeanMapper);
105 		
106 		tokenizerBeanReaderWithMapper = new CsvDozerBeanReader(tokenizer, PREFS, beanMapper);
107 	}
108 	
109 	/**
110 	 * Closes the readers after the test.
111 	 */
112 	@After
113 	public void tearDown() throws IOException {
114 		beanReader.close();
115 		tokenizerBeanReader.close();
116 		beanReaderWithMapper.close();
117 		beanReaderWithConfiguredMapper.close();
118 		tokenizerBeanReaderWithMapper.close();
119 	}
120 	
121 	/**
122 	 * Tests the read() method without any processors for a standard bean reader.
123 	 */
124 	@Test
125 	public void testReadForBeanReader() throws IOException {
126 		testRead(beanReader, NO_PROCESSORS, NOT_CONFIGURED, CREATE_NEW_BEAN);
127 	}
128 	
129 	/**
130 	 * Tests the read() method without any processors for a standard bean reader.
131 	 */
132 	@Test
133 	public void testReadForBeanReaderWithExistingBean() throws IOException {
134 		testRead(beanReader, NO_PROCESSORS, NOT_CONFIGURED, EXISTING_BEAN);
135 	}
136 	
137 	/**
138 	 * Tests the read() method using processors for a standard bean reader.
139 	 */
140 	@Test
141 	public void testReadForBeanReaderUsingProcessors() throws IOException {
142 		testRead(beanReader, USE_PROCESSORS, NOT_CONFIGURED, CREATE_NEW_BEAN);
143 	}
144 	
145 	/**
146 	 * Tests the read() method using processors for a standard bean reader.
147 	 */
148 	@Test
149 	public void testReadForBeanReaderUsingProcessorsWithExistingBean() throws IOException {
150 		testRead(beanReader, USE_PROCESSORS, NOT_CONFIGURED, EXISTING_BEAN);
151 	}
152 	
153 	/**
154 	 * Tests the read() method without any processors for a bean reader with custom tokenizer.
155 	 */
156 	@Test
157 	public void testReadForTokenizerBeanReader() throws IOException {
158 		testRead(tokenizerBeanReader, NO_PROCESSORS, NOT_CONFIGURED, CREATE_NEW_BEAN);
159 	}
160 	
161 	/**
162 	 * Tests the read() method without any processors for a bean reader with custom tokenizer.
163 	 */
164 	@Test
165 	public void testReadForTokenizerBeanReaderWithExistingBean() throws IOException {
166 		testRead(tokenizerBeanReader, NO_PROCESSORS, NOT_CONFIGURED, EXISTING_BEAN);
167 	}
168 	
169 	/**
170 	 * Tests the read() method using processors for a bean reader with custom tokenizer.
171 	 */
172 	@Test
173 	public void testReadForTokenizerBeanReaderUsingProcessors() throws IOException {
174 		testRead(tokenizerBeanReader, USE_PROCESSORS, NOT_CONFIGURED, CREATE_NEW_BEAN);
175 	}
176 	
177 	/**
178 	 * Tests the read() method using processors for a bean reader with custom tokenizer.
179 	 */
180 	@Test
181 	public void testReadForTokenizerBeanReaderUsingProcessorsWithExistingBean() throws IOException {
182 		testRead(tokenizerBeanReader, USE_PROCESSORS, NOT_CONFIGURED, EXISTING_BEAN);
183 	}
184 	
185 	/**
186 	 * Tests the read() method without any processors for a bean reader with custom DozerBeanMapper.
187 	 */
188 	@Test
189 	public void testReadForBeanReaderWithMapper() throws IOException {
190 		testRead(beanReaderWithMapper, NO_PROCESSORS, NOT_CONFIGURED, CREATE_NEW_BEAN);
191 	}
192 	
193 	/**
194 	 * Tests the read() method without any processors for a bean reader with custom DozerBeanMapper.
195 	 */
196 	@Test
197 	public void testReadForBeanReaderWithMapperWithExistingBean() throws IOException {
198 		testRead(beanReaderWithMapper, NO_PROCESSORS, NOT_CONFIGURED, EXISTING_BEAN);
199 	}
200 	
201 	/**
202 	 * Tests the read() method using processors for a bean reader with custom DozerBeanMapper.
203 	 */
204 	@Test
205 	public void testReadForBeanReaderWithMapperUsingProcessors() throws IOException {
206 		testRead(beanReaderWithMapper, USE_PROCESSORS, NOT_CONFIGURED, CREATE_NEW_BEAN);
207 	}
208 	
209 	/**
210 	 * Tests the read() method using processors for a bean reader with custom DozerBeanMapper.
211 	 */
212 	@Test
213 	public void testReadForBeanReaderWithMapperUsingProcessorsWithExistingBean() throws IOException {
214 		testRead(beanReaderWithMapper, USE_PROCESSORS, NOT_CONFIGURED, EXISTING_BEAN);
215 	}
216 	
217 	/**
218 	 * Tests the read() method without any processors for a bean reader with a pre-configured DozerBeanMapper (no need
219 	 * to call configureBeanMapping() at all).
220 	 */
221 	@Test
222 	public void testReadForBeanReaderWithConfiguredMapper() throws IOException {
223 		testRead(beanReaderWithConfiguredMapper, NO_PROCESSORS, CONFIGURED, CREATE_NEW_BEAN);
224 	}
225 	
226 	/**
227 	 * Tests the read() method without any processors for a bean reader with a pre-configured DozerBeanMapper (no need
228 	 * to call configureBeanMapping() at all).
229 	 */
230 	@Test
231 	public void testReadForBeanReaderWithConfiguredMapperWithExistingBean() throws IOException {
232 		testRead(beanReaderWithConfiguredMapper, NO_PROCESSORS, CONFIGURED, EXISTING_BEAN);
233 	}
234 	
235 	/**
236 	 * Tests the read() method using processors for a bean reader with a pre-configured DozerBeanMapper (no need to call
237 	 * configureBeanMapping() at all).
238 	 */
239 	@Test
240 	public void testReadForBeanReaderWithConfiguredMapperUsingProcessors() throws IOException {
241 		testRead(beanReaderWithConfiguredMapper, USE_PROCESSORS, CONFIGURED, CREATE_NEW_BEAN);
242 	}
243 	
244 	/**
245 	 * Tests the read() method using processors for a bean reader with a pre-configured DozerBeanMapper (no need to call
246 	 * configureBeanMapping() at all).
247 	 */
248 	@Test
249 	public void testReadForBeanReaderWithConfiguredMapperUsingProcessorsWithExistingBean() throws IOException {
250 		testRead(beanReaderWithConfiguredMapper, USE_PROCESSORS, CONFIGURED, EXISTING_BEAN);
251 	}
252 
253 	@Test
254 	public void testReadForBeanReaderWithBrokenMaxSingleLineQuotes() throws IOException {
255 		reader = new StringReader(BROKEN_CSV);
256 		final CsvDozerBeanReader brokenCsvBeanReader = new CsvDozerBeanReader(reader, new CsvPreference.Builder(PREFS).maxLinesPerRow(1).build(), beanMapper);
257 		final List<SurveyResponse> responses = new ArrayList<SurveyResponse>();
258 		testReadForBrokenCSV(brokenCsvBeanReader, responses, "unexpected end of line while reading quoted column on line 2", 2);
259 	}
260 
261 	@Test
262 	public void testReadForBeanReaderWithBrokenMaxTwoLineQuotes() throws IOException {
263 		reader = new StringReader(BROKEN_CSV);
264 		final CsvDozerBeanReader brokenCsvBeanReader = new CsvDozerBeanReader(reader, new CsvPreference.Builder(PREFS).maxLinesPerRow(2).build(), beanMapper);
265 		final List<SurveyResponse> responses = new ArrayList<SurveyResponse>();
266 		testReadForBrokenCSV(brokenCsvBeanReader, responses, "max number of lines to read exceeded while reading quoted column beginning on line 2 and ending on line 3", 1);
267 	}
268 
269 	private void testReadForBrokenCSV(final CsvDozerBeanReader brokenCsvBeanReader, final List<SurveyResponse> responses,
270 			final String expectedMessage, final int expectedRowsRead) throws IOException {
271 		brokenCsvBeanReader.getHeader(true);
272 		brokenCsvBeanReader.configureBeanMapping(SurveyResponse.class, FIELD_MAPPING);
273 		Exception expected = null;
274 		for(int i = 0; i < 4; i++) {
275 			try{
276 				final SurveyResponse response = readSurveyResponse(brokenCsvBeanReader, USE_PROCESSORS, EXISTING_BEAN);
277 				if(response != null) {
278 					responses.add(response);
279 				}
280 			} catch (final SuperCsvException e) {
281 				expected = e;
282 			}
283 		}
284 		assertNotNull(expected);
285 		assertEquals(expectedMessage, expected.getLocalizedMessage());
286 		assertEquals(expectedRowsRead, responses.size());
287 	}
288 
289 	
290 	
291 	/**
292 	 * Tests the read() method without any processors for a bean reader with custom tokenizer and DozerBeanMapper.
293 	 */
294 	@Test
295 	public void testReadForTokenizerBeanReaderWithMapper() throws IOException {
296 		testRead(tokenizerBeanReaderWithMapper, NO_PROCESSORS, NOT_CONFIGURED, CREATE_NEW_BEAN);
297 	}
298 	
299 	/**
300 	 * Tests the read() method without any processors for a bean reader with custom tokenizer and DozerBeanMapper.
301 	 */
302 	@Test
303 	public void testReadForTokenizerBeanReaderWithMapperWithExistingBean() throws IOException {
304 		testRead(tokenizerBeanReaderWithMapper, NO_PROCESSORS, NOT_CONFIGURED, EXISTING_BEAN);
305 	}
306 	
307 	/**
308 	 * Tests the read() method using processors for a bean reader with custom tokenizer and DozerBeanMapper.
309 	 */
310 	@Test
311 	public void testReadForTokenizerBeanReaderWithMapperUsingProcessors() throws IOException {
312 		testRead(tokenizerBeanReaderWithMapper, USE_PROCESSORS, NOT_CONFIGURED, CREATE_NEW_BEAN);
313 	}
314 	
315 	/**
316 	 * Tests the read() method using processors for a bean reader with custom tokenizer and DozerBeanMapper.
317 	 */
318 	@Test
319 	public void testReadForTokenizerBeanReaderWithMapperUsingProcessorsWithExistingBean() throws IOException {
320 		testRead(tokenizerBeanReaderWithMapper, USE_PROCESSORS, NOT_CONFIGURED, EXISTING_BEAN);
321 	}
322 	
323 	/**
324 	 * Tests the read() method with or without processors. The great thing here is that Dozer is smart enough to map the
325 	 * Booleans and Integers even if no processors are used, so the results should be identical!
326 	 * 
327 	 * @param beanReader
328 	 *            the bean reader to use for the test
329 	 * @param useProcessors
330 	 *            whether processors should be used for the test
331 	 * @param configured
332 	 *            whether the reader is already configured
333 	 * @param useExistingBean
334 	 *            whether to map to an existing bean, or let Dozer create a new instance of a class
335 	 * @throws IOException
336 	 */
337 	private void testRead(final CsvDozerBeanReader beanReader, final boolean useProcessors, final boolean configured,
338 		final boolean useExistingBean) throws IOException {
339 		
340 		beanReader.getHeader(true);
341 		
342 		if( !configured ) {
343 			beanReader.configureBeanMapping(SurveyResponse.class, FIELD_MAPPING);
344 		}
345 		
346 		SurveyResponse response1 = readSurveyResponse(beanReader, useProcessors, useExistingBean);
347 		assertEquals(23, response1.getAge());
348 		assertEquals(Boolean.TRUE, response1.getConsentGiven());
349 		assertEquals(3, response1.getAnswers().size());
350 		assertEquals(1, response1.getAnswers().get(0).getQuestionNo().intValue());
351 		assertEquals("Nikola Tesla", response1.getAnswers().get(0).getAnswer());
352 		assertEquals(2, response1.getAnswers().get(1).getQuestionNo().intValue());
353 		assertEquals("\"A brief history of time\" by Steven Hawking", response1.getAnswers().get(1).getAnswer());
354 		assertEquals(3, response1.getAnswers().get(2).getQuestionNo().intValue());
355 		assertEquals("Theoretical physicist", response1.getAnswers().get(2).getAnswer());
356 		
357 		SurveyResponse response2 = readSurveyResponse(beanReader, useProcessors, useExistingBean);
358 		assertEquals(16, response2.getAge());
359 		assertEquals(Boolean.TRUE, response2.getConsentGiven());
360 		assertEquals(3, response2.getAnswers().size());
361 		assertEquals(1, response2.getAnswers().get(0).getQuestionNo().intValue());
362 		assertEquals("Genghis Kahn", response2.getAnswers().get(0).getAnswer());
363 		assertEquals(2, response2.getAnswers().get(1).getQuestionNo().intValue());
364 		assertEquals("\"Monsoon\" by Wilbur Smith", response2.getAnswers().get(1).getAnswer());
365 		assertEquals(3, response2.getAnswers().get(2).getQuestionNo().intValue());
366 		assertNull(response2.getAnswers().get(2).getAnswer());
367 		
368 		SurveyResponse response3 = readSurveyResponse(beanReader, useProcessors, useExistingBean);
369 		assertEquals(44, response3.getAge());
370 		assertEquals(Boolean.TRUE, response3.getConsentGiven());
371 		assertEquals(3, response3.getAnswers().size());
372 		assertEquals(1, response3.getAnswers().get(0).getQuestionNo().intValue());
373 		assertNull(response3.getAnswers().get(0).getAnswer());
374 		assertEquals(2, response3.getAnswers().get(1).getQuestionNo().intValue());
375 		assertNull(response3.getAnswers().get(1).getAnswer());
376 		assertEquals(3, response3.getAnswers().get(2).getQuestionNo().intValue());
377 		assertEquals("I hate surveys, thanks for wasting my time!", response3.getAnswers().get(2).getAnswer());
378 		
379 		assertNull(readSurveyResponse(beanReader, useProcessors, useExistingBean));
380 		
381 	}
382 	
383 	/**
384 	 * Reads a SurveyResponse.
385 	 * 
386 	 * @param beanReader
387 	 *            the bean reader
388 	 * @param useProcessors
389 	 *            whether to use cell processors or not
390 	 * @param useExistingBean
391 	 *            whether to populate an existing bean, or let Dozer instantiate an instance of a class
392 	 * @return the SurveyResponse
393 	 * @throws IOException
394 	 */
395 	private SurveyResponse readSurveyResponse(final CsvDozerBeanReader beanReader, final boolean useProcessors,
396 		final boolean useExistingBean) throws IOException {
397 		if( useExistingBean ) {
398 			return useProcessors ? beanReader.read(new SurveyResponse(), PROCESSORS) : beanReader
399 				.read(new SurveyResponse());
400 		} else {
401 			return useProcessors ? beanReader.read(SurveyResponse.class, PROCESSORS) : beanReader
402 				.read(SurveyResponse.class);
403 		}
404 	}
405 	
406 	/**
407 	 * Tests the read() method without any processors with null elements in the fieldMapping (ignored columns) for a
408 	 * standard bean reader.
409 	 */
410 	@Test
411 	public void testPartialReadForBeanReader() throws IOException {
412 		testPartialRead(beanReader, NO_PROCESSORS, CREATE_NEW_BEAN);
413 	}
414 	
415 	/**
416 	 * Tests the read() method without any processors with null elements in the fieldMapping (ignored columns) for a
417 	 * standard bean reader.
418 	 */
419 	@Test
420 	public void testPartialReadForBeanReaderWithExistingBean() throws IOException {
421 		testPartialRead(beanReader, NO_PROCESSORS, EXISTING_BEAN);
422 	}
423 	
424 	/**
425 	 * Tests the read() method using processors with null elements in the fieldMapping (ignored columns) for a standard
426 	 * bean reader.
427 	 */
428 	@Test
429 	public void testPartialReadForBeanReaderUsingProcessors() throws IOException {
430 		testPartialRead(beanReader, USE_PROCESSORS, CREATE_NEW_BEAN);
431 	}
432 	
433 	/**
434 	 * Tests the read() method using processors with null elements in the fieldMapping (ignored columns) for a standard
435 	 * bean reader.
436 	 */
437 	@Test
438 	public void testPartialReadForBeanReaderUsingProcessorsWithExistingBean() throws IOException {
439 		testPartialRead(beanReader, USE_PROCESSORS, EXISTING_BEAN);
440 	}
441 	
442 	/**
443 	 * Tests the read() method without any processors with null elements in the fieldMapping (ignored columns) for a
444 	 * bean reader with custom tokenizer.
445 	 */
446 	@Test
447 	public void testPartialReadForTokenizerBeanReader() throws IOException {
448 		testPartialRead(tokenizerBeanReader, NO_PROCESSORS, CREATE_NEW_BEAN);
449 	}
450 	
451 	/**
452 	 * Tests the read() method without any processors with null elements in the fieldMapping (ignored columns) for a
453 	 * bean reader with custom tokenizer.
454 	 */
455 	@Test
456 	public void testPartialReadForTokenizerBeanReaderWithExistingBean() throws IOException {
457 		testPartialRead(tokenizerBeanReader, NO_PROCESSORS, EXISTING_BEAN);
458 	}
459 	
460 	/**
461 	 * Tests the read() method using processors with null elements in the fieldMapping (ignored columns) for a bean
462 	 * reader with custom tokenizer.
463 	 */
464 	@Test
465 	public void testPartialReadForTokenizerBeanReaderUsingProcessors() throws IOException {
466 		testPartialRead(tokenizerBeanReader, USE_PROCESSORS, CREATE_NEW_BEAN);
467 	}
468 	
469 	/**
470 	 * Tests the read() method using processors with null elements in the fieldMapping (ignored columns) for a bean
471 	 * reader with custom tokenizer.
472 	 */
473 	@Test
474 	public void testPartialReadForTokenizerBeanReaderUsingProcessorsWithExistingBean() throws IOException {
475 		testPartialRead(tokenizerBeanReader, USE_PROCESSORS, EXISTING_BEAN);
476 	}
477 	
478 	/**
479 	 * Tests the read() method without any processors with null elements in the fieldMapping (ignored columns) for a
480 	 * bean reader with custom DozerBeanMapper.
481 	 */
482 	@Test
483 	public void testPartialReadForBeanReaderWithMapper() throws IOException {
484 		testPartialRead(beanReaderWithMapper, NO_PROCESSORS, CREATE_NEW_BEAN);
485 	}
486 	
487 	/**
488 	 * Tests the read() method without any processors with null elements in the fieldMapping (ignored columns) for a
489 	 * bean reader with custom DozerBeanMapper.
490 	 */
491 	@Test
492 	public void testPartialReadForBeanReaderWithMapperWithExistingBean() throws IOException {
493 		testPartialRead(beanReaderWithMapper, NO_PROCESSORS, EXISTING_BEAN);
494 	}
495 	
496 	/**
497 	 * Tests the read() method using processors with null elements in the fieldMapping (ignored columns) for a bean
498 	 * reader with custom DozerBeanMapper.
499 	 */
500 	@Test
501 	public void testPartialReadForBeanReaderWithMapperUsingProcessors() throws IOException {
502 		testPartialRead(beanReaderWithMapper, USE_PROCESSORS, CREATE_NEW_BEAN);
503 	}
504 	
505 	/**
506 	 * Tests the read() method using processors with null elements in the fieldMapping (ignored columns) for a bean
507 	 * reader with custom DozerBeanMapper.
508 	 */
509 	@Test
510 	public void testPartialReadForBeanReaderWithMapperUsingProcessorsWithExistingBean() throws IOException {
511 		testPartialRead(beanReaderWithMapper, USE_PROCESSORS, EXISTING_BEAN);
512 	}
513 	
514 	/**
515 	 * Tests the read() method without any processors with null elements in the fieldMapping (ignored columns) for a
516 	 * bean reader with custom tokenizer and DozerBeanMapper.
517 	 */
518 	@Test
519 	public void testPartialReadForTokenizerBeanReaderWithMapper() throws IOException {
520 		testPartialRead(tokenizerBeanReaderWithMapper, NO_PROCESSORS, CREATE_NEW_BEAN);
521 	}
522 	
523 	/**
524 	 * Tests the read() method without any processors with null elements in the fieldMapping (ignored columns) for a
525 	 * bean reader with custom tokenizer and DozerBeanMapper.
526 	 */
527 	@Test
528 	public void testPartialReadForTokenizerBeanReaderWithMapperWithExistingBean() throws IOException {
529 		testPartialRead(tokenizerBeanReaderWithMapper, NO_PROCESSORS, EXISTING_BEAN);
530 	}
531 	
532 	/**
533 	 * Tests the read() method using processors with null elements in the fieldMapping (ignored columns) for a bean
534 	 * reader with custom tokenizer and DozerBeanMapper.
535 	 */
536 	@Test
537 	public void testPartialReadForTokenizerBeanReaderWithMapperUsingProcessors() throws IOException {
538 		testPartialRead(tokenizerBeanReaderWithMapper, USE_PROCESSORS, CREATE_NEW_BEAN);
539 	}
540 	
541 	/**
542 	 * Tests the read() method using processors with null elements in the fieldMapping (ignored columns) for a bean
543 	 * reader with custom tokenizer and DozerBeanMapper.
544 	 */
545 	@Test
546 	public void testPartialReadForTokenizerBeanReaderWithMapperUsingProcessorsWithExistingBean() throws IOException {
547 		testPartialRead(tokenizerBeanReaderWithMapper, USE_PROCESSORS, EXISTING_BEAN);
548 	}
549 	
550 	/**
551 	 * Tests the read() method with or without any processors with null elements in the fieldMapping (ignored columns).
552 	 * As Dozer is smart enough to do basic type mapping, the result will be the same regardless of whether processors
553 	 * are used.
554 	 * 
555 	 * @param beanReader
556 	 *            the bean reader to use for the test
557 	 * @param useProcessors
558 	 *            whether processors should be used for the test
559 	 * @param useExistingBean
560 	 *            whether to populate an existing bean, or let Dozer instantiate an instance of a class
561 	 * @throws IOException
562 	 */
563 	private void testPartialRead(final CsvDozerBeanReader beanReader, final boolean useProcessors,
564 		final boolean useExistingBean) throws IOException {
565 		
566 		beanReader.getHeader(true);
567 		
568 		// ignore age, and the last question/answer
569 		final String[] partialMapping = new String[] { null, "consentGiven", "answers[0].questionNo",
570 			"answers[0].answer", "answers[1].questionNo", "answers[1].answer", null, null };
571 		beanReader.configureBeanMapping(SurveyResponse.class, partialMapping);
572 		
573 		SurveyResponse response1 = readSurveyResponse(beanReader, useProcessors, useExistingBean);
574 		assertEquals(0, response1.getAge());
575 		assertEquals(Boolean.TRUE, response1.getConsentGiven());
576 		assertEquals(2, response1.getAnswers().size());
577 		assertEquals(1, response1.getAnswers().get(0).getQuestionNo().intValue());
578 		assertEquals("Nikola Tesla", response1.getAnswers().get(0).getAnswer());
579 		assertEquals(2, response1.getAnswers().get(1).getQuestionNo().intValue());
580 		assertEquals("\"A brief history of time\" by Steven Hawking", response1.getAnswers().get(1).getAnswer());
581 		
582 		SurveyResponse response2 = readSurveyResponse(beanReader, useProcessors, useExistingBean);
583 		assertEquals(0, response2.getAge());
584 		assertEquals(Boolean.TRUE, response2.getConsentGiven());
585 		assertEquals(2, response2.getAnswers().size());
586 		assertEquals(1, response2.getAnswers().get(0).getQuestionNo().intValue());
587 		assertEquals("Genghis Kahn", response2.getAnswers().get(0).getAnswer());
588 		assertEquals(2, response2.getAnswers().get(1).getQuestionNo().intValue());
589 		assertEquals("\"Monsoon\" by Wilbur Smith", response2.getAnswers().get(1).getAnswer());
590 		
591 		SurveyResponse response3 = readSurveyResponse(beanReader, useProcessors, useExistingBean);
592 		assertEquals(0, response3.getAge());
593 		assertEquals(Boolean.TRUE, response3.getConsentGiven());
594 		assertEquals(2, response3.getAnswers().size());
595 		assertEquals(1, response3.getAnswers().get(0).getQuestionNo().intValue());
596 		assertNull(response3.getAnswers().get(0).getAnswer());
597 		assertEquals(2, response3.getAnswers().get(1).getQuestionNo().intValue());
598 		assertNull(response3.getAnswers().get(1).getAnswer());
599 		
600 		assertNull(readSurveyResponse(beanReader, useProcessors, useExistingBean));
601 		
602 	}
603 	
604 	/**
605 	 * Tests mapping columns to indexed list elements (with no deep mapping). Dozer requires a hint in this situation.
606 	 */
607 	@Test
608 	public void testReadToListElement() throws IOException {
609 		final String csv = "age,answer1,answer2,answer3\n"
610 			+ "23,Nikola Tesla,\"\"\"A brief history of time\"\" by Steven Hawking\",Theoretical physicist\n"
611 			+ ",Genghis Kahn,\"\"\"Monsoon\"\" by Wilbur Smith\",\n"
612 			+ "44,,,\"I hate surveys, thanks for wasting my time!\"";
613 		reader = new StringReader(csv);
614 		beanReader = new CsvDozerBeanReader(reader, PREFS);
615 		
616 		beanReader.getHeader(true); // skip header
617 		
618 		final String[] fieldMapping = new String[] { "age", "answers[0]", "answers[1]", "answers[2]" };
619 		final Class<?>[] hintTypes = new Class<?>[] { null, Answer.class, Answer.class, Answer.class };
620 		beanReader.configureBeanMapping(SurveyResponse.class, fieldMapping, hintTypes);
621 		
622 		final CellProcessor parseAnswer = new CellProcessor() {
623 			public Object execute(Object value, CsvContext context) {
624 				return value == null ? null : new Answer(0, (String) value);
625 			}
626 		};
627 		final CellProcessor[] processors = new CellProcessor[] { new Optional(new ParseInt()), parseAnswer,
628 			parseAnswer, parseAnswer };
629 		
630 		SurveyResponse response1 = beanReader.read(SurveyResponse.class, processors);
631 		assertEquals(23, response1.getAge());
632 		assertEquals(3, response1.getAnswers().size());
633 		assertEquals("Nikola Tesla", response1.getAnswers().get(0).getAnswer());
634 		assertEquals("\"A brief history of time\" by Steven Hawking", response1.getAnswers().get(1).getAnswer());
635 		assertEquals("Theoretical physicist", response1.getAnswers().get(2).getAnswer());
636 		
637 		SurveyResponse response2 = beanReader.read(SurveyResponse.class, processors);
638 		assertEquals(0, response2.getAge());
639 		assertEquals(3, response2.getAnswers().size());
640 		assertEquals("Genghis Kahn", response2.getAnswers().get(0).getAnswer());
641 		assertEquals("\"Monsoon\" by Wilbur Smith", response2.getAnswers().get(1).getAnswer());
642 		assertNull(response2.getAnswers().get(2));
643 		
644 		SurveyResponse response3 = beanReader.read(SurveyResponse.class, processors);
645 		assertEquals(44, response3.getAge());
646 		assertEquals(3, response3.getAnswers().size());
647 		assertNull(response3.getAnswers().get(0));
648 		assertNull(response3.getAnswers().get(1));
649 		assertEquals("I hate surveys, thanks for wasting my time!", response3.getAnswers().get(2).getAnswer());
650 		
651 		assertNull(beanReader.read(SurveyResponse.class, processors));
652 		
653 	}
654 	
655 	/**
656 	 * Tests all of the constructors with null values (should throw an Exception).
657 	 */
658 	@Test
659 	public void testConstructorsWithNulls() {
660 		
661 		// constructor one - null reader
662 		try {
663 			new CsvDozerBeanReader((Reader) null, PREFS);
664 			fail("should have thrown NullPointerException");
665 		}
666 		catch(NullPointerException e) {}
667 		
668 		// constructor one - null prefs
669 		try {
670 			new CsvDozerBeanReader(reader, null);
671 			fail("should have thrown NullPointerException");
672 		}
673 		catch(NullPointerException e) {}
674 		
675 		// constructor two - null tokenizer
676 		try {
677 			new CsvDozerBeanReader((ITokenizer) null, PREFS);
678 			fail("should have thrown NullPointerException");
679 		}
680 		catch(NullPointerException e) {}
681 		
682 		// constructor two - null prefs
683 		try {
684 			new CsvDozerBeanReader(tokenizer, null);
685 			fail("should have thrown NullPointerException");
686 		}
687 		catch(NullPointerException e) {}
688 		
689 		// constructor three - null reader
690 		try {
691 			new CsvDozerBeanReader((Reader) null, PREFS, beanMapper);
692 			fail("should have thrown NullPointerException");
693 		}
694 		catch(NullPointerException e) {}
695 		
696 		// constructor three - null prefs
697 		try {
698 			new CsvDozerBeanReader(reader, null, beanMapper);
699 			fail("should have thrown NullPointerException");
700 		}
701 		catch(NullPointerException e) {}
702 		
703 		// constructor three - null dozerBeanMapper
704 		try {
705 			new CsvDozerBeanReader(reader, PREFS, null);
706 			fail("should have thrown NullPointerException");
707 		}
708 		catch(NullPointerException e) {}
709 		
710 		// constructor four - null tokenizer
711 		try {
712 			new CsvDozerBeanReader((ITokenizer) null, PREFS, beanMapper);
713 			fail("should have thrown NullPointerException");
714 		}
715 		catch(NullPointerException e) {}
716 		
717 		// constructor four - null prefs
718 		try {
719 			new CsvDozerBeanReader(tokenizer, null, beanMapper);
720 			fail("should have thrown NullPointerException");
721 		}
722 		catch(NullPointerException e) {}
723 		
724 		// constructor four - null dozerBeanMapper
725 		try {
726 			new CsvDozerBeanReader(tokenizer, PREFS, null);
727 			fail("should have thrown NullPointerException");
728 		}
729 		catch(NullPointerException e) {}
730 		
731 	}
732 	
733 	/**
734 	 * Tests the configureBeanMapping() method with a null clazz (should throw an exception).
735 	 */
736 	@Test
737 	public void testConfigureBeanMappingWithNullClazz() {
738 		try {
739 			beanReader.configureBeanMapping(null, FIELD_MAPPING);
740 			fail("should have thrown NullPointerException");
741 		}
742 		catch(NullPointerException e) {}
743 		
744 		try {
745 			beanReader.configureBeanMapping(null, FIELD_MAPPING, new Class<?>[FIELD_MAPPING.length]);
746 			fail("should have thrown NullPointerException");
747 		}
748 		catch(NullPointerException e) {}
749 	}
750 	
751 	/**
752 	 * Tests the configureBeanMapping() method with a null fieldMapping array (should throw an exception).
753 	 */
754 	@Test
755 	public void testConfigureBeanMappingWithNullFieldMapping() {
756 		try {
757 			beanReader.configureBeanMapping(SurveyResponse.class, null);
758 			fail("should have thrown NullPointerException");
759 		}
760 		catch(NullPointerException e) {}
761 		
762 		try {
763 			beanReader.configureBeanMapping(SurveyResponse.class, null, new Class<?>[FIELD_MAPPING.length]);
764 			fail("should have thrown NullPointerException");
765 		}
766 		catch(NullPointerException e) {}
767 	}
768 	
769 	/**
770 	 * Tests the configureBeanMapping() method with a null hintTypes array (should throw an exception).
771 	 */
772 	@Test(expected = NullPointerException.class)
773 	public void testConfigureBeanMappingWithNullHintTypes() {
774 		beanReader.configureBeanMapping(SurveyResponse.class, FIELD_MAPPING, null);
775 	}
776 	
777 	/**
778 	 * Tests the configureBeanMapping() method with a hintTypes array of the wrong size (should throw an exception).
779 	 */
780 	@Test(expected = IllegalArgumentException.class)
781 	public void testConfigureBeanMappingWithInvalidHintTypes() {
782 		beanReader.configureBeanMapping(SurveyResponse.class, FIELD_MAPPING, new Class<?>[0]);
783 	}
784 	
785 	/**
786 	 * Tests the read() method with a null clazz (should throw an exception).
787 	 */
788 	@Test(expected = NullPointerException.class)
789 	public void testReadWithNullClazz() throws IOException {
790 		beanReader.read((Class<?>) null);
791 	}
792 	
793 	/**
794 	 * Tests the read() method with a null bean (should throw an exception).
795 	 */
796 	@Test(expected = NullPointerException.class)
797 	public void testReadWithNullBean() throws IOException {
798 		beanReader.read((Object) null);
799 	}
800 	
801 	/**
802 	 * Tests the read() method (with processors) with a null clazz (should throw an exception).
803 	 */
804 	@Test(expected = NullPointerException.class)
805 	public void testReadWithProcessorsWithNullClazz() throws IOException {
806 		beanReader.read((Class<?>) null, PROCESSORS);
807 	}
808 	
809 	/**
810 	 * Tests the read() method (with processors) with a null bean (should throw an exception).
811 	 */
812 	@Test(expected = NullPointerException.class)
813 	public void testReadWithProcessorsWithNullBean() throws IOException {
814 		beanReader.read((Object) null, PROCESSORS);
815 	}
816 	
817 	/**
818 	 * Tests the read() method (with processors) with a null cell processor array (should throw an exception).
819 	 */
820 	@Test(expected = NullPointerException.class)
821 	public void testReadWithProcessorsWithNullProcessors() throws IOException {
822 		beanReader.read(SurveyResponse.class, (CellProcessor[]) null);
823 	}
824 	
825 	/**
826 	 * Tests the read() method (with processors) with a null cell processor array (should throw an exception).
827 	 */
828 	@Test(expected = NullPointerException.class)
829 	public void testReadBeanWithProcessorsWithNullProcessors() throws IOException {
830 		beanReader.read(new SurveyResponse(), (CellProcessor[]) null);
831 	}
832 	
833 }