1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package com.opencsv.bean;
17
18 import com.opencsv.CSVWriter;
19 import com.opencsv.ICSVParser;
20 import com.opencsv.ICSVWriter;
21 import com.opencsv.bean.concurrent.BeanExecutor;
22 import com.opencsv.bean.concurrent.ProcessCsvBean;
23 import com.opencsv.bean.exceptionhandler.CsvExceptionHandler;
24 import com.opencsv.bean.exceptionhandler.ExceptionHandlerThrow;
25 import com.opencsv.bean.util.OpencsvUtils;
26 import com.opencsv.bean.util.OrderedObject;
27 import com.opencsv.exceptions.CsvDataTypeMismatchException;
28 import com.opencsv.exceptions.CsvException;
29 import com.opencsv.exceptions.CsvRequiredFieldEmptyException;
30 import com.opencsv.exceptions.CsvRuntimeException;
31 import org.apache.commons.collections4.CollectionUtils;
32 import org.apache.commons.collections4.MultiValuedMap;
33 import org.apache.commons.collections4.iterators.PeekingIterator;
34 import org.apache.commons.lang3.ObjectUtils;
35 import org.apache.commons.lang3.StringUtils;
36
37 import java.io.Writer;
38 import java.lang.reflect.Field;
39 import java.util.*;
40 import java.util.concurrent.ArrayBlockingQueue;
41 import java.util.concurrent.BlockingQueue;
42 import java.util.concurrent.LinkedBlockingQueue;
43 import java.util.concurrent.RejectedExecutionException;
44 import java.util.stream.Stream;
45 import java.util.stream.StreamSupport;
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60 public class StatefulBeanToCsv<T> {
61 private static final char NO_CHARACTER = '\0';
62
63
64
65 private int lineNumber = 0;
66
67 private final char separator;
68 private final char quotechar;
69 private final char escapechar;
70 private final String lineEnd;
71 private boolean headerWritten = false;
72 private MappingStrategy<T> mappingStrategy;
73 private final Writer writer;
74 private ICSVWriter csvwriter;
75 private final CsvExceptionHandler exceptionHandler;
76 private List<CsvException> capturedExceptions = new ArrayList<>();
77 private boolean orderedResults = true;
78 private BeanExecutor<T> executor = null;
79 private Locale errorLocale = Locale.getDefault();
80 private final boolean applyQuotesToAll;
81 private final MultiValuedMap<Class<?>, Field> ignoredFields;
82 private final String profile;
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99 StatefulBeanToCsv(char escapechar, String lineEnd,
100 MappingStrategy<T> mappingStrategy, char quotechar, char separator,
101 CsvExceptionHandler exceptionHandler, Writer writer, boolean applyQuotesToAll,
102 MultiValuedMap<Class<?>, Field> ignoredFields, String profile) {
103 this.escapechar = escapechar;
104 this.lineEnd = lineEnd;
105 this.mappingStrategy = mappingStrategy;
106 this.quotechar = quotechar;
107 this.separator = separator;
108 this.exceptionHandler = exceptionHandler;
109 this.writer = writer;
110 this.applyQuotesToAll = applyQuotesToAll;
111 this.ignoredFields = ignoredFields;
112 this.profile = StringUtils.defaultString(profile);
113 }
114
115
116
117
118
119
120
121
122
123
124
125
126 public StatefulBeanToCsv(MappingStrategy<T> mappingStrategy,
127 CsvExceptionHandler exceptionHandler, boolean applyQuotesToAll,
128 ICSVWriter csvWriter,
129 MultiValuedMap<Class<?>, Field> ignoredFields, String profile) {
130 this.mappingStrategy = mappingStrategy;
131 this.exceptionHandler = exceptionHandler;
132 this.applyQuotesToAll = applyQuotesToAll;
133 this.csvwriter = csvWriter;
134
135 this.escapechar = NO_CHARACTER;
136 this.lineEnd = "";
137 this.quotechar = NO_CHARACTER;
138 this.separator = NO_CHARACTER;
139 this.writer = null;
140 this.ignoredFields = ignoredFields;
141 this.profile = StringUtils.defaultString(profile);
142 }
143
144
145
146
147
148
149
150
151
152
153
154
155
156 private void beforeFirstWrite(T bean) throws CsvRequiredFieldEmptyException {
157
158
159 if (mappingStrategy == null) {
160 mappingStrategy = OpencsvUtils.determineMappingStrategy(
161 (Class<T>) bean.getClass(), errorLocale, profile);
162 }
163
164
165
166
167 if(!ignoredFields.isEmpty()) {
168 mappingStrategy.ignoreFields(ignoredFields);
169 }
170
171
172 if (csvwriter == null) {
173 csvwriter = new CSVWriter(writer, separator, quotechar, escapechar, lineEnd);
174 }
175
176
177 String[] header = mappingStrategy.generateHeader(bean);
178 if (header.length > 0) {
179 csvwriter.writeNext(header, applyQuotesToAll);
180 }
181 headerWritten = true;
182 }
183
184
185
186
187
188
189
190
191
192
193
194
195
196 public void write(T bean) throws CsvDataTypeMismatchException,
197 CsvRequiredFieldEmptyException {
198
199
200 if (bean != null) {
201 if (!headerWritten) {
202 beforeFirstWrite(bean);
203 }
204
205
206 BlockingQueue<OrderedObject<String[]>> resultantLineQueue = new ArrayBlockingQueue<>(1);
207 BlockingQueue<OrderedObject<CsvException>> thrownExceptionsQueue = new LinkedBlockingQueue<>();
208 ProcessCsvBean<T> proc = new ProcessCsvBean<>(++lineNumber,
209 mappingStrategy, bean, resultantLineQueue,
210 thrownExceptionsQueue, new TreeSet<>(), exceptionHandler);
211 try {
212 proc.run();
213 } catch (RuntimeException re) {
214 if (re.getCause() != null) {
215 if (re.getCause() instanceof CsvRuntimeException) {
216
217
218
219 throw (CsvRuntimeException) re.getCause();
220 }
221 if (re.getCause() instanceof CsvDataTypeMismatchException) {
222 throw (CsvDataTypeMismatchException) re.getCause();
223 }
224 if (re.getCause() instanceof CsvRequiredFieldEmptyException) {
225 throw (CsvRequiredFieldEmptyException) re.getCause();
226 }
227 }
228 throw re;
229 }
230
231
232 if (!thrownExceptionsQueue.isEmpty()) {
233 OrderedObject<CsvException> o = thrownExceptionsQueue.poll();
234 while (o != null && o.getElement() != null) {
235 capturedExceptions.add(o.getElement());
236 o = thrownExceptionsQueue.poll();
237 }
238 } else {
239
240 OrderedObject<String[]> result = resultantLineQueue.poll();
241 if (result != null && result.getElement() != null) {
242 csvwriter.writeNext(result.getElement(), applyQuotesToAll);
243 }
244 }
245 }
246 }
247
248 private void submitAllLines(Iterator<T> beans) throws InterruptedException {
249 while (beans.hasNext()) {
250 T bean = beans.next();
251 if (bean != null) {
252 executor.submitBean(++lineNumber, mappingStrategy, bean, exceptionHandler);
253 }
254 }
255 executor.complete();
256 }
257
258
259
260
261
262
263
264
265
266
267
268
269 public void write(List<T> beans) throws CsvDataTypeMismatchException,
270 CsvRequiredFieldEmptyException {
271 if (CollectionUtils.isNotEmpty(beans)) {
272 write(beans.iterator());
273 }
274 }
275
276
277
278
279
280
281
282
283
284
285 public void write(Iterator<T> iBeans) throws CsvDataTypeMismatchException, CsvRequiredFieldEmptyException {
286
287 PeekingIterator<T> beans = new PeekingIterator<>(iBeans);
288 T firstBean = beans.peek();
289
290 if (!beans.hasNext()) {
291 return;
292 }
293
294
295 if (!headerWritten) {
296 beforeFirstWrite(firstBean);
297 }
298
299 executor = new BeanExecutor<>(orderedResults, errorLocale);
300 executor.prepare();
301
302
303 try {
304 submitAllLines(beans);
305 } catch (RejectedExecutionException e) {
306
307
308 if (executor.getTerminalException() instanceof RuntimeException) {
309 throw (RuntimeException) executor.getTerminalException();
310 }
311 if (executor.getTerminalException() instanceof CsvDataTypeMismatchException) {
312 throw (CsvDataTypeMismatchException) executor.getTerminalException();
313 }
314 if (executor.getTerminalException() instanceof CsvRequiredFieldEmptyException) {
315 throw (CsvRequiredFieldEmptyException) executor
316 .getTerminalException();
317 }
318 throw new RuntimeException(ResourceBundle.getBundle(ICSVParser.DEFAULT_BUNDLE_NAME, errorLocale)
319 .getString("error.writing.beans"), executor.getTerminalException());
320 } catch (Exception e) {
321
322
323
324 executor.shutdownNow();
325 if (executor.getTerminalException() instanceof RuntimeException) {
326 throw (RuntimeException) executor.getTerminalException();
327 }
328 throw new RuntimeException(ResourceBundle.getBundle(ICSVParser.DEFAULT_BUNDLE_NAME, errorLocale)
329 .getString("error.writing.beans"), e);
330 }
331 finally {
332 capturedExceptions.addAll(executor.getCapturedExceptions());
333 }
334
335 StreamSupport.stream(executor, false)
336 .forEach(l -> csvwriter.writeNext(l, applyQuotesToAll));
337 }
338
339
340
341
342
343
344
345
346
347
348 public void write(Stream<T> beans) throws CsvDataTypeMismatchException, CsvRequiredFieldEmptyException {
349 write(beans.iterator());
350 }
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365 public void setOrderedResults(boolean orderedResults) {
366 this.orderedResults = orderedResults;
367 }
368
369
370
371
372
373
374 @Deprecated
375 public boolean isThrowExceptions() {
376 return exceptionHandler instanceof ExceptionHandlerThrow;
377 }
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392 public List<CsvException> getCapturedExceptions() {
393 List<CsvException> intermediate = capturedExceptions;
394 capturedExceptions = new ArrayList<>();
395 intermediate.sort(Comparator.comparingLong(CsvException::getLineNumber));
396 return intermediate;
397 }
398
399
400
401
402
403
404
405
406 public void setErrorLocale(Locale errorLocale) {
407 this.errorLocale = ObjectUtils.defaultIfNull(errorLocale, Locale.getDefault());
408 }
409 }