Skip to content

Commit 97d3457

Browse files
committed
Revert back to NOT skipping a record when withHeader is called with a non-empty array. Add skipHeaderRecord setting to CSVFormat and use when headers are initialized.
git-svn-id: https://svn.apache.org/repos/asf/commons/proper/csv/trunk@1508933 13f79535-47bb-0310-9956-ffa450edef68
1 parent e6759b8 commit 97d3457

3 files changed

Lines changed: 54 additions & 26 deletions

File tree

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

Lines changed: 44 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
public class CSVFormat implements Serializable {
5454

5555
private static final long serialVersionUID = 1L;
56+
5657
/**
5758
* Returns true if the given character is a line break character.
5859
*
@@ -76,6 +77,7 @@ static boolean isLineBreak(final Character c) {
7677
private final String recordSeparator; // for outputs
7778
private final String nullString;
7879
private final String[] header;
80+
private final boolean skipHeaderRecord;
7981

8082
/**
8183
* Standard comma separated format, as for {@link #RFC4180} but allowing empty lines.
@@ -90,7 +92,7 @@ static boolean isLineBreak(final Character c) {
9092
* <li>withIgnoreEmptyLines(true)</li>
9193
* </ul>
9294
*/
93-
public static final CSVFormat DEFAULT = new CSVFormat(COMMA, DOUBLE_QUOTE_CHAR, null, null, null, false, true, CRLF, null, null);
95+
public static final CSVFormat DEFAULT = new CSVFormat(COMMA, DOUBLE_QUOTE_CHAR, null, null, null, false, true, CRLF, null, null, false);
9496

9597
/**
9698
* Comma separated format as defined by <a href="http://tools.ietf.org/html/rfc4180">RFC 4180</a>.
@@ -166,7 +168,7 @@ static boolean isLineBreak(final char c) {
166168
* @throws IllegalArgumentException if the delimiter is a line break character
167169
*/
168170
public static CSVFormat newFormat(final char delimiter) {
169-
return new CSVFormat(delimiter, null, null, null, null, false, false, null, null, null);
171+
return new CSVFormat(delimiter, null, null, null, null, false, false, null, null, null, false);
170172
}
171173

172174
/**
@@ -203,14 +205,15 @@ public static CSVFormat copy(final CSVFormat format) {
203205
* the line separator to use for output
204206
* @param header
205207
* the header
208+
* @param skipHeaderRecord TODO
206209
* @throws IllegalArgumentException if the delimiter is a line break character
207210
*/
208211
// package protected to give access without needing a synthetic accessor
209212
CSVFormat(final char delimiter, final Character quoteChar,
210213
final Quote quotePolicy, final Character commentStart,
211214
final Character escape, final boolean ignoreSurroundingSpaces,
212215
final boolean ignoreEmptyLines, final String recordSeparator,
213-
final String nullString, final String[] header) {
216+
final String nullString, final String[] header, boolean skipHeaderRecord) {
214217
if (isLineBreak(delimiter)) {
215218
throw new IllegalArgumentException("The delimiter cannot be a line break");
216219
}
@@ -224,12 +227,13 @@ public static CSVFormat copy(final CSVFormat format) {
224227
this.recordSeparator = recordSeparator;
225228
this.nullString = nullString;
226229
this.header = header == null ? null : header.clone();
230+
this.skipHeaderRecord = skipHeaderRecord;
227231
}
228232

229233
CSVFormat(final CSVFormat format) {
230234
this(format.getDelimiter(), format.getQuoteChar(), format.getQuotePolicy(), format.getCommentStart(),
231235
format.getEscape(), format.getIgnoreSurroundingSpaces(), format.getIgnoreEmptyLines(),
232-
format.getRecordSeparator(), format.getNullString(), format.getHeader());
236+
format.getRecordSeparator(), format.getNullString(), format.getHeader(), format.getSkipHeaderRecord());
233237
}
234238

235239
@Override
@@ -409,6 +413,15 @@ public String getRecordSeparator() {
409413
return recordSeparator;
410414
}
411415

416+
/**
417+
* Returns whether to skip the header record.
418+
*
419+
* @return whether to skip the header record.
420+
*/
421+
public boolean getSkipHeaderRecord() {
422+
return skipHeaderRecord;
423+
}
424+
412425
@Override
413426
public int hashCode()
414427
{
@@ -573,7 +586,7 @@ public CSVFormat withCommentStart(final Character commentStart) {
573586
throw new IllegalArgumentException("The comment start character cannot be a line break");
574587
}
575588
return new CSVFormat(delimiter, quoteChar, quotePolicy, commentStart, escape,
576-
ignoreSurroundingSpaces, ignoreEmptyLines, recordSeparator, nullString, header);
589+
ignoreSurroundingSpaces, ignoreEmptyLines, recordSeparator, nullString, header, skipHeaderRecord);
577590
}
578591

579592
/**
@@ -590,7 +603,7 @@ public CSVFormat withDelimiter(final char delimiter) {
590603
throw new IllegalArgumentException("The delimiter cannot be a line break");
591604
}
592605
return new CSVFormat(delimiter, quoteChar, quotePolicy, commentStart, escape,
593-
ignoreSurroundingSpaces, ignoreEmptyLines, recordSeparator, nullString, header);
606+
ignoreSurroundingSpaces, ignoreEmptyLines, recordSeparator, nullString, header, skipHeaderRecord);
594607
}
595608

596609
/**
@@ -620,33 +633,29 @@ public CSVFormat withEscape(final Character escape) {
620633
throw new IllegalArgumentException("The escape character cannot be a line break");
621634
}
622635
return new CSVFormat(delimiter, quoteChar, quotePolicy, commentStart, escape,
623-
ignoreSurroundingSpaces, ignoreEmptyLines, recordSeparator, nullString, header);
636+
ignoreSurroundingSpaces, ignoreEmptyLines, recordSeparator, nullString, header, skipHeaderRecord);
624637
}
625638

626639
/**
627640
* Sets the header of the format. The header can either be parsed automatically from the input file with:
628641
*
629642
* <pre>
630-
* CSVFormat format = aformat.withHeader();
631-
* </pre>
643+
* CSVFormat format = aformat.withHeader();</pre>
632644
*
633645
* or specified manually with:
634646
*
635647
* <pre>
636-
* CSVFormat format = aformat.withHeader(&quot;name&quot;, &quot;email&quot;, &quot;phone&quot;);
637-
* </pre>
638-
*
639-
* When this option is is set to any non-null value, the first record is the first <em>data</em> record, not the
640-
* header record.
648+
* CSVFormat format = aformat.withHeader(&quot;name&quot;, &quot;email&quot;, &quot;phone&quot;);</pre>
641649
*
642650
* @param header
643651
* the header, <tt>null</tt> if disabled, empty if parsed automatically, user specified otherwise.
644652
*
645653
* @return A new CSVFormat that is equal to this but with the specified header
654+
* @see #withSkipHeaderRecord(boolean)
646655
*/
647656
public CSVFormat withHeader(final String... header) {
648657
return new CSVFormat(delimiter, quoteChar, quotePolicy, commentStart, escape,
649-
ignoreSurroundingSpaces, ignoreEmptyLines, recordSeparator, nullString, header);
658+
ignoreSurroundingSpaces, ignoreEmptyLines, recordSeparator, nullString, header, skipHeaderRecord);
650659
}
651660

652661
/**
@@ -659,7 +668,7 @@ public CSVFormat withHeader(final String... header) {
659668
*/
660669
public CSVFormat withIgnoreEmptyLines(final boolean ignoreEmptyLines) {
661670
return new CSVFormat(delimiter, quoteChar, quotePolicy, commentStart, escape,
662-
ignoreSurroundingSpaces, ignoreEmptyLines, recordSeparator, nullString, header);
671+
ignoreSurroundingSpaces, ignoreEmptyLines, recordSeparator, nullString, header, skipHeaderRecord);
663672
}
664673

665674
/**
@@ -672,7 +681,7 @@ public CSVFormat withIgnoreEmptyLines(final boolean ignoreEmptyLines) {
672681
*/
673682
public CSVFormat withIgnoreSurroundingSpaces(final boolean ignoreSurroundingSpaces) {
674683
return new CSVFormat(delimiter, quoteChar, quotePolicy, commentStart, escape,
675-
ignoreSurroundingSpaces, ignoreEmptyLines, recordSeparator, nullString, header);
684+
ignoreSurroundingSpaces, ignoreEmptyLines, recordSeparator, nullString, header, skipHeaderRecord);
676685
}
677686

678687
/**
@@ -692,7 +701,7 @@ public CSVFormat withIgnoreSurroundingSpaces(final boolean ignoreSurroundingSpac
692701
*/
693702
public CSVFormat withNullString(final String nullString) {
694703
return new CSVFormat(delimiter, quoteChar, quotePolicy, commentStart, escape,
695-
ignoreSurroundingSpaces, ignoreEmptyLines, recordSeparator, nullString, header);
704+
ignoreSurroundingSpaces, ignoreEmptyLines, recordSeparator, nullString, header, skipHeaderRecord);
696705
}
697706

698707
/**
@@ -722,7 +731,7 @@ public CSVFormat withQuoteChar(final Character quoteChar) {
722731
throw new IllegalArgumentException("The quoteChar cannot be a line break");
723732
}
724733
return new CSVFormat(delimiter, quoteChar, quotePolicy, commentStart, escape,
725-
ignoreSurroundingSpaces, ignoreEmptyLines, recordSeparator, nullString, header);
734+
ignoreSurroundingSpaces, ignoreEmptyLines, recordSeparator, nullString, header, skipHeaderRecord);
726735
}
727736

728737
/**
@@ -735,7 +744,7 @@ public CSVFormat withQuoteChar(final Character quoteChar) {
735744
*/
736745
public CSVFormat withQuotePolicy(final Quote quotePolicy) {
737746
return new CSVFormat(delimiter, quoteChar, quotePolicy, commentStart, escape,
738-
ignoreSurroundingSpaces, ignoreEmptyLines, recordSeparator, nullString, header);
747+
ignoreSurroundingSpaces, ignoreEmptyLines, recordSeparator, nullString, header, skipHeaderRecord);
739748
}
740749

741750
/**
@@ -760,6 +769,20 @@ public CSVFormat withRecordSeparator(final char recordSeparator) {
760769
*/
761770
public CSVFormat withRecordSeparator(final String recordSeparator) {
762771
return new CSVFormat(delimiter, quoteChar, quotePolicy, commentStart, escape,
763-
ignoreSurroundingSpaces, ignoreEmptyLines, recordSeparator, nullString, header);
772+
ignoreSurroundingSpaces, ignoreEmptyLines, recordSeparator, nullString, header, skipHeaderRecord);
773+
}
774+
775+
/**
776+
* Sets whether to skip the header record.
777+
*
778+
* @param skipHeaderRecord
779+
* whether to skip the header record.
780+
*
781+
* @return A new CSVFormat that is equal to this but with the the specified skipHeaderRecord setting.
782+
* @see #withHeader(String...)
783+
*/
784+
public CSVFormat withSkipHeaderRecord(final boolean skipHeaderRecord) {
785+
return new CSVFormat(delimiter, quoteChar, quotePolicy, commentStart, escape,
786+
ignoreSurroundingSpaces, ignoreEmptyLines, recordSeparator, nullString, header, skipHeaderRecord);
764787
}
765788
}

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -323,16 +323,19 @@ private Map<String, Integer> initializeHeader() throws IOException {
323323
Map<String, Integer> hdrMap = null;
324324
String[] formatHeader = this.format.getHeader();
325325
if (formatHeader != null) {
326-
final CSVRecord record = this.nextRecord();
327326
hdrMap = new LinkedHashMap<String, Integer>();
328327

329328
String[] header = null;
330329
if (formatHeader.length == 0) {
331330
// read the header from the first line of the file
331+
final CSVRecord record = this.nextRecord();
332332
if (record != null) {
333333
header = record.values();
334334
}
335335
} else {
336+
if (this.format.getSkipHeaderRecord()) {
337+
this.nextRecord();
338+
}
336339
header = formatHeader;
337340
}
338341

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

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -509,7 +509,8 @@ public void testHeader() throws Exception {
509509
@Test
510510
public void testSkipSetHeader() throws Exception {
511511
final Reader in = new StringReader("a,b,c\n1,2,3\nx,y,z");
512-
final Iterator<CSVRecord> records = CSVFormat.DEFAULT.withHeader("a", "b", "c").parse(in).iterator();
512+
final Iterator<CSVRecord> records = CSVFormat.DEFAULT.withHeader("a", "b", "c").withSkipHeaderRecord(true)
513+
.parse(in).iterator();
513514
final CSVRecord record = records.next();
514515
assertEquals("1", record.get("a"));
515516
assertEquals("2", record.get("b"));
@@ -549,7 +550,7 @@ public void testProvidedHeader() throws Exception {
549550

550551
final Iterator<CSVRecord> records = CSVFormat.DEFAULT.withHeader("A", "B", "C").parse(in).iterator();
551552

552-
for (int i = 0; i < 2; i++) {
553+
for (int i = 0; i < 3; i++) {
553554
assertTrue(records.hasNext());
554555
final CSVRecord record = records.next();
555556
assertTrue(record.isMapped("A"));
@@ -588,7 +589,8 @@ public void testProvidedHeaderAuto() throws Exception {
588589
@Test
589590
public void testMappedButNotSetAsOutlook2007ContactExport() throws Exception {
590591
final Reader in = new StringReader("a,b,c\n1,2\nx,y,z");
591-
final Iterator<CSVRecord> records = CSVFormat.DEFAULT.withHeader("A", "B", "C").parse(in).iterator();
592+
final Iterator<CSVRecord> records = CSVFormat.DEFAULT.withHeader("A", "B", "C").withSkipHeaderRecord(true)
593+
.parse(in).iterator();
592594
CSVRecord record;
593595

594596
// 1st record
@@ -631,7 +633,7 @@ public void testGetHeaderMap() throws Exception {
631633
final Iterator<CSVRecord> records = parser.iterator();
632634

633635
// Parse to make sure getHeaderMap did not have a side-effect.
634-
for (int i = 0; i < 2; i++) {
636+
for (int i = 0; i < 3; i++) {
635637
assertTrue(records.hasNext());
636638
final CSVRecord record = records.next();
637639
assertEquals(record.get(0), record.get("A"));

0 commit comments

Comments
 (0)