Skip to content

Commit 289ffa1

Browse files
committed
Validates that a CSVFormat rejects empty header names
1 parent 8fbf1b1 commit 289ffa1

2 files changed

Lines changed: 35 additions & 15 deletions

File tree

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

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,7 @@ private Builder(final CSVFormat csvFormat) {
253253
this.recordSeparator = csvFormat.recordSeparator;
254254
this.nullString = csvFormat.nullString;
255255
this.headerComments = csvFormat.headerComments;
256-
this.headers = csvFormat.header;
256+
this.headers = csvFormat.headers;
257257
this.skipHeaderRecord = csvFormat.skipHeaderRecord;
258258
this.ignoreHeaderCase = csvFormat.ignoreHeaderCase;
259259
this.trailingDelimiter = csvFormat.trailingDelimiter;
@@ -1311,7 +1311,7 @@ public static CSVFormat valueOf(final String format) {
13111311

13121312
private final Character escapeCharacter; // null if escaping is disabled
13131313

1314-
private final String[] header; // array of header column names
1314+
private final String[] headers; // array of header column names
13151315

13161316
private final String[] headerComments; // array of header comment lines
13171317

@@ -1349,7 +1349,7 @@ private CSVFormat(final Builder builder) {
13491349
this.recordSeparator = builder.recordSeparator;
13501350
this.nullString = builder.nullString;
13511351
this.headerComments = builder.headerComments;
1352-
this.header = builder.headers;
1352+
this.headers = builder.headers;
13531353
this.skipHeaderRecord = builder.skipHeaderRecord;
13541354
this.ignoreHeaderCase = builder.ignoreHeaderCase;
13551355
this.trailingDelimiter = builder.trailingDelimiter;
@@ -1399,7 +1399,7 @@ private CSVFormat(final String delimiter, final Character quoteChar, final Quote
13991399
this.recordSeparator = recordSeparator;
14001400
this.nullString = nullString;
14011401
this.headerComments = toStringArray(headerComments);
1402-
this.header = clone(header);
1402+
this.headers = clone(header);
14031403
this.skipHeaderRecord = skipHeaderRecord;
14041404
this.ignoreHeaderCase = ignoreHeaderCase;
14051405
this.trailingDelimiter = trailingDelimiter;
@@ -1455,7 +1455,7 @@ public boolean equals(final Object obj) {
14551455
final CSVFormat other = (CSVFormat) obj;
14561456
return duplicateHeaderMode == other.duplicateHeaderMode && allowMissingColumnNames == other.allowMissingColumnNames &&
14571457
autoFlush == other.autoFlush && Objects.equals(commentMarker, other.commentMarker) && Objects.equals(delimiter, other.delimiter) &&
1458-
Objects.equals(escapeCharacter, other.escapeCharacter) && Arrays.equals(header, other.header) &&
1458+
Objects.equals(escapeCharacter, other.escapeCharacter) && Arrays.equals(headers, other.headers) &&
14591459
Arrays.equals(headerComments, other.headerComments) && ignoreEmptyLines == other.ignoreEmptyLines &&
14601460
ignoreHeaderCase == other.ignoreHeaderCase && ignoreSurroundingSpaces == other.ignoreSurroundingSpaces &&
14611461
Objects.equals(nullString, other.nullString) && Objects.equals(quoteCharacter, other.quoteCharacter) && quoteMode == other.quoteMode &&
@@ -1568,7 +1568,7 @@ public Character getEscapeCharacter() {
15681568
* @return a copy of the header array; {@code null} if disabled, the empty array if to be read from the file
15691569
*/
15701570
public String[] getHeader() {
1571-
return header != null ? header.clone() : null;
1571+
return headers != null ? headers.clone() : null;
15721572
}
15731573

15741574
/**
@@ -1681,7 +1681,7 @@ public boolean getTrim() {
16811681
public int hashCode() {
16821682
final int prime = 31;
16831683
int result = 1;
1684-
result = prime * result + Arrays.hashCode(header);
1684+
result = prime * result + Arrays.hashCode(headers);
16851685
result = prime * result + Arrays.hashCode(headerComments);
16861686
return prime * result + Objects.hash(duplicateHeaderMode, allowMissingColumnNames, autoFlush, commentMarker, delimiter, escapeCharacter,
16871687
ignoreEmptyLines, ignoreHeaderCase, ignoreSurroundingSpaces, nullString, quoteCharacter, quoteMode, quotedNullString, recordSeparator,
@@ -2243,9 +2243,9 @@ public String toString() {
22432243
sb.append(' ');
22442244
sb.append("HeaderComments:").append(Arrays.toString(headerComments));
22452245
}
2246-
if (header != null) {
2246+
if (headers != null) {
22472247
sb.append(' ');
2248-
sb.append("Header:").append(Arrays.toString(header));
2248+
sb.append("Header:").append(Arrays.toString(headers));
22492249
}
22502250
return sb.toString();
22512251
}
@@ -2284,12 +2284,17 @@ private void validate() throws IllegalArgumentException {
22842284
throw new IllegalArgumentException("No quotes mode set but no escape character is set");
22852285
}
22862286

2287-
// validate header
2288-
if (header != null && duplicateHeaderMode != DuplicateHeaderMode.ALLOW_ALL) {
2289-
final Set<String> dupCheck = new HashSet<>();
2290-
for (final String hdr : header) {
2291-
if (!dupCheck.add(hdr)) {
2292-
throw new IllegalArgumentException("The header contains a duplicate entry: '" + hdr + "' in " + Arrays.toString(header));
2287+
// Validate headers
2288+
if (headers != null && duplicateHeaderMode != DuplicateHeaderMode.ALLOW_ALL) {
2289+
final Set<String> dupCheckSet = new HashSet<>(headers.length);
2290+
final boolean rejectEmpty = duplicateHeaderMode != DuplicateHeaderMode.ALLOW_EMPTY;
2291+
for (final String header : headers) {
2292+
final boolean empty = header == null || header.isEmpty();
2293+
if (rejectEmpty && empty) {
2294+
throw new IllegalArgumentException("Header is empty");
2295+
}
2296+
if (!empty && !dupCheckSet.add(header)) {
2297+
throw new IllegalArgumentException(String.format("Header '%s' is a duplicate in %s", header, Arrays.toString(headers)));
22932298
}
22942299
}
22952300
}

src/test/java/org/apache/commons/csv/CSVFormatTest.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,21 @@ public void testDuplicateHeaderElementsTrue_Deprecated() {
151151
CSVFormat.DEFAULT.withAllowDuplicateHeaderNames(true).withHeader("A", "A");
152152
}
153153

154+
@Test
155+
public void testDuplicateHeaderElementsTrueContainsEmpty1() {
156+
CSVFormat.DEFAULT.builder().setAllowDuplicateHeaderNames(false).setHeader("A", "", "B", "").build();
157+
}
158+
159+
@Test
160+
public void testDuplicateHeaderElementsTrueContainsEmpty2() {
161+
CSVFormat.DEFAULT.builder().setDuplicateHeaderMode(DuplicateHeaderMode.ALLOW_EMPTY).setHeader("A", "", "B", "").build();
162+
}
163+
164+
@Test
165+
public void testDuplicateHeaderElementsTrueContainsEmpty3() {
166+
CSVFormat.DEFAULT.builder().setAllowDuplicateHeaderNames(false).setAllowMissingColumnNames(true).setHeader("A", "", "B", "").build();
167+
}
168+
154169
@Test
155170
public void testEquals() {
156171
final CSVFormat right = CSVFormat.DEFAULT;

0 commit comments

Comments
 (0)