Merge pull request #279 from aherbert/csvparser-duplicate-missing-headers
CSVParser: Identify duplicates in null, empty and blank header names (WIP)
This commit is contained in:
commit
5a7b030537
|
@ -289,7 +289,7 @@ public final class CSVFormat implements Serializable {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the missing column names parser behavior, {@code true} to allow missing column names in the header line, {@code false} to cause an
|
* Sets the parser missing column names behavior, {@code true} to allow missing column names in the header line, {@code false} to cause an
|
||||||
* {@link IllegalArgumentException} to be thrown.
|
* {@link IllegalArgumentException} to be thrown.
|
||||||
*
|
*
|
||||||
* @param allowMissingColumnNames the missing column names behavior, {@code true} to allow missing column names in the header line, {@code false} to
|
* @param allowMissingColumnNames the missing column names behavior, {@code true} to allow missing column names in the header line, {@code false} to
|
||||||
|
@ -564,7 +564,7 @@ public final class CSVFormat implements Serializable {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the case mapping behavior, {@code true} to access name/values, {@code false} to leave the mapping as is.
|
* Sets the parser case mapping behavior, {@code true} to access name/values, {@code false} to leave the mapping as is.
|
||||||
*
|
*
|
||||||
* @param ignoreHeaderCase the case mapping behavior, {@code true} to access name/values, {@code false} to leave the mapping as is.
|
* @param ignoreHeaderCase the case mapping behavior, {@code true} to access name/values, {@code false} to leave the mapping as is.
|
||||||
* @return This instance.
|
* @return This instance.
|
||||||
|
@ -1599,7 +1599,7 @@ public final class CSVFormat implements Serializable {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets whether header names will be accessed ignoring case.
|
* Gets whether header names will be accessed ignoring case when parsing input.
|
||||||
*
|
*
|
||||||
* @return {@code true} if header names cases are ignored, {@code false} if they are case sensitive.
|
* @return {@code true} if header names cases are ignored, {@code false} if they are case sensitive.
|
||||||
* @since 1.3
|
* @since 1.3
|
||||||
|
|
|
@ -499,6 +499,8 @@ public final class CSVParser implements Iterable<CSVRecord>, Closeable {
|
||||||
|
|
||||||
// build the name to index mappings
|
// build the name to index mappings
|
||||||
if (headerRecord != null) {
|
if (headerRecord != null) {
|
||||||
|
// Track an occurrence of a null, empty or blank header.
|
||||||
|
boolean observedMissing = false;
|
||||||
for (int i = 0; i < headerRecord.length; i++) {
|
for (int i = 0; i < headerRecord.length; i++) {
|
||||||
final String header = headerRecord[i];
|
final String header = headerRecord[i];
|
||||||
final boolean blankHeader = CSVFormat.isBlank(header);
|
final boolean blankHeader = CSVFormat.isBlank(header);
|
||||||
|
@ -507,7 +509,7 @@ public final class CSVParser implements Iterable<CSVRecord>, Closeable {
|
||||||
"A header name is missing in " + Arrays.toString(headerRecord));
|
"A header name is missing in " + Arrays.toString(headerRecord));
|
||||||
}
|
}
|
||||||
|
|
||||||
final boolean containsHeader = header != null && hdrMap.containsKey(header);
|
final boolean containsHeader = blankHeader ? observedMissing : hdrMap.containsKey(header);
|
||||||
final DuplicateHeaderMode headerMode = this.format.getDuplicateHeaderMode();
|
final DuplicateHeaderMode headerMode = this.format.getDuplicateHeaderMode();
|
||||||
final boolean duplicatesAllowed = headerMode == DuplicateHeaderMode.ALLOW_ALL;
|
final boolean duplicatesAllowed = headerMode == DuplicateHeaderMode.ALLOW_ALL;
|
||||||
final boolean emptyDuplicatesAllowed = headerMode == DuplicateHeaderMode.ALLOW_EMPTY;
|
final boolean emptyDuplicatesAllowed = headerMode == DuplicateHeaderMode.ALLOW_EMPTY;
|
||||||
|
@ -518,6 +520,7 @@ public final class CSVParser implements Iterable<CSVRecord>, Closeable {
|
||||||
"The header contains a duplicate name: \"%s\" in %s. If this is valid then use CSVFormat.Builder.setDuplicateHeaderMode().",
|
"The header contains a duplicate name: \"%s\" in %s. If this is valid then use CSVFormat.Builder.setDuplicateHeaderMode().",
|
||||||
header, Arrays.toString(headerRecord)));
|
header, Arrays.toString(headerRecord)));
|
||||||
}
|
}
|
||||||
|
observedMissing |= blankHeader;
|
||||||
if (header != null) {
|
if (header != null) {
|
||||||
hdrMap.put(header, Integer.valueOf(i));
|
hdrMap.put(header, Integer.valueOf(i));
|
||||||
if (headerNames == null) {
|
if (headerNames == null) {
|
||||||
|
|
|
@ -46,144 +46,181 @@ public class CSVDuplicateHeaderTest {
|
||||||
*/
|
*/
|
||||||
static Stream<Arguments> duplicateHeaderData() {
|
static Stream<Arguments> duplicateHeaderData() {
|
||||||
return Stream.of(
|
return Stream.of(
|
||||||
// TODO: Fix CSVParser which does not sanitise 'empty' names or
|
|
||||||
// equate null names with empty as duplications
|
|
||||||
|
|
||||||
// Any combination with a valid header
|
// Any combination with a valid header
|
||||||
Arguments.of(DuplicateHeaderMode.DISALLOW, false, new String[] {"A", "B"}, true),
|
Arguments.of(DuplicateHeaderMode.DISALLOW, false, false, new String[] {"A", "B"}, true),
|
||||||
Arguments.of(DuplicateHeaderMode.ALLOW_EMPTY, false, new String[] {"A", "B"}, true),
|
Arguments.of(DuplicateHeaderMode.ALLOW_EMPTY, false, false, new String[] {"A", "B"}, true),
|
||||||
Arguments.of(DuplicateHeaderMode.ALLOW_ALL, false, new String[] {"A", "B"}, true),
|
Arguments.of(DuplicateHeaderMode.ALLOW_ALL, false, false, new String[] {"A", "B"}, true),
|
||||||
Arguments.of(DuplicateHeaderMode.DISALLOW, true, new String[] {"A", "B"}, true),
|
Arguments.of(DuplicateHeaderMode.DISALLOW, true, false, new String[] {"A", "B"}, true),
|
||||||
Arguments.of(DuplicateHeaderMode.ALLOW_EMPTY, true, new String[] {"A", "B"}, true),
|
Arguments.of(DuplicateHeaderMode.ALLOW_EMPTY, true, false, new String[] {"A", "B"}, true),
|
||||||
Arguments.of(DuplicateHeaderMode.ALLOW_ALL, true, new String[] {"A", "B"}, true),
|
Arguments.of(DuplicateHeaderMode.ALLOW_ALL, true, false, new String[] {"A", "B"}, true),
|
||||||
|
|
||||||
// Any combination with a valid header including empty
|
// Any combination with a valid header including empty
|
||||||
Arguments.of(DuplicateHeaderMode.DISALLOW, false, new String[] {"A", ""}, false),
|
Arguments.of(DuplicateHeaderMode.DISALLOW, false, false, new String[] {"A", ""}, false),
|
||||||
Arguments.of(DuplicateHeaderMode.ALLOW_EMPTY, false, new String[] {"A", ""}, false),
|
Arguments.of(DuplicateHeaderMode.ALLOW_EMPTY, false, false, new String[] {"A", ""}, false),
|
||||||
Arguments.of(DuplicateHeaderMode.ALLOW_ALL, false, new String[] {"A", ""}, false),
|
Arguments.of(DuplicateHeaderMode.ALLOW_ALL, false, false, new String[] {"A", ""}, false),
|
||||||
Arguments.of(DuplicateHeaderMode.DISALLOW, true, new String[] {"A", ""}, true),
|
Arguments.of(DuplicateHeaderMode.DISALLOW, true, false, new String[] {"A", ""}, true),
|
||||||
Arguments.of(DuplicateHeaderMode.ALLOW_EMPTY, true, new String[] {"A", ""}, true),
|
Arguments.of(DuplicateHeaderMode.ALLOW_EMPTY, true, false, new String[] {"A", ""}, true),
|
||||||
Arguments.of(DuplicateHeaderMode.ALLOW_ALL, true, new String[] {"A", ""}, true),
|
Arguments.of(DuplicateHeaderMode.ALLOW_ALL, true, false, new String[] {"A", ""}, true),
|
||||||
|
|
||||||
// Any combination with a valid header including blank (1 space)
|
// Any combination with a valid header including blank (1 space)
|
||||||
Arguments.of(DuplicateHeaderMode.DISALLOW, false, new String[] {"A", " "}, false),
|
Arguments.of(DuplicateHeaderMode.DISALLOW, false, false, new String[] {"A", " "}, false),
|
||||||
Arguments.of(DuplicateHeaderMode.ALLOW_EMPTY, false, new String[] {"A", " "}, false),
|
Arguments.of(DuplicateHeaderMode.ALLOW_EMPTY, false, false, new String[] {"A", " "}, false),
|
||||||
Arguments.of(DuplicateHeaderMode.ALLOW_ALL, false, new String[] {"A", " "}, false),
|
Arguments.of(DuplicateHeaderMode.ALLOW_ALL, false, false, new String[] {"A", " "}, false),
|
||||||
Arguments.of(DuplicateHeaderMode.DISALLOW, true, new String[] {"A", " "}, true),
|
Arguments.of(DuplicateHeaderMode.DISALLOW, true, false, new String[] {"A", " "}, true),
|
||||||
Arguments.of(DuplicateHeaderMode.ALLOW_EMPTY, true, new String[] {"A", " "}, true),
|
Arguments.of(DuplicateHeaderMode.ALLOW_EMPTY, true, false, new String[] {"A", " "}, true),
|
||||||
Arguments.of(DuplicateHeaderMode.ALLOW_ALL, true, new String[] {"A", " "}, true),
|
Arguments.of(DuplicateHeaderMode.ALLOW_ALL, true, false, new String[] {"A", " "}, true),
|
||||||
|
|
||||||
// Any combination with a valid header including null
|
// Any combination with a valid header including null
|
||||||
Arguments.of(DuplicateHeaderMode.DISALLOW, false, new String[] {"A", null}, false),
|
Arguments.of(DuplicateHeaderMode.DISALLOW, false, false, new String[] {"A", null}, false),
|
||||||
Arguments.of(DuplicateHeaderMode.ALLOW_EMPTY, false, new String[] {"A", null}, false),
|
Arguments.of(DuplicateHeaderMode.ALLOW_EMPTY, false, false, new String[] {"A", null}, false),
|
||||||
Arguments.of(DuplicateHeaderMode.ALLOW_ALL, false, new String[] {"A", null}, false),
|
Arguments.of(DuplicateHeaderMode.ALLOW_ALL, false, false, new String[] {"A", null}, false),
|
||||||
Arguments.of(DuplicateHeaderMode.DISALLOW, true, new String[] {"A", null}, true),
|
Arguments.of(DuplicateHeaderMode.DISALLOW, true, false, new String[] {"A", null}, true),
|
||||||
Arguments.of(DuplicateHeaderMode.ALLOW_EMPTY, true, new String[] {"A", null}, true),
|
Arguments.of(DuplicateHeaderMode.ALLOW_EMPTY, true, false, new String[] {"A", null}, true),
|
||||||
Arguments.of(DuplicateHeaderMode.ALLOW_ALL, true, new String[] {"A", null}, true),
|
Arguments.of(DuplicateHeaderMode.ALLOW_ALL, true, false, new String[] {"A", null}, true),
|
||||||
|
|
||||||
// Duplicate non-empty names
|
// Duplicate non-empty names
|
||||||
Arguments.of(DuplicateHeaderMode.DISALLOW, false, new String[] {"A", "A"}, false),
|
Arguments.of(DuplicateHeaderMode.DISALLOW, false, false, new String[] {"A", "A"}, false),
|
||||||
Arguments.of(DuplicateHeaderMode.ALLOW_EMPTY, false, new String[] {"A", "A"}, false),
|
Arguments.of(DuplicateHeaderMode.ALLOW_EMPTY, false, false, new String[] {"A", "A"}, false),
|
||||||
Arguments.of(DuplicateHeaderMode.ALLOW_ALL, false, new String[] {"A", "A"}, true),
|
Arguments.of(DuplicateHeaderMode.ALLOW_ALL, false, false, new String[] {"A", "A"}, true),
|
||||||
Arguments.of(DuplicateHeaderMode.DISALLOW, true, new String[] {"A", "A"}, false),
|
Arguments.of(DuplicateHeaderMode.DISALLOW, true, false, new String[] {"A", "A"}, false),
|
||||||
Arguments.of(DuplicateHeaderMode.ALLOW_EMPTY, true, new String[] {"A", "A"}, false),
|
Arguments.of(DuplicateHeaderMode.ALLOW_EMPTY, true, false, new String[] {"A", "A"}, false),
|
||||||
Arguments.of(DuplicateHeaderMode.ALLOW_ALL, true, new String[] {"A", "A"}, true),
|
Arguments.of(DuplicateHeaderMode.ALLOW_ALL, true, false, new String[] {"A", "A"}, true),
|
||||||
|
|
||||||
// Duplicate empty names
|
// Duplicate empty names
|
||||||
Arguments.of(DuplicateHeaderMode.DISALLOW, false, new String[] {"", ""}, false),
|
Arguments.of(DuplicateHeaderMode.DISALLOW, false, false, new String[] {"", ""}, false),
|
||||||
Arguments.of(DuplicateHeaderMode.ALLOW_EMPTY, false, new String[] {"", ""}, false),
|
Arguments.of(DuplicateHeaderMode.ALLOW_EMPTY, false, false, new String[] {"", ""}, false),
|
||||||
Arguments.of(DuplicateHeaderMode.ALLOW_ALL, false, new String[] {"", ""}, false),
|
Arguments.of(DuplicateHeaderMode.ALLOW_ALL, false, false, new String[] {"", ""}, false),
|
||||||
Arguments.of(DuplicateHeaderMode.DISALLOW, true, new String[] {"", ""}, false),
|
Arguments.of(DuplicateHeaderMode.DISALLOW, true, false, new String[] {"", ""}, false),
|
||||||
Arguments.of(DuplicateHeaderMode.ALLOW_EMPTY, true, new String[] {"", ""}, true),
|
Arguments.of(DuplicateHeaderMode.ALLOW_EMPTY, true, false, new String[] {"", ""}, true),
|
||||||
Arguments.of(DuplicateHeaderMode.ALLOW_ALL, true, new String[] {"", ""}, true),
|
Arguments.of(DuplicateHeaderMode.ALLOW_ALL, true, false, new String[] {"", ""}, true),
|
||||||
|
|
||||||
// Duplicate blank names (1 space)
|
// Duplicate blank names (1 space)
|
||||||
Arguments.of(DuplicateHeaderMode.DISALLOW, false, new String[] {" ", " "}, false),
|
Arguments.of(DuplicateHeaderMode.DISALLOW, false, false, new String[] {" ", " "}, false),
|
||||||
Arguments.of(DuplicateHeaderMode.ALLOW_EMPTY, false, new String[] {" ", " "}, false),
|
Arguments.of(DuplicateHeaderMode.ALLOW_EMPTY, false, false, new String[] {" ", " "}, false),
|
||||||
Arguments.of(DuplicateHeaderMode.ALLOW_ALL, false, new String[] {" ", " "}, false),
|
Arguments.of(DuplicateHeaderMode.ALLOW_ALL, false, false, new String[] {" ", " "}, false),
|
||||||
Arguments.of(DuplicateHeaderMode.DISALLOW, true, new String[] {" ", " "}, false),
|
Arguments.of(DuplicateHeaderMode.DISALLOW, true, false, new String[] {" ", " "}, false),
|
||||||
Arguments.of(DuplicateHeaderMode.ALLOW_EMPTY, true, new String[] {" ", " "}, true),
|
Arguments.of(DuplicateHeaderMode.ALLOW_EMPTY, true, false, new String[] {" ", " "}, true),
|
||||||
Arguments.of(DuplicateHeaderMode.ALLOW_ALL, true, new String[] {" ", " "}, true),
|
Arguments.of(DuplicateHeaderMode.ALLOW_ALL, true, false, new String[] {" ", " "}, true),
|
||||||
|
|
||||||
// Duplicate blank names (3 spaces)
|
// Duplicate blank names (3 spaces)
|
||||||
Arguments.of(DuplicateHeaderMode.DISALLOW, false, new String[] {" ", " "}, false),
|
Arguments.of(DuplicateHeaderMode.DISALLOW, false, false, new String[] {" ", " "}, false),
|
||||||
Arguments.of(DuplicateHeaderMode.ALLOW_EMPTY, false, new String[] {" ", " "}, false),
|
Arguments.of(DuplicateHeaderMode.ALLOW_EMPTY, false, false, new String[] {" ", " "}, false),
|
||||||
Arguments.of(DuplicateHeaderMode.ALLOW_ALL, false, new String[] {" ", " "}, false),
|
Arguments.of(DuplicateHeaderMode.ALLOW_ALL, false, false, new String[] {" ", " "}, false),
|
||||||
Arguments.of(DuplicateHeaderMode.DISALLOW, true, new String[] {" ", " "}, false),
|
Arguments.of(DuplicateHeaderMode.DISALLOW, true, false, new String[] {" ", " "}, false),
|
||||||
Arguments.of(DuplicateHeaderMode.ALLOW_EMPTY, true, new String[] {" ", " "}, true),
|
Arguments.of(DuplicateHeaderMode.ALLOW_EMPTY, true, false, new String[] {" ", " "}, true),
|
||||||
Arguments.of(DuplicateHeaderMode.ALLOW_ALL, true, new String[] {" ", " "}, true),
|
Arguments.of(DuplicateHeaderMode.ALLOW_ALL, true, false, new String[] {" ", " "}, true),
|
||||||
|
|
||||||
// Duplicate null names
|
// Duplicate null names
|
||||||
Arguments.of(DuplicateHeaderMode.DISALLOW, false, new String[] {null, null}, false),
|
Arguments.of(DuplicateHeaderMode.DISALLOW, false, false, new String[] {null, null}, false),
|
||||||
Arguments.of(DuplicateHeaderMode.ALLOW_EMPTY, false, new String[] {null, null}, false),
|
Arguments.of(DuplicateHeaderMode.ALLOW_EMPTY, false, false, new String[] {null, null}, false),
|
||||||
Arguments.of(DuplicateHeaderMode.ALLOW_ALL, false, new String[] {null, null}, false),
|
Arguments.of(DuplicateHeaderMode.ALLOW_ALL, false, false, new String[] {null, null}, false),
|
||||||
// Arguments.of(DuplicateHeaderMode.DISALLOW, true, new String[] {null, null}, false),
|
Arguments.of(DuplicateHeaderMode.DISALLOW, true, false, new String[] {null, null}, false),
|
||||||
Arguments.of(DuplicateHeaderMode.ALLOW_EMPTY, true, new String[] {null, null}, true),
|
Arguments.of(DuplicateHeaderMode.ALLOW_EMPTY, true, false, new String[] {null, null}, true),
|
||||||
Arguments.of(DuplicateHeaderMode.ALLOW_ALL, true, new String[] {null, null}, true),
|
Arguments.of(DuplicateHeaderMode.ALLOW_ALL, true, false, new String[] {null, null}, true),
|
||||||
|
|
||||||
// Duplicate blank names (1+3 spaces)
|
// Duplicate blank names (1+3 spaces)
|
||||||
Arguments.of(DuplicateHeaderMode.DISALLOW, false, new String[] {" ", " "}, false),
|
Arguments.of(DuplicateHeaderMode.DISALLOW, false, false, new String[] {" ", " "}, false),
|
||||||
Arguments.of(DuplicateHeaderMode.ALLOW_EMPTY, false, new String[] {" ", " "}, false),
|
Arguments.of(DuplicateHeaderMode.ALLOW_EMPTY, false, false, new String[] {" ", " "}, false),
|
||||||
Arguments.of(DuplicateHeaderMode.ALLOW_ALL, false, new String[] {" ", " "}, false),
|
Arguments.of(DuplicateHeaderMode.ALLOW_ALL, false, false, new String[] {" ", " "}, false),
|
||||||
// Arguments.of(DuplicateHeaderMode.DISALLOW, true, new String[] {" ", " "}, false),
|
Arguments.of(DuplicateHeaderMode.DISALLOW, true, false, new String[] {" ", " "}, false),
|
||||||
Arguments.of(DuplicateHeaderMode.ALLOW_EMPTY, true, new String[] {" ", " "}, true),
|
Arguments.of(DuplicateHeaderMode.ALLOW_EMPTY, true, false, new String[] {" ", " "}, true),
|
||||||
Arguments.of(DuplicateHeaderMode.ALLOW_ALL, true, new String[] {" ", " "}, true),
|
Arguments.of(DuplicateHeaderMode.ALLOW_ALL, true, false, new String[] {" ", " "}, true),
|
||||||
|
|
||||||
// Duplicate blank names and null names
|
// Duplicate blank names and null names
|
||||||
Arguments.of(DuplicateHeaderMode.DISALLOW, false, new String[] {" ", null}, false),
|
Arguments.of(DuplicateHeaderMode.DISALLOW, false, false, new String[] {" ", null}, false),
|
||||||
Arguments.of(DuplicateHeaderMode.ALLOW_EMPTY, false, new String[] {" ", null}, false),
|
Arguments.of(DuplicateHeaderMode.ALLOW_EMPTY, false, false, new String[] {" ", null}, false),
|
||||||
Arguments.of(DuplicateHeaderMode.ALLOW_ALL, false, new String[] {" ", null}, false),
|
Arguments.of(DuplicateHeaderMode.ALLOW_ALL, false, false, new String[] {" ", null}, false),
|
||||||
// Arguments.of(DuplicateHeaderMode.DISALLOW, true, new String[] {" ", null}, false),
|
Arguments.of(DuplicateHeaderMode.DISALLOW, true, false, new String[] {" ", null}, false),
|
||||||
Arguments.of(DuplicateHeaderMode.ALLOW_EMPTY, true, new String[] {" ", null}, true),
|
Arguments.of(DuplicateHeaderMode.ALLOW_EMPTY, true, false, new String[] {" ", null}, true),
|
||||||
Arguments.of(DuplicateHeaderMode.ALLOW_ALL, true, new String[] {" ", null}, true),
|
Arguments.of(DuplicateHeaderMode.ALLOW_ALL, true, false, new String[] {" ", null}, true),
|
||||||
|
|
||||||
// Duplicate non-empty and empty names
|
// Duplicate non-empty and empty names
|
||||||
Arguments.of(DuplicateHeaderMode.DISALLOW, false, new String[] {"A", "A", "", ""}, false),
|
Arguments.of(DuplicateHeaderMode.DISALLOW, false, false, new String[] {"A", "A", "", ""}, false),
|
||||||
Arguments.of(DuplicateHeaderMode.ALLOW_EMPTY, false, new String[] {"A", "A", "", ""}, false),
|
Arguments.of(DuplicateHeaderMode.ALLOW_EMPTY, false, false, new String[] {"A", "A", "", ""}, false),
|
||||||
Arguments.of(DuplicateHeaderMode.ALLOW_ALL, false, new String[] {"A", "A", "", ""}, false),
|
Arguments.of(DuplicateHeaderMode.ALLOW_ALL, false, false, new String[] {"A", "A", "", ""}, false),
|
||||||
Arguments.of(DuplicateHeaderMode.DISALLOW, true, new String[] {"A", "A", "", ""}, false),
|
Arguments.of(DuplicateHeaderMode.DISALLOW, true, false, new String[] {"A", "A", "", ""}, false),
|
||||||
Arguments.of(DuplicateHeaderMode.ALLOW_EMPTY, true, new String[] {"A", "A", "", ""}, false),
|
Arguments.of(DuplicateHeaderMode.ALLOW_EMPTY, true, false, new String[] {"A", "A", "", ""}, false),
|
||||||
Arguments.of(DuplicateHeaderMode.ALLOW_ALL, true, new String[] {"A", "A", "", ""}, true),
|
Arguments.of(DuplicateHeaderMode.ALLOW_ALL, true, false, new String[] {"A", "A", "", ""}, true),
|
||||||
|
|
||||||
|
// Non-duplicate non-empty and duplicate empty names
|
||||||
|
Arguments.of(DuplicateHeaderMode.DISALLOW, false, false, new String[] {"A", "B", "", ""}, false),
|
||||||
|
Arguments.of(DuplicateHeaderMode.ALLOW_EMPTY, false, false, new String[] {"A", "B", "", ""}, false),
|
||||||
|
Arguments.of(DuplicateHeaderMode.ALLOW_ALL, false, false, new String[] {"A", "B", "", ""}, false),
|
||||||
|
Arguments.of(DuplicateHeaderMode.DISALLOW, true, false, new String[] {"A", "B", "", ""}, false),
|
||||||
|
Arguments.of(DuplicateHeaderMode.ALLOW_EMPTY, true, false, new String[] {"A", "B", "", ""}, true),
|
||||||
|
Arguments.of(DuplicateHeaderMode.ALLOW_ALL, true, false, new String[] {"A", "B", "", ""}, true),
|
||||||
|
|
||||||
// Duplicate non-empty and blank names
|
// Duplicate non-empty and blank names
|
||||||
Arguments.of(DuplicateHeaderMode.DISALLOW, false, new String[] {"A", "A", " ", " "}, false),
|
Arguments.of(DuplicateHeaderMode.DISALLOW, false, false, new String[] {"A", "A", " ", " "}, false),
|
||||||
Arguments.of(DuplicateHeaderMode.ALLOW_EMPTY, false, new String[] {"A", "A", " ", " "}, false),
|
Arguments.of(DuplicateHeaderMode.ALLOW_EMPTY, false, false, new String[] {"A", "A", " ", " "}, false),
|
||||||
Arguments.of(DuplicateHeaderMode.ALLOW_ALL, false, new String[] {"A", "A", " ", " "}, false),
|
Arguments.of(DuplicateHeaderMode.ALLOW_ALL, false, false, new String[] {"A", "A", " ", " "}, false),
|
||||||
Arguments.of(DuplicateHeaderMode.DISALLOW, true, new String[] {"A", "A", " ", " "}, false),
|
Arguments.of(DuplicateHeaderMode.DISALLOW, true, false, new String[] {"A", "A", " ", " "}, false),
|
||||||
Arguments.of(DuplicateHeaderMode.ALLOW_EMPTY, true, new String[] {"A", "A", " ", " "}, false),
|
Arguments.of(DuplicateHeaderMode.ALLOW_EMPTY, true, false, new String[] {"A", "A", " ", " "}, false),
|
||||||
Arguments.of(DuplicateHeaderMode.ALLOW_ALL, true, new String[] {"A", "A", " ", " "}, true),
|
Arguments.of(DuplicateHeaderMode.ALLOW_ALL, true, false, new String[] {"A", "A", " ", " "}, true),
|
||||||
|
|
||||||
// Duplicate non-empty and null names
|
// Duplicate non-empty and null names
|
||||||
Arguments.of(DuplicateHeaderMode.DISALLOW, false, new String[] {"A", "A", null, null}, false),
|
Arguments.of(DuplicateHeaderMode.DISALLOW, false, false, new String[] {"A", "A", null, null}, false),
|
||||||
Arguments.of(DuplicateHeaderMode.ALLOW_EMPTY, false, new String[] {"A", "A", null, null}, false),
|
Arguments.of(DuplicateHeaderMode.ALLOW_EMPTY, false, false, new String[] {"A", "A", null, null}, false),
|
||||||
Arguments.of(DuplicateHeaderMode.ALLOW_ALL, false, new String[] {"A", "A", null, null}, false),
|
Arguments.of(DuplicateHeaderMode.ALLOW_ALL, false, false, new String[] {"A", "A", null, null}, false),
|
||||||
Arguments.of(DuplicateHeaderMode.DISALLOW, true, new String[] {"A", "A", null, null}, false),
|
Arguments.of(DuplicateHeaderMode.DISALLOW, true, false, new String[] {"A", "A", null, null}, false),
|
||||||
Arguments.of(DuplicateHeaderMode.ALLOW_EMPTY, true, new String[] {"A", "A", null, null}, false),
|
Arguments.of(DuplicateHeaderMode.ALLOW_EMPTY, true, false, new String[] {"A", "A", null, null}, false),
|
||||||
Arguments.of(DuplicateHeaderMode.ALLOW_ALL, true, new String[] {"A", "A", null, null}, true),
|
Arguments.of(DuplicateHeaderMode.ALLOW_ALL, true, false, new String[] {"A", "A", null, null}, true),
|
||||||
|
|
||||||
// Duplicate blank names
|
// Duplicate blank names
|
||||||
Arguments.of(DuplicateHeaderMode.DISALLOW, false, new String[] {"A", "", ""}, false),
|
Arguments.of(DuplicateHeaderMode.DISALLOW, false, false, new String[] {"A", "", ""}, false),
|
||||||
Arguments.of(DuplicateHeaderMode.ALLOW_EMPTY, false, new String[] {"A", "", ""}, false),
|
Arguments.of(DuplicateHeaderMode.ALLOW_EMPTY, false, false, new String[] {"A", "", ""}, false),
|
||||||
Arguments.of(DuplicateHeaderMode.ALLOW_ALL, false, new String[] {"A", "", ""}, false),
|
Arguments.of(DuplicateHeaderMode.ALLOW_ALL, false, false, new String[] {"A", "", ""}, false),
|
||||||
Arguments.of(DuplicateHeaderMode.DISALLOW, true, new String[] {"A", "", ""}, false),
|
Arguments.of(DuplicateHeaderMode.DISALLOW, true, false, new String[] {"A", "", ""}, false),
|
||||||
Arguments.of(DuplicateHeaderMode.ALLOW_EMPTY, true, new String[] {"A", "", ""}, true),
|
Arguments.of(DuplicateHeaderMode.ALLOW_EMPTY, true, false, new String[] {"A", "", ""}, true),
|
||||||
Arguments.of(DuplicateHeaderMode.ALLOW_ALL, true, new String[] {"A", "", ""}, true),
|
Arguments.of(DuplicateHeaderMode.ALLOW_ALL, true, false, new String[] {"A", "", ""}, true),
|
||||||
|
|
||||||
// Duplicate null names
|
// Duplicate null names
|
||||||
Arguments.of(DuplicateHeaderMode.DISALLOW, false, new String[] {"A", null, null}, false),
|
Arguments.of(DuplicateHeaderMode.DISALLOW, false, false, new String[] {"A", null, null}, false),
|
||||||
Arguments.of(DuplicateHeaderMode.ALLOW_EMPTY, false, new String[] {"A", null, null}, false),
|
Arguments.of(DuplicateHeaderMode.ALLOW_EMPTY, false, false, new String[] {"A", null, null}, false),
|
||||||
Arguments.of(DuplicateHeaderMode.ALLOW_ALL, false, new String[] {"A", null, null}, false),
|
Arguments.of(DuplicateHeaderMode.ALLOW_ALL, false, false, new String[] {"A", null, null}, false),
|
||||||
// Arguments.of(DuplicateHeaderMode.DISALLOW, true, new String[] {"A", null, null}, false),
|
Arguments.of(DuplicateHeaderMode.DISALLOW, true, false, new String[] {"A", null, null}, false),
|
||||||
Arguments.of(DuplicateHeaderMode.ALLOW_EMPTY, true, new String[] {"A", null, null}, true),
|
Arguments.of(DuplicateHeaderMode.ALLOW_EMPTY, true, false, new String[] {"A", null, null}, true),
|
||||||
Arguments.of(DuplicateHeaderMode.ALLOW_ALL, true, new String[] {"A", null, null}, true),
|
Arguments.of(DuplicateHeaderMode.ALLOW_ALL, true, false, new String[] {"A", null, null}, true),
|
||||||
|
|
||||||
// Duplicate blank names (1+3 spaces)
|
// Duplicate blank names (1+3 spaces)
|
||||||
Arguments.of(DuplicateHeaderMode.DISALLOW, false, new String[] {"A", " ", " "}, false),
|
Arguments.of(DuplicateHeaderMode.DISALLOW, false, false, new String[] {"A", " ", " "}, false),
|
||||||
Arguments.of(DuplicateHeaderMode.ALLOW_EMPTY, false, new String[] {"A", " ", " "}, false),
|
Arguments.of(DuplicateHeaderMode.ALLOW_EMPTY, false, false, new String[] {"A", " ", " "}, false),
|
||||||
Arguments.of(DuplicateHeaderMode.ALLOW_ALL, false, new String[] {"A", " ", " "}, false),
|
Arguments.of(DuplicateHeaderMode.ALLOW_ALL, false, false, new String[] {"A", " ", " "}, false),
|
||||||
// Arguments.of(DuplicateHeaderMode.DISALLOW, true, new String[] {"A", " ", " "}, false),
|
Arguments.of(DuplicateHeaderMode.DISALLOW, true, false, new String[] {"A", " ", " "}, false),
|
||||||
Arguments.of(DuplicateHeaderMode.ALLOW_EMPTY, true, new String[] {"A", " ", " "}, true),
|
Arguments.of(DuplicateHeaderMode.ALLOW_EMPTY, true, false, new String[] {"A", " ", " "}, true),
|
||||||
Arguments.of(DuplicateHeaderMode.ALLOW_ALL, true, new String[] {"A", " ", " "}, true)
|
Arguments.of(DuplicateHeaderMode.ALLOW_ALL, true, false, new String[] {"A", " ", " "}, true),
|
||||||
|
|
||||||
|
// Duplicate names (case insensitive)
|
||||||
|
Arguments.of(DuplicateHeaderMode.DISALLOW, false, true , new String[] {"A", "a"}, false),
|
||||||
|
Arguments.of(DuplicateHeaderMode.ALLOW_EMPTY, false, true , new String[] {"A", "a"}, false),
|
||||||
|
Arguments.of(DuplicateHeaderMode.ALLOW_ALL, false, true , new String[] {"A", "a"}, true),
|
||||||
|
Arguments.of(DuplicateHeaderMode.DISALLOW, true, true , new String[] {"A", "a"}, false),
|
||||||
|
Arguments.of(DuplicateHeaderMode.ALLOW_EMPTY, true, true , new String[] {"A", "a"}, false),
|
||||||
|
Arguments.of(DuplicateHeaderMode.ALLOW_ALL, true, true , new String[] {"A", "a"}, true),
|
||||||
|
|
||||||
|
// Duplicate non-empty (case insensitive) and empty names
|
||||||
|
Arguments.of(DuplicateHeaderMode.DISALLOW, false, true, new String[] {"A", "a", "", ""}, false),
|
||||||
|
Arguments.of(DuplicateHeaderMode.ALLOW_EMPTY, false, true, new String[] {"A", "a", "", ""}, false),
|
||||||
|
Arguments.of(DuplicateHeaderMode.ALLOW_ALL, false, true, new String[] {"A", "a", "", ""}, false),
|
||||||
|
Arguments.of(DuplicateHeaderMode.DISALLOW, true, true, new String[] {"A", "a", "", ""}, false),
|
||||||
|
Arguments.of(DuplicateHeaderMode.ALLOW_EMPTY, true, true, new String[] {"A", "a", "", ""}, false),
|
||||||
|
Arguments.of(DuplicateHeaderMode.ALLOW_ALL, true, true, new String[] {"A", "a", "", ""}, true),
|
||||||
|
|
||||||
|
// Duplicate non-empty (case insensitive) and blank names
|
||||||
|
Arguments.of(DuplicateHeaderMode.DISALLOW, false, true, new String[] {"A", "a", " ", " "}, false),
|
||||||
|
Arguments.of(DuplicateHeaderMode.ALLOW_EMPTY, false, true, new String[] {"A", "a", " ", " "}, false),
|
||||||
|
Arguments.of(DuplicateHeaderMode.ALLOW_ALL, false, true, new String[] {"A", "a", " ", " "}, false),
|
||||||
|
Arguments.of(DuplicateHeaderMode.DISALLOW, true, true, new String[] {"A", "a", " ", " "}, false),
|
||||||
|
Arguments.of(DuplicateHeaderMode.ALLOW_EMPTY, true, true, new String[] {"A", "a", " ", " "}, false),
|
||||||
|
Arguments.of(DuplicateHeaderMode.ALLOW_ALL, true, true, new String[] {"A", "a", " ", " "}, true),
|
||||||
|
|
||||||
|
// Duplicate non-empty (case insensitive) and null names
|
||||||
|
Arguments.of(DuplicateHeaderMode.DISALLOW, false, true, new String[] {"A", "a", null, null}, false),
|
||||||
|
Arguments.of(DuplicateHeaderMode.ALLOW_EMPTY, false, true, new String[] {"A", "a", null, null}, false),
|
||||||
|
Arguments.of(DuplicateHeaderMode.ALLOW_ALL, false, true, new String[] {"A", "a", null, null}, false),
|
||||||
|
Arguments.of(DuplicateHeaderMode.DISALLOW, true, true, new String[] {"A", "a", null, null}, false),
|
||||||
|
Arguments.of(DuplicateHeaderMode.ALLOW_EMPTY, true, true, new String[] {"A", "a", null, null}, false),
|
||||||
|
Arguments.of(DuplicateHeaderMode.ALLOW_ALL, true, true, new String[] {"A", "a", null, null}, true)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -191,23 +228,33 @@ public class CSVDuplicateHeaderTest {
|
||||||
* Return test cases for duplicate header data for use in CSVFormat.
|
* Return test cases for duplicate header data for use in CSVFormat.
|
||||||
* <p>
|
* <p>
|
||||||
* This filters the parsing test data to all cases where the allow missing column
|
* This filters the parsing test data to all cases where the allow missing column
|
||||||
* names flag is true. The allow missing column names is exclusively for parsing.
|
* names flag is true and ignore header case is false: these flags are exclusively for parsing.
|
||||||
* CSVFormat validation applies to both parsing and writing and thus validation
|
* CSVFormat validation applies to both parsing and writing and thus validation
|
||||||
* is less strict and behaves as if the missing column names constraint is absent.
|
* is less strict and behaves as if the allow missing column names constraint and
|
||||||
* The filtered data is then returned with the missing column names flag set to both
|
* the ignore header case behaviour are absent.
|
||||||
* true and false for each test case.
|
* The filtered data is then returned with the parser flags set to both true and false
|
||||||
|
* for each test case.
|
||||||
* </p>
|
* </p>
|
||||||
*
|
*
|
||||||
* @return the stream of arguments
|
* @return the stream of arguments
|
||||||
*/
|
*/
|
||||||
static Stream<Arguments> duplicateHeaderAllowsMissingColumnsNamesData() {
|
static Stream<Arguments> duplicateHeaderAllowsMissingColumnsNamesData() {
|
||||||
return duplicateHeaderData()
|
return duplicateHeaderData()
|
||||||
.filter(arg -> Boolean.TRUE.equals(arg.get()[1]))
|
.filter(arg -> Boolean.TRUE.equals(arg.get()[1]) && Boolean.FALSE.equals(arg.get()[2]))
|
||||||
.flatMap(arg -> {
|
.flatMap(arg -> {
|
||||||
// Return test case with flag as both true and false
|
// Return test case with flags as all true/false combinations
|
||||||
final Object[] data = arg.get().clone();
|
final Object[][] data = new Object[4][];
|
||||||
data[1] = Boolean.FALSE;
|
final Boolean[] flags = {Boolean.TRUE, Boolean.FALSE};
|
||||||
return Stream.of(arg, Arguments.of(data));
|
int i = 0;
|
||||||
|
for (final Boolean a : flags) {
|
||||||
|
for (final Boolean b : flags) {
|
||||||
|
data[i] = arg.get().clone();
|
||||||
|
data[i][1] = a;
|
||||||
|
data[i][2] = b;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Arrays.stream(data).map(Arguments::of);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -216,6 +263,7 @@ public class CSVDuplicateHeaderTest {
|
||||||
*
|
*
|
||||||
* @param duplicateHeaderMode the duplicate header mode
|
* @param duplicateHeaderMode the duplicate header mode
|
||||||
* @param allowMissingColumnNames the allow missing column names flag (only used for parsing)
|
* @param allowMissingColumnNames the allow missing column names flag (only used for parsing)
|
||||||
|
* @param ignoreHeaderCase the ignore header case flag (only used for parsing)
|
||||||
* @param headers the headers
|
* @param headers the headers
|
||||||
* @param valid true if the settings are expected to be valid, otherwise expect a IllegalArgumentException
|
* @param valid true if the settings are expected to be valid, otherwise expect a IllegalArgumentException
|
||||||
*/
|
*/
|
||||||
|
@ -223,12 +271,14 @@ public class CSVDuplicateHeaderTest {
|
||||||
@MethodSource(value = {"duplicateHeaderAllowsMissingColumnsNamesData"})
|
@MethodSource(value = {"duplicateHeaderAllowsMissingColumnsNamesData"})
|
||||||
public void testCSVFormat(final DuplicateHeaderMode duplicateHeaderMode,
|
public void testCSVFormat(final DuplicateHeaderMode duplicateHeaderMode,
|
||||||
final boolean allowMissingColumnNames,
|
final boolean allowMissingColumnNames,
|
||||||
|
final boolean ignoreHeaderCase,
|
||||||
final String[] headers,
|
final String[] headers,
|
||||||
final boolean valid) {
|
final boolean valid) {
|
||||||
final CSVFormat.Builder builder =
|
final CSVFormat.Builder builder =
|
||||||
CSVFormat.DEFAULT.builder()
|
CSVFormat.DEFAULT.builder()
|
||||||
.setDuplicateHeaderMode(duplicateHeaderMode)
|
.setDuplicateHeaderMode(duplicateHeaderMode)
|
||||||
.setAllowMissingColumnNames(allowMissingColumnNames)
|
.setAllowMissingColumnNames(allowMissingColumnNames)
|
||||||
|
.setIgnoreHeaderCase(ignoreHeaderCase)
|
||||||
.setHeader(headers);
|
.setHeader(headers);
|
||||||
if (valid) {
|
if (valid) {
|
||||||
final CSVFormat format = builder.build();
|
final CSVFormat format = builder.build();
|
||||||
|
@ -245,6 +295,7 @@ public class CSVDuplicateHeaderTest {
|
||||||
*
|
*
|
||||||
* @param duplicateHeaderMode the duplicate header mode
|
* @param duplicateHeaderMode the duplicate header mode
|
||||||
* @param allowMissingColumnNames the allow missing column names flag (only used for parsing)
|
* @param allowMissingColumnNames the allow missing column names flag (only used for parsing)
|
||||||
|
* @param ignoreHeaderCase the ignore header case flag (only used for parsing)
|
||||||
* @param headers the headers (joined with the CSVFormat delimiter to create a string input)
|
* @param headers the headers (joined with the CSVFormat delimiter to create a string input)
|
||||||
* @param valid true if the settings are expected to be valid, otherwise expect a IllegalArgumentException
|
* @param valid true if the settings are expected to be valid, otherwise expect a IllegalArgumentException
|
||||||
* @throws IOException Signals that an I/O exception has occurred.
|
* @throws IOException Signals that an I/O exception has occurred.
|
||||||
|
@ -253,17 +304,19 @@ public class CSVDuplicateHeaderTest {
|
||||||
@MethodSource(value = {"duplicateHeaderData"})
|
@MethodSource(value = {"duplicateHeaderData"})
|
||||||
public void testCSVParser(final DuplicateHeaderMode duplicateHeaderMode,
|
public void testCSVParser(final DuplicateHeaderMode duplicateHeaderMode,
|
||||||
final boolean allowMissingColumnNames,
|
final boolean allowMissingColumnNames,
|
||||||
|
final boolean ignoreHeaderCase,
|
||||||
final String[] headers,
|
final String[] headers,
|
||||||
final boolean valid) throws IOException {
|
final boolean valid) throws IOException {
|
||||||
final CSVFormat format =
|
final CSVFormat format =
|
||||||
CSVFormat.DEFAULT.builder()
|
CSVFormat.DEFAULT.builder()
|
||||||
.setDuplicateHeaderMode(duplicateHeaderMode)
|
.setDuplicateHeaderMode(duplicateHeaderMode)
|
||||||
.setAllowMissingColumnNames(allowMissingColumnNames)
|
.setAllowMissingColumnNames(allowMissingColumnNames)
|
||||||
|
.setIgnoreHeaderCase(ignoreHeaderCase)
|
||||||
.setNullString("NULL")
|
.setNullString("NULL")
|
||||||
.setHeader()
|
.setHeader()
|
||||||
.build();
|
.build();
|
||||||
final String input = Arrays.stream(headers)
|
final String input = Arrays.stream(headers)
|
||||||
.map(s -> s == null ? "NULL" : s)
|
.map(s -> s == null ? format.getNullString() : s)
|
||||||
.collect(Collectors.joining(format.getDelimiterString()));
|
.collect(Collectors.joining(format.getDelimiterString()));
|
||||||
if (valid) {
|
if (valid) {
|
||||||
try(CSVParser parser = CSVParser.parse(input, format)) {
|
try(CSVParser parser = CSVParser.parse(input, format)) {
|
||||||
|
|
Loading…
Reference in New Issue