diff --git a/src/main/java/htsjdk/tribble/readers/AsciiLineReader.java b/src/main/java/htsjdk/tribble/readers/AsciiLineReader.java index ad66a17c7..39c7d409c 100644 --- a/src/main/java/htsjdk/tribble/readers/AsciiLineReader.java +++ b/src/main/java/htsjdk/tribble/readers/AsciiLineReader.java @@ -40,8 +40,9 @@ private static final byte LINEFEED = (byte) ('\n' & 0xff); private static final byte CARRIAGE_RETURN = (byte) ('\r' & 0xff); - PositionalBufferedStream is; - char[] lineBuffer; + private final PositionalBufferedStream is; + private char[] lineBuffer; + private int lineTerminatorLength = -1; public AsciiLineReader(final InputStream is){ this(new PositionalBufferedStream(is)); @@ -65,6 +66,16 @@ public long getPosition(){ return is.getPosition(); } + /** Returns the length of the line terminator read after the last read line. Returns either: + * -1 if no line has been read + * 0 after the last line if the last line in the file had no CR or LF line ending + * 1 if the line ended with CR or LF + * 2 if the line ended with CR and LF + */ + public int getLineTerminatorLength() { + return this.lineTerminatorLength; + } + /** * Read a line of text. A line is considered to be terminated by any one * of a line feed ('\n'), a carriage return ('\r'), or a carriage return @@ -83,6 +94,7 @@ public final String readLine(final PositionalBufferedStream stream) throws IOExc if (b == -1) { // eof reached. Return the last line, or null if this is a new line if (linePosition > 0) { + this.lineTerminatorLength = 0; return new String(lineBuffer, 0, linePosition); } else { return null; @@ -93,6 +105,10 @@ public final String readLine(final PositionalBufferedStream stream) throws IOExc if (c == LINEFEED || c == CARRIAGE_RETURN) { if (c == CARRIAGE_RETURN && stream.peek() == LINEFEED) { stream.read(); // <= skip the trailing \n in case of \r\n termination + this.lineTerminatorLength = 2; + } + else { + this.lineTerminatorLength = 1; } return new String(lineBuffer, 0, linePosition); diff --git a/src/test/java/htsjdk/tribble/readers/AsciiLineReaderTest.java b/src/test/java/htsjdk/tribble/readers/AsciiLineReaderTest.java index b4aa6ba2c..b0a8de371 100644 --- a/src/test/java/htsjdk/tribble/readers/AsciiLineReaderTest.java +++ b/src/test/java/htsjdk/tribble/readers/AsciiLineReaderTest.java @@ -2,10 +2,10 @@ import htsjdk.HtsjdkTest; import htsjdk.tribble.TestUtils; -import org.testng.annotations.AfterMethod; -import org.testng.annotations.BeforeMethod; +import org.testng.Assert; import org.testng.annotations.Test; +import java.io.ByteArrayInputStream; import java.io.FileInputStream; import java.io.InputStream; @@ -40,4 +40,32 @@ public void testReadLines() throws Exception { assertEquals(expectedNumber, actualLines); } + + @Test public void voidTestLineEndingLength() throws Exception { + final String input = "Hello\nThis\rIs A Silly Test\r\nSo There"; + final InputStream is = new ByteArrayInputStream(input.getBytes()); + final AsciiLineReader in = new AsciiLineReader(is); + + Assert.assertEquals(in.getLineTerminatorLength(), -1); + Assert.assertEquals(in.readLine(), "Hello"); + Assert.assertEquals(in.getLineTerminatorLength(), 1); + Assert.assertEquals(in.readLine(), "This"); + Assert.assertEquals(in.getLineTerminatorLength(), 1); + Assert.assertEquals(in.readLine(), "Is A Silly Test"); + Assert.assertEquals(in.getLineTerminatorLength(), 2); + Assert.assertEquals(in.readLine(), "So There"); + Assert.assertEquals(in.getLineTerminatorLength(), 0); + } + + @Test public void voidTestLineEndingLengthAtEof() throws Exception { + final String input = "Hello\nWorld\r\n"; + final InputStream is = new ByteArrayInputStream(input.getBytes()); + final AsciiLineReader in = new AsciiLineReader(is); + + Assert.assertEquals(in.getLineTerminatorLength(), -1); + Assert.assertEquals(in.readLine(), "Hello"); + Assert.assertEquals(in.getLineTerminatorLength(), 1); + Assert.assertEquals(in.readLine(), "World"); + Assert.assertEquals(in.getLineTerminatorLength(), 2); + } }