jaroslav@557: /* jaroslav@557: * Copyright (c) 1996, 2011, Oracle and/or its affiliates. All rights reserved. jaroslav@557: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. jaroslav@557: * jaroslav@557: * This code is free software; you can redistribute it and/or modify it jaroslav@557: * under the terms of the GNU General Public License version 2 only, as jaroslav@557: * published by the Free Software Foundation. Oracle designates this jaroslav@557: * particular file as subject to the "Classpath" exception as provided jaroslav@557: * by Oracle in the LICENSE file that accompanied this code. jaroslav@557: * jaroslav@557: * This code is distributed in the hope that it will be useful, but WITHOUT jaroslav@557: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or jaroslav@557: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License jaroslav@557: * version 2 for more details (a copy is included in the LICENSE file that jaroslav@557: * accompanied this code). jaroslav@557: * jaroslav@557: * You should have received a copy of the GNU General Public License version jaroslav@557: * 2 along with this work; if not, write to the Free Software Foundation, jaroslav@557: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. jaroslav@557: * jaroslav@557: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA jaroslav@557: * or visit www.oracle.com if you need additional information or have any jaroslav@557: * questions. jaroslav@557: */ jaroslav@557: jaroslav@557: package java.io; jaroslav@557: jaroslav@560: jaroslav@557: jaroslav@557: /** jaroslav@557: * Reads text from a character-input stream, buffering characters so as to jaroslav@557: * provide for the efficient reading of characters, arrays, and lines. jaroslav@557: * jaroslav@557: *
The buffer size may be specified, or the default size may be used. The jaroslav@557: * default is large enough for most purposes. jaroslav@557: * jaroslav@557: *
In general, each read request made of a Reader causes a corresponding jaroslav@557: * read request to be made of the underlying character or byte stream. It is jaroslav@557: * therefore advisable to wrap a BufferedReader around any Reader whose read() jaroslav@557: * operations may be costly, such as FileReaders and InputStreamReaders. For jaroslav@557: * example, jaroslav@557: * jaroslav@557: *
jaroslav@557: * BufferedReader in jaroslav@557: * = new BufferedReader(new FileReader("foo.in")); jaroslav@557: *jaroslav@557: * jaroslav@557: * will buffer the input from the specified file. Without buffering, each jaroslav@557: * invocation of read() or readLine() could cause bytes to be read from the jaroslav@557: * file, converted into characters, and then returned, which can be very jaroslav@557: * inefficient. jaroslav@557: * jaroslav@557: *
Programs that use DataInputStreams for textual input can be localized by jaroslav@557: * replacing each DataInputStream with an appropriate BufferedReader. jaroslav@557: * jaroslav@557: * @see FileReader jaroslav@557: * @see InputStreamReader jaroslav@557: * @see java.nio.file.Files#newBufferedReader jaroslav@557: * jaroslav@557: * @author Mark Reinhold jaroslav@557: * @since JDK1.1 jaroslav@557: */ jaroslav@557: jaroslav@557: public class BufferedReader extends Reader { jaroslav@557: jaroslav@557: private Reader in; jaroslav@557: jaroslav@557: private char cb[]; jaroslav@557: private int nChars, nextChar; jaroslav@557: jaroslav@557: private static final int INVALIDATED = -2; jaroslav@557: private static final int UNMARKED = -1; jaroslav@557: private int markedChar = UNMARKED; jaroslav@557: private int readAheadLimit = 0; /* Valid only when markedChar > 0 */ jaroslav@557: jaroslav@557: /** If the next character is a line feed, skip it */ jaroslav@557: private boolean skipLF = false; jaroslav@557: jaroslav@557: /** The skipLF flag when the mark was set */ jaroslav@557: private boolean markedSkipLF = false; jaroslav@557: jaroslav@557: private static int defaultCharBufferSize = 8192; jaroslav@557: private static int defaultExpectedLineLength = 80; jaroslav@557: jaroslav@557: /** jaroslav@557: * Creates a buffering character-input stream that uses an input buffer of jaroslav@557: * the specified size. jaroslav@557: * jaroslav@557: * @param in A Reader jaroslav@557: * @param sz Input-buffer size jaroslav@557: * jaroslav@557: * @exception IllegalArgumentException If sz is <= 0 jaroslav@557: */ jaroslav@557: public BufferedReader(Reader in, int sz) { jaroslav@557: super(in); jaroslav@557: if (sz <= 0) jaroslav@557: throw new IllegalArgumentException("Buffer size <= 0"); jaroslav@557: this.in = in; jaroslav@557: cb = new char[sz]; jaroslav@557: nextChar = nChars = 0; jaroslav@557: } jaroslav@557: jaroslav@557: /** jaroslav@557: * Creates a buffering character-input stream that uses a default-sized jaroslav@557: * input buffer. jaroslav@557: * jaroslav@557: * @param in A Reader jaroslav@557: */ jaroslav@557: public BufferedReader(Reader in) { jaroslav@557: this(in, defaultCharBufferSize); jaroslav@557: } jaroslav@557: jaroslav@557: /** Checks to make sure that the stream has not been closed */ jaroslav@557: private void ensureOpen() throws IOException { jaroslav@557: if (in == null) jaroslav@557: throw new IOException("Stream closed"); jaroslav@557: } jaroslav@557: jaroslav@557: /** jaroslav@557: * Fills the input buffer, taking the mark into account if it is valid. jaroslav@557: */ jaroslav@557: private void fill() throws IOException { jaroslav@557: int dst; jaroslav@557: if (markedChar <= UNMARKED) { jaroslav@557: /* No mark */ jaroslav@557: dst = 0; jaroslav@557: } else { jaroslav@557: /* Marked */ jaroslav@557: int delta = nextChar - markedChar; jaroslav@557: if (delta >= readAheadLimit) { jaroslav@557: /* Gone past read-ahead limit: Invalidate mark */ jaroslav@557: markedChar = INVALIDATED; jaroslav@557: readAheadLimit = 0; jaroslav@557: dst = 0; jaroslav@557: } else { jaroslav@557: if (readAheadLimit <= cb.length) { jaroslav@557: /* Shuffle in the current buffer */ jaroslav@557: System.arraycopy(cb, markedChar, cb, 0, delta); jaroslav@557: markedChar = 0; jaroslav@557: dst = delta; jaroslav@557: } else { jaroslav@557: /* Reallocate buffer to accommodate read-ahead limit */ jaroslav@557: char ncb[] = new char[readAheadLimit]; jaroslav@557: System.arraycopy(cb, markedChar, ncb, 0, delta); jaroslav@557: cb = ncb; jaroslav@557: markedChar = 0; jaroslav@557: dst = delta; jaroslav@557: } jaroslav@557: nextChar = nChars = delta; jaroslav@557: } jaroslav@557: } jaroslav@557: jaroslav@557: int n; jaroslav@557: do { jaroslav@557: n = in.read(cb, dst, cb.length - dst); jaroslav@557: } while (n == 0); jaroslav@557: if (n > 0) { jaroslav@557: nChars = dst + n; jaroslav@557: nextChar = dst; jaroslav@557: } jaroslav@557: } jaroslav@557: jaroslav@557: /** jaroslav@557: * Reads a single character. jaroslav@557: * jaroslav@557: * @return The character read, as an integer in the range jaroslav@557: * 0 to 65535 (0x00-0xffff), or -1 if the jaroslav@557: * end of the stream has been reached jaroslav@557: * @exception IOException If an I/O error occurs jaroslav@557: */ jaroslav@557: public int read() throws IOException { jaroslav@557: synchronized (lock) { jaroslav@557: ensureOpen(); jaroslav@557: for (;;) { jaroslav@557: if (nextChar >= nChars) { jaroslav@557: fill(); jaroslav@557: if (nextChar >= nChars) jaroslav@557: return -1; jaroslav@557: } jaroslav@557: if (skipLF) { jaroslav@557: skipLF = false; jaroslav@557: if (cb[nextChar] == '\n') { jaroslav@557: nextChar++; jaroslav@557: continue; jaroslav@557: } jaroslav@557: } jaroslav@557: return cb[nextChar++]; jaroslav@557: } jaroslav@557: } jaroslav@557: } jaroslav@557: jaroslav@557: /** jaroslav@557: * Reads characters into a portion of an array, reading from the underlying jaroslav@557: * stream if necessary. jaroslav@557: */ jaroslav@557: private int read1(char[] cbuf, int off, int len) throws IOException { jaroslav@557: if (nextChar >= nChars) { jaroslav@557: /* If the requested length is at least as large as the buffer, and jaroslav@557: if there is no mark/reset activity, and if line feeds are not jaroslav@557: being skipped, do not bother to copy the characters into the jaroslav@557: local buffer. In this way buffered streams will cascade jaroslav@557: harmlessly. */ jaroslav@557: if (len >= cb.length && markedChar <= UNMARKED && !skipLF) { jaroslav@557: return in.read(cbuf, off, len); jaroslav@557: } jaroslav@557: fill(); jaroslav@557: } jaroslav@557: if (nextChar >= nChars) return -1; jaroslav@557: if (skipLF) { jaroslav@557: skipLF = false; jaroslav@557: if (cb[nextChar] == '\n') { jaroslav@557: nextChar++; jaroslav@557: if (nextChar >= nChars) jaroslav@557: fill(); jaroslav@557: if (nextChar >= nChars) jaroslav@557: return -1; jaroslav@557: } jaroslav@557: } jaroslav@557: int n = Math.min(len, nChars - nextChar); jaroslav@557: System.arraycopy(cb, nextChar, cbuf, off, n); jaroslav@557: nextChar += n; jaroslav@557: return n; jaroslav@557: } jaroslav@557: jaroslav@557: /** jaroslav@557: * Reads characters into a portion of an array. jaroslav@557: * jaroslav@557: *
This method implements the general contract of the corresponding
jaroslav@557: * {@link Reader#read(char[], int, int) read}
method of the
jaroslav@557: * {@link Reader}
class. As an additional convenience, it
jaroslav@557: * attempts to read as many characters as possible by repeatedly invoking
jaroslav@557: * the read
method of the underlying stream. This iterated
jaroslav@557: * read
continues until one of the following conditions becomes
jaroslav@557: * true:
read
method of the underlying stream returns
jaroslav@557: * -1
, indicating end-of-file, or
jaroslav@557: *
jaroslav@557: * ready
method of the underlying stream
jaroslav@557: * returns false
, indicating that further input requests
jaroslav@557: * would block.
jaroslav@557: *
jaroslav@557: * read
on the underlying stream returns
jaroslav@557: * -1
to indicate end-of-file then this method returns
jaroslav@557: * -1
. Otherwise this method returns the number of characters
jaroslav@557: * actually read.
jaroslav@557: *
jaroslav@557: * Subclasses of this class are encouraged, but not required, to jaroslav@557: * attempt to read as many characters as possible in the same fashion. jaroslav@557: * jaroslav@557: *
Ordinarily this method takes characters from this stream's character
jaroslav@557: * buffer, filling it from the underlying stream as necessary. If,
jaroslav@557: * however, the buffer is empty, the mark is not valid, and the requested
jaroslav@557: * length is at least as large as the buffer, then this method will read
jaroslav@557: * characters directly from the underlying stream into the given array.
jaroslav@557: * Thus redundant BufferedReader
s will not copy data
jaroslav@557: * unnecessarily.
jaroslav@557: *
jaroslav@557: * @param cbuf Destination buffer
jaroslav@557: * @param off Offset at which to start storing characters
jaroslav@557: * @param len Maximum number of characters to read
jaroslav@557: *
jaroslav@557: * @return The number of characters read, or -1 if the end of the
jaroslav@557: * stream has been reached
jaroslav@557: *
jaroslav@557: * @exception IOException If an I/O error occurs
jaroslav@557: */
jaroslav@557: public int read(char cbuf[], int off, int len) throws IOException {
jaroslav@557: synchronized (lock) {
jaroslav@557: ensureOpen();
jaroslav@557: if ((off < 0) || (off > cbuf.length) || (len < 0) ||
jaroslav@557: ((off + len) > cbuf.length) || ((off + len) < 0)) {
jaroslav@557: throw new IndexOutOfBoundsException();
jaroslav@557: } else if (len == 0) {
jaroslav@557: return 0;
jaroslav@557: }
jaroslav@557:
jaroslav@557: int n = read1(cbuf, off, len);
jaroslav@557: if (n <= 0) return n;
jaroslav@557: while ((n < len) && in.ready()) {
jaroslav@557: int n1 = read1(cbuf, off + n, len - n);
jaroslav@557: if (n1 <= 0) break;
jaroslav@557: n += n1;
jaroslav@557: }
jaroslav@557: return n;
jaroslav@557: }
jaroslav@557: }
jaroslav@557:
jaroslav@557: /**
jaroslav@557: * Reads a line of text. A line is considered to be terminated by any one
jaroslav@557: * of a line feed ('\n'), a carriage return ('\r'), or a carriage return
jaroslav@557: * followed immediately by a linefeed.
jaroslav@557: *
jaroslav@557: * @param ignoreLF If true, the next '\n' will be skipped
jaroslav@557: *
jaroslav@557: * @return A String containing the contents of the line, not including
jaroslav@557: * any line-termination characters, or null if the end of the
jaroslav@557: * stream has been reached
jaroslav@557: *
jaroslav@557: * @see java.io.LineNumberReader#readLine()
jaroslav@557: *
jaroslav@557: * @exception IOException If an I/O error occurs
jaroslav@557: */
jaroslav@557: String readLine(boolean ignoreLF) throws IOException {
jaroslav@557: StringBuffer s = null;
jaroslav@557: int startChar;
jaroslav@557:
jaroslav@557: synchronized (lock) {
jaroslav@557: ensureOpen();
jaroslav@557: boolean omitLF = ignoreLF || skipLF;
jaroslav@557:
jaroslav@557: bufferLoop:
jaroslav@557: for (;;) {
jaroslav@557:
jaroslav@557: if (nextChar >= nChars)
jaroslav@557: fill();
jaroslav@557: if (nextChar >= nChars) { /* EOF */
jaroslav@557: if (s != null && s.length() > 0)
jaroslav@557: return s.toString();
jaroslav@557: else
jaroslav@557: return null;
jaroslav@557: }
jaroslav@557: boolean eol = false;
jaroslav@557: char c = 0;
jaroslav@557: int i;
jaroslav@557:
jaroslav@557: /* Skip a leftover '\n', if necessary */
jaroslav@557: if (omitLF && (cb[nextChar] == '\n'))
jaroslav@557: nextChar++;
jaroslav@557: skipLF = false;
jaroslav@557: omitLF = false;
jaroslav@557:
jaroslav@557: charLoop:
jaroslav@557: for (i = nextChar; i < nChars; i++) {
jaroslav@557: c = cb[i];
jaroslav@557: if ((c == '\n') || (c == '\r')) {
jaroslav@557: eol = true;
jaroslav@557: break charLoop;
jaroslav@557: }
jaroslav@557: }
jaroslav@557:
jaroslav@557: startChar = nextChar;
jaroslav@557: nextChar = i;
jaroslav@557:
jaroslav@557: if (eol) {
jaroslav@557: String str;
jaroslav@557: if (s == null) {
jaroslav@557: str = new String(cb, startChar, i - startChar);
jaroslav@557: } else {
jaroslav@557: s.append(cb, startChar, i - startChar);
jaroslav@557: str = s.toString();
jaroslav@557: }
jaroslav@557: nextChar++;
jaroslav@557: if (c == '\r') {
jaroslav@557: skipLF = true;
jaroslav@557: }
jaroslav@557: return str;
jaroslav@557: }
jaroslav@557:
jaroslav@557: if (s == null)
jaroslav@557: s = new StringBuffer(defaultExpectedLineLength);
jaroslav@557: s.append(cb, startChar, i - startChar);
jaroslav@557: }
jaroslav@557: }
jaroslav@557: }
jaroslav@557:
jaroslav@557: /**
jaroslav@557: * Reads a line of text. A line is considered to be terminated by any one
jaroslav@557: * of a line feed ('\n'), a carriage return ('\r'), or a carriage return
jaroslav@557: * followed immediately by a linefeed.
jaroslav@557: *
jaroslav@557: * @return A String containing the contents of the line, not including
jaroslav@557: * any line-termination characters, or null if the end of the
jaroslav@557: * stream has been reached
jaroslav@557: *
jaroslav@557: * @exception IOException If an I/O error occurs
jaroslav@557: *
jaroslav@557: * @see java.nio.file.Files#readAllLines
jaroslav@557: */
jaroslav@557: public String readLine() throws IOException {
jaroslav@557: return readLine(false);
jaroslav@557: }
jaroslav@557:
jaroslav@557: /**
jaroslav@557: * Skips characters.
jaroslav@557: *
jaroslav@557: * @param n The number of characters to skip
jaroslav@557: *
jaroslav@557: * @return The number of characters actually skipped
jaroslav@557: *
jaroslav@557: * @exception IllegalArgumentException If n
is negative.
jaroslav@557: * @exception IOException If an I/O error occurs
jaroslav@557: */
jaroslav@557: public long skip(long n) throws IOException {
jaroslav@557: if (n < 0L) {
jaroslav@557: throw new IllegalArgumentException("skip value is negative");
jaroslav@557: }
jaroslav@557: synchronized (lock) {
jaroslav@557: ensureOpen();
jaroslav@557: long r = n;
jaroslav@557: while (r > 0) {
jaroslav@557: if (nextChar >= nChars)
jaroslav@557: fill();
jaroslav@557: if (nextChar >= nChars) /* EOF */
jaroslav@557: break;
jaroslav@557: if (skipLF) {
jaroslav@557: skipLF = false;
jaroslav@557: if (cb[nextChar] == '\n') {
jaroslav@557: nextChar++;
jaroslav@557: }
jaroslav@557: }
jaroslav@557: long d = nChars - nextChar;
jaroslav@557: if (r <= d) {
jaroslav@557: nextChar += r;
jaroslav@557: r = 0;
jaroslav@557: break;
jaroslav@557: }
jaroslav@557: else {
jaroslav@557: r -= d;
jaroslav@557: nextChar = nChars;
jaroslav@557: }
jaroslav@557: }
jaroslav@557: return n - r;
jaroslav@557: }
jaroslav@557: }
jaroslav@557:
jaroslav@557: /**
jaroslav@557: * Tells whether this stream is ready to be read. A buffered character
jaroslav@557: * stream is ready if the buffer is not empty, or if the underlying
jaroslav@557: * character stream is ready.
jaroslav@557: *
jaroslav@557: * @exception IOException If an I/O error occurs
jaroslav@557: */
jaroslav@557: public boolean ready() throws IOException {
jaroslav@557: synchronized (lock) {
jaroslav@557: ensureOpen();
jaroslav@557:
jaroslav@557: /*
jaroslav@557: * If newline needs to be skipped and the next char to be read
jaroslav@557: * is a newline character, then just skip it right away.
jaroslav@557: */
jaroslav@557: if (skipLF) {
jaroslav@557: /* Note that in.ready() will return true if and only if the next
jaroslav@557: * read on the stream will not block.
jaroslav@557: */
jaroslav@557: if (nextChar >= nChars && in.ready()) {
jaroslav@557: fill();
jaroslav@557: }
jaroslav@557: if (nextChar < nChars) {
jaroslav@557: if (cb[nextChar] == '\n')
jaroslav@557: nextChar++;
jaroslav@557: skipLF = false;
jaroslav@557: }
jaroslav@557: }
jaroslav@557: return (nextChar < nChars) || in.ready();
jaroslav@557: }
jaroslav@557: }
jaroslav@557:
jaroslav@557: /**
jaroslav@557: * Tells whether this stream supports the mark() operation, which it does.
jaroslav@557: */
jaroslav@557: public boolean markSupported() {
jaroslav@557: return true;
jaroslav@557: }
jaroslav@557:
jaroslav@557: /**
jaroslav@557: * Marks the present position in the stream. Subsequent calls to reset()
jaroslav@557: * will attempt to reposition the stream to this point.
jaroslav@557: *
jaroslav@557: * @param readAheadLimit Limit on the number of characters that may be
jaroslav@557: * read while still preserving the mark. An attempt
jaroslav@557: * to reset the stream after reading characters
jaroslav@557: * up to this limit or beyond may fail.
jaroslav@557: * A limit value larger than the size of the input
jaroslav@557: * buffer will cause a new buffer to be allocated
jaroslav@557: * whose size is no smaller than limit.
jaroslav@557: * Therefore large values should be used with care.
jaroslav@557: *
jaroslav@557: * @exception IllegalArgumentException If readAheadLimit is < 0
jaroslav@557: * @exception IOException If an I/O error occurs
jaroslav@557: */
jaroslav@557: public void mark(int readAheadLimit) throws IOException {
jaroslav@557: if (readAheadLimit < 0) {
jaroslav@557: throw new IllegalArgumentException("Read-ahead limit < 0");
jaroslav@557: }
jaroslav@557: synchronized (lock) {
jaroslav@557: ensureOpen();
jaroslav@557: this.readAheadLimit = readAheadLimit;
jaroslav@557: markedChar = nextChar;
jaroslav@557: markedSkipLF = skipLF;
jaroslav@557: }
jaroslav@557: }
jaroslav@557:
jaroslav@557: /**
jaroslav@557: * Resets the stream to the most recent mark.
jaroslav@557: *
jaroslav@557: * @exception IOException If the stream has never been marked,
jaroslav@557: * or if the mark has been invalidated
jaroslav@557: */
jaroslav@557: public void reset() throws IOException {
jaroslav@557: synchronized (lock) {
jaroslav@557: ensureOpen();
jaroslav@557: if (markedChar < 0)
jaroslav@557: throw new IOException((markedChar == INVALIDATED)
jaroslav@557: ? "Mark invalid"
jaroslav@557: : "Stream not marked");
jaroslav@557: nextChar = markedChar;
jaroslav@557: skipLF = markedSkipLF;
jaroslav@557: }
jaroslav@557: }
jaroslav@557:
jaroslav@557: public void close() throws IOException {
jaroslav@557: synchronized (lock) {
jaroslav@557: if (in == null)
jaroslav@557: return;
jaroslav@557: in.close();
jaroslav@557: in = null;
jaroslav@557: cb = null;
jaroslav@557: }
jaroslav@557: }
jaroslav@557: }