Skip to content

Commit a0d9759

Browse files
committed
[CSV-96] CSVRecord does not verify that the length of the header mapping matches the number of values - convert ArrayIndexOutOfBoundsException to IllegalArgumentException to give users a better feedback about what went wrong
git-svn-id: https://svn.apache.org/repos/asf/commons/proper/csv/trunk@1465738 13f79535-47bb-0310-9956-ffa450edef68
1 parent 5744ee8 commit a0d9759

2 files changed

Lines changed: 119 additions & 4 deletions

File tree

src/main/java/org/apache/commons/csv/CSVRecord.java

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ public class CSVRecord implements Serializable, Iterable<String> {
5555

5656
/**
5757
* Returns a value by index.
58-
*
58+
*
5959
* @param i
6060
* a column index (0-based)
6161
* @return the String at the given index
@@ -72,27 +72,37 @@ public String get(final int i) {
7272
* @return the column value, or {@code null} if the column name is not found
7373
* @throws IllegalStateException
7474
* if no header mapping was provided
75+
* @throws IllegalArgumentException
76+
* if the record is inconsistent
77+
* @see #isConsistent()
7578
*/
7679
public String get(final String name) {
7780
if (mapping == null) {
7881
throw new IllegalStateException(
7982
"No header mapping was specified, the record values can't be accessed by name");
8083
}
8184
final Integer index = mapping.get(name);
82-
return index != null ? values[index.intValue()] : null;
85+
try {
86+
return index != null ? values[index.intValue()] : null;
87+
} catch (ArrayIndexOutOfBoundsException e) {
88+
throw new IllegalArgumentException(
89+
String.format(
90+
"Index for header '%s' is %d but CSVRecord only has %d values!",
91+
name, index.intValue(), values.length));
92+
}
8393
}
8494

8595
/**
8696
* Returns true if this record is consistent, false if not. Currently, the only check is matching the record size to
8797
* the header size. Some programs can export files that fails this test but still produce parsable files.
88-
*
98+
*
8999
* @return true of this record is valid, false if not
90100
* @see CSVParserTest#org.apache.commons.csv.CSVParserTest.testMappedButNotSetAsOutlook2007ContactExport()
91101
*/
92102
public boolean isConsistent() {
93103
return mapping == null ? true : mapping.size() == values.length;
94104
}
95-
105+
96106
/**
97107
* Checks whether a given column is mapped.
98108
*
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
package org.apache.commons.csv;
18+
19+
import static org.junit.Assert.assertEquals;
20+
import static org.junit.Assert.assertFalse;
21+
import static org.junit.Assert.assertTrue;
22+
23+
import java.util.HashMap;
24+
import java.util.Iterator;
25+
import java.util.Map;
26+
27+
import org.junit.Before;
28+
import org.junit.Test;
29+
30+
public class CSVRecordTest {
31+
32+
private String[] values;
33+
private CSVRecord record, recordWithHeader;
34+
private Map<String, Integer> header;
35+
36+
@Before
37+
public void setUp() throws Exception {
38+
values = new String[] { "first", "second", "third" };
39+
record = new CSVRecord(values, null, null, 0);
40+
header = new HashMap<String, Integer>();
41+
header.put("first", Integer.valueOf(0));
42+
header.put("second", Integer.valueOf(1));
43+
header.put("third", Integer.valueOf(2));
44+
recordWithHeader = new CSVRecord(values, header, null, 0);
45+
}
46+
47+
@Test
48+
public void testGetInt() {
49+
assertEquals(values[0], record.get(0));
50+
assertEquals(values[1], record.get(1));
51+
assertEquals(values[2], record.get(2));
52+
}
53+
54+
@Test
55+
public void testGetString() {
56+
assertEquals(values[0], recordWithHeader.get("first"));
57+
assertEquals(values[1], recordWithHeader.get("second"));
58+
assertEquals(values[2], recordWithHeader.get("third"));
59+
}
60+
61+
@Test(expected = IllegalStateException.class)
62+
public void testGetStringNoHeader() {
63+
record.get("first");
64+
}
65+
66+
@Test(expected = IllegalArgumentException.class)
67+
public void testGetStringInconsistentRecord() {
68+
header.put("fourth", Integer.valueOf(4));
69+
recordWithHeader.get("fourth");
70+
}
71+
72+
@Test
73+
public void testIsConsistent() {
74+
assertTrue(record.isConsistent());
75+
assertTrue(recordWithHeader.isConsistent());
76+
77+
header.put("fourth", Integer.valueOf(4));
78+
assertFalse(recordWithHeader.isConsistent());
79+
}
80+
81+
@Test
82+
public void testIsMapped() {
83+
assertFalse(record.isMapped("first"));
84+
assertTrue(recordWithHeader.isMapped("first"));
85+
assertFalse(recordWithHeader.isMapped("fourth"));
86+
}
87+
88+
@Test
89+
public void testIsSet() {
90+
assertFalse(record.isSet("first"));
91+
assertTrue(recordWithHeader.isSet("first"));
92+
assertFalse(recordWithHeader.isSet("fourth"));
93+
}
94+
95+
@Test
96+
public void testIterator() {
97+
int i = 0;
98+
for (Iterator<String> itr = record.iterator(); itr.hasNext();) {
99+
String value = itr.next();
100+
assertEquals(values[i], value);
101+
i++;
102+
}
103+
}
104+
105+
}

0 commit comments

Comments
 (0)