Skip to content

Commit 2e851bc

Browse files
authored
Merge pull request apache#257 from pedro-w/csv-304-header-comments
[CSV-304] Accessors for header/trailer comments
2 parents d634220 + ee7b3a7 commit 2e851bc

2 files changed

Lines changed: 218 additions & 1 deletion

File tree

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

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -353,6 +353,10 @@ public static CSVParser parse(final URL url, final Charset charset, final CSVFor
353353
return new CSVParser(new InputStreamReader(url.openStream(), charset), format);
354354
}
355355

356+
private String headerComment;
357+
358+
private String trailerComment;
359+
356360
private final CSVFormat format;
357361

358362
private final Headers headers;
@@ -480,10 +484,14 @@ private Headers createHeaders() throws IOException {
480484
final CSVRecord nextRecord = this.nextRecord();
481485
if (nextRecord != null) {
482486
headerRecord = nextRecord.values();
487+
headerComment = nextRecord.getComment();
483488
}
484489
} else {
485490
if (this.format.getSkipHeaderRecord()) {
486-
this.nextRecord();
491+
final CSVRecord nextRecord = this.nextRecord();
492+
if (nextRecord != null) {
493+
headerComment = nextRecord.getComment();
494+
}
487495
}
488496
headerRecord = formatHeader;
489497
}
@@ -597,6 +605,57 @@ public List<String> getHeaderNames() {
597605
return Collections.unmodifiableList(headers.headerNames);
598606
}
599607

608+
/**
609+
* Checks whether there is a header comment.
610+
* The header comment appears before the header record.
611+
* Note that if the parser's format has been given an explicit header
612+
* (with {@link CSVFormat.Builder#setHeader(String... )} or another overload)
613+
* and the header record is not being skipped
614+
* ({@link CSVFormat.Builder#setSkipHeaderRecord} is false) then any initial comments
615+
* will be associated with the first record, not the header.
616+
*
617+
* @return true if this parser has seen a header comment, false otherwise
618+
* @since 1.10.0
619+
*/
620+
public boolean hasHeaderComment() {
621+
return headerComment != null;
622+
}
623+
624+
/**
625+
* Returns the header comment, if any.
626+
* The header comment appears before the header record.
627+
*
628+
* @return the header comment for this stream, or null if no comment is available.
629+
* @since 1.10.0
630+
*/
631+
public String getHeaderComment() {
632+
return headerComment;
633+
}
634+
635+
/**
636+
* Checks whether there is a trailer comment.
637+
* Trailer comments are located between the last record and EOF.
638+
* The trailer comments will only be available after the parser has
639+
* finished processing this stream.
640+
*
641+
* @return true if this parser has seen a trailer comment, false otherwise
642+
* @since 1.10.0
643+
*/
644+
public boolean hasTrailerComment() {
645+
return trailerComment != null;
646+
}
647+
648+
/**
649+
* Returns the trailer comment, if any.
650+
* Trailer comments are located between the last record and EOF
651+
*
652+
* @return the trailer comment for this stream, or null if no comment is available.
653+
* @since 1.10.0
654+
*/
655+
public String getTrailerComment() {
656+
return trailerComment;
657+
}
658+
600659
/**
601660
* Returns the current record number in the input stream.
602661
*
@@ -713,6 +772,8 @@ CSVRecord nextRecord() throws IOException {
713772
case EOF:
714773
if (this.reusableToken.isReady) {
715774
this.addRecordValue(true);
775+
} else if (sb != null) {
776+
trailerComment = sb.toString();
716777
}
717778
break;
718779
case INVALID:

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

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1349,4 +1349,160 @@ private void validateRecordPosition(final String lineSeparator) throws IOExcepti
13491349

13501350
parser.close();
13511351
}
1352+
1353+
// CSV with no header comments
1354+
static private final String CSV_INPUT_NO_COMMENT = "A,B"+CRLF+"1,2"+CRLF;
1355+
// CSV with a header comment
1356+
static private final String CSV_INPUT_HEADER_COMMENT = "# header comment" + CRLF + "A,B" + CRLF + "1,2" + CRLF;
1357+
// CSV with a single line header and trailer comment
1358+
static private final String CSV_INPUT_HEADER_TRAILER_COMMENT = "# header comment" + CRLF + "A,B" + CRLF + "1,2" + CRLF + "# comment";
1359+
// CSV with a multi-line header and trailer comment
1360+
static private final String CSV_INPUT_MULTILINE_HEADER_TRAILER_COMMENT = "# multi-line" + CRLF + "# header comment" + CRLF + "A,B" + CRLF + "1,2" + CRLF + "# multi-line" + CRLF + "# comment";
1361+
// Format with auto-detected header
1362+
static private final CSVFormat FORMAT_AUTO_HEADER = CSVFormat.Builder.create(CSVFormat.DEFAULT).setCommentMarker('#').setHeader().build();
1363+
// Format with explicit header
1364+
static private final CSVFormat FORMAT_EXPLICIT_HEADER = CSVFormat.Builder.create(CSVFormat.DEFAULT)
1365+
.setSkipHeaderRecord(true)
1366+
.setCommentMarker('#')
1367+
.setHeader("A", "B")
1368+
.build();
1369+
// Format with explicit header that does not skip the header line
1370+
CSVFormat FORMAT_EXPLICIT_HEADER_NOSKIP = CSVFormat.Builder.create(CSVFormat.DEFAULT)
1371+
.setCommentMarker('#')
1372+
.setHeader("A", "B")
1373+
.build();
1374+
1375+
@Test
1376+
public void testGetHeaderComment_NoComment1() throws IOException {
1377+
try (CSVParser parser = CSVParser.parse(CSV_INPUT_NO_COMMENT, FORMAT_AUTO_HEADER)) {
1378+
parser.getRecords();
1379+
// Expect no header comment
1380+
assertFalse(parser.hasHeaderComment());
1381+
assertNull(parser.getHeaderComment());
1382+
}
1383+
}
1384+
1385+
@Test
1386+
public void testGetHeaderComment_HeaderComment1() throws IOException {
1387+
try (CSVParser parser = CSVParser.parse(CSV_INPUT_HEADER_COMMENT, FORMAT_AUTO_HEADER)) {
1388+
parser.getRecords();
1389+
// Expect a header comment
1390+
assertTrue(parser.hasHeaderComment());
1391+
assertEquals("header comment", parser.getHeaderComment());
1392+
}
1393+
}
1394+
1395+
@Test
1396+
public void testGetHeaderComment_HeaderTrailerComment() throws IOException {
1397+
try (CSVParser parser = CSVParser.parse(CSV_INPUT_MULTILINE_HEADER_TRAILER_COMMENT, FORMAT_AUTO_HEADER)) {
1398+
parser.getRecords();
1399+
// Expect a header comment
1400+
assertTrue(parser.hasHeaderComment());
1401+
assertEquals("multi-line"+LF+"header comment", parser.getHeaderComment());
1402+
}
1403+
}
1404+
1405+
@Test
1406+
public void testGetHeaderComment_NoComment2() throws IOException {
1407+
try (CSVParser parser = CSVParser.parse(CSV_INPUT_NO_COMMENT, FORMAT_EXPLICIT_HEADER)) {
1408+
parser.getRecords();
1409+
// Expect no header comment
1410+
assertFalse(parser.hasHeaderComment());
1411+
assertNull(parser.getHeaderComment());
1412+
}
1413+
}
1414+
1415+
@Test
1416+
public void testGetHeaderComment_HeaderComment2() throws IOException {
1417+
try (CSVParser parser = CSVParser.parse(CSV_INPUT_HEADER_COMMENT, FORMAT_EXPLICIT_HEADER)) {
1418+
parser.getRecords();
1419+
// Expect a header comment
1420+
assertTrue(parser.hasHeaderComment());
1421+
assertEquals("header comment", parser.getHeaderComment());
1422+
}
1423+
}
1424+
1425+
@Test
1426+
public void testGetHeaderComment_NoComment3() throws IOException {
1427+
try (CSVParser parser = CSVParser.parse(CSV_INPUT_NO_COMMENT, FORMAT_EXPLICIT_HEADER_NOSKIP)) {
1428+
parser.getRecords();
1429+
// Expect no header comment
1430+
assertFalse(parser.hasHeaderComment());
1431+
assertNull(parser.getHeaderComment());
1432+
}
1433+
}
1434+
1435+
@Test
1436+
public void testGetHeaderComment_HeaderComment3() throws IOException {
1437+
try (CSVParser parser = CSVParser.parse(CSV_INPUT_HEADER_COMMENT, FORMAT_EXPLICIT_HEADER_NOSKIP)) {
1438+
parser.getRecords();
1439+
// Expect no header comment - the text "comment" is attached to the first record
1440+
assertFalse(parser.hasHeaderComment());
1441+
assertNull(parser.getHeaderComment());
1442+
}
1443+
}
1444+
1445+
@Test
1446+
public void testGetTrailerComment_HeaderComment1() throws IOException {
1447+
try (CSVParser parser = CSVParser.parse(CSV_INPUT_HEADER_COMMENT, FORMAT_AUTO_HEADER)) {
1448+
parser.getRecords();
1449+
assertFalse(parser.hasTrailerComment());
1450+
assertNull(parser.getTrailerComment());
1451+
}
1452+
}
1453+
1454+
@Test
1455+
public void testGetTrailerComment_HeaderTrailerComment1() throws IOException {
1456+
try (CSVParser parser = CSVParser.parse(CSV_INPUT_HEADER_TRAILER_COMMENT, FORMAT_AUTO_HEADER)) {
1457+
parser.getRecords();
1458+
assertTrue(parser.hasTrailerComment());
1459+
assertEquals("comment", parser.getTrailerComment());
1460+
}
1461+
}
1462+
1463+
@Test
1464+
public void testGetTrailerComment_MultilineComment() throws IOException {
1465+
try (CSVParser parser = CSVParser.parse(CSV_INPUT_MULTILINE_HEADER_TRAILER_COMMENT, FORMAT_AUTO_HEADER)) {
1466+
parser.getRecords();
1467+
assertTrue(parser.hasTrailerComment());
1468+
assertEquals("multi-line"+LF+"comment", parser.getTrailerComment());
1469+
}
1470+
}
1471+
1472+
@Test
1473+
public void testGetTrailerComment_HeaderComment2() throws IOException {
1474+
try (CSVParser parser = CSVParser.parse(CSV_INPUT_HEADER_COMMENT, FORMAT_EXPLICIT_HEADER)) {
1475+
parser.getRecords();
1476+
assertFalse(parser.hasTrailerComment());
1477+
assertNull(parser.getTrailerComment());
1478+
}
1479+
}
1480+
1481+
@Test
1482+
public void testGetTrailerComment_HeaderTrailerComment2() throws IOException {
1483+
try (CSVParser parser = CSVParser.parse(CSV_INPUT_HEADER_TRAILER_COMMENT, FORMAT_EXPLICIT_HEADER)) {
1484+
parser.getRecords();
1485+
assertTrue(parser.hasTrailerComment());
1486+
assertEquals("comment", parser.getTrailerComment());
1487+
}
1488+
}
1489+
1490+
@Test
1491+
public void testGetTrailerComment_HeaderComment3() throws IOException {
1492+
try (CSVParser parser = CSVParser.parse(CSV_INPUT_HEADER_COMMENT, FORMAT_EXPLICIT_HEADER_NOSKIP)) {
1493+
parser.getRecords();
1494+
assertFalse(parser.hasTrailerComment());
1495+
assertNull(parser.getTrailerComment());
1496+
}
1497+
}
1498+
1499+
@Test
1500+
public void testGetTrailerComment_HeaderTrailerComment3() throws IOException {
1501+
try (CSVParser parser = CSVParser.parse(CSV_INPUT_HEADER_TRAILER_COMMENT, FORMAT_EXPLICIT_HEADER_NOSKIP)) {
1502+
parser.getRecords();
1503+
assertTrue(parser.hasTrailerComment());
1504+
assertEquals("comment", parser.getTrailerComment());
1505+
}
1506+
}
1507+
13521508
}

0 commit comments

Comments
 (0)