Author: ggregory
Date: Thu Jun 12 15:38:24 2014
New Revision: 1602206
URL: http://svn.apache.org/r1602206
Log:
[CSV-121] Exception that the header contains duplicate names when the column
names are empty. Added the setting ignoreEmptyHeaders, defaults to false to
keep the IAE as the default behavior.
Modified:
commons/proper/csv/trunk/src/changes/changes.xml
commons/proper/csv/trunk/src/main/java/org/apache/commons/csv/CSVFormat.java
commons/proper/csv/trunk/src/main/java/org/apache/commons/csv/CSVParser.java
commons/proper/csv/trunk/src/test/java/org/apache/commons/csv/CSVParserTest.java
Modified: commons/proper/csv/trunk/src/changes/changes.xml
URL:
http://svn.apache.org/viewvc/commons/proper/csv/trunk/src/changes/changes.xml?rev=1602206&r1=1602205&r2=1602206&view=diff
==============================================================================
--- commons/proper/csv/trunk/src/changes/changes.xml (original)
+++ commons/proper/csv/trunk/src/changes/changes.xml Thu Jun 12 15:38:24 2014
@@ -40,6 +40,7 @@
<body>
<release version="1.0" date="TBD" description="First release">
+ <action issue="CSV-121" type="add" dev="ggregory" due-to="Sebastian
Hardt">IllegalArgumentException thrown when the header contains duplicate names
when the column names are empty.</action>
<action issue="CSV-120" type="add" dev="ggregory" due-to="Sergei
Lebedev">CSVFormat#withHeader doesn't work with CSVPrinter</action>
<action issue="CSV-119" type="add" dev="ggregory" due-to="Sergei
Lebedev">CSVFormat is missing a print(...) method</action>
<action issue="CSV-118" type="fix" dev="ggregory" due-to="Enrique
Lara">CSVRecord.toMap() throws NPE on formats with no
Modified:
commons/proper/csv/trunk/src/main/java/org/apache/commons/csv/CSVFormat.java
URL:
http://svn.apache.org/viewvc/commons/proper/csv/trunk/src/main/java/org/apache/commons/csv/CSVFormat.java?rev=1602206&r1=1602205&r2=1602206&view=diff
==============================================================================
---
commons/proper/csv/trunk/src/main/java/org/apache/commons/csv/CSVFormat.java
(original)
+++
commons/proper/csv/trunk/src/main/java/org/apache/commons/csv/CSVFormat.java
Thu Jun 12 15:38:24 2014
@@ -152,6 +152,7 @@ public final class CSVFormat implements
private final Character commentStart; // null if commenting is disabled
private final Character escape; // null if escaping is disabled
private final boolean ignoreSurroundingSpaces; // Should leading/trailing
spaces be ignored around values?
+ private boolean ignoreEmptyHeaders;
private final boolean ignoreEmptyLines;
private final String recordSeparator; // for outputs
private final String nullString; // the string to be used for null values
@@ -172,7 +173,7 @@ public final class CSVFormat implements
* </ul>
*/
public static final CSVFormat DEFAULT = new CSVFormat(COMMA,
DOUBLE_QUOTE_CHAR, null, null, null,
- false, true, CRLF,
null, null, false);
+ false, true, CRLF,
null, null, false, false);
/**
* Comma separated format as defined by <a
href="http://tools.ietf.org/html/rfc4180">RFC 4180</a>.
@@ -264,7 +265,7 @@ public final class CSVFormat implements
* @throws IllegalArgumentException if the delimiter is a line break
character
*/
public static CSVFormat newFormat(final char delimiter) {
- return new CSVFormat(delimiter, null, null, null, null, false, false,
null, null, null, false);
+ return new CSVFormat(delimiter, null, null, null, null, false, false,
null, null, null, false, false);
}
/**
@@ -291,13 +292,15 @@ public final class CSVFormat implements
* @param header
* the header
* @param skipHeaderRecord TODO
+ * @param ignoreEmptyHeaders TODO
* @throws IllegalArgumentException if the delimiter is a line break
character
*/
private CSVFormat(final char delimiter, final Character quoteChar,
final Quote quotePolicy, final Character commentStart,
final Character escape, final boolean ignoreSurroundingSpaces,
final boolean ignoreEmptyLines, final String recordSeparator,
- final String nullString, final String[] header, final boolean
skipHeaderRecord) {
+ final String nullString, final String[] header, final boolean
skipHeaderRecord,
+ final boolean ignoreEmptyHeaders) {
if (isLineBreak(delimiter)) {
throw new IllegalArgumentException("The delimiter cannot be a line
break");
}
@@ -307,6 +310,7 @@ public final class CSVFormat implements
this.commentStart = commentStart;
this.escape = escape;
this.ignoreSurroundingSpaces = ignoreSurroundingSpaces;
+ this.ignoreEmptyHeaders = ignoreEmptyHeaders;
this.ignoreEmptyLines = ignoreEmptyLines;
this.recordSeparator = recordSeparator;
this.nullString = nullString;
@@ -448,6 +452,16 @@ public final class CSVFormat implements
}
/**
+ * Specifies whether empty headers are ignored when parsing the header
line.
+ *
+ * @return <tt>true</tt> if headers are ignored when parsing the header
line, <tt>false</tt> to throw an
+ * {@link IllegalArgumentException}..
+ */
+ public boolean getIgnoreEmptyHeaders() {
+ return ignoreEmptyHeaders;
+ }
+
+ /**
* Specifies whether empty lines between records are ignored when parsing
input.
*
* @return <tt>true</tt> if empty lines between records are ignored,
<tt>false</tt> if they are turned into empty
@@ -718,7 +732,8 @@ public final class CSVFormat implements
throw new IllegalArgumentException("The comment start character
cannot be a line break");
}
return new CSVFormat(delimiter, quoteChar, quotePolicy, commentStart,
escape,
- ignoreSurroundingSpaces, ignoreEmptyLines, recordSeparator,
nullString, header, skipHeaderRecord);
+ ignoreSurroundingSpaces, ignoreEmptyLines, recordSeparator,
nullString, header, skipHeaderRecord,
+ ignoreEmptyHeaders);
}
/**
@@ -735,7 +750,8 @@ public final class CSVFormat implements
throw new IllegalArgumentException("The delimiter cannot be a line
break");
}
return new CSVFormat(delimiter, quoteChar, quotePolicy, commentStart,
escape,
- ignoreSurroundingSpaces, ignoreEmptyLines, recordSeparator,
nullString, header, skipHeaderRecord);
+ ignoreSurroundingSpaces, ignoreEmptyLines, recordSeparator,
nullString, header, skipHeaderRecord,
+ ignoreEmptyHeaders);
}
/**
@@ -765,7 +781,8 @@ public final class CSVFormat implements
throw new IllegalArgumentException("The escape character cannot be
a line break");
}
return new CSVFormat(delimiter, quoteChar, quotePolicy, commentStart,
escape,
- ignoreSurroundingSpaces, ignoreEmptyLines, recordSeparator,
nullString, header, skipHeaderRecord);
+ ignoreSurroundingSpaces, ignoreEmptyLines, recordSeparator,
nullString, header, skipHeaderRecord,
+ ignoreEmptyHeaders);
}
/**
@@ -787,7 +804,22 @@ public final class CSVFormat implements
*/
public CSVFormat withHeader(final String... header) {
return new CSVFormat(delimiter, quoteChar, quotePolicy, commentStart,
escape,
- ignoreSurroundingSpaces, ignoreEmptyLines, recordSeparator,
nullString, header, skipHeaderRecord);
+ ignoreSurroundingSpaces, ignoreEmptyLines, recordSeparator,
nullString, header, skipHeaderRecord,
+ ignoreEmptyHeaders);
+ }
+
+ /**
+ * Sets the empty header behavior of the format.
+ *
+ * @param ignoreEmptyHeaders
+ * the empty header behavior, <tt>true</tt> to ignore empty
headers in the header line,
+ * <tt>false</tt> to cause an {@link IllegalArgumentException}
to be thrown.
+ * @return A new CSVFormat that is equal to this but with the specified
empty header behavior.
+ */
+ public CSVFormat withIgnoreEmptyHeaders(final boolean ignoreEmptyHeaders) {
+ return new CSVFormat(delimiter, quoteChar, quotePolicy, commentStart,
escape,
+ ignoreSurroundingSpaces, ignoreEmptyHeaders, recordSeparator,
nullString, header, skipHeaderRecord,
+ ignoreEmptyHeaders);
}
/**
@@ -800,7 +832,8 @@ public final class CSVFormat implements
*/
public CSVFormat withIgnoreEmptyLines(final boolean ignoreEmptyLines) {
return new CSVFormat(delimiter, quoteChar, quotePolicy, commentStart,
escape,
- ignoreSurroundingSpaces, ignoreEmptyLines, recordSeparator,
nullString, header, skipHeaderRecord);
+ ignoreSurroundingSpaces, ignoreEmptyLines, recordSeparator,
nullString, header, skipHeaderRecord,
+ ignoreEmptyHeaders);
}
/**
@@ -813,7 +846,8 @@ public final class CSVFormat implements
*/
public CSVFormat withIgnoreSurroundingSpaces(final boolean
ignoreSurroundingSpaces) {
return new CSVFormat(delimiter, quoteChar, quotePolicy, commentStart,
escape,
- ignoreSurroundingSpaces, ignoreEmptyLines, recordSeparator,
nullString, header, skipHeaderRecord);
+ ignoreSurroundingSpaces, ignoreEmptyLines, recordSeparator,
nullString, header, skipHeaderRecord,
+ ignoreEmptyHeaders);
}
/**
@@ -833,7 +867,8 @@ public final class CSVFormat implements
*/
public CSVFormat withNullString(final String nullString) {
return new CSVFormat(delimiter, quoteChar, quotePolicy, commentStart,
escape,
- ignoreSurroundingSpaces, ignoreEmptyLines, recordSeparator,
nullString, header, skipHeaderRecord);
+ ignoreSurroundingSpaces, ignoreEmptyLines, recordSeparator,
nullString, header, skipHeaderRecord,
+ ignoreEmptyHeaders);
}
/**
@@ -863,7 +898,8 @@ public final class CSVFormat implements
throw new IllegalArgumentException("The quoteChar cannot be a line
break");
}
return new CSVFormat(delimiter, quoteChar, quotePolicy, commentStart,
escape,
- ignoreSurroundingSpaces, ignoreEmptyLines, recordSeparator,
nullString, header, skipHeaderRecord);
+ ignoreSurroundingSpaces, ignoreEmptyLines, recordSeparator,
nullString, header, skipHeaderRecord,
+ ignoreEmptyHeaders);
}
/**
@@ -876,7 +912,8 @@ public final class CSVFormat implements
*/
public CSVFormat withQuotePolicy(final Quote quotePolicy) {
return new CSVFormat(delimiter, quoteChar, quotePolicy, commentStart,
escape,
- ignoreSurroundingSpaces, ignoreEmptyLines, recordSeparator,
nullString, header, skipHeaderRecord);
+ ignoreSurroundingSpaces, ignoreEmptyLines, recordSeparator,
nullString, header, skipHeaderRecord,
+ ignoreEmptyHeaders);
}
/**
@@ -901,7 +938,8 @@ public final class CSVFormat implements
*/
public CSVFormat withRecordSeparator(final String recordSeparator) {
return new CSVFormat(delimiter, quoteChar, quotePolicy, commentStart,
escape,
- ignoreSurroundingSpaces, ignoreEmptyLines, recordSeparator,
nullString, header, skipHeaderRecord);
+ ignoreSurroundingSpaces, ignoreEmptyLines, recordSeparator,
nullString, header, skipHeaderRecord,
+ ignoreEmptyHeaders);
}
/**
@@ -915,6 +953,7 @@ public final class CSVFormat implements
*/
public CSVFormat withSkipHeaderRecord(final boolean skipHeaderRecord) {
return new CSVFormat(delimiter, quoteChar, quotePolicy, commentStart,
escape,
- ignoreSurroundingSpaces, ignoreEmptyLines, recordSeparator,
nullString, header, skipHeaderRecord);
+ ignoreSurroundingSpaces, ignoreEmptyLines, recordSeparator,
nullString, header, skipHeaderRecord,
+ ignoreEmptyHeaders);
}
}
Modified:
commons/proper/csv/trunk/src/main/java/org/apache/commons/csv/CSVParser.java
URL:
http://svn.apache.org/viewvc/commons/proper/csv/trunk/src/main/java/org/apache/commons/csv/CSVParser.java?rev=1602206&r1=1602205&r2=1602206&view=diff
==============================================================================
---
commons/proper/csv/trunk/src/main/java/org/apache/commons/csv/CSVParser.java
(original)
+++
commons/proper/csv/trunk/src/main/java/org/apache/commons/csv/CSVParser.java
Thu Jun 12 15:38:24 2014
@@ -360,28 +360,31 @@ public final class CSVParser implements
if (formatHeader != null) {
hdrMap = new LinkedHashMap<String, Integer>();
- String[] header = null;
+ String[] headerRecord = null;
if (formatHeader.length == 0) {
// read the header from the first line of the file
final CSVRecord nextRecord = this.nextRecord();
if (nextRecord != null) {
- header = nextRecord.values();
+ headerRecord = nextRecord.values();
}
} else {
if (this.format.getSkipHeaderRecord()) {
this.nextRecord();
}
- header = formatHeader;
+ headerRecord = formatHeader;
}
// build the name to index mappings
- if (header != null) {
- for (int i = 0; i < header.length; i++) {
- if (hdrMap.containsKey(header[i])) {
- throw new IllegalArgumentException("The header
contains duplicate names: " +
- Arrays.toString(header));
+ if (headerRecord != null) {
+ for (int i = 0; i < headerRecord.length; i++) {
+ final String header = headerRecord[i];
+ final boolean containsHeader = hdrMap.containsKey(header);
+ final boolean emptyHeader = header.trim().isEmpty();
+ if (containsHeader && (!emptyHeader || (emptyHeader &&
!this.format.getIgnoreEmptyHeaders()))) {
+ throw new IllegalArgumentException("The header
contains a duplicate name: \"" + header
+ + "\" in " + Arrays.toString(headerRecord));
}
- hdrMap.put(header[i], Integer.valueOf(i));
+ hdrMap.put(header, Integer.valueOf(i));
}
}
}
Modified:
commons/proper/csv/trunk/src/test/java/org/apache/commons/csv/CSVParserTest.java
URL:
http://svn.apache.org/viewvc/commons/proper/csv/trunk/src/test/java/org/apache/commons/csv/CSVParserTest.java?rev=1602206&r1=1602205&r2=1602206&view=diff
==============================================================================
---
commons/proper/csv/trunk/src/test/java/org/apache/commons/csv/CSVParserTest.java
(original)
+++
commons/proper/csv/trunk/src/test/java/org/apache/commons/csv/CSVParserTest.java
Thu Jun 12 15:38:24 2014
@@ -493,7 +493,7 @@ public class CSVParserTest {
}
@Test(expected = IllegalArgumentException.class)
- public void testDuplicateHeaderEntries() throws Exception {
+ public void testDuplicateHeaders() throws Exception {
CSVParser.parse("a,b,a\n1,2,3\nx,y,z",
CSVFormat.DEFAULT.withHeader(new String[]{}));
}
@@ -656,6 +656,12 @@ public class CSVParserTest {
}
@Test
+ public void testHeadersMissing() throws Exception {
+ final Reader in = new StringReader("a,,c,,d\n1,2,3,4\nx,y,z,zz");
+
CSVFormat.DEFAULT.withHeader().withIgnoreEmptyHeaders(true).parse(in).iterator();
+ }
+
+ @Test
public void testHeaderComment() throws Exception {
final Reader in = new StringReader("# comment\na,b,c\n1,2,3\nx,y,z");