1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.csv;
18
19 import static org.junit.jupiter.api.Assertions.assertArrayEquals;
20 import static org.junit.jupiter.api.Assertions.assertEquals;
21 import static org.junit.jupiter.api.Assertions.assertFalse;
22 import static org.junit.jupiter.api.Assertions.assertNotNull;
23 import static org.junit.jupiter.api.Assertions.assertNull;
24 import static org.junit.jupiter.api.Assertions.assertThrows;
25 import static org.junit.jupiter.api.Assertions.assertTrue;
26
27 import java.io.ByteArrayInputStream;
28 import java.io.ByteArrayOutputStream;
29 import java.io.IOException;
30 import java.io.ObjectInputStream;
31 import java.io.ObjectOutputStream;
32 import java.io.StringReader;
33 import java.util.ArrayList;
34 import java.util.List;
35 import java.util.Map;
36 import java.util.TreeMap;
37 import java.util.concurrent.ConcurrentHashMap;
38 import java.util.concurrent.atomic.AtomicInteger;
39
40 import org.apache.commons.lang3.StringUtils;
41 import org.junit.jupiter.api.BeforeEach;
42 import org.junit.jupiter.api.Test;
43
44 public class CSVRecordTest {
45
46 private enum EnumFixture {
47 UNKNOWN_COLUMN
48 }
49
50
51 public enum EnumHeader {
52 FIRST("first"), SECOND("second"), THIRD("third");
53
54 private final String number;
55
56 EnumHeader(final String number) {
57 this.number = number;
58 }
59
60 @Override
61 public String toString() {
62 return number;
63 }
64 }
65
66 private Map<String, Integer> headerMap;
67 private CSVRecord record, recordWithHeader;
68 private String[] values;
69
70 @BeforeEach
71 public void setUp() throws Exception {
72 values = new String[] { "A", "B", "C" };
73 final String rowData = StringUtils.join(values, ',');
74 try (final CSVParser parser = CSVFormat.DEFAULT.parse(new StringReader(rowData))) {
75 record = parser.iterator().next();
76 }
77 try (final CSVParser parser = CSVFormat.DEFAULT.builder().setHeader(EnumHeader.class).build().parse(new StringReader(rowData))) {
78 recordWithHeader = parser.iterator().next();
79 headerMap = parser.getHeaderMap();
80 }
81 }
82
83 @Test
84 public void testCSVRecordNULLValues() throws IOException {
85 final CSVParser parser = CSVParser.parse("A,B\r\nONE,TWO", CSVFormat.DEFAULT.withHeader());
86 final CSVRecord csvRecord = new CSVRecord(parser, null, null, 0L, 0L);
87 assertEquals(0, csvRecord.size());
88 assertThrows(IllegalArgumentException.class, () -> csvRecord.get("B"));
89 }
90
91 @Test
92 public void testGetInt() {
93 assertEquals(values[0], record.get(0));
94 assertEquals(values[1], record.get(1));
95 assertEquals(values[2], record.get(2));
96 }
97
98 @Test
99 public void testGetNullEnum() {
100 assertThrows(IllegalArgumentException.class, () -> recordWithHeader.get((Enum<?>) null));
101 }
102
103 @Test
104 public void testGetString() {
105 assertEquals(values[0], recordWithHeader.get(EnumHeader.FIRST.name()));
106 assertEquals(values[1], recordWithHeader.get(EnumHeader.SECOND.name()));
107 assertEquals(values[2], recordWithHeader.get(EnumHeader.THIRD.name()));
108 }
109
110 @Test
111 public void testGetStringInconsistentRecord() {
112 headerMap.put("fourth", Integer.valueOf(4));
113 assertThrows(IllegalArgumentException.class, () -> recordWithHeader.get("fourth"));
114 }
115
116 @Test
117 public void testGetStringNoHeader() {
118 assertThrows(IllegalStateException.class, () -> record.get("first"));
119 }
120
121 @Test
122 public void testGetUnmappedEnum() {
123 assertThrows(IllegalArgumentException.class, () -> recordWithHeader.get(EnumFixture.UNKNOWN_COLUMN));
124 }
125
126 @Test
127 public void testGetUnmappedName() {
128 assertThrows(IllegalArgumentException.class, () -> assertNull(recordWithHeader.get("fourth")));
129 }
130
131 @Test
132 public void testGetUnmappedNegativeInt() {
133 assertThrows(ArrayIndexOutOfBoundsException.class, () -> recordWithHeader.get(Integer.MIN_VALUE));
134 }
135
136 @Test
137 public void testGetUnmappedPositiveInt() {
138 assertThrows(ArrayIndexOutOfBoundsException.class, () -> recordWithHeader.get(Integer.MAX_VALUE));
139 }
140
141 @Test
142 public void testGetWithEnum() {
143 assertEquals(recordWithHeader.get("FIRST"), recordWithHeader.get(EnumHeader.FIRST));
144 assertEquals(recordWithHeader.get("SECOND"), recordWithHeader.get(EnumHeader.SECOND));
145 assertThrows(IllegalArgumentException.class, () -> recordWithHeader.get(EnumFixture.UNKNOWN_COLUMN));
146 }
147
148 @Test
149 public void testIsConsistent() {
150 assertTrue(record.isConsistent());
151 assertTrue(recordWithHeader.isConsistent());
152 final Map<String, Integer> map = recordWithHeader.getParser().getHeaderMap();
153 map.put("fourth", Integer.valueOf(4));
154
155 assertTrue(recordWithHeader.isConsistent());
156 }
157
158 @Test
159 public void testIsInconsistent() throws IOException {
160 final String[] headers = { "first", "second", "third" };
161 final String rowData = StringUtils.join(values, ',');
162 try (final CSVParser parser = CSVFormat.DEFAULT.withHeader(headers).parse(new StringReader(rowData))) {
163 final Map<String, Integer> map = parser.getHeaderMapRaw();
164 final CSVRecord record1 = parser.iterator().next();
165 map.put("fourth", Integer.valueOf(4));
166 assertFalse(record1.isConsistent());
167 }
168 }
169
170 @Test
171 public void testIsMapped() {
172 assertFalse(record.isMapped("first"));
173 assertTrue(recordWithHeader.isMapped(EnumHeader.FIRST.name()));
174 assertFalse(recordWithHeader.isMapped("fourth"));
175 }
176
177 @Test
178 public void testIsSetInt() {
179 assertFalse(record.isSet(-1));
180 assertTrue(record.isSet(0));
181 assertTrue(record.isSet(2));
182 assertFalse(record.isSet(3));
183 assertTrue(recordWithHeader.isSet(1));
184 assertFalse(recordWithHeader.isSet(1000));
185 }
186
187 @Test
188 public void testIsSetString() {
189 assertFalse(record.isSet("first"));
190 assertTrue(recordWithHeader.isSet(EnumHeader.FIRST.name()));
191 assertFalse(recordWithHeader.isSet("DOES NOT EXIST"));
192 }
193
194 @Test
195 public void testIterator() {
196 int i = 0;
197 for (final String value : record) {
198 assertEquals(values[i], value);
199 i++;
200 }
201 }
202
203 @Test
204 public void testPutInMap() {
205 final Map<String, String> map = new ConcurrentHashMap<>();
206 this.recordWithHeader.putIn(map);
207 this.validateMap(map, false);
208
209 final TreeMap<String, String> map2 = recordWithHeader.putIn(new TreeMap<>());
210 this.validateMap(map2, false);
211 }
212
213 @Test
214 public void testRemoveAndAddColumns() throws IOException {
215
216 try (final CSVPrinter printer = new CSVPrinter(new StringBuilder(), CSVFormat.DEFAULT)) {
217 final Map<String, String> map = recordWithHeader.toMap();
218 map.remove("OldColumn");
219 map.put("ZColumn", "NewValue");
220
221 final ArrayList<String> list = new ArrayList<>(map.values());
222 list.sort(null);
223 printer.printRecord(list);
224 assertEquals("A,B,C,NewValue" + CSVFormat.DEFAULT.getRecordSeparator(), printer.getOut().toString());
225 }
226 }
227
228 @Test
229 public void testSerialization() throws IOException, ClassNotFoundException {
230 final CSVRecord shortRec;
231 try (final CSVParser parser = CSVParser.parse("A,B\n#my comment\nOne,Two", CSVFormat.DEFAULT.withHeader().withCommentMarker('#'))) {
232 shortRec = parser.iterator().next();
233 }
234 final ByteArrayOutputStream out = new ByteArrayOutputStream();
235 try (ObjectOutputStream oos = new ObjectOutputStream(out)) {
236 oos.writeObject(shortRec);
237 }
238 final ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
239 try (ObjectInputStream ois = new ObjectInputStream(in)) {
240 final Object object = ois.readObject();
241 assertTrue(object instanceof CSVRecord);
242 final CSVRecord rec = (CSVRecord) object;
243 assertEquals(1L, rec.getRecordNumber());
244 assertEquals("One", rec.get(0));
245 assertEquals("Two", rec.get(1));
246 assertEquals(2, rec.size());
247 assertEquals(shortRec.getCharacterPosition(), rec.getCharacterPosition());
248 assertEquals("my comment", rec.getComment());
249
250 assertNull(rec.getParser());
251
252 assertTrue(rec.isConsistent());
253 assertFalse(rec.isMapped("A"));
254 assertFalse(rec.isSet("A"));
255 assertEquals(0, rec.toMap().size());
256
257 try {
258 rec.get("A");
259 org.junit.jupiter.api.Assertions.fail("Access by name is not expected after deserialisation");
260 } catch (final IllegalStateException expected) {
261
262 }
263 }
264 }
265
266 @Test
267 public void testStream() {
268 final AtomicInteger i = new AtomicInteger();
269 record.stream().forEach(value -> {
270 assertEquals(values[i.get()], value);
271 i.incrementAndGet();
272 });
273 }
274
275 @Test
276 public void testToListAdd() {
277 final String[] expected = values.clone();
278 final List<String> list = record.toList();
279 list.add("Last");
280 assertEquals("Last", list.get(list.size() - 1));
281 assertEquals(list.size(), values.length + 1);
282 assertArrayEquals(expected, values);
283 }
284
285 @Test
286 public void testToListFor() {
287 int i = 0;
288 for (final String value : record.toList()) {
289 assertEquals(values[i], value);
290 i++;
291 }
292 }
293
294 @Test
295 public void testToListForEach() {
296 final AtomicInteger i = new AtomicInteger();
297 record.toList().forEach(e -> {
298 assertEquals(values[i.getAndIncrement()], e);
299 });
300 }
301
302 @Test
303 public void testToListSet() {
304 final String[] expected = values.clone();
305 final List<String> list = record.toList();
306 list.set(list.size() - 1, "Last");
307 assertEquals("Last", list.get(list.size() - 1));
308 assertEquals(list.size(), values.length);
309 assertArrayEquals(expected, values);
310 }
311
312 @Test
313 public void testToMap() {
314 final Map<String, String> map = this.recordWithHeader.toMap();
315 this.validateMap(map, true);
316 }
317
318 @Test
319 public void testToMapWithNoHeader() throws Exception {
320 try (final CSVParser parser = CSVParser.parse("a,b", CSVFormat.newFormat(','))) {
321 final CSVRecord shortRec = parser.iterator().next();
322 final Map<String, String> map = shortRec.toMap();
323 assertNotNull(map, "Map is not null.");
324 assertTrue(map.isEmpty(), "Map is empty.");
325 }
326 }
327
328 @Test
329 public void testToMapWithShortRecord() throws Exception {
330 try (final CSVParser parser = CSVParser.parse("a,b", CSVFormat.DEFAULT.withHeader("A", "B", "C"))) {
331 final CSVRecord shortRec = parser.iterator().next();
332 shortRec.toMap();
333 }
334 }
335
336 @Test
337 public void testToString() {
338 assertNotNull(recordWithHeader.toString());
339 assertTrue(recordWithHeader.toString().contains("comment="));
340 assertTrue(recordWithHeader.toString().contains("recordNumber="));
341 assertTrue(recordWithHeader.toString().contains("values="));
342 }
343
344 private void validateMap(final Map<String, String> map, final boolean allowsNulls) {
345 assertTrue(map.containsKey(EnumHeader.FIRST.name()));
346 assertTrue(map.containsKey(EnumHeader.SECOND.name()));
347 assertTrue(map.containsKey(EnumHeader.THIRD.name()));
348 assertFalse(map.containsKey("fourth"));
349 if (allowsNulls) {
350 assertFalse(map.containsKey(null));
351 }
352 assertEquals("A", map.get(EnumHeader.FIRST.name()));
353 assertEquals("B", map.get(EnumHeader.SECOND.name()));
354 assertEquals("C", map.get(EnumHeader.THIRD.name()));
355 assertNull(map.get("fourth"));
356 }
357 }