diff --git a/src/main/java/org/apache/commons/csv/CSVFormat.java b/src/main/java/org/apache/commons/csv/CSVFormat.java index dbd9e6e2..f0e8b561 100644 --- a/src/main/java/org/apache/commons/csv/CSVFormat.java +++ b/src/main/java/org/apache/commons/csv/CSVFormat.java @@ -449,16 +449,16 @@ public class CSVFormat implements Serializable { return quoteChar != null; } - /** - * Parses the specified content. - * - * @param in - * the input stream - * @return a stream of CSVRecord - * @throws IOException - * If an I/O error occurs - */ - public Iterable parse(final Reader in) throws IOException { + /** + * Parses the specified content. + * + * @param in + * the input stream + * @return a parser over a stream of {@link #CSVRecord}s. + * @throws IOException + * If an I/O error occurs + */ + public CSVParser parse(final Reader in) throws IOException { return new CSVParser(in, this); } diff --git a/src/main/java/org/apache/commons/csv/CSVParser.java b/src/main/java/org/apache/commons/csv/CSVParser.java index 0d415aad..75155bd3 100644 --- a/src/main/java/org/apache/commons/csv/CSVParser.java +++ b/src/main/java/org/apache/commons/csv/CSVParser.java @@ -236,6 +236,9 @@ public class CSVParser implements Iterable, Closeable { /** * Closes resources. + * + * @throws IOException + * If an I/O error occurs */ public void close() throws IOException { if (lexer != null) { @@ -309,6 +312,9 @@ public class CSVParser implements Iterable, Closeable { } public boolean hasNext() { + if (isClosed()) { + return false; + } if (current == null) { current = getNextRecord(); } @@ -317,6 +323,9 @@ public class CSVParser implements Iterable, Closeable { } public CSVRecord next() { + if (isClosed()) { + return null; + } CSVRecord next = current; current = null; @@ -337,4 +346,8 @@ public class CSVParser implements Iterable, Closeable { }; } + public boolean isClosed() { + return lexer.isClosed(); + } + } diff --git a/src/main/java/org/apache/commons/csv/ExtendedBufferedReader.java b/src/main/java/org/apache/commons/csv/ExtendedBufferedReader.java index 278ec298..d6614dd3 100644 --- a/src/main/java/org/apache/commons/csv/ExtendedBufferedReader.java +++ b/src/main/java/org/apache/commons/csv/ExtendedBufferedReader.java @@ -41,6 +41,8 @@ final class ExtendedBufferedReader extends BufferedReader { /** The count of EOLs (CR/LF/CRLF) seen so far */ private long eolCounter = 0; + + private boolean closed; /** * Created extended buffered reader using default buffer-size @@ -154,4 +156,23 @@ final class ExtendedBufferedReader extends BufferedReader { } return eolCounter + 1; // Allow for counter being incremented only at EOL } + + public boolean isClosed() { + return closed; + } + + /** + * Closes the stream. + * + * @throws IOException + * If an I/O error occurs + */ + @Override + public void close() throws IOException { + // Set ivars before calling super close() in case close() throws an IOException. + closed = true; + lastChar = END_OF_STREAM; + super.close(); + } + } diff --git a/src/main/java/org/apache/commons/csv/Lexer.java b/src/main/java/org/apache/commons/csv/Lexer.java index 0e37e050..95bd6e72 100644 --- a/src/main/java/org/apache/commons/csv/Lexer.java +++ b/src/main/java/org/apache/commons/csv/Lexer.java @@ -148,6 +148,10 @@ abstract class Lexer implements Closeable { abstract Token nextToken(Token reusableToken) throws IOException; + boolean isClosed() { + return in.isClosed(); + } + /** * @return true if the given char is a whitespace character */ @@ -197,10 +201,11 @@ abstract class Lexer implements Closeable { /** * Closes resources. + * + * @throws IOException + * If an I/O error occurs */ public void close() throws IOException { - if (in != null) { - in.close(); - } + in.close(); } } diff --git a/src/test/java/org/apache/commons/csv/CSVParserTest.java b/src/test/java/org/apache/commons/csv/CSVParserTest.java index ab4438e9..e694ec4a 100644 --- a/src/test/java/org/apache/commons/csv/CSVParserTest.java +++ b/src/test/java/org/apache/commons/csv/CSVParserTest.java @@ -39,7 +39,6 @@ import java.util.Map; import java.util.NoSuchElementException; import org.junit.Assert; - import org.junit.Ignore; import org.junit.Test; @@ -395,6 +394,19 @@ public class CSVParserTest { assertEquals(4, records.size()); } + @Test + public void testClose() throws Exception { + final Reader in = new StringReader("# comment\na,b,c\n1,2,3\nx,y,z"); + final CSVParser parser = CSVFormat.DEFAULT.withCommentStart('#').withHeader().parse(in); + final Iterator records = parser.iterator(); + assertTrue(records.hasNext()); + parser.close(); + assertFalse(records.hasNext()); + assertNull(records.next()); + assertFalse(records.hasNext()); + assertNull(records.next()); + } + @Test public void testCarriageReturnEndings() throws IOException { final String code = "foo\rbaar,\rhello,world\r,kanu"; @@ -605,22 +617,22 @@ public class CSVParserTest { @Test public void testGetLineNumberWithLF() throws Exception { - validateLineNumbers(String.valueOf(LF)); + this.validateLineNumbers(String.valueOf(LF)); } @Test public void testGetLineNumberWithCRLF() throws Exception { - validateLineNumbers(CRLF); + this.validateLineNumbers(CRLF); } @Test public void testGetLineNumberWithCR() throws Exception { - validateLineNumbers(String.valueOf(CR)); + this.validateLineNumbers(String.valueOf(CR)); } @Test public void testGetRecordNumberWithLF() throws Exception { - validateRecordNumbers(String.valueOf(LF)); + this.validateRecordNumbers(String.valueOf(LF)); } @Test @@ -649,17 +661,17 @@ public class CSVParserTest { @Test public void testGetRecordNumberWithCRLF() throws Exception { - validateRecordNumbers(CRLF); + this.validateRecordNumbers(CRLF); } @Test public void testGetRecordNumberWithCR() throws Exception { - validateRecordNumbers(String.valueOf(CR)); + this.validateRecordNumbers(String.valueOf(CR)); } @Test(expected = IllegalArgumentException.class) public void testInvalidFormat() throws Exception { - CSVFormat invalidFormat = CSVFormat.DEFAULT.withDelimiter(CR); + final CSVFormat invalidFormat = CSVFormat.DEFAULT.withDelimiter(CR); new CSVParser((Reader) null, invalidFormat); }