1 package com.github.mygreen.supercsv.io;
2
3 import java.io.IOException;
4 import java.io.Reader;
5 import java.io.UncheckedIOException;
6 import java.lang.reflect.Method;
7 import java.util.ArrayList;
8 import java.util.Arrays;
9 import java.util.Iterator;
10 import java.util.List;
11 import java.util.NoSuchElementException;
12 import java.util.Optional;
13 import java.util.Spliterator;
14 import java.util.Spliterators;
15 import java.util.stream.Collectors;
16 import java.util.stream.Stream;
17 import java.util.stream.StreamSupport;
18
19 import org.supercsv.cellprocessor.ift.CellProcessor;
20 import org.supercsv.exception.SuperCsvCellProcessorException;
21 import org.supercsv.exception.SuperCsvException;
22 import org.supercsv.exception.SuperCsvReflectionException;
23 import org.supercsv.io.AbstractCsvReader;
24 import org.supercsv.io.CsvBeanReader;
25 import org.supercsv.io.ITokenizer;
26 import org.supercsv.prefs.CsvPreference;
27 import org.supercsv.util.BeanInterfaceProxy;
28 import org.supercsv.util.CsvContext;
29 import org.supercsv.util.MethodCache;
30
31 import com.github.mygreen.supercsv.builder.BeanMapping;
32 import com.github.mygreen.supercsv.builder.CallbackMethod;
33 import com.github.mygreen.supercsv.exception.SuperCsvBindingException;
34 import com.github.mygreen.supercsv.exception.SuperCsvNoMatchColumnSizeException;
35 import com.github.mygreen.supercsv.exception.SuperCsvNoMatchHeaderException;
36 import com.github.mygreen.supercsv.exception.SuperCsvRowException;
37 import com.github.mygreen.supercsv.validation.CsvBindingErrors;
38 import com.github.mygreen.supercsv.validation.CsvError;
39 import com.github.mygreen.supercsv.validation.CsvExceptionConverter;
40 import com.github.mygreen.supercsv.validation.CsvValidator;
41 import com.github.mygreen.supercsv.validation.ValidationContext;
42
43
44
45
46
47
48
49
50
51
52
53
54 public abstract class AbstractCsvAnnotationBeanReader<T> extends AbstractCsvReader {
55
56
57
58
59
60 protected BeanMappingCache<T> beanMappingCache;
61
62
63 protected final List<Object> processedColumns = new ArrayList<>();
64
65
66 protected final MethodCache cache = new MethodCache();
67
68
69 protected CsvExceptionConverteronverter.html#CsvExceptionConverter">CsvExceptionConverter exceptionConverter = new CsvExceptionConverter();
70
71
72 protected final List<String> errorMessages = new ArrayList<>();
73
74
75 protected final List<CsvValidator<T>> validators = new ArrayList<>();
76
77 public AbstractCsvAnnotationBeanReader(final Reader reader, final CsvPreference preference) {
78 super(reader, preference);
79 }
80
81 public AbstractCsvAnnotationBeanReader(final ITokenizer tokenizer, final CsvPreference preference) {
82 super(tokenizer, preference);
83 }
84
85
86
87
88
89
90
91
92
93
94
95
96 public T read() throws IOException {
97
98 if(readRow()) {
99
100 final T bean = instantiateBean(beanMappingCache.getOriginal().getType());
101 final CsvBindingErrorsBindingErrors.html#CsvBindingErrors">CsvBindingErrors bindingErrors = new CsvBindingErrors(beanMappingCache.getOriginal().getType());
102
103 final CsvContext context = new CsvContext(getLineNumber(), getRowNumber(), 1);
104 context.setRowSource(new ArrayList<Object>(processedColumns));
105
106 Optional<SuperCsvRowException> rowException = Optional.empty();
107 try {
108 executeCellProcessor(processedColumns, getColumns(), beanMappingCache.getCellProcessorsForReading(), context);
109
110 } catch(SuperCsvRowException e) {
111
112
113
114
115 rowException = Optional.of(e);
116
117 final List<CsvError> errors = exceptionConverter.convert(e, beanMappingCache.getOriginal());
118 bindingErrors.addAllErrors(errors);
119
120 } catch(SuperCsvException e) {
121 errorMessages.addAll(exceptionConverter.convertAndFormat(e, beanMappingCache.getOriginal()));
122 throw e;
123 }
124
125
126 for(CallbackMethod callback : beanMappingCache.getOriginal().getPreReadMethods()) {
127 callback.invoke(bean, context, bindingErrors, beanMappingCache.getOriginal());
128 }
129
130
131 populateBean(bean, beanMappingCache.getNameMapping(), bindingErrors);
132
133
134 for(CsvValidator<T> recordValidator : validators) {
135 recordValidator.validate(bean, bindingErrors, new ValidationContext<>(context, beanMappingCache.getOriginal()));
136 }
137
138
139 for(CallbackMethod callback : beanMappingCache.getOriginal().getPostReadMethods()) {
140 callback.invoke(bean, context, bindingErrors, beanMappingCache.getOriginal());
141 }
142
143
144 processErrors(bindingErrors, context, rowException);
145
146 return bean;
147
148 }
149
150 return null;
151
152
153 }
154
155
156
157
158
159
160
161
162
163
164 public CsvReadStatus read(final CsvSuccessHandler<T> successHandler, final CsvErrorHandler errorHandler) throws IOException {
165
166 try {
167 final T bean = read();
168 if(bean != null) {
169 successHandler.onSuccess(bean);
170 return CsvReadStatus.SUCCESS;
171 } else {
172 return CsvReadStatus.EOF;
173 }
174
175 } catch(SuperCsvException e) {
176 errorHandler.onError(e);
177 return CsvReadStatus.ERROR;
178
179 }
180
181 }
182
183
184
185
186
187
188
189
190
191 public Stream<T> lines() {
192
193 Iterator<T> itr = new Iterator<T>() {
194
195 T nextLine = null;
196
197 @Override
198 public boolean hasNext() {
199 if(nextLine != null) {
200 return true;
201
202 } else {
203 try {
204 nextLine = read();
205 return (nextLine != null);
206 } catch(IOException e) {
207 throw new UncheckedIOException(e);
208 }
209 }
210 }
211
212 @Override
213 public T next() {
214 if (nextLine != null || hasNext()) {
215 T line = nextLine;
216 nextLine = null;
217 return line;
218 } else {
219 throw new NoSuchElementException();
220 }
221 }
222
223 };
224
225 return StreamSupport.stream(Spliterators.spliteratorUnknownSize(
226 itr, Spliterator.ORDERED | Spliterator.NONNULL), false);
227
228 }
229
230
231
232
233
234
235
236
237
238 protected void validateHeader(final String[] sourceHeader, final String[] definedHeader) {
239
240
241 if(sourceHeader.length != definedHeader.length) {
242 final CsvContext context = new CsvContext(1, 1, 1);
243 throw new SuperCsvNoMatchColumnSizeException(sourceHeader.length, definedHeader.length, context);
244
245 }
246
247
248 for(int i=0; i < sourceHeader.length; i++) {
249 if(!sourceHeader[i].equals(definedHeader[i])) {
250 final CsvContext context = new CsvContext(1, 1, i+1);
251 throw new SuperCsvNoMatchHeaderException(sourceHeader, definedHeader, context);
252 }
253 }
254
255 }
256
257
258
259
260
261
262
263 protected void processErrors(final CsvBindingErrors bindingErrors, final CsvContext context,
264 final Optional<SuperCsvRowException> rowException) {
265 if(bindingErrors.hasErrors()) {
266 final List<String> message = bindingErrors.getAllErrors().stream()
267 .map(error -> error.format(exceptionConverter.getMessageResolver(), exceptionConverter.getMessageInterpolator()))
268 .collect(Collectors.toList());
269 errorMessages.addAll(message);
270
271 final SuperCsvBindingExceptionException.html#SuperCsvBindingException">SuperCsvBindingException bindingException = new SuperCsvBindingException("has binding error.", context, bindingErrors);
272 rowException.ifPresent(re -> bindingException.addAllProcessingErrors(re.getColumnErrors()));
273
274 throw bindingException;
275 }
276 }
277
278
279
280
281
282
283
284
285 protected T instantiateBean(final Class<T> clazz) {
286
287 final T bean;
288 if( clazz.isInterface() ) {
289 bean = BeanInterfaceProxy.createProxy(clazz);
290 } else {
291 try {
292 bean = clazz.newInstance();
293 } catch(InstantiationException e) {
294 throw new SuperCsvReflectionException(String.format(
295 "error instantiating bean, check that %s has a default no-args constructor", clazz.getName()), e);
296 } catch(IllegalAccessException e) {
297 throw new SuperCsvReflectionException("error instantiating bean", e);
298 }
299 }
300
301 return bean;
302
303 }
304
305
306
307
308
309
310
311
312
313
314 protected void executeCellProcessor(final List<Object> destination, final List<String> source,
315 final CellProcessor[] processors, final CsvContext context) {
316
317 if(source.size() != processors.length) {
318 throw new SuperCsvNoMatchColumnSizeException(source.size(), processors.length, context);
319
320 }
321
322 destination.clear();
323
324 final SuperCsvRowExceptionvRowException.html#SuperCsvRowException">SuperCsvRowException rowException = new SuperCsvRowException(
325 String.format("row (%d) has errors column", context.getRowNumber()), context);
326
327 for( int i = 0; i < source.size(); i++ ) {
328
329 try {
330 context.setColumnNumber(i + 1);
331
332 if( processors[i] == null ) {
333 destination.add(source.get(i));
334 } else {
335 destination.add(processors[i].execute(source.get(i), context));
336 }
337 } catch(SuperCsvCellProcessorException e) {
338 rowException.addError(e);
339
340
341 destination.add(source.get(i));
342
343 } catch(SuperCsvException e) {
344
345 throw e;
346
347 }
348 }
349
350 if(rowException.isNotEmptyColumnErrors()) {
351 throw rowException;
352 }
353
354 }
355
356
357
358
359
360
361
362 protected void populateBean(final T resultBean, final String[] nameMapping, final CsvBindingErrors bindingErrors) {
363
364
365 for( int i = 0; i < nameMapping.length; i++ ) {
366 final String fieldName = nameMapping[i];
367 final Object fieldValue = processedColumns.get(i);
368
369
370 if( fieldName == null || fieldValue == null || bindingErrors.hasFieldErrors(fieldName)) {
371 continue;
372 }
373
374
375 final Method setMethod = cache.getSetMethod(resultBean, fieldName, fieldValue.getClass());
376 try {
377 setMethod.invoke(resultBean, fieldValue);
378
379 } catch(final Exception e) {
380 throw new SuperCsvReflectionException(String.format("error invoking method %s()", setMethod.getName()), e);
381 }
382
383 }
384
385 }
386
387
388
389
390
391 protected BeanMappingCache<T> getBeanMappingCache() {
392 return beanMappingCache;
393 }
394
395
396
397
398
399 protected void setBeanMappingCache(BeanMappingCache<T> beanMappingCache) {
400 this.beanMappingCache = beanMappingCache;
401 }
402
403
404
405
406
407 public String[] getDefinedHeader() {
408 return beanMappingCache.getHeader();
409 }
410
411
412
413
414
415 public BeanMapping<T> getBeanMapping() {
416 return beanMappingCache.getOriginal();
417 }
418
419
420
421
422
423 public List<String> getErrorMessages() {
424 return errorMessages;
425 }
426
427
428
429
430
431 public CsvExceptionConverter getExceptionConverter() {
432 return exceptionConverter;
433 }
434
435
436
437
438
439 public void setExceptionConverter(CsvExceptionConverter exceptionConverter) {
440 this.exceptionConverter = exceptionConverter;
441 }
442
443
444
445
446
447 @SuppressWarnings("unchecked")
448 public void addValidator(CsvValidator<T>... validators) {
449 this.validators.addAll(Arrays.asList(validators));
450 }
451
452
453
454
455
456 public List<CsvValidator<T>> getValidators() {
457 return validators;
458 }
459
460 }