1 package com.opencsv.bean;
2
3 import com.opencsv.CSVReader;
4 import com.opencsv.ICSVParser;
5 import com.opencsv.exceptions.CsvRequiredFieldEmptyException;
6 import org.apache.commons.collections4.ListValuedMap;
7 import org.apache.commons.lang3.ArrayUtils;
8
9 import java.io.IOException;
10 import java.lang.annotation.Annotation;
11 import java.lang.reflect.Field;
12 import java.util.*;
13
14
15
16
17
18
19
20
21
22
23
24
25
26 public class ColumnPositionMappingStrategy<T> extends AbstractMappingStrategy<String, Integer, ComplexFieldMapEntry<String, Integer, T>, T> {
27
28
29
30
31
32 private boolean columnsExplicitlySet = false;
33
34
35
36
37 private FieldMapByPosition<T> fieldMap;
38
39
40
41
42 private Comparator<Integer> writeOrder;
43
44
45
46
47
48 private Integer[] columnIndexForWriting = null;
49
50
51
52
53
54 public ColumnPositionMappingStrategy() {
55 }
56
57
58
59
60
61
62
63 @Override
64 public void captureHeader(CSVReader reader) throws IOException {
65
66 if (type == null) {
67 throw new IllegalStateException(ResourceBundle
68 .getBundle(ICSVParser.DEFAULT_BUNDLE_NAME, errorLocale)
69 .getString("type.unset"));
70 }
71
72 String[] firstLine = ArrayUtils.nullToEmpty(reader.peek());
73 fieldMap.setMaxIndex(firstLine.length - 1);
74 if (!columnsExplicitlySet) {
75 headerIndex.clear();
76 for (FieldMapByPositionEntry<T> entry : fieldMap) {
77 Field f = entry.getField().getField();
78 if (f.getAnnotation(CsvCustomBindByPosition.class) != null
79 || f.getAnnotation(CsvBindAndSplitByPosition.class) != null
80 || f.getAnnotation(CsvBindAndJoinByPosition.class) != null
81 || f.getAnnotation(CsvBindByPosition.class) != null) {
82 headerIndex.put(entry.getPosition(), f.getName().toUpperCase().trim());
83 }
84 }
85 }
86 }
87
88
89
90
91
92
93 @Override
94 protected Integer chooseMultivaluedFieldIndexFromHeaderIndex(int index) {
95 return Integer.valueOf(index);
96 }
97
98 @Override
99 protected BeanField<T, Integer> findField(int col) {
100
101
102 if (columnIndexForWriting != null) {
103 return col < columnIndexForWriting.length ? fieldMap.get(columnIndexForWriting[col]) : null;
104 }
105 return fieldMap.get(col);
106 }
107
108
109
110
111
112
113
114
115
116 @Override
117 public String[] generateHeader(T bean) throws CsvRequiredFieldEmptyException {
118 String[] h = super.generateHeader(bean);
119 columnIndexForWriting = new Integer[h.length];
120 Arrays.setAll(columnIndexForWriting, i -> i);
121
122
123 Arrays.sort(columnIndexForWriting, writeOrder);
124 return ArrayUtils.EMPTY_STRING_ARRAY;
125 }
126
127
128
129
130
131
132
133 @Override
134 public String getColumnName(int col) {
135 return headerIndex.getByPosition(col);
136 }
137
138
139
140
141
142
143 public String[] getColumnMapping() {
144 return headerIndex.getHeaderIndex();
145 }
146
147
148
149
150
151
152
153
154 public void setColumnMapping(String... columnMapping) {
155 if (columnMapping != null) {
156 headerIndex.initializeHeaderIndex(columnMapping);
157 } else {
158 headerIndex.clear();
159 }
160 columnsExplicitlySet = true;
161 if(getType() != null) {
162 loadFieldMap();
163 }
164 }
165
166
167
168
169
170
171
172
173 private void registerCustomBinding(CsvCustomBindByPosition annotation, Class<?> localType, Field localField) {
174 @SuppressWarnings("unchecked")
175 Class<? extends AbstractBeanField<T, Integer>> converter = (Class<? extends AbstractBeanField<T, Integer>>)annotation.converter();
176 BeanField<T, Integer> bean = instantiateCustomConverter(converter);
177 bean.setType(localType);
178 bean.setField(localField);
179 bean.setRequired(annotation.required());
180 fieldMap.put(annotation.position(), bean);
181 }
182
183
184
185
186
187
188
189
190
191 private void registerSplitBinding(CsvBindAndSplitByPosition annotation, Class<?> localType, Field localField) {
192 String fieldLocale = annotation.locale();
193 String fieldWriteLocale = annotation.writeLocaleEqualsReadLocale()
194 ? fieldLocale
195 : annotation.writeLocale();
196 Class<?> elementType = annotation.elementType();
197 CsvConverter converter = determineConverter(localField, elementType,
198 fieldLocale, fieldWriteLocale, annotation.converter());
199 fieldMap.put(annotation.position(), new BeanFieldSplit<>(
200 localType, localField, annotation.required(), errorLocale, converter,
201 annotation.splitOn(), annotation.writeDelimiter(),
202 annotation.collectionType(), elementType, annotation.capture(),
203 annotation.format()));
204 }
205
206
207
208
209
210
211
212
213
214 private void registerJoinBinding(CsvBindAndJoinByPosition annotation, Class<?> localType, Field localField) {
215 String fieldLocale = annotation.locale();
216 String fieldWriteLocale = annotation.writeLocaleEqualsReadLocale()
217 ? fieldLocale
218 : annotation.writeLocale();
219 CsvConverter converter = determineConverter(localField, annotation.elementType(),
220 fieldLocale, fieldWriteLocale, annotation.converter());
221 fieldMap.putComplex(annotation.position(), new BeanFieldJoinIntegerIndex<>(
222 localType, localField, annotation.required(), errorLocale, converter,
223 annotation.mapType(), annotation.capture(), annotation.format()));
224 }
225
226
227
228
229
230
231
232
233 private void registerBinding(CsvBindByPosition annotation, Class<?> localType, Field localField) {
234 String fieldLocale = annotation.locale();
235 String fieldWriteLocale = annotation.writeLocaleEqualsReadLocale()
236 ? fieldLocale
237 : annotation.writeLocale();
238 CsvConverter converter = determineConverter(localField, localField.getType(), fieldLocale, fieldWriteLocale, null);
239 fieldMap.put(annotation.position(), new BeanFieldSingleValue<>(
240 localType, localField, annotation.required(), errorLocale,
241 converter, annotation.capture(), annotation.format()));
242 }
243
244
245
246
247
248
249 @Override
250 protected void loadAnnotatedFieldMap(ListValuedMap<Class<?>, Field> fields) {
251 for (Map.Entry<Class<?>, Field> classAndField : fields.entries()) {
252 Class<?> localType = classAndField.getKey();
253 Field localField = classAndField.getValue();
254
255
256 if (localField.isAnnotationPresent(CsvCustomBindByPosition.class)
257 || localField.isAnnotationPresent(CsvCustomBindByPositions.class)) {
258 CsvCustomBindByPosition annotation = selectAnnotationForProfile(
259 localField.getAnnotationsByType(CsvCustomBindByPosition.class),
260 CsvCustomBindByPosition::profiles);
261 if (annotation != null) {
262 registerCustomBinding(annotation, localType, localField);
263 }
264 }
265
266
267 else if (localField.isAnnotationPresent(CsvBindAndSplitByPosition.class)
268 || localField.isAnnotationPresent(CsvBindAndSplitByPositions.class)) {
269 CsvBindAndSplitByPosition annotation = selectAnnotationForProfile(
270 localField.getAnnotationsByType(CsvBindAndSplitByPosition.class),
271 CsvBindAndSplitByPosition::profiles);
272 if (annotation != null) {
273 registerSplitBinding(annotation, localType, localField);
274 }
275 }
276
277
278 else if (localField.isAnnotationPresent(CsvBindAndJoinByPosition.class)
279 || localField.isAnnotationPresent(CsvBindAndJoinByPositions.class)) {
280 CsvBindAndJoinByPosition annotation = selectAnnotationForProfile(
281 localField.getAnnotationsByType(CsvBindAndJoinByPosition.class),
282 CsvBindAndJoinByPosition::profiles);
283 if (annotation != null) {
284 registerJoinBinding(annotation, localType, localField);
285 }
286 }
287
288
289 else {
290 CsvBindByPosition annotation = selectAnnotationForProfile(
291 localField.getAnnotationsByType(CsvBindByPosition.class),
292 CsvBindByPosition::profiles);
293 if (annotation != null) {
294 registerBinding(annotation, localType, localField);
295 }
296 }
297 }
298 }
299
300 @Override
301 protected void loadUnadornedFieldMap(ListValuedMap<Class<?>, Field> fields) {
302 for(Map.Entry<Class<?>, Field> classAndField : fields.entries()) {
303 Class<?> localType = classAndField.getKey();
304 Field localField = classAndField.getValue();
305 CsvConverter converter = determineConverter(localField, localField.getType(), null, null, null);
306 int[] indices = headerIndex.getByName(localField.getName());
307 if(indices.length != 0) {
308 fieldMap.put(indices[0], new BeanFieldSingleValue<>(
309 localType, localField, false, errorLocale, converter, null, null));
310 }
311 }
312 }
313
314
315
316
317
318
319
320
321
322
323
324 @Override
325 protected Set<Class<? extends Annotation>> getBindingAnnotations() {
326
327 return new HashSet<>(Arrays.asList(
328 CsvBindByPositions.class,
329 CsvCustomBindByPositions.class,
330 CsvBindAndJoinByPositions.class,
331 CsvBindAndSplitByPositions.class,
332 CsvBindByPosition.class,
333 CsvCustomBindByPosition.class,
334 CsvBindAndJoinByPosition.class,
335 CsvBindAndSplitByPosition.class));
336 }
337
338 @Override
339 protected void initializeFieldMap() {
340 fieldMap = new FieldMapByPosition<>(errorLocale);
341 fieldMap.setColumnOrderOnWrite(writeOrder);
342 }
343
344 @Override
345 protected void verifyLineLength(int numberOfFields) throws CsvRequiredFieldEmptyException {
346 if (!headerIndex.isEmpty()) {
347 BeanField<T, Integer> f;
348 StringBuilder sb = null;
349 for (int i = numberOfFields; i <= headerIndex.findMaxIndex(); i++) {
350 f = findField(i);
351 if (f != null && f.isRequired()) {
352 if (sb == null) {
353 sb = new StringBuilder(ResourceBundle.getBundle(ICSVParser.DEFAULT_BUNDLE_NAME, errorLocale).getString("multiple.required.field.empty"));
354 }
355 sb.append(' ');
356 sb.append(f.getField().getName());
357 }
358 }
359 if (sb != null) {
360 throw new CsvRequiredFieldEmptyException(type, sb.toString());
361 }
362 }
363 }
364
365
366
367
368
369
370
371 @Override
372 public String findHeader(int col) {
373 return Integer.toString(col);
374 }
375
376 @Override
377 protected FieldMap<String, Integer, ? extends ComplexFieldMapEntry<String, Integer, T>, T> getFieldMap() {
378 return fieldMap;
379 }
380
381
382
383
384
385
386
387
388
389
390
391 public void setColumnOrderOnWrite(Comparator<Integer> writeOrder) {
392 this.writeOrder = writeOrder;
393 if (fieldMap != null) {
394 fieldMap.setColumnOrderOnWrite(this.writeOrder);
395 }
396 }
397 }