From 31a546adab3a95ac2378000cbd4a79fb21dba794 Mon Sep 17 00:00:00 2001 From: Peter Hull Date: Tue, 6 Sep 2022 14:37:34 +0100 Subject: [PATCH 1/7] [CSV-304] Accessors for header/trailer comments Add accessors for header comments (before the header row) and trailer comments (after the last record) Also add javadoc and tests --- .../org/apache/commons/csv/CSVParser.java | 55 +++++++- .../org/apache/commons/csv/CSVParserTest.java | 124 ++++++++++++++++++ 2 files changed, 177 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/apache/commons/csv/CSVParser.java b/src/main/java/org/apache/commons/csv/CSVParser.java index 58cdb146..3385f418 100644 --- a/src/main/java/org/apache/commons/csv/CSVParser.java +++ b/src/main/java/org/apache/commons/csv/CSVParser.java @@ -352,6 +352,9 @@ public final class CSVParser implements Iterable, Closeable { return new CSVParser(new InputStreamReader(url.openStream(), charset), format); } + private String headerComment; + + private String trailerComment; private final CSVFormat format; @@ -480,10 +483,12 @@ public final class CSVParser implements Iterable, Closeable { final CSVRecord nextRecord = this.nextRecord(); if (nextRecord != null) { headerRecord = nextRecord.values(); + headerComment = nextRecord.getComment(); } } else { if (this.format.getSkipHeaderRecord()) { - this.nextRecord(); + final CSVRecord csvRecord = this.nextRecord(); + headerComment = csvRecord.getComment(); } headerRecord = formatHeader; } @@ -596,7 +601,49 @@ public final class CSVParser implements Iterable, Closeable { public List getHeaderNames() { return Collections.unmodifiableList(headers.headerNames); } - + /** + * Checks whether this parser has a header comment, false otherwise. + * The header comment appears before the header record. + * Note that if the parser's format has been given an explicit header + * (with {@link CSVFormat.Builder#setHeader(String... )} or another overload) + * and the header record is not being skipped + * ({@link CSVFormat.Builder#setSkipHeaderRecord} is false) then any initial comments + * will be associated with the first record, not the header. + * + * @return true if this parser has seen a header comment, false otherwise + */ + public boolean hasHeaderComment() { + return headerComment != null; + } + /** + * Returns the header comment for this parser, if any. + * The header comment appears before the header record. + * + * @return the header comment for this stream, or null if no comment is available. + */ + public String getHeaderComment() { + return headerComment; + } + /** + * Checks whether this parser has seen a trailer comment, false otherwise. + * Trailer comments are located between the last record and EOF. + * The trailer comments will only be available after the parser has + * finished processing this stream. + * + * @return true if this parser has seen a trailer comment, false otherwise + */ + public boolean hasTrailerComment() { + return trailerComment != null; + } + /** + * Returns the trailer comment for this record, if any. + * Trailer comments are located between the last record and EOF + * + * @return the trailer comment for this stream, or null if no comment is available. + */ + public String getTrailerComment() { + return trailerComment; + } /** * Returns the current record number in the input stream. * @@ -713,6 +760,10 @@ public final class CSVParser implements Iterable, Closeable { case EOF: if (this.reusableToken.isReady) { this.addRecordValue(true); + } else { + if (sb != null) { + trailerComment = sb.toString(); + } } break; case INVALID: diff --git a/src/test/java/org/apache/commons/csv/CSVParserTest.java b/src/test/java/org/apache/commons/csv/CSVParserTest.java index 38663a16..aa6c9777 100644 --- a/src/test/java/org/apache/commons/csv/CSVParserTest.java +++ b/src/test/java/org/apache/commons/csv/CSVParserTest.java @@ -1375,4 +1375,128 @@ public class CSVParserTest { parser.close(); } + @Test + public void getHeaderComment() throws IOException { + // File with no header comments + String text_1 = "A,B"+CRLF+"1,2"+CRLF; + // File with a single line header comment + String text_2 = "# comment"+CRLF+"A,B"+CRLF+"1,2"+CRLF; + // File with a multi-line header comment + String text_3 = "# multi-line" + CRLF + "# comment"+CRLF+"A,B"+CRLF+"1,2"+CRLF; + // Format with auto-detected header + CSVFormat format_a = CSVFormat.Builder.create(CSVFormat.DEFAULT).setCommentMarker('#').setHeader().build(); + // Format with explicit header + CSVFormat format_b = CSVFormat.Builder.create(CSVFormat.DEFAULT) + .setSkipHeaderRecord(true) + .setCommentMarker('#') + .setHeader("A","B") + .build(); + // Format with explicit header that does not skip the header line + CSVFormat format_c = CSVFormat.Builder.create(CSVFormat.DEFAULT) + .setCommentMarker('#') + .setHeader("A","B") + .build(); + + try (CSVParser parser = CSVParser.parse(text_1, format_a)) { + parser.getRecords(); + // Expect no header comment + assertFalse(parser.hasHeaderComment()); + assertNull(parser.getHeaderComment()); + } + try (CSVParser parser = CSVParser.parse(text_2, format_a)) { + parser.getRecords(); + // Expect a header comment + assertTrue(parser.hasHeaderComment()); + assertEquals("comment", parser.getHeaderComment()); + } + try (CSVParser parser = CSVParser.parse(text_3, format_a)) { + parser.getRecords(); + // Expect a header comment + assertTrue(parser.hasHeaderComment()); + assertEquals("multi-line"+LF+"comment", parser.getHeaderComment()); + } + try (CSVParser parser = CSVParser.parse(text_1, format_b)) { + parser.getRecords(); + // Expect no header comment + assertFalse(parser.hasHeaderComment()); + assertNull(parser.getHeaderComment()); + } + try (CSVParser parser = CSVParser.parse(text_2, format_b)) { + parser.getRecords(); + // Expect a header comment + assertTrue(parser.hasHeaderComment()); + assertEquals("comment", parser.getHeaderComment()); + } + try (CSVParser parser = CSVParser.parse(text_1, format_c)) { + parser.getRecords(); + // Expect no header comment + assertFalse(parser.hasHeaderComment()); + assertNull(parser.getHeaderComment()); + } + try (CSVParser parser = CSVParser.parse(text_2, format_c)) { + parser.getRecords(); + // Expect no header comment - the text "comment" is attached to the first record + assertFalse(parser.hasHeaderComment()); + assertNull(parser.getHeaderComment()); + } + } + @Test + public void getTrailerComment() throws IOException { + // File with a header comment + String text_1 = "# header comment"+CRLF+"A,B"+CRLF+"1,2"+CRLF; + // File with a single line header and trailer comment + String text_2 = "# header comment"+CRLF+"A,B"+CRLF+"1,2"+CRLF + "# comment"; + // File with a multi-line header and trailer comment + String text_3 = "# multi-line" + CRLF + "# header comment"+CRLF+"A,B"+CRLF+"1,2"+CRLF+"# multi-line"+CRLF+"# comment"; + // Format with auto-detected header + CSVFormat format_a = CSVFormat.Builder.create(CSVFormat.DEFAULT).setCommentMarker('#').setHeader().build(); + // Format with explicit header + CSVFormat format_b = CSVFormat.Builder.create(CSVFormat.DEFAULT) + .setSkipHeaderRecord(true) + .setCommentMarker('#') + .setHeader("A","B") + .build(); + // Format with explicit header that does not skip the header line + CSVFormat format_c = CSVFormat.Builder.create(CSVFormat.DEFAULT) + .setCommentMarker('#') + .setHeader("A","B") + .build(); + + try (CSVParser parser = CSVParser.parse(text_1, format_a)) { + parser.getRecords(); + assertFalse(parser.hasTrailerComment()); + assertNull(parser.getTrailerComment()); + } + try (CSVParser parser = CSVParser.parse(text_2, format_a)) { + parser.getRecords(); + assertTrue(parser.hasTrailerComment()); + assertEquals("comment", parser.getTrailerComment()); + } + try (CSVParser parser = CSVParser.parse(text_3, format_a)) { + parser.getRecords(); + assertTrue(parser.hasTrailerComment()); + assertEquals("multi-line"+LF+"comment", parser.getTrailerComment()); + } + try (CSVParser parser = CSVParser.parse(text_1, format_b)) { + parser.getRecords(); + assertFalse(parser.hasTrailerComment()); + assertNull(parser.getTrailerComment()); + } + try (CSVParser parser = CSVParser.parse(text_2, format_b)) { + parser.getRecords(); + assertTrue(parser.hasTrailerComment()); + assertEquals("comment", parser.getTrailerComment()); + } + try (CSVParser parser = CSVParser.parse(text_1, format_c)) { + parser.getRecords(); + assertFalse(parser.hasTrailerComment()); + assertNull(parser.getTrailerComment()); + } + try (CSVParser parser = CSVParser.parse(text_2, format_c)) { + parser.getRecords(); + assertTrue(parser.hasTrailerComment()); + assertEquals("comment", parser.getTrailerComment()); + } + } + } From 0414d1e4b79a4f42d24c8b9a7547a8cbf4a40cf0 Mon Sep 17 00:00:00 2001 From: Peter Hull Date: Tue, 6 Sep 2022 19:23:45 +0100 Subject: [PATCH 2/7] Split tests into separate methods As requested in code review --- .../org/apache/commons/csv/CSVParserTest.java | 135 ++++++++++-------- 1 file changed, 77 insertions(+), 58 deletions(-) diff --git a/src/test/java/org/apache/commons/csv/CSVParserTest.java b/src/test/java/org/apache/commons/csv/CSVParserTest.java index aa6c9777..7d2dfbdb 100644 --- a/src/test/java/org/apache/commons/csv/CSVParserTest.java +++ b/src/test/java/org/apache/commons/csv/CSVParserTest.java @@ -1375,124 +1375,143 @@ public class CSVParserTest { parser.close(); } + // CSV with no header comments + static private final String CSV_INPUT_NO_COMMENT = "A,B"+CRLF+"1,2"+CRLF; + // CSV with a header comment + static private final String CSV_INPUT_HEADER_COMMENT = "# header comment" + CRLF + "A,B" + CRLF + "1,2" + CRLF; + // CSV with a single line header and trailer comment + static private final String CSV_INPUT_HEADER_TRAILER_COMMENT = "# header comment" + CRLF + "A,B" + CRLF + "1,2" + CRLF + "# comment"; + // CSV with a multi-line header and trailer comment + 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"; + // Format with auto-detected header + static private final CSVFormat FORMAT_AUTO_HEADER = CSVFormat.Builder.create(CSVFormat.DEFAULT).setCommentMarker('#').setHeader().build(); + // Format with explicit header + static private final CSVFormat FORMAT_EXPLICIT_HEADER = CSVFormat.Builder.create(CSVFormat.DEFAULT) + .setSkipHeaderRecord(true) + .setCommentMarker('#') + .setHeader("A", "B") + .build(); + // Format with explicit header that does not skip the header line + CSVFormat FORMAT_EXPLICIT_HEADER_NOSKIP = CSVFormat.Builder.create(CSVFormat.DEFAULT) + .setCommentMarker('#') + .setHeader("A", "B") + .build(); @Test - public void getHeaderComment() throws IOException { - // File with no header comments - String text_1 = "A,B"+CRLF+"1,2"+CRLF; - // File with a single line header comment - String text_2 = "# comment"+CRLF+"A,B"+CRLF+"1,2"+CRLF; - // File with a multi-line header comment - String text_3 = "# multi-line" + CRLF + "# comment"+CRLF+"A,B"+CRLF+"1,2"+CRLF; - // Format with auto-detected header - CSVFormat format_a = CSVFormat.Builder.create(CSVFormat.DEFAULT).setCommentMarker('#').setHeader().build(); - // Format with explicit header - CSVFormat format_b = CSVFormat.Builder.create(CSVFormat.DEFAULT) - .setSkipHeaderRecord(true) - .setCommentMarker('#') - .setHeader("A","B") - .build(); - // Format with explicit header that does not skip the header line - CSVFormat format_c = CSVFormat.Builder.create(CSVFormat.DEFAULT) - .setCommentMarker('#') - .setHeader("A","B") - .build(); + public void testGetHeaderComment_NoComment1() throws IOException { - try (CSVParser parser = CSVParser.parse(text_1, format_a)) { + try (CSVParser parser = CSVParser.parse(CSV_INPUT_NO_COMMENT, FORMAT_AUTO_HEADER)) { parser.getRecords(); // Expect no header comment assertFalse(parser.hasHeaderComment()); assertNull(parser.getHeaderComment()); } - try (CSVParser parser = CSVParser.parse(text_2, format_a)) { + } + @Test + public void testGetHeaderComment_HeaderComment1() throws IOException { + try (CSVParser parser = CSVParser.parse(CSV_INPUT_HEADER_COMMENT, FORMAT_AUTO_HEADER)) { parser.getRecords(); // Expect a header comment assertTrue(parser.hasHeaderComment()); - assertEquals("comment", parser.getHeaderComment()); + assertEquals("header comment", parser.getHeaderComment()); } - try (CSVParser parser = CSVParser.parse(text_3, format_a)) { + } + @Test + public void testGetHeaderComment_HeaderTrailerComment() throws IOException { + try (CSVParser parser = CSVParser.parse(CSV_INPUT_MULTILINE_HEADER_TRAILER_COMMENT, FORMAT_AUTO_HEADER)) { parser.getRecords(); // Expect a header comment assertTrue(parser.hasHeaderComment()); - assertEquals("multi-line"+LF+"comment", parser.getHeaderComment()); + assertEquals("multi-line"+LF+"header comment", parser.getHeaderComment()); } - try (CSVParser parser = CSVParser.parse(text_1, format_b)) { + } + @Test + public void testGetHeaderComment_NoComment2() throws IOException { + try (CSVParser parser = CSVParser.parse(CSV_INPUT_NO_COMMENT, FORMAT_EXPLICIT_HEADER)) { parser.getRecords(); // Expect no header comment assertFalse(parser.hasHeaderComment()); assertNull(parser.getHeaderComment()); } - try (CSVParser parser = CSVParser.parse(text_2, format_b)) { + } + @Test + public void testGetHeaderComment_HeaderComment2() throws IOException { + try (CSVParser parser = CSVParser.parse(CSV_INPUT_HEADER_COMMENT, FORMAT_EXPLICIT_HEADER)) { parser.getRecords(); // Expect a header comment assertTrue(parser.hasHeaderComment()); - assertEquals("comment", parser.getHeaderComment()); + assertEquals("header comment", parser.getHeaderComment()); } - try (CSVParser parser = CSVParser.parse(text_1, format_c)) { + } + @Test + public void testGetHeaderComment_NoComment3() throws IOException { + try (CSVParser parser = CSVParser.parse(CSV_INPUT_NO_COMMENT, FORMAT_EXPLICIT_HEADER_NOSKIP)) { parser.getRecords(); // Expect no header comment assertFalse(parser.hasHeaderComment()); assertNull(parser.getHeaderComment()); } - try (CSVParser parser = CSVParser.parse(text_2, format_c)) { + } + @Test + public void testGetHeaderComment_HeaderComment3() throws IOException { + try (CSVParser parser = CSVParser.parse(CSV_INPUT_HEADER_COMMENT, FORMAT_EXPLICIT_HEADER_NOSKIP)) { parser.getRecords(); // Expect no header comment - the text "comment" is attached to the first record assertFalse(parser.hasHeaderComment()); assertNull(parser.getHeaderComment()); } } - @Test - public void getTrailerComment() throws IOException { - // File with a header comment - String text_1 = "# header comment"+CRLF+"A,B"+CRLF+"1,2"+CRLF; - // File with a single line header and trailer comment - String text_2 = "# header comment"+CRLF+"A,B"+CRLF+"1,2"+CRLF + "# comment"; - // File with a multi-line header and trailer comment - String text_3 = "# multi-line" + CRLF + "# header comment"+CRLF+"A,B"+CRLF+"1,2"+CRLF+"# multi-line"+CRLF+"# comment"; - // Format with auto-detected header - CSVFormat format_a = CSVFormat.Builder.create(CSVFormat.DEFAULT).setCommentMarker('#').setHeader().build(); - // Format with explicit header - CSVFormat format_b = CSVFormat.Builder.create(CSVFormat.DEFAULT) - .setSkipHeaderRecord(true) - .setCommentMarker('#') - .setHeader("A","B") - .build(); - // Format with explicit header that does not skip the header line - CSVFormat format_c = CSVFormat.Builder.create(CSVFormat.DEFAULT) - .setCommentMarker('#') - .setHeader("A","B") - .build(); - try (CSVParser parser = CSVParser.parse(text_1, format_a)) { + @Test + public void testGetTrailerComment_HeaderComment1() throws IOException { + try (CSVParser parser = CSVParser.parse(CSV_INPUT_HEADER_COMMENT, FORMAT_AUTO_HEADER)) { parser.getRecords(); assertFalse(parser.hasTrailerComment()); assertNull(parser.getTrailerComment()); } - try (CSVParser parser = CSVParser.parse(text_2, format_a)) { + } + @Test + public void testGetTrailerComment_HeaderTrailerComment1() throws IOException { + try (CSVParser parser = CSVParser.parse(CSV_INPUT_HEADER_TRAILER_COMMENT, FORMAT_AUTO_HEADER)) { parser.getRecords(); assertTrue(parser.hasTrailerComment()); assertEquals("comment", parser.getTrailerComment()); } - try (CSVParser parser = CSVParser.parse(text_3, format_a)) { + } + @Test + public void testGetTrailerComment_MultilineComment() throws IOException { + try (CSVParser parser = CSVParser.parse(CSV_INPUT_MULTILINE_HEADER_TRAILER_COMMENT, FORMAT_AUTO_HEADER)) { parser.getRecords(); assertTrue(parser.hasTrailerComment()); assertEquals("multi-line"+LF+"comment", parser.getTrailerComment()); } - try (CSVParser parser = CSVParser.parse(text_1, format_b)) { + } + @Test + public void testGetTrailerComment_HeaderComment2() throws IOException { + try (CSVParser parser = CSVParser.parse(CSV_INPUT_HEADER_COMMENT, FORMAT_EXPLICIT_HEADER)) { parser.getRecords(); assertFalse(parser.hasTrailerComment()); assertNull(parser.getTrailerComment()); } - try (CSVParser parser = CSVParser.parse(text_2, format_b)) { + } + @Test + public void testGetTrailerComment_HeaderTrailerComment2() throws IOException { + try (CSVParser parser = CSVParser.parse(CSV_INPUT_HEADER_TRAILER_COMMENT, FORMAT_EXPLICIT_HEADER)) { parser.getRecords(); assertTrue(parser.hasTrailerComment()); assertEquals("comment", parser.getTrailerComment()); } - try (CSVParser parser = CSVParser.parse(text_1, format_c)) { + } + @Test + public void testGetTrailerComment_HeaderComment3() throws IOException { + try (CSVParser parser = CSVParser.parse(CSV_INPUT_HEADER_COMMENT, FORMAT_EXPLICIT_HEADER_NOSKIP)) { parser.getRecords(); assertFalse(parser.hasTrailerComment()); assertNull(parser.getTrailerComment()); } - try (CSVParser parser = CSVParser.parse(text_2, format_c)) { + } + @Test + public void testGetTrailerComment_HeaderTrailerComment3() throws IOException { + try (CSVParser parser = CSVParser.parse(CSV_INPUT_HEADER_TRAILER_COMMENT, FORMAT_EXPLICIT_HEADER_NOSKIP)) { parser.getRecords(); assertTrue(parser.hasTrailerComment()); assertEquals("comment", parser.getTrailerComment()); From 82ca0355d426bf022f54c3a7cc0733416fbeb482 Mon Sep 17 00:00:00 2001 From: Peter Hull Date: Wed, 7 Sep 2022 08:58:29 +0100 Subject: [PATCH 3/7] Add @since annotation to javadoc Also reworded some documentation text to read better --- src/main/java/org/apache/commons/csv/CSVParser.java | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/apache/commons/csv/CSVParser.java b/src/main/java/org/apache/commons/csv/CSVParser.java index 3385f418..e06b95f1 100644 --- a/src/main/java/org/apache/commons/csv/CSVParser.java +++ b/src/main/java/org/apache/commons/csv/CSVParser.java @@ -602,7 +602,7 @@ public final class CSVParser implements Iterable, Closeable { return Collections.unmodifiableList(headers.headerNames); } /** - * Checks whether this parser has a header comment, false otherwise. + * Checks whether there is a header comment. * The header comment appears before the header record. * Note that if the parser's format has been given an explicit header * (with {@link CSVFormat.Builder#setHeader(String... )} or another overload) @@ -611,35 +611,39 @@ public final class CSVParser implements Iterable, Closeable { * will be associated with the first record, not the header. * * @return true if this parser has seen a header comment, false otherwise + * @since 1.10.0 */ public boolean hasHeaderComment() { return headerComment != null; } /** - * Returns the header comment for this parser, if any. + * Returns the header comment, if any. * The header comment appears before the header record. * * @return the header comment for this stream, or null if no comment is available. + * @since 1.10.0 */ public String getHeaderComment() { return headerComment; } /** - * Checks whether this parser has seen a trailer comment, false otherwise. + * Checks whether there is a trailer comment. * Trailer comments are located between the last record and EOF. * The trailer comments will only be available after the parser has * finished processing this stream. * * @return true if this parser has seen a trailer comment, false otherwise + * @since 1.10.0 */ public boolean hasTrailerComment() { return trailerComment != null; } /** - * Returns the trailer comment for this record, if any. + * Returns the trailer comment, if any. * Trailer comments are located between the last record and EOF * * @return the trailer comment for this stream, or null if no comment is available. + * @since 1.10.0 */ public String getTrailerComment() { return trailerComment; From 9c702bd405d3d246f2d6d7e0f2f07f9753cc8373 Mon Sep 17 00:00:00 2001 From: Peter Hull Date: Thu, 8 Sep 2022 21:54:29 +0100 Subject: [PATCH 4/7] Guard against NPE in createHeaders Also, formatting and whitespace changes as requested in code review. --- src/main/java/org/apache/commons/csv/CSVParser.java | 11 ++++++----- .../java/org/apache/commons/csv/CSVParserTest.java | 13 +++++++++++++ 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/apache/commons/csv/CSVParser.java b/src/main/java/org/apache/commons/csv/CSVParser.java index e06b95f1..94e1dd04 100644 --- a/src/main/java/org/apache/commons/csv/CSVParser.java +++ b/src/main/java/org/apache/commons/csv/CSVParser.java @@ -352,6 +352,7 @@ public final class CSVParser implements Iterable, Closeable { return new CSVParser(new InputStreamReader(url.openStream(), charset), format); } + private String headerComment; private String trailerComment; @@ -487,8 +488,10 @@ public final class CSVParser implements Iterable, Closeable { } } else { if (this.format.getSkipHeaderRecord()) { - final CSVRecord csvRecord = this.nextRecord(); - headerComment = csvRecord.getComment(); + final CSVRecord nextRecord = this.nextRecord(); + if (nextRecord != null) { + headerComment = nextRecord.getComment(); + } } headerRecord = formatHeader; } @@ -764,10 +767,8 @@ public final class CSVParser implements Iterable, Closeable { case EOF: if (this.reusableToken.isReady) { this.addRecordValue(true); - } else { - if (sb != null) { + } else if (sb != null) { trailerComment = sb.toString(); - } } break; case INVALID: diff --git a/src/test/java/org/apache/commons/csv/CSVParserTest.java b/src/test/java/org/apache/commons/csv/CSVParserTest.java index 7d2dfbdb..46522b11 100644 --- a/src/test/java/org/apache/commons/csv/CSVParserTest.java +++ b/src/test/java/org/apache/commons/csv/CSVParserTest.java @@ -1396,6 +1396,7 @@ public class CSVParserTest { .setCommentMarker('#') .setHeader("A", "B") .build(); + @Test public void testGetHeaderComment_NoComment1() throws IOException { @@ -1406,6 +1407,7 @@ public class CSVParserTest { assertNull(parser.getHeaderComment()); } } + @Test public void testGetHeaderComment_HeaderComment1() throws IOException { try (CSVParser parser = CSVParser.parse(CSV_INPUT_HEADER_COMMENT, FORMAT_AUTO_HEADER)) { @@ -1415,6 +1417,7 @@ public class CSVParserTest { assertEquals("header comment", parser.getHeaderComment()); } } + @Test public void testGetHeaderComment_HeaderTrailerComment() throws IOException { try (CSVParser parser = CSVParser.parse(CSV_INPUT_MULTILINE_HEADER_TRAILER_COMMENT, FORMAT_AUTO_HEADER)) { @@ -1424,6 +1427,7 @@ public class CSVParserTest { assertEquals("multi-line"+LF+"header comment", parser.getHeaderComment()); } } + @Test public void testGetHeaderComment_NoComment2() throws IOException { try (CSVParser parser = CSVParser.parse(CSV_INPUT_NO_COMMENT, FORMAT_EXPLICIT_HEADER)) { @@ -1433,6 +1437,7 @@ public class CSVParserTest { assertNull(parser.getHeaderComment()); } } + @Test public void testGetHeaderComment_HeaderComment2() throws IOException { try (CSVParser parser = CSVParser.parse(CSV_INPUT_HEADER_COMMENT, FORMAT_EXPLICIT_HEADER)) { @@ -1442,6 +1447,7 @@ public class CSVParserTest { assertEquals("header comment", parser.getHeaderComment()); } } + @Test public void testGetHeaderComment_NoComment3() throws IOException { try (CSVParser parser = CSVParser.parse(CSV_INPUT_NO_COMMENT, FORMAT_EXPLICIT_HEADER_NOSKIP)) { @@ -1451,6 +1457,7 @@ public class CSVParserTest { assertNull(parser.getHeaderComment()); } } + @Test public void testGetHeaderComment_HeaderComment3() throws IOException { try (CSVParser parser = CSVParser.parse(CSV_INPUT_HEADER_COMMENT, FORMAT_EXPLICIT_HEADER_NOSKIP)) { @@ -1469,6 +1476,7 @@ public class CSVParserTest { assertNull(parser.getTrailerComment()); } } + @Test public void testGetTrailerComment_HeaderTrailerComment1() throws IOException { try (CSVParser parser = CSVParser.parse(CSV_INPUT_HEADER_TRAILER_COMMENT, FORMAT_AUTO_HEADER)) { @@ -1477,6 +1485,7 @@ public class CSVParserTest { assertEquals("comment", parser.getTrailerComment()); } } + @Test public void testGetTrailerComment_MultilineComment() throws IOException { try (CSVParser parser = CSVParser.parse(CSV_INPUT_MULTILINE_HEADER_TRAILER_COMMENT, FORMAT_AUTO_HEADER)) { @@ -1485,6 +1494,7 @@ public class CSVParserTest { assertEquals("multi-line"+LF+"comment", parser.getTrailerComment()); } } + @Test public void testGetTrailerComment_HeaderComment2() throws IOException { try (CSVParser parser = CSVParser.parse(CSV_INPUT_HEADER_COMMENT, FORMAT_EXPLICIT_HEADER)) { @@ -1493,6 +1503,7 @@ public class CSVParserTest { assertNull(parser.getTrailerComment()); } } + @Test public void testGetTrailerComment_HeaderTrailerComment2() throws IOException { try (CSVParser parser = CSVParser.parse(CSV_INPUT_HEADER_TRAILER_COMMENT, FORMAT_EXPLICIT_HEADER)) { @@ -1501,6 +1512,7 @@ public class CSVParserTest { assertEquals("comment", parser.getTrailerComment()); } } + @Test public void testGetTrailerComment_HeaderComment3() throws IOException { try (CSVParser parser = CSVParser.parse(CSV_INPUT_HEADER_COMMENT, FORMAT_EXPLICIT_HEADER_NOSKIP)) { @@ -1509,6 +1521,7 @@ public class CSVParserTest { assertNull(parser.getTrailerComment()); } } + @Test public void testGetTrailerComment_HeaderTrailerComment3() throws IOException { try (CSVParser parser = CSVParser.parse(CSV_INPUT_HEADER_TRAILER_COMMENT, FORMAT_EXPLICIT_HEADER_NOSKIP)) { From 882884f28a5c7e51bcd9b3b1f24aed7366fc9511 Mon Sep 17 00:00:00 2001 From: Gary Gregory Date: Mon, 12 Sep 2022 09:34:15 -0700 Subject: [PATCH 5/7] Fix formatting --- src/main/java/org/apache/commons/csv/CSVParser.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/java/org/apache/commons/csv/CSVParser.java b/src/main/java/org/apache/commons/csv/CSVParser.java index 94e1dd04..3c681bd3 100644 --- a/src/main/java/org/apache/commons/csv/CSVParser.java +++ b/src/main/java/org/apache/commons/csv/CSVParser.java @@ -604,6 +604,7 @@ public final class CSVParser implements Iterable, Closeable { public List getHeaderNames() { return Collections.unmodifiableList(headers.headerNames); } + /** * Checks whether there is a header comment. * The header comment appears before the header record. @@ -619,6 +620,7 @@ public final class CSVParser implements Iterable, Closeable { public boolean hasHeaderComment() { return headerComment != null; } + /** * Returns the header comment, if any. * The header comment appears before the header record. @@ -629,6 +631,7 @@ public final class CSVParser implements Iterable, Closeable { public String getHeaderComment() { return headerComment; } + /** * Checks whether there is a trailer comment. * Trailer comments are located between the last record and EOF. @@ -641,6 +644,7 @@ public final class CSVParser implements Iterable, Closeable { public boolean hasTrailerComment() { return trailerComment != null; } + /** * Returns the trailer comment, if any. * Trailer comments are located between the last record and EOF @@ -651,6 +655,7 @@ public final class CSVParser implements Iterable, Closeable { public String getTrailerComment() { return trailerComment; } + /** * Returns the current record number in the input stream. * From f926a300a1e7edc223a482ee8793208bff2aa5f5 Mon Sep 17 00:00:00 2001 From: Gary Gregory Date: Mon, 12 Sep 2022 09:36:27 -0700 Subject: [PATCH 6/7] Fix formatting --- src/main/java/org/apache/commons/csv/CSVParser.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/apache/commons/csv/CSVParser.java b/src/main/java/org/apache/commons/csv/CSVParser.java index 3c681bd3..d92600c1 100644 --- a/src/main/java/org/apache/commons/csv/CSVParser.java +++ b/src/main/java/org/apache/commons/csv/CSVParser.java @@ -773,7 +773,7 @@ public final class CSVParser implements Iterable, Closeable { if (this.reusableToken.isReady) { this.addRecordValue(true); } else if (sb != null) { - trailerComment = sb.toString(); + trailerComment = sb.toString(); } break; case INVALID: From ee7b3a7d34955fecbe829de472d39ec9d3a9b70a Mon Sep 17 00:00:00 2001 From: Gary Gregory Date: Mon, 12 Sep 2022 09:38:15 -0700 Subject: [PATCH 7/7] Fix formatting --- src/test/java/org/apache/commons/csv/CSVParserTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/org/apache/commons/csv/CSVParserTest.java b/src/test/java/org/apache/commons/csv/CSVParserTest.java index 46522b11..2aa9f607 100644 --- a/src/test/java/org/apache/commons/csv/CSVParserTest.java +++ b/src/test/java/org/apache/commons/csv/CSVParserTest.java @@ -1375,6 +1375,7 @@ public class CSVParserTest { parser.close(); } + // CSV with no header comments static private final String CSV_INPUT_NO_COMMENT = "A,B"+CRLF+"1,2"+CRLF; // CSV with a header comment @@ -1399,7 +1400,6 @@ public class CSVParserTest { @Test public void testGetHeaderComment_NoComment1() throws IOException { - try (CSVParser parser = CSVParser.parse(CSV_INPUT_NO_COMMENT, FORMAT_AUTO_HEADER)) { parser.getRecords(); // Expect no header comment