1 package com.github.mygreen.supercsv.io;
2
3 import java.io.IOException;
4 import java.io.Writer;
5 import java.lang.reflect.Method;
6 import java.util.ArrayList;
7 import java.util.Arrays;
8 import java.util.Collections;
9 import java.util.List;
10 import java.util.Objects;
11 import java.util.Optional;
12 import java.util.stream.Collectors;
13
14 import org.supercsv.cellprocessor.ift.CellProcessor;
15 import org.supercsv.exception.SuperCsvCellProcessorException;
16 import org.supercsv.exception.SuperCsvException;
17 import org.supercsv.exception.SuperCsvReflectionException;
18 import org.supercsv.io.AbstractCsvWriter;
19 import org.supercsv.io.CsvBeanWriter;
20 import org.supercsv.prefs.CsvPreference;
21 import org.supercsv.util.CsvContext;
22 import org.supercsv.util.MethodCache;
23
24 import com.github.mygreen.supercsv.builder.BeanMapping;
25 import com.github.mygreen.supercsv.builder.CallbackMethod;
26 import com.github.mygreen.supercsv.builder.ColumnMapping;
27 import com.github.mygreen.supercsv.builder.FixedSizeColumnProperty;
28 import com.github.mygreen.supercsv.cellprocessor.conversion.PaddingProcessor;
29 import com.github.mygreen.supercsv.exception.SuperCsvBindingException;
30 import com.github.mygreen.supercsv.exception.SuperCsvRowException;
31 import com.github.mygreen.supercsv.validation.CsvBindingErrors;
32 import com.github.mygreen.supercsv.validation.CsvError;
33 import com.github.mygreen.supercsv.validation.CsvExceptionConverter;
34 import com.github.mygreen.supercsv.validation.CsvValidator;
35 import com.github.mygreen.supercsv.validation.ValidationContext;
36
37
38
39
40
41
42
43
44
45
46
47
48 public abstract class AbstractCsvAnnotationBeanWriter<T> extends AbstractCsvWriter {
49
50
51
52
53
54 protected BeanMappingCache<T> beanMappingCache;
55
56
57 protected final List<Object> beanValues = new ArrayList<>();
58
59
60 protected final List<Object> processedColumns = new ArrayList<>();
61
62
63 protected final MethodCache cache = new MethodCache();
64
65
66 protected CsvExceptionConverteronverter.html#CsvExceptionConverter">CsvExceptionConverter exceptionConverter = new CsvExceptionConverter();
67
68
69 protected final List<String> errorMessages = new ArrayList<>();
70
71
72 protected final List<CsvValidator<T>> validators = new ArrayList<>();
73
74 public AbstractCsvAnnotationBeanWriter(final Writer writer, final CsvPreference preference) {
75 super(writer, preference);
76
77 }
78
79
80
81
82
83
84
85
86
87
88 public void write(final T source) throws IOException {
89
90 Objects.requireNonNull(source, "the bean to write should not be null.");
91
92
93 incrementRowAndLineNo();
94
95 final CsvContext context = new CsvContext(getLineNumber(), getRowNumber(), 1);
96 context.setRowSource(Collections.emptyList());
97
98 final CsvBindingErrorsBindingErrors.html#CsvBindingErrors">CsvBindingErrors bindingErrors = new CsvBindingErrors(beanMappingCache.getOriginal().getType());
99
100
101 for(CallbackMethod callback : beanMappingCache.getOriginal().getPreWriteMethods()) {
102 callback.invoke(source, context, bindingErrors, beanMappingCache.getOriginal());
103 }
104
105
106 extractBeanValues(source, beanMappingCache.getNameMapping());
107 context.setRowSource(new ArrayList<Object>(beanValues));
108
109 Optional<SuperCsvRowException> rowException = Optional.empty();
110 try {
111 executeCellProcessors(processedColumns, beanValues, beanMappingCache.getCellProcessorsForWriting(), context);
112
113 } catch(SuperCsvRowException e) {
114
115
116
117
118 rowException = Optional.of(e);
119
120 final List<CsvError> errors = exceptionConverter.convert(e, beanMappingCache.getOriginal());
121 bindingErrors.addAllErrors(errors);
122
123 } catch(SuperCsvException e) {
124
125 errorMessages.addAll(exceptionConverter.convertAndFormat(e, beanMappingCache.getOriginal()));
126 throw e;
127 }
128
129
130 if(!beanMappingCache.getOriginal().isSkipValidationOnWrite()) {
131 for(CsvValidator<T> validator : validators) {
132 validator.validate(source, bindingErrors, new ValidationContext<>(context, beanMappingCache.getOriginal()));
133 }
134 }
135
136
137 processErrors(bindingErrors, context, rowException);
138
139
140 writeRow(processedColumns);
141
142
143 for(CallbackMethod callback : beanMappingCache.getOriginal().getPostWriteMethods()) {
144 callback.invoke(source, context, bindingErrors, beanMappingCache.getOriginal());
145 }
146
147
148 processErrors(bindingErrors, context, rowException);
149
150 }
151
152
153
154
155
156
157
158
159
160
161 public CsvWriteStatus write(final T source, final CsvErrorHandler errorHandler) throws IOException {
162
163 try {
164 write(source);
165 return CsvWriteStatus.SUCCESS;
166
167 } catch(SuperCsvException e) {
168 errorHandler.onError(e);
169 return CsvWriteStatus.ERROR;
170
171 }
172
173 }
174
175
176
177
178
179
180
181 protected void processErrors(final CsvBindingErrors bindingErrors, final CsvContext context,
182 final Optional<SuperCsvRowException> rowException) {
183
184 if(bindingErrors.hasErrors()) {
185 final List<String> message = bindingErrors.getAllErrors().stream()
186 .map(error -> error.format(exceptionConverter.getMessageResolver(), exceptionConverter.getMessageInterpolator()))
187 .collect(Collectors.toList());
188 errorMessages.addAll(message);
189
190 final SuperCsvBindingExceptionException.html#SuperCsvBindingException">SuperCsvBindingException bindingException = new SuperCsvBindingException("has binding error.", context, bindingErrors);
191 rowException.ifPresent(re -> bindingException.addAllProcessingErrors(re.getColumnErrors()));
192
193 throw bindingException;
194
195 }
196 }
197
198
199
200
201
202
203
204
205
206
207
208
209
210 protected void extractBeanValues(final Object source, final String[] nameMapping) throws SuperCsvReflectionException {
211
212 Objects.requireNonNull(nameMapping, "the nameMapping array can't be null as it's used to map from fields to columns");
213
214 beanValues.clear();
215
216 for( int i = 0; i < nameMapping.length; i++ ) {
217
218 final String fieldName = nameMapping[i];
219
220 if( fieldName == null ) {
221 beanValues.add(null);
222
223 } else {
224 Method getMethod = cache.getGetMethod(source, fieldName);
225 try {
226 beanValues.add(getMethod.invoke(source));
227 }
228 catch(final Exception e) {
229 throw new SuperCsvReflectionException(String.format("error extracting bean value for field %s",
230 fieldName), e);
231 }
232 }
233
234 }
235
236 }
237
238
239
240
241
242
243
244
245
246 protected void executeCellProcessors(final List<Object> destination, final List<?> source,
247 final CellProcessor[] processors, final CsvContext context) {
248
249 destination.clear();
250
251 final SuperCsvRowExceptionvRowException.html#SuperCsvRowException">SuperCsvRowException rowException = new SuperCsvRowException(
252 String.format("row (%d) has errors column", context.getRowNumber()), context);
253 for( int i = 0; i < source.size(); i++ ) {
254
255 try {
256 context.setColumnNumber(i + 1);
257
258 if( processors[i] == null ) {
259 destination.add(executeNonCellProcessor(source.get(i), context));
260 } else {
261 destination.add(processors[i].execute(source.get(i), context));
262 }
263 } catch(SuperCsvCellProcessorException e) {
264 rowException.addError(e);
265
266
267 destination.add(source.get(i));
268
269 } catch(SuperCsvException e) {
270 throw e;
271 }
272 }
273
274 if(rowException.isNotEmptyColumnErrors()) {
275 throw rowException;
276 }
277
278 }
279
280
281
282
283
284
285
286 protected Object executeNonCellProcessor(Object value, CsvContext context) {
287
288 if (value != null) {
289 return value;
290 }
291
292 Optional<ColumnMapping> columnMapping = beanMappingCache.getOriginal().getColumnMapping(context.getColumnNumber());
293 if (!columnMapping.isPresent()) {
294 return value;
295 }
296
297
298
299
300
301 if (columnMapping.get().isPartialized() && columnMapping.get().getFixedSizeProperty() != null) {
302 FixedSizeColumnProperty fixedSizeProperty = columnMapping.get().getFixedSizeProperty();
303 PaddingProcessor paddingProcessor = columnMapping.get().getFixedSizeProperty().getPaddingProcessor();
304 return paddingProcessor.pad("",
305 fixedSizeProperty.getSize(),
306 fixedSizeProperty.getPadChar(),
307 fixedSizeProperty.isRightAlign(),
308 fixedSizeProperty.isChopped());
309
310 }
311
312 return value;
313 }
314
315
316
317
318
319
320 public String[] getDefinedHeader() {
321 return beanMappingCache.getHeader();
322 }
323
324
325
326
327
328 public BeanMapping<T> getBeanMapping() {
329 return beanMappingCache.getOriginal();
330 }
331
332
333
334
335
336 public List<String> getErrorMessages() {
337 return errorMessages;
338 }
339
340
341
342
343
344 public CsvExceptionConverter getExceptionConverter() {
345 return exceptionConverter;
346 }
347
348
349
350
351
352 public void setExceptionConverter(CsvExceptionConverter exceptionConverter) {
353 this.exceptionConverter = exceptionConverter;
354 }
355
356
357
358
359
360 @SuppressWarnings("unchecked")
361 public void addValidator(CsvValidator<T>... validators ) {
362 this.validators.addAll(Arrays.asList(validators));
363 }
364
365
366
367
368
369 public List<CsvValidator<T>> getValidators() {
370 return validators;
371 }
372
373
374
375 }