mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-26 22:34:27 +02:00
8235792: LineNumberReader.getLineNumber() behavior is inconsistent with respect to EOF
Reviewed-by: alanb, darcy, rriggs
This commit is contained in:
parent
339016a0f2
commit
3ea5fdc9ac
3 changed files with 136 additions and 24 deletions
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 1996, 2019, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
@ -302,6 +302,8 @@ public class BufferedReader extends Reader {
|
||||||
* (EOF).
|
* (EOF).
|
||||||
*
|
*
|
||||||
* @param ignoreLF If true, the next '\n' will be skipped
|
* @param ignoreLF If true, the next '\n' will be skipped
|
||||||
|
* @param term Output: Whether a line terminator was encountered
|
||||||
|
* while reading the line; may be {@code null}.
|
||||||
*
|
*
|
||||||
* @return A String containing the contents of the line, not including
|
* @return A String containing the contents of the line, not including
|
||||||
* any line-termination characters, or null if the end of the
|
* any line-termination characters, or null if the end of the
|
||||||
|
@ -311,13 +313,14 @@ public class BufferedReader extends Reader {
|
||||||
*
|
*
|
||||||
* @throws IOException If an I/O error occurs
|
* @throws IOException If an I/O error occurs
|
||||||
*/
|
*/
|
||||||
String readLine(boolean ignoreLF) throws IOException {
|
String readLine(boolean ignoreLF, boolean[] term) throws IOException {
|
||||||
StringBuilder s = null;
|
StringBuilder s = null;
|
||||||
int startChar;
|
int startChar;
|
||||||
|
|
||||||
synchronized (lock) {
|
synchronized (lock) {
|
||||||
ensureOpen();
|
ensureOpen();
|
||||||
boolean omitLF = ignoreLF || skipLF;
|
boolean omitLF = ignoreLF || skipLF;
|
||||||
|
if (term != null) term[0] = false;
|
||||||
|
|
||||||
bufferLoop:
|
bufferLoop:
|
||||||
for (;;) {
|
for (;;) {
|
||||||
|
@ -344,6 +347,7 @@ public class BufferedReader extends Reader {
|
||||||
for (i = nextChar; i < nChars; i++) {
|
for (i = nextChar; i < nChars; i++) {
|
||||||
c = cb[i];
|
c = cb[i];
|
||||||
if ((c == '\n') || (c == '\r')) {
|
if ((c == '\n') || (c == '\r')) {
|
||||||
|
if (term != null) term[0] = true;
|
||||||
eol = true;
|
eol = true;
|
||||||
break charLoop;
|
break charLoop;
|
||||||
}
|
}
|
||||||
|
@ -389,7 +393,7 @@ public class BufferedReader extends Reader {
|
||||||
* @see java.nio.file.Files#readAllLines
|
* @see java.nio.file.Files#readAllLines
|
||||||
*/
|
*/
|
||||||
public String readLine() throws IOException {
|
public String readLine() throws IOException {
|
||||||
return readLine(false);
|
return readLine(false, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 1996, 2019, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
@ -25,7 +25,6 @@
|
||||||
|
|
||||||
package java.io;
|
package java.io;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A buffered character-input stream that keeps track of line numbers. This
|
* A buffered character-input stream that keeps track of line numbers. This
|
||||||
* class defines methods {@link #setLineNumber(int)} and {@link
|
* class defines methods {@link #setLineNumber(int)} and {@link
|
||||||
|
@ -33,15 +32,17 @@ package java.io;
|
||||||
* respectively.
|
* respectively.
|
||||||
*
|
*
|
||||||
* <p> By default, line numbering begins at 0. This number increments at every
|
* <p> By default, line numbering begins at 0. This number increments at every
|
||||||
* <a href="#lt">line terminator</a> as the data is read, and can be changed
|
* <a href="#lt">line terminator</a> as the data is read, and at the end of the
|
||||||
* with a call to {@code setLineNumber(int)}. Note however, that
|
* stream if the last character in the stream is not a line terminator. This
|
||||||
* {@code setLineNumber(int)} does not actually change the current position in
|
* number can be changed with a call to {@code setLineNumber(int)}. Note
|
||||||
* the stream; it only changes the value that will be returned by
|
* however, that {@code setLineNumber(int)} does not actually change the current
|
||||||
|
* position in the stream; it only changes the value that will be returned by
|
||||||
* {@code getLineNumber()}.
|
* {@code getLineNumber()}.
|
||||||
*
|
*
|
||||||
* <p> A line is considered to be <a id="lt">terminated</a> by any one of a
|
* <p> A line is considered to be <a id="lt">terminated</a> by any one of a
|
||||||
* line feed ('\n'), a carriage return ('\r'), or a carriage return followed
|
* line feed ('\n'), a carriage return ('\r'), or a carriage return followed
|
||||||
* immediately by a linefeed.
|
* immediately by a linefeed, or any of the previous terminators followed by
|
||||||
|
* end of stream, or end of stream not preceded by another terminator.
|
||||||
*
|
*
|
||||||
* @author Mark Reinhold
|
* @author Mark Reinhold
|
||||||
* @since 1.1
|
* @since 1.1
|
||||||
|
@ -49,6 +50,15 @@ package java.io;
|
||||||
|
|
||||||
public class LineNumberReader extends BufferedReader {
|
public class LineNumberReader extends BufferedReader {
|
||||||
|
|
||||||
|
/** Previous character types */
|
||||||
|
private static final int NONE = 0; // no previous character
|
||||||
|
private static final int CHAR = 1; // non-line terminator
|
||||||
|
private static final int EOL = 2; // line terminator
|
||||||
|
private static final int EOF = 3; // end-of-file
|
||||||
|
|
||||||
|
/** The previous character type */
|
||||||
|
private int prevChar = NONE;
|
||||||
|
|
||||||
/** The current line number */
|
/** The current line number */
|
||||||
private int lineNumber = 0;
|
private int lineNumber = 0;
|
||||||
|
|
||||||
|
@ -111,8 +121,10 @@ public class LineNumberReader extends BufferedReader {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read a single character. <a href="#lt">Line terminators</a> are
|
* Read a single character. <a href="#lt">Line terminators</a> are
|
||||||
* compressed into single newline ('\n') characters. Whenever a line
|
* compressed into single newline ('\n') characters. The current line
|
||||||
* terminator is read the current line number is incremented.
|
* number is incremented whenever a line terminator is read, or when the
|
||||||
|
* end of the stream is reached and the last character in the stream is
|
||||||
|
* not a line terminator.
|
||||||
*
|
*
|
||||||
* @return The character read, or -1 if the end of the stream has been
|
* @return The character read, or -1 if the end of the stream has been
|
||||||
* reached
|
* reached
|
||||||
|
@ -134,16 +146,27 @@ public class LineNumberReader extends BufferedReader {
|
||||||
skipLF = true;
|
skipLF = true;
|
||||||
case '\n': /* Fall through */
|
case '\n': /* Fall through */
|
||||||
lineNumber++;
|
lineNumber++;
|
||||||
|
prevChar = EOL;
|
||||||
return '\n';
|
return '\n';
|
||||||
|
case -1:
|
||||||
|
if (prevChar == CHAR)
|
||||||
|
lineNumber++;
|
||||||
|
prevChar = EOF;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
prevChar = CHAR;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read characters into a portion of an array. Whenever a <a
|
* Read characters into a portion of an array.
|
||||||
* href="#lt">line terminator</a> is read the current line number is
|
* <a href="#lt">Line terminators</a> are compressed into single newline
|
||||||
* incremented.
|
* ('\n') characters. The current line number is incremented whenever a
|
||||||
|
* line terminator is read, or when the end of the stream is reached and
|
||||||
|
* the last character in the stream is not a line terminator.
|
||||||
*
|
*
|
||||||
* @param cbuf
|
* @param cbuf
|
||||||
* Destination buffer
|
* Destination buffer
|
||||||
|
@ -154,8 +177,8 @@ public class LineNumberReader extends BufferedReader {
|
||||||
* @param len
|
* @param len
|
||||||
* Maximum number of characters to read
|
* Maximum number of characters to read
|
||||||
*
|
*
|
||||||
* @return The number of bytes read, or -1 if the end of the stream has
|
* @return The number of characters read, or -1 if the end of the stream
|
||||||
* already been reached
|
* has already been reached
|
||||||
*
|
*
|
||||||
* @throws IOException
|
* @throws IOException
|
||||||
* If an I/O error occurs
|
* If an I/O error occurs
|
||||||
|
@ -167,6 +190,13 @@ public class LineNumberReader extends BufferedReader {
|
||||||
synchronized (lock) {
|
synchronized (lock) {
|
||||||
int n = super.read(cbuf, off, len);
|
int n = super.read(cbuf, off, len);
|
||||||
|
|
||||||
|
if (n == -1) {
|
||||||
|
if (prevChar == CHAR)
|
||||||
|
lineNumber++;
|
||||||
|
prevChar = EOF;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
for (int i = off; i < off + n; i++) {
|
for (int i = off; i < off + n; i++) {
|
||||||
int c = cbuf[i];
|
int c = cbuf[i];
|
||||||
if (skipLF) {
|
if (skipLF) {
|
||||||
|
@ -183,13 +213,28 @@ public class LineNumberReader extends BufferedReader {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (n > 0) {
|
||||||
|
switch ((int)cbuf[off + n - 1]) {
|
||||||
|
case '\r':
|
||||||
|
case '\n': /* Fall through */
|
||||||
|
prevChar = EOL;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
prevChar = CHAR;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read a line of text. Whenever a <a href="#lt">line terminator</a> is
|
* Read a line of text. <a href="#lt">Line terminators</a> are compressed
|
||||||
* read the current line number is incremented.
|
* into single newline ('\n') characters. The current line number is
|
||||||
|
* incremented whenever a line terminator is read, or when the end of the
|
||||||
|
* stream is reached and the last character in the stream is not a line
|
||||||
|
* terminator.
|
||||||
*
|
*
|
||||||
* @return A String containing the contents of the line, not including
|
* @return A String containing the contents of the line, not including
|
||||||
* any <a href="#lt">line termination characters</a>, or
|
* any <a href="#lt">line termination characters</a>, or
|
||||||
|
@ -200,10 +245,17 @@ public class LineNumberReader extends BufferedReader {
|
||||||
*/
|
*/
|
||||||
public String readLine() throws IOException {
|
public String readLine() throws IOException {
|
||||||
synchronized (lock) {
|
synchronized (lock) {
|
||||||
String l = super.readLine(skipLF);
|
boolean[] term = new boolean[1];
|
||||||
|
String l = super.readLine(skipLF, term);
|
||||||
skipLF = false;
|
skipLF = false;
|
||||||
if (l != null)
|
if (l != null) {
|
||||||
lineNumber++;
|
lineNumber++;
|
||||||
|
prevChar = term[0] ? EOL : EOF;
|
||||||
|
} else { // l == null
|
||||||
|
if (prevChar == CHAR)
|
||||||
|
lineNumber++;
|
||||||
|
prevChar = EOF;
|
||||||
|
}
|
||||||
return l;
|
return l;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -242,6 +294,9 @@ public class LineNumberReader extends BufferedReader {
|
||||||
break;
|
break;
|
||||||
r -= nc;
|
r -= nc;
|
||||||
}
|
}
|
||||||
|
if (n - r > 0) {
|
||||||
|
prevChar = NONE;
|
||||||
|
}
|
||||||
return n - r;
|
return n - r;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 1998, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 1998, 2020, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
@ -22,16 +22,24 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* @test
|
/* @test
|
||||||
@bug 4074875 4063511
|
@bug 4074875 4063511 8235792
|
||||||
@summary Make sure LineNumberReader.read(char, int , int) will increase
|
@summary Make sure LineNumberReader.read(char, int , int) will increase
|
||||||
the linenumber correctly.
|
the linenumber correctly.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.IOException;
|
||||||
|
import java.io.LineNumberReader;
|
||||||
|
import java.io.StringReader;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
public class Read {
|
public class Read {
|
||||||
|
|
||||||
public static void main(String[] args) throws Exception {
|
public static void main(String[] args) throws Exception {
|
||||||
|
testReadChars();
|
||||||
|
testEofs();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void testReadChars() throws Exception {
|
||||||
String s = "aaaa\nbbb\n";
|
String s = "aaaa\nbbb\n";
|
||||||
char[] buf = new char[5];
|
char[] buf = new char[5];
|
||||||
int n = 0;
|
int n = 0;
|
||||||
|
@ -49,4 +57,49 @@ public class Read {
|
||||||
throw new Exception("Failed test: Expected line number: 2, got "
|
throw new Exception("Failed test: Expected line number: 2, got "
|
||||||
+ line);
|
+ line);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void testEofs() throws Exception {
|
||||||
|
String string = "first \n second";
|
||||||
|
|
||||||
|
Consumer<LineNumberReader> c = (LineNumberReader r) -> {
|
||||||
|
try {
|
||||||
|
while (r.read() != -1)
|
||||||
|
continue;
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
testEof(c, string, 2);
|
||||||
|
|
||||||
|
c = (LineNumberReader r) -> {
|
||||||
|
try {
|
||||||
|
char[] buf = new char[128];
|
||||||
|
while (r.read(buf) != -1)
|
||||||
|
continue;
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
testEof(c, string, 2);
|
||||||
|
|
||||||
|
c = (LineNumberReader r) -> {
|
||||||
|
try {
|
||||||
|
while (r.readLine() != null)
|
||||||
|
continue;
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
testEof(c, string, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void testEof(Consumer<LineNumberReader> c, String s, int n)
|
||||||
|
throws Exception {
|
||||||
|
LineNumberReader r = new LineNumberReader(new StringReader(s));
|
||||||
|
c.accept(r);
|
||||||
|
int line;
|
||||||
|
if ((line = r.getLineNumber()) != n)
|
||||||
|
throw new Exception("Failed test: Expected line number: " + n +
|
||||||
|
" , got " + line);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue