/*
* Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved.
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*/
package java.io;
import java.util.
Iterator;
import java.util.
NoSuchElementException;
import java.util.
Spliterator;
import java.util.
Spliterators;
import java.util.stream.
Stream;
import java.util.stream.
StreamSupport;
/**
* Reads text from a character-input stream, buffering characters so as to
* provide for the efficient reading of characters, arrays, and lines.
*
* <p> The buffer size may be specified, or the default size may be used. The
* default is large enough for most purposes.
*
* <p> In general, each read request made of a Reader causes a corresponding
* read request to be made of the underlying character or byte stream. It is
* therefore advisable to wrap a BufferedReader around any Reader whose read()
* operations may be costly, such as FileReaders and InputStreamReaders. For
* example,
*
* <pre>
* BufferedReader in
* = new BufferedReader(new FileReader("foo.in"));
* </pre>
*
* will buffer the input from the specified file. Without buffering, each
* invocation of read() or readLine() could cause bytes to be read from the
* file, converted into characters, and then returned, which can be very
* inefficient.
*
* <p> Programs that use DataInputStreams for textual input can be localized by
* replacing each DataInputStream with an appropriate BufferedReader.
*
* @see FileReader
* @see InputStreamReader
* @see java.nio.file.Files#newBufferedReader
*
* @author Mark Reinhold
* @since JDK1.1
*/
public class
BufferedReader extends
Reader {
private
Reader in;
private char
cb[];
private int
nChars,
nextChar;
private static final int
INVALIDATED = -2;
private static final int
UNMARKED = -1;
private int
markedChar =
UNMARKED;
private int
readAheadLimit = 0; /* Valid only when markedChar > 0 */
/** If the next character is a line feed, skip it */
private boolean
skipLF = false;
/** The skipLF flag when the mark was set */
private boolean
markedSkipLF = false;
private static int
defaultCharBufferSize = 8192;
private static int
defaultExpectedLineLength = 80;
/**
* Creates a buffering character-input stream that uses an input buffer of
* the specified size.
*
* @param in A Reader
* @param sz Input-buffer size
*
* @exception IllegalArgumentException If {@code sz <= 0}
*/
public
BufferedReader(
Reader in, int
sz) {
super(
in);
if (
sz <= 0)
throw new
IllegalArgumentException("Buffer size <= 0");
this.
in =
in;
cb = new char[
sz];
nextChar =
nChars = 0;
}
/**
* Creates a buffering character-input stream that uses a default-sized
* input buffer.
*
* @param in A Reader
*/
public
BufferedReader(
Reader in) {
this(
in,
defaultCharBufferSize);
}
/** Checks to make sure that the stream has not been closed */
private void
ensureOpen() throws
IOException {
if (
in == null)
throw new
IOException("Stream closed");
}
/**
* Fills the input buffer, taking the mark into account if it is valid.
*/
private void
fill() throws
IOException {
int
dst;
if (
markedChar <=
UNMARKED) {
/* No mark */
dst = 0;
} else {
/* Marked */
int
delta =
nextChar -
markedChar;
if (
delta >=
readAheadLimit) {
/* Gone past read-ahead limit: Invalidate mark */
markedChar =
INVALIDATED;
readAheadLimit = 0;
dst = 0;
} else {
if (
readAheadLimit <=
cb.length) {
/* Shuffle in the current buffer */
System.
arraycopy(
cb,
markedChar,
cb, 0,
delta);
markedChar = 0;
dst =
delta;
} else {
/* Reallocate buffer to accommodate read-ahead limit */
char
ncb[] = new char[
readAheadLimit];
System.
arraycopy(
cb,
markedChar,
ncb, 0,
delta);
cb =
ncb;
markedChar = 0;
dst =
delta;
}
nextChar =
nChars =
delta;
}
}
int
n;
do {
n =
in.
read(
cb,
dst,
cb.length -
dst);
} while (
n == 0);
if (
n > 0) {
nChars =
dst +
n;
nextChar =
dst;
}
}
/**
* Reads a single character.
*
* @return The character read, as an integer in the range
* 0 to 65535 (<tt>0x00-0xffff</tt>), or -1 if the
* end of the stream has been reached
* @exception IOException If an I/O error occurs
*/
public int
read() throws
IOException {
synchronized (
lock) {
ensureOpen();
for (;;) {
if (
nextChar >=
nChars) {
fill();
if (
nextChar >=
nChars)
return -1;
}
if (
skipLF) {
skipLF = false;
if (
cb[
nextChar] == '\n') {
nextChar++;
continue;
}
}
return
cb[
nextChar++];
}
}
}
/**
* Reads characters into a portion of an array, reading from the underlying
* stream if necessary.
*/
private int
read1(char[]
cbuf, int
off, int
len) throws
IOException {
if (
nextChar >=
nChars) {
/* If the requested length is at least as large as the buffer, and
if there is no mark/reset activity, and if line feeds are not
being skipped, do not bother to copy the characters into the
local buffer. In this way buffered streams will cascade
harmlessly. */
if (
len >=
cb.length &&
markedChar <=
UNMARKED && !
skipLF) {
return
in.
read(
cbuf,
off,
len);
}
fill();
}
if (
nextChar >=
nChars) return -1;
if (
skipLF) {
skipLF = false;
if (
cb[
nextChar] == '\n') {
nextChar++;
if (
nextChar >=
nChars)
fill();
if (
nextChar >=
nChars)
return -1;
}
}
int
n =
Math.
min(
len,
nChars -
nextChar);
System.
arraycopy(
cb,
nextChar,
cbuf,
off,
n);
nextChar +=
n;
return
n;
}
/**
* Reads characters into a portion of an array.
*
* <p> This method implements the general contract of the corresponding
* <code>{@link Reader#read(char[], int, int) read}</code> method of the
* <code>{@link Reader}</code> class. As an additional convenience, it
* attempts to read as many characters as possible by repeatedly invoking
* the <code>read</code> method of the underlying stream. This iterated
* <code>read</code> continues until one of the following conditions becomes
* true: <ul>
*
* <li> The specified number of characters have been read,
*
* <li> The <code>read</code> method of the underlying stream returns
* <code>-1</code>, indicating end-of-file, or
*
* <li> The <code>ready</code> method of the underlying stream
* returns <code>false</code>, indicating that further input requests
* would block.
*
* </ul> If the first <code>read</code> on the underlying stream returns
* <code>-1</code> to indicate end-of-file then this method returns
* <code>-1</code>. Otherwise this method returns the number of characters
* actually read.
*
* <p> Subclasses of this class are encouraged, but not required, to
* attempt to read as many characters as possible in the same fashion.
*
* <p> Ordinarily this method takes characters from this stream's character
* buffer, filling it from the underlying stream as necessary. If,
* however, the buffer is empty, the mark is not valid, and the requested
* length is at least as large as the buffer, then this method will read
* characters directly from the underlying stream into the given array.
* Thus redundant <code>BufferedReader</code>s will not copy data
* unnecessarily.
*
* @param cbuf Destination buffer
* @param off Offset at which to start storing characters
* @param len Maximum number of characters to read
*
* @return The number of characters read, or -1 if the end of the
* stream has been reached
*
* @exception IOException If an I/O error occurs
*/
public int
read(char
cbuf[], int
off, int
len) throws
IOException {
synchronized (
lock) {
ensureOpen();
if ((
off < 0) || (
off >
cbuf.length) || (
len < 0) ||
((
off +
len) >
cbuf.length) || ((
off +
len) < 0)) {
throw new
IndexOutOfBoundsException();
} else if (
len == 0) {
return 0;
}
int
n =
read1(
cbuf,
off,
len);
if (
n <= 0) return
n;
while ((
n <
len) &&
in.
ready()) {
int
n1 =
read1(
cbuf,
off +
n,
len -
n);
if (
n1 <= 0) break;
n +=
n1;
}
return
n;
}
}
/**
* Reads 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
* followed immediately by a linefeed.
*
* @param ignoreLF If true, the next '\n' will be skipped
*
* @return A String containing the contents of the line, not including
* any line-termination characters, or null if the end of the
* stream has been reached
*
* @see java.io.LineNumberReader#readLine()
*
* @exception IOException If an I/O error occurs
*/
String readLine(boolean
ignoreLF) throws
IOException {
StringBuffer s = null;
int
startChar;
synchronized (
lock) {
ensureOpen();
boolean
omitLF =
ignoreLF ||
skipLF;
bufferLoop:
for (;;) {
if (
nextChar >=
nChars)
fill();
if (
nextChar >=
nChars) { /* EOF */
if (
s != null &&
s.
length() > 0)
return
s.
toString();
else
return null;
}
boolean
eol = false;
char
c = 0;
int
i;
/* Skip a leftover '\n', if necessary */
if (
omitLF && (
cb[
nextChar] == '\n'))
nextChar++;
skipLF = false;
omitLF = false;
charLoop:
for (
i =
nextChar;
i <
nChars;
i++) {
c =
cb[
i];
if ((
c == '\n') || (
c == '\r')) {
eol = true;
break
charLoop;
}
}
startChar =
nextChar;
nextChar =
i;
if (
eol) {
String str;
if (
s == null) {
str = new
String(
cb,
startChar,
i -
startChar);
} else {
s.
append(
cb,
startChar,
i -
startChar);
str =
s.
toString();
}
nextChar++;
if (
c == '\r') {
skipLF = true;
}
return
str;
}
if (
s == null)
s = new
StringBuffer(
defaultExpectedLineLength);
s.
append(
cb,
startChar,
i -
startChar);
}
}
}
/**
* Reads 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
* followed immediately by a linefeed.
*
* @return A String containing the contents of the line, not including
* any line-termination characters, or null if the end of the
* stream has been reached
*
* @exception IOException If an I/O error occurs
*
* @see java.nio.file.Files#readAllLines
*/
public
String readLine() throws
IOException {
return
readLine(false);
}
/**
* Skips characters.
*
* @param n The number of characters to skip
*
* @return The number of characters actually skipped
*
* @exception IllegalArgumentException If <code>n</code> is negative.
* @exception IOException If an I/O error occurs
*/
public long
skip(long
n) throws
IOException {
if (
n < 0L) {
throw new
IllegalArgumentException("skip value is negative");
}
synchronized (
lock) {
ensureOpen();
long
r =
n;
while (
r > 0) {
if (
nextChar >=
nChars)
fill();
if (
nextChar >=
nChars) /* EOF */
break;
if (
skipLF) {
skipLF = false;
if (
cb[
nextChar] == '\n') {
nextChar++;
}
}
long
d =
nChars -
nextChar;
if (
r <=
d) {
nextChar +=
r;
r = 0;
break;
}
else {
r -=
d;
nextChar =
nChars;
}
}
return
n -
r;
}
}
/**
* Tells whether this stream is ready to be read. A buffered character
* stream is ready if the buffer is not empty, or if the underlying
* character stream is ready.
*
* @exception IOException If an I/O error occurs
*/
public boolean
ready() throws
IOException {
synchronized (
lock) {
ensureOpen();
/*
* If newline needs to be skipped and the next char to be read
* is a newline character, then just skip it right away.
*/
if (
skipLF) {
/* Note that in.ready() will return true if and only if the next
* read on the stream will not block.
*/
if (
nextChar >=
nChars &&
in.
ready()) {
fill();
}
if (
nextChar <
nChars) {
if (
cb[
nextChar] == '\n')
nextChar++;
skipLF = false;
}
}
return (
nextChar <
nChars) ||
in.
ready();
}
}
/**
* Tells whether this stream supports the mark() operation, which it does.
*/
public boolean
markSupported() {
return true;
}
/**
* Marks the present position in the stream. Subsequent calls to reset()
* will attempt to reposition the stream to this point.
*
* @param readAheadLimit Limit on the number of characters that may be
* read while still preserving the mark. An attempt
* to reset the stream after reading characters
* up to this limit or beyond may fail.
* A limit value larger than the size of the input
* buffer will cause a new buffer to be allocated
* whose size is no smaller than limit.
* Therefore large values should be used with care.
*
* @exception IllegalArgumentException If {@code readAheadLimit < 0}
* @exception IOException If an I/O error occurs
*/
public void
mark(int
readAheadLimit) throws
IOException {
if (
readAheadLimit < 0) {
throw new
IllegalArgumentException("Read-ahead limit < 0");
}
synchronized (
lock) {
ensureOpen();
this.
readAheadLimit =
readAheadLimit;
markedChar =
nextChar;
markedSkipLF =
skipLF;
}
}
/**
* Resets the stream to the most recent mark.
*
* @exception IOException If the stream has never been marked,
* or if the mark has been invalidated
*/
public void
reset() throws
IOException {
synchronized (
lock) {
ensureOpen();
if (
markedChar < 0)
throw new
IOException((
markedChar ==
INVALIDATED)
? "Mark invalid"
: "Stream not marked");
nextChar =
markedChar;
skipLF =
markedSkipLF;
}
}
public void
close() throws
IOException {
synchronized (
lock) {
if (
in == null)
return;
try {
in.
close();
} finally {
in = null;
cb = null;
}
}
}
/**
* Returns a {@code Stream}, the elements of which are lines read from
* this {@code BufferedReader}. The {@link Stream} is lazily populated,
* i.e., read only occurs during the
* <a href="../util/stream/package-summary.html#StreamOps">terminal
* stream operation</a>.
*
* <p> The reader must not be operated on during the execution of the
* terminal stream operation. Otherwise, the result of the terminal stream
* operation is undefined.
*
* <p> After execution of the terminal stream operation there are no
* guarantees that the reader will be at a specific position from which to
* read the next character or line.
*
* <p> If an {@link IOException} is thrown when accessing the underlying
* {@code BufferedReader}, it is wrapped in an {@link
* UncheckedIOException} which will be thrown from the {@code Stream}
* method that caused the read to take place. This method will return a
* Stream if invoked on a BufferedReader that is closed. Any operation on
* that stream that requires reading from the BufferedReader after it is
* closed, will cause an UncheckedIOException to be thrown.
*
* @return a {@code Stream<String>} providing the lines of text
* described by this {@code BufferedReader}
*
* @since 1.8
*/
public
Stream<
String>
lines() {
Iterator<
String>
iter = new
Iterator<
String>() {
String nextLine = null;
@
Override
public boolean
hasNext() {
if (
nextLine != null) {
return true;
} else {
try {
nextLine =
readLine();
return (
nextLine != null);
} catch (
IOException e) {
throw new
UncheckedIOException(
e);
}
}
}
@
Override
public
String next() {
if (
nextLine != null ||
hasNext()) {
String line =
nextLine;
nextLine = null;
return
line;
} else {
throw new
NoSuchElementException();
}
}
};
return
StreamSupport.
stream(
Spliterators.
spliteratorUnknownSize(
iter,
Spliterator.
ORDERED |
Spliterator.
NONNULL), false);
}
}