1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package com.orangesignal.csv.io;
18
19 import static com.orangesignal.csv.bean.CsvEntityTemplate.defaultIfEmpty;
20 import static com.orangesignal.csv.bean.FieldUtils.getFieldValue;
21
22 import java.io.Closeable;
23 import java.io.Flushable;
24 import java.io.IOException;
25 import java.lang.reflect.Array;
26 import java.lang.reflect.Field;
27 import java.util.Arrays;
28 import java.util.Collections;
29 import java.util.List;
30
31 import com.orangesignal.csv.CsvWriter;
32 import com.orangesignal.csv.annotation.CsvColumn;
33 import com.orangesignal.csv.annotation.CsvColumns;
34 import com.orangesignal.csv.annotation.CsvEntity;
35 import com.orangesignal.csv.annotation.CsvColumnException;
36 import com.orangesignal.csv.bean.CsvEntityTemplate;
37
38
39
40
41
42
43
44 public class CsvEntityWriter<T> implements Closeable, Flushable {
45
46
47
48
49 private CsvWriter writer;
50
51
52
53
54 private final CsvEntityTemplate<T> template;
55
56
57
58
59
60
61 private final boolean disableWriteHeader;
62
63
64
65
66 private List<String> columnNames;
67
68 private int columnCount = -1;
69
70
71
72
73
74
75
76
77
78
79
80
81
82 public static <T> CsvEntityWriter<T> newInstance(final CsvWriter writer, final Class<T> entityClass) {
83 return new CsvEntityWriter<T>(writer, entityClass);
84 }
85
86
87
88
89
90
91
92
93
94
95
96
97 public static <T> CsvEntityWriter<T> newInstance(final CsvWriter writer, final Class<T> entityClass, final boolean disableWriteHeader) {
98 return new CsvEntityWriter<T>(writer, entityClass, disableWriteHeader);
99 }
100
101
102
103
104
105
106
107
108
109
110 public static <T> CsvEntityWriter<T> newInstance(final CsvWriter writer, final CsvEntityTemplate<T> template) {
111 return new CsvEntityWriter<T>(writer, template);
112 }
113
114
115
116
117
118
119
120
121
122
123
124
125 public static <T> CsvEntityWriter<T> newInstance(final CsvWriter writer, final CsvEntityTemplate<T> template, final boolean disableWriteHeader) {
126 return new CsvEntityWriter<T>(writer, template, disableWriteHeader);
127 }
128
129
130
131
132
133
134
135
136
137
138
139 public CsvEntityWriter(final CsvWriter writer, final Class<T> entityClass) {
140 this(writer, new CsvEntityTemplate<T>(entityClass), false);
141 }
142
143
144
145
146
147
148
149
150
151
152 public CsvEntityWriter(final CsvWriter writer, final Class<T> entityClass, final boolean disableWriteHeader) {
153 this(writer, new CsvEntityTemplate<T>(entityClass), disableWriteHeader);
154 }
155
156
157
158
159
160
161
162
163 public CsvEntityWriter(final CsvWriter writer, final CsvEntityTemplate<T> template) {
164 this(writer, template, false);
165 }
166
167
168
169
170
171
172
173
174
175
176 public CsvEntityWriter(final CsvWriter writer, final CsvEntityTemplate<T> template, final boolean disableWriteHeader) {
177 if (writer == null) {
178 throw new IllegalArgumentException("CsvWriter must not be null");
179 }
180 if (template == null) {
181 throw new IllegalArgumentException("CsvEntityTemplate must not be null");
182 }
183 this.writer = writer;
184 this.template = template;
185 this.disableWriteHeader = disableWriteHeader;
186 }
187
188
189
190
191
192
193
194 private void ensureOpen() throws IOException {
195 if (writer == null) {
196 throw new IOException("CsvWriter closed");
197 }
198 }
199
200 private void ensureHeader() throws IOException {
201 synchronized (this) {
202 if (columnNames == null) {
203 final List<String> names = template.createWritableColumnNames();
204
205 if (!disableWriteHeader && template.getType().getAnnotation(CsvEntity.class).header()) {
206 writer.writeValues(names);
207 }
208 template.prepare(names, template.getType().getDeclaredFields());
209 columnNames = Collections.unmodifiableList(names);
210 columnCount = names.size();
211 }
212 }
213 }
214
215
216
217
218 @Override
219 public void flush() throws IOException {
220 synchronized (this) {
221 ensureOpen();
222 writer.flush();
223 }
224 }
225
226 @Override
227 public void close() throws IOException {
228 synchronized (this) {
229 ensureOpen();
230 writer.close();
231 writer = null;
232 columnNames = null;
233 columnCount = -1;
234 }
235 }
236
237
238
239
240
241
242
243
244
245
246
247 public void writeHeader() throws IOException {
248 synchronized (this) {
249 ensureOpen();
250 ensureHeader();
251 }
252 }
253
254
255
256
257
258
259
260
261
262
263 public boolean write(final T entity) throws IOException {
264 synchronized (this) {
265 ensureOpen();
266 ensureHeader();
267
268
269 if (entity == null || entity.getClass().getAnnotation(CsvEntity.class) == null) {
270 writer.writeValues(null);
271 return true;
272 }
273
274 final List<String> values = toValues(entity);
275 if (template.isAccept(columnNames, values)) {
276 return false;
277 }
278 writer.writeValues(values);
279 return true;
280 }
281 }
282
283 private List<String> toValues(final T entity) throws IOException {
284 final String[] values = new String[columnCount];
285 for (final Field field : entity.getClass().getDeclaredFields()) {
286 final CsvColumns columns = field.getAnnotation(CsvColumns.class);
287 if (columns != null) {
288 int arrayIndex = 0;
289 for (final CsvColumn column : columns.value()) {
290 if (!column.access().isWriteable()) {
291 arrayIndex++;
292 continue;
293 }
294 int pos = column.position();
295 if (pos < 0) {
296 pos = columnNames.indexOf(defaultIfEmpty(column.name(), field.getName()));
297 }
298 if (pos == -1) {
299 throw new IOException(String.format("Invalid CsvColumn field %s", field.getName()));
300 }
301 Object o = getFieldValue(entity, field);
302 if (field.getType().isArray()) {
303 if (o != null) {
304 o = Array.get(o, arrayIndex);
305 }
306 arrayIndex++;
307 }
308 values[pos] = template.objectToString(pos, o);
309 if (values[pos] == null && !column.defaultValue().isEmpty()) {
310
311 values[pos] = column.defaultValue();
312 }
313 if (values[pos] == null && column.required()) {
314 throw new CsvColumnException(String.format("%s must not be null", columnNames.get(pos)), entity);
315 }
316 }
317 }
318 final CsvColumn column = field.getAnnotation(CsvColumn.class);
319 if (column != null && column.access().isWriteable()) {
320 int pos = column.position();
321 if (pos < 0) {
322 pos = columnNames.indexOf(defaultIfEmpty(column.name(), field.getName()));
323 }
324 if (pos == -1) {
325 throw new IOException(String.format("Invalid CsvColumn field %s", field.getName()));
326 }
327 values[pos] = template.objectToString(pos, getFieldValue(entity, field));
328 if (values[pos] == null && !column.defaultValue().isEmpty()) {
329
330 values[pos] = column.defaultValue();
331 }
332 if (values[pos] == null && column.required()) {
333 throw new CsvColumnException(String.format("%s must not be null", columnNames.get(pos)), entity);
334 }
335 }
336 }
337 return Arrays.asList(values);
338 }
339
340
341
342
343
344
345
346
347
348
349 public CsvEntityTemplate<T> getTemplate() {
350 return template;
351 }
352
353
354
355
356
357
358
359 public boolean isDisableWriteHeader() {
360 return disableWriteHeader;
361 }
362
363 }