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