Change CSVFormat#Iterable<CSVRecord> parse(final Reader in) to return a CSVParser, which is compatible since CSVParser implements Iterable<CSVRecord>. This allows a caller to end the parsing by calling CSVParser#close() or to use CSVParser in a Java 7 try-with-resources, without tracking a reader or input stream.

git-svn-id: https://svn.apache.org/repos/asf/commons/proper/csv/trunk@1508509 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Gary D. Gregory 2013-07-30 16:22:07 +00:00
parent 7af334d7d5
commit f78b5a14ca
5 changed files with 72 additions and 21 deletions

View File

@ -454,11 +454,11 @@ public class CSVFormat implements Serializable {
* *
* @param in * @param in
* the input stream * the input stream
* @return a stream of CSVRecord * @return a parser over a stream of {@link #CSVRecord}s.
* @throws IOException * @throws IOException
* If an I/O error occurs * If an I/O error occurs
*/ */
public Iterable<CSVRecord> parse(final Reader in) throws IOException { public CSVParser parse(final Reader in) throws IOException {
return new CSVParser(in, this); return new CSVParser(in, this);
} }

View File

@ -236,6 +236,9 @@ public class CSVParser implements Iterable<CSVRecord>, Closeable {
/** /**
* Closes resources. * Closes resources.
*
* @throws IOException
* If an I/O error occurs
*/ */
public void close() throws IOException { public void close() throws IOException {
if (lexer != null) { if (lexer != null) {
@ -309,6 +312,9 @@ public class CSVParser implements Iterable<CSVRecord>, Closeable {
} }
public boolean hasNext() { public boolean hasNext() {
if (isClosed()) {
return false;
}
if (current == null) { if (current == null) {
current = getNextRecord(); current = getNextRecord();
} }
@ -317,6 +323,9 @@ public class CSVParser implements Iterable<CSVRecord>, Closeable {
} }
public CSVRecord next() { public CSVRecord next() {
if (isClosed()) {
return null;
}
CSVRecord next = current; CSVRecord next = current;
current = null; current = null;
@ -337,4 +346,8 @@ public class CSVParser implements Iterable<CSVRecord>, Closeable {
}; };
} }
public boolean isClosed() {
return lexer.isClosed();
}
} }

View File

@ -42,6 +42,8 @@ final class ExtendedBufferedReader extends BufferedReader {
/** The count of EOLs (CR/LF/CRLF) seen so far */ /** The count of EOLs (CR/LF/CRLF) seen so far */
private long eolCounter = 0; private long eolCounter = 0;
private boolean closed;
/** /**
* Created extended buffered reader using default buffer-size * 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 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();
}
} }

View File

@ -148,6 +148,10 @@ abstract class Lexer implements Closeable {
abstract Token nextToken(Token reusableToken) throws IOException; abstract Token nextToken(Token reusableToken) throws IOException;
boolean isClosed() {
return in.isClosed();
}
/** /**
* @return true if the given char is a whitespace character * @return true if the given char is a whitespace character
*/ */
@ -197,10 +201,11 @@ abstract class Lexer implements Closeable {
/** /**
* Closes resources. * Closes resources.
*
* @throws IOException
* If an I/O error occurs
*/ */
public void close() throws IOException { public void close() throws IOException {
if (in != null) {
in.close(); in.close();
} }
}
} }

View File

@ -39,7 +39,6 @@ import java.util.Map;
import java.util.NoSuchElementException; import java.util.NoSuchElementException;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Ignore; import org.junit.Ignore;
import org.junit.Test; import org.junit.Test;
@ -395,6 +394,19 @@ public class CSVParserTest {
assertEquals(4, records.size()); 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<CSVRecord> records = parser.iterator();
assertTrue(records.hasNext());
parser.close();
assertFalse(records.hasNext());
assertNull(records.next());
assertFalse(records.hasNext());
assertNull(records.next());
}
@Test @Test
public void testCarriageReturnEndings() throws IOException { public void testCarriageReturnEndings() throws IOException {
final String code = "foo\rbaar,\rhello,world\r,kanu"; final String code = "foo\rbaar,\rhello,world\r,kanu";
@ -605,22 +617,22 @@ public class CSVParserTest {
@Test @Test
public void testGetLineNumberWithLF() throws Exception { public void testGetLineNumberWithLF() throws Exception {
validateLineNumbers(String.valueOf(LF)); this.validateLineNumbers(String.valueOf(LF));
} }
@Test @Test
public void testGetLineNumberWithCRLF() throws Exception { public void testGetLineNumberWithCRLF() throws Exception {
validateLineNumbers(CRLF); this.validateLineNumbers(CRLF);
} }
@Test @Test
public void testGetLineNumberWithCR() throws Exception { public void testGetLineNumberWithCR() throws Exception {
validateLineNumbers(String.valueOf(CR)); this.validateLineNumbers(String.valueOf(CR));
} }
@Test @Test
public void testGetRecordNumberWithLF() throws Exception { public void testGetRecordNumberWithLF() throws Exception {
validateRecordNumbers(String.valueOf(LF)); this.validateRecordNumbers(String.valueOf(LF));
} }
@Test @Test
@ -649,17 +661,17 @@ public class CSVParserTest {
@Test @Test
public void testGetRecordNumberWithCRLF() throws Exception { public void testGetRecordNumberWithCRLF() throws Exception {
validateRecordNumbers(CRLF); this.validateRecordNumbers(CRLF);
} }
@Test @Test
public void testGetRecordNumberWithCR() throws Exception { public void testGetRecordNumberWithCR() throws Exception {
validateRecordNumbers(String.valueOf(CR)); this.validateRecordNumbers(String.valueOf(CR));
} }
@Test(expected = IllegalArgumentException.class) @Test(expected = IllegalArgumentException.class)
public void testInvalidFormat() throws Exception { public void testInvalidFormat() throws Exception {
CSVFormat invalidFormat = CSVFormat.DEFAULT.withDelimiter(CR); final CSVFormat invalidFormat = CSVFormat.DEFAULT.withDelimiter(CR);
new CSVParser((Reader) null, invalidFormat); new CSVParser((Reader) null, invalidFormat);
} }