From 68f17d8a9bd91a60b336f87eee040b3d667cfd97 Mon Sep 17 00:00:00 2001 From: Naveed Khan Date: Thu, 23 Jul 2026 10:35:59 +0530 Subject: [PATCH] handle null name in CSVRecord accessors under ignoreHeaderCase the case-insensitive header map used by ignoreHeaderCase rejects null keys, so guard the name in isMapped and get to return false / throw IllegalArgumentException like the default case-sensitive path. --- .../java/org/apache/commons/csv/CSVRecord.java | 4 ++-- .../org/apache/commons/csv/CSVRecordTest.java | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/apache/commons/csv/CSVRecord.java b/src/main/java/org/apache/commons/csv/CSVRecord.java index 0e162989de..3cca402e30 100644 --- a/src/main/java/org/apache/commons/csv/CSVRecord.java +++ b/src/main/java/org/apache/commons/csv/CSVRecord.java @@ -127,7 +127,7 @@ public String get(final String name) { if (headerMap == null) { throw new IllegalStateException("No header mapping was specified, the record values can't be accessed by name"); } - final Integer index = headerMap.get(name); + final Integer index = name == null ? null : headerMap.get(name); if (index == null) { throw new IllegalArgumentException(String.format("Mapping for %s not found, expected one of %s", name, headerMap.keySet())); } @@ -243,7 +243,7 @@ public boolean isConsistent() { */ public boolean isMapped(final String name) { final Map headerMap = getHeaderMapRaw(); - return headerMap != null && headerMap.containsKey(name); + return name != null && headerMap != null && headerMap.containsKey(name); } /** diff --git a/src/test/java/org/apache/commons/csv/CSVRecordTest.java b/src/test/java/org/apache/commons/csv/CSVRecordTest.java index 94060d62b2..1f4ebb1ae3 100644 --- a/src/test/java/org/apache/commons/csv/CSVRecordTest.java +++ b/src/test/java/org/apache/commons/csv/CSVRecordTest.java @@ -44,6 +44,8 @@ import org.apache.commons.lang3.StringUtils; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; class CSVRecordTest { @@ -228,6 +230,22 @@ void testIsSetString() { assertFalse(recordWithHeader.isSet("DOES NOT EXIST")); } + @ParameterizedTest + @ValueSource(booleans = { false, true }) + void testNullNameAccessorsMatchAcrossIgnoreHeaderCase(final boolean ignoreHeaderCase) throws IOException { + final CSVFormat format = CSVFormat.DEFAULT.builder().setHeader().setSkipHeaderRecord(true).setIgnoreHeaderCase(ignoreHeaderCase).get(); + try (CSVParser parser = CSVParser.parse("A,B\n1,2", format)) { + final CSVRecord rec = parser.iterator().next(); + // A null name is never a mapped header, so the boolean guards return false rather than throwing, + // regardless of ignoreHeaderCase (the case-insensitive header map rejects null keys). + assertFalse(rec.isMapped(null)); + assertFalse(rec.isSet((String) null)); + // A null name (also reached from get((Enum) null)) reports a missing mapping, not an NPE. + assertThrows(IllegalArgumentException.class, () -> rec.get((String) null)); + assertThrows(IllegalArgumentException.class, () -> rec.get((Enum) null)); + } + } + @Test void testIterator() { int i = 0;