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.ICSVParser;
19 import com.opencsv.exceptions.CsvRequiredFieldEmptyException;
20 import org.apache.commons.collections4.MultiValuedMap;
21 import org.apache.commons.collections4.iterators.LazyIteratorChain;
22 import org.apache.commons.collections4.iterators.TransformIterator;
23
24 import java.lang.reflect.Field;
25 import java.util.*;
26 import java.util.stream.Collectors;
27
28
29
30
31
32
33
34
35
36 public class FieldMapByPosition<T> extends AbstractFieldMap<String, Integer, PositionToBeanField<T>, T> implements Iterable<FieldMapByPositionEntry<T>> {
37
38 private int maxIndex = Integer.MAX_VALUE;
39
40
41 private Comparator<Integer> writeOrder = null;
42
43
44
45
46
47
48 public FieldMapByPosition(final Locale errorLocale) {
49 super(errorLocale);
50 }
51
52
53
54
55
56
57
58
59
60
61 @Override
62 public String[] generateHeader(final T bean) throws CsvRequiredFieldEmptyException {
63 final List<Field> missingRequiredHeaders = new LinkedList<>();
64 final SortedMap<Integer, String> headerMap = new TreeMap<>(writeOrder);
65 for(Map.Entry<Integer, BeanField<T, Integer>> entry : simpleMap.entrySet()) {
66 headerMap.put(entry.getKey(), entry.getValue().getField().getName());
67 }
68 for(ComplexFieldMapEntry<String, Integer, T> r : complexMapList) {
69 @SuppressWarnings("unchecked")
70 final MultiValuedMap<Integer,T> m = (MultiValuedMap<Integer, T>) r.getBeanField().getFieldValue(bean);
71 boolean oneEntryMatched = false;
72 if(m != null && !m.isEmpty()) {
73 for(Map.Entry<Integer,T> entry : m.entries()) {
74 Integer key = entry.getKey();
75 if(r.contains(key)) {
76 headerMap.put(entry.getKey(), r.getBeanField().getField().getName());
77 oneEntryMatched = true;
78 }
79 }
80 }
81 if(m == null || m.isEmpty() || !oneEntryMatched) {
82 if(r.getBeanField().isRequired()) {
83 missingRequiredHeaders.add(r.getBeanField().getField());
84 }
85 }
86 }
87
88
89
90
91 SortedSet<Integer> headerSet = new TreeSet<>(headerMap.keySet());
92 int arraySize = headerSet.isEmpty() ? 0 : headerSet.last()+1;
93 final String[] headers = new String[arraySize];
94 int previousIndex = headerSet.isEmpty() ? 0 : headerSet.first();
95 for(Integer i : headerSet) {
96 for(int j = previousIndex+1; j < i ; j++) {
97 headerMap.put(j, null);
98 }
99 previousIndex = i;
100 }
101 previousIndex = 0;
102 for(String value : headerMap.values()) {
103 headers[previousIndex++] = value;
104 }
105
106
107 if(!missingRequiredHeaders.isEmpty()) {
108 String errorMessage = String.format(
109 ResourceBundle
110 .getBundle(ICSVParser.DEFAULT_BUNDLE_NAME, errorLocale)
111 .getString("header.required.field.absent"),
112 missingRequiredHeaders.stream()
113 .map(Field::getName)
114 .collect(Collectors.joining(" ")),
115 String.join(" ", headers));
116 throw new CsvRequiredFieldEmptyException(bean.getClass(), missingRequiredHeaders, errorMessage);
117 }
118
119 return headers;
120 }
121
122
123
124
125
126
127
128 @Override
129 public void putComplex(final String rangeDefinition, final BeanField<T, Integer> field) {
130 complexMapList.add(new PositionToBeanField<>(rangeDefinition, maxIndex, field, errorLocale));
131 }
132
133
134
135
136
137
138
139
140
141
142
143 public void setMaxIndex(int maxIndex) {
144 this.maxIndex = maxIndex;
145
146
147 complexMapList.forEach(p -> p.attenuateRanges(maxIndex));
148 }
149
150 @Override
151 public Iterator<FieldMapByPositionEntry<T>> iterator() {
152 return new LazyIteratorChain<FieldMapByPositionEntry<T>>() {
153
154 @Override
155 protected Iterator<FieldMapByPositionEntry<T>> nextIterator(int count) {
156 if(count <= complexMapList.size()) {
157 return complexMapList.get(count-1).iterator();
158 }
159 if(count == complexMapList.size()+1) {
160 return new TransformIterator<>(
161 simpleMap.entrySet().iterator(),
162 input -> new FieldMapByPositionEntry<T>(input.getKey(), input.getValue()));
163 }
164 return null;
165 }
166 };
167 }
168
169
170
171
172
173
174
175
176
177 public void setColumnOrderOnWrite(Comparator<Integer> writeOrder) {
178 this.writeOrder = writeOrder;
179 }
180 }