1 /*
   2  * Copyright (c) 1996, 2012, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package java.io;
  27 
  28 
  29 import java.util.Iterator;
  30 import java.util.NoSuchElementException;
  31 import java.util.stream.Stream;
  32 import java.util.stream.Streams;
  33 
  34 /**
  35  * Reads text from a character-input stream, buffering characters so as to
  36  * provide for the efficient reading of characters, arrays, and lines.
  37  *
  38  * <p> The buffer size may be specified, or the default size may be used.  The
  39  * default is large enough for most purposes.
  40  *
  41  * <p> In general, each read request made of a Reader causes a corresponding
  42  * read request to be made of the underlying character or byte stream.  It is
  43  * therefore advisable to wrap a BufferedReader around any Reader whose read()
  44  * operations may be costly, such as FileReaders and InputStreamReaders.  For
  45  * example,
  46  *
  47  * <pre>
  48  * BufferedReader in
  49  *   = new BufferedReader(new FileReader("foo.in"));
  50  * </pre>
  51  *
  52  * will buffer the input from the specified file.  Without buffering, each
  53  * invocation of read() or readLine() could cause bytes to be read from the
  54  * file, converted into characters, and then returned, which can be very
  55  * inefficient.
  56  *
  57  * <p> Programs that use DataInputStreams for textual input can be localized by
  58  * replacing each DataInputStream with an appropriate BufferedReader.
  59  *
  60  * @see FileReader
  61  * @see InputStreamReader
  62  * @see java.nio.file.Files#newBufferedReader
  63  *
  64  * @author      Mark Reinhold
  65  * @since       JDK1.1
  66  */
  67 
  68 public class BufferedReader extends Reader {
  69 
  70     private Reader in;
  71 
  72     private char cb[];
  73     private int nChars, nextChar;
  74 
  75     private static final int INVALIDATED = -2;
  76     private static final int UNMARKED = -1;
  77     private int markedChar = UNMARKED;
  78     private int readAheadLimit = 0; /* Valid only when markedChar > 0 */
  79 
  80     /** If the next character is a line feed, skip it */
  81     private boolean skipLF = false;
  82 
  83     /** The skipLF flag when the mark was set */
  84     private boolean markedSkipLF = false;
  85 
  86     private static int defaultCharBufferSize = 8192;
  87     private static int defaultExpectedLineLength = 80;
  88 
  89     /**
  90      * Creates a buffering character-input stream that uses an input buffer of
  91      * the specified size.
  92      *
  93      * @param  in   A Reader
  94      * @param  sz   Input-buffer size
  95      *
  96      * @exception  IllegalArgumentException  If sz is <= 0
  97      */
  98     public BufferedReader(Reader in, int sz) {
  99         super(in);
 100         if (sz <= 0)
 101             throw new IllegalArgumentException("Buffer size <= 0");
 102         this.in = in;
 103         cb = new char[sz];
 104         nextChar = nChars = 0;
 105     }
 106 
 107     /**
 108      * Creates a buffering character-input stream that uses a default-sized
 109      * input buffer.
 110      *
 111      * @param  in   A Reader
 112      */
 113     public BufferedReader(Reader in) {
 114         this(in, defaultCharBufferSize);
 115     }
 116 
 117     /** Checks to make sure that the stream has not been closed */
 118     private void ensureOpen() throws IOException {
 119         if (in == null)
 120             throw new IOException("Stream closed");
 121     }
 122 
 123     /**
 124      * Fills the input buffer, taking the mark into account if it is valid.
 125      */
 126     private void fill() throws IOException {
 127         int dst;
 128         if (markedChar <= UNMARKED) {
 129             /* No mark */
 130             dst = 0;
 131         } else {
 132             /* Marked */
 133             int delta = nextChar - markedChar;
 134             if (delta >= readAheadLimit) {
 135                 /* Gone past read-ahead limit: Invalidate mark */
 136                 markedChar = INVALIDATED;
 137                 readAheadLimit = 0;
 138                 dst = 0;
 139             } else {
 140                 if (readAheadLimit <= cb.length) {
 141                     /* Shuffle in the current buffer */
 142                     System.arraycopy(cb, markedChar, cb, 0, delta);
 143                     markedChar = 0;
 144                     dst = delta;
 145                 } else {
 146                     /* Reallocate buffer to accommodate read-ahead limit */
 147                     char ncb[] = new char[readAheadLimit];
 148                     System.arraycopy(cb, markedChar, ncb, 0, delta);
 149                     cb = ncb;
 150                     markedChar = 0;
 151                     dst = delta;
 152                 }
 153                 nextChar = nChars = delta;
 154             }
 155         }
 156 
 157         int n;
 158         do {
 159             n = in.read(cb, dst, cb.length - dst);
 160         } while (n == 0);
 161         if (n > 0) {
 162             nChars = dst + n;
 163             nextChar = dst;
 164         }
 165     }
 166 
 167     /**
 168      * Reads a single character.
 169      *
 170      * @return The character read, as an integer in the range
 171      *         0 to 65535 (<tt>0x00-0xffff</tt>), or -1 if the
 172      *         end of the stream has been reached
 173      * @exception  IOException  If an I/O error occurs
 174      */
 175     public int read() throws IOException {
 176         synchronized (lock) {
 177             ensureOpen();
 178             for (;;) {
 179                 if (nextChar >= nChars) {
 180                     fill();
 181                     if (nextChar >= nChars)
 182                         return -1;
 183                 }
 184                 if (skipLF) {
 185                     skipLF = false;
 186                     if (cb[nextChar] == '\n') {
 187                         nextChar++;
 188                         continue;
 189                     }
 190                 }
 191                 return cb[nextChar++];
 192             }
 193         }
 194     }
 195 
 196     /**
 197      * Reads characters into a portion of an array, reading from the underlying
 198      * stream if necessary.
 199      */
 200     private int read1(char[] cbuf, int off, int len) throws IOException {
 201         if (nextChar >= nChars) {
 202             /* If the requested length is at least as large as the buffer, and
 203                if there is no mark/reset activity, and if line feeds are not
 204                being skipped, do not bother to copy the characters into the
 205                local buffer.  In this way buffered streams will cascade
 206                harmlessly. */
 207             if (len >= cb.length && markedChar <= UNMARKED && !skipLF) {
 208                 return in.read(cbuf, off, len);
 209             }
 210             fill();
 211         }
 212         if (nextChar >= nChars) return -1;
 213         if (skipLF) {
 214             skipLF = false;
 215             if (cb[nextChar] == '\n') {
 216                 nextChar++;
 217                 if (nextChar >= nChars)
 218                     fill();
 219                 if (nextChar >= nChars)
 220                     return -1;
 221             }
 222         }
 223         int n = Math.min(len, nChars - nextChar);
 224         System.arraycopy(cb, nextChar, cbuf, off, n);
 225         nextChar += n;
 226         return n;
 227     }
 228 
 229     /**
 230      * Reads characters into a portion of an array.
 231      *
 232      * <p> This method implements the general contract of the corresponding
 233      * <code>{@link Reader#read(char[], int, int) read}</code> method of the
 234      * <code>{@link Reader}</code> class.  As an additional convenience, it
 235      * attempts to read as many characters as possible by repeatedly invoking
 236      * the <code>read</code> method of the underlying stream.  This iterated
 237      * <code>read</code> continues until one of the following conditions becomes
 238      * true: <ul>
 239      *
 240      *   <li> The specified number of characters have been read,
 241      *
 242      *   <li> The <code>read</code> method of the underlying stream returns
 243      *   <code>-1</code>, indicating end-of-file, or
 244      *
 245      *   <li> The <code>ready</code> method of the underlying stream
 246      *   returns <code>false</code>, indicating that further input requests
 247      *   would block.
 248      *
 249      * </ul> If the first <code>read</code> on the underlying stream returns
 250      * <code>-1</code> to indicate end-of-file then this method returns
 251      * <code>-1</code>.  Otherwise this method returns the number of characters
 252      * actually read.
 253      *
 254      * <p> Subclasses of this class are encouraged, but not required, to
 255      * attempt to read as many characters as possible in the same fashion.
 256      *
 257      * <p> Ordinarily this method takes characters from this stream's character
 258      * buffer, filling it from the underlying stream as necessary.  If,
 259      * however, the buffer is empty, the mark is not valid, and the requested
 260      * length is at least as large as the buffer, then this method will read
 261      * characters directly from the underlying stream into the given array.
 262      * Thus redundant <code>BufferedReader</code>s will not copy data
 263      * unnecessarily.
 264      *
 265      * @param      cbuf  Destination buffer
 266      * @param      off   Offset at which to start storing characters
 267      * @param      len   Maximum number of characters to read
 268      *
 269      * @return     The number of characters read, or -1 if the end of the
 270      *             stream has been reached
 271      *
 272      * @exception  IOException  If an I/O error occurs
 273      */
 274     public int read(char cbuf[], int off, int len) throws IOException {
 275         synchronized (lock) {
 276             ensureOpen();
 277             if ((off < 0) || (off > cbuf.length) || (len < 0) ||
 278                 ((off + len) > cbuf.length) || ((off + len) < 0)) {
 279                 throw new IndexOutOfBoundsException();
 280             } else if (len == 0) {
 281                 return 0;
 282             }
 283 
 284             int n = read1(cbuf, off, len);
 285             if (n <= 0) return n;
 286             while ((n < len) && in.ready()) {
 287                 int n1 = read1(cbuf, off + n, len - n);
 288                 if (n1 <= 0) break;
 289                 n += n1;
 290             }
 291             return n;
 292         }
 293     }
 294 
 295     /**
 296      * Reads a line of text.  A line is considered to be terminated by any one
 297      * of a line feed ('\n'), a carriage return ('\r'), or a carriage return
 298      * followed immediately by a linefeed.
 299      *
 300      * @param      ignoreLF  If true, the next '\n' will be skipped
 301      *
 302      * @return     A String containing the contents of the line, not including
 303      *             any line-termination characters, or null if the end of the
 304      *             stream has been reached
 305      *
 306      * @see        java.io.LineNumberReader#readLine()
 307      *
 308      * @exception  IOException  If an I/O error occurs
 309      */
 310     String readLine(boolean ignoreLF) throws IOException {
 311         StringBuffer s = null;
 312         int startChar;
 313 
 314         synchronized (lock) {
 315             ensureOpen();
 316             boolean omitLF = ignoreLF || skipLF;
 317 
 318         bufferLoop:
 319             for (;;) {
 320 
 321                 if (nextChar >= nChars)
 322                     fill();
 323                 if (nextChar >= nChars) { /* EOF */
 324                     if (s != null && s.length() > 0)
 325                         return s.toString();
 326                     else
 327                         return null;
 328                 }
 329                 boolean eol = false;
 330                 char c = 0;
 331                 int i;
 332 
 333                 /* Skip a leftover '\n', if necessary */
 334                 if (omitLF && (cb[nextChar] == '\n'))
 335                     nextChar++;
 336                 skipLF = false;
 337                 omitLF = false;
 338 
 339             charLoop:
 340                 for (i = nextChar; i < nChars; i++) {
 341                     c = cb[i];
 342                     if ((c == '\n') || (c == '\r')) {
 343                         eol = true;
 344                         break charLoop;
 345                     }
 346                 }
 347 
 348                 startChar = nextChar;
 349                 nextChar = i;
 350 
 351                 if (eol) {
 352                     String str;
 353                     if (s == null) {
 354                         str = new String(cb, startChar, i - startChar);
 355                     } else {
 356                         s.append(cb, startChar, i - startChar);
 357                         str = s.toString();
 358                     }
 359                     nextChar++;
 360                     if (c == '\r') {
 361                         skipLF = true;
 362                     }
 363                     return str;
 364                 }
 365 
 366                 if (s == null)
 367                     s = new StringBuffer(defaultExpectedLineLength);
 368                 s.append(cb, startChar, i - startChar);
 369             }
 370         }
 371     }
 372 
 373     /**
 374      * Reads a line of text.  A line is considered to be terminated by any one
 375      * of a line feed ('\n'), a carriage return ('\r'), or a carriage return
 376      * followed immediately by a linefeed.
 377      *
 378      * @return     A String containing the contents of the line, not including
 379      *             any line-termination characters, or null if the end of the
 380      *             stream has been reached
 381      *
 382      * @exception  IOException  If an I/O error occurs
 383      *
 384      * @see java.nio.file.Files#readAllLines
 385      */
 386     public String readLine() throws IOException {
 387         return readLine(false);
 388     }
 389 
 390     /**
 391      * Skips characters.
 392      *
 393      * @param  n  The number of characters to skip
 394      *
 395      * @return    The number of characters actually skipped
 396      *
 397      * @exception  IllegalArgumentException  If <code>n</code> is negative.
 398      * @exception  IOException  If an I/O error occurs
 399      */
 400     public long skip(long n) throws IOException {
 401         if (n < 0L) {
 402             throw new IllegalArgumentException("skip value is negative");
 403         }
 404         synchronized (lock) {
 405             ensureOpen();
 406             long r = n;
 407             while (r > 0) {
 408                 if (nextChar >= nChars)
 409                     fill();
 410                 if (nextChar >= nChars) /* EOF */
 411                     break;
 412                 if (skipLF) {
 413                     skipLF = false;
 414                     if (cb[nextChar] == '\n') {
 415                         nextChar++;
 416                     }
 417                 }
 418                 long d = nChars - nextChar;
 419                 if (r <= d) {
 420                     nextChar += r;
 421                     r = 0;
 422                     break;
 423                 }
 424                 else {
 425                     r -= d;
 426                     nextChar = nChars;
 427                 }
 428             }
 429             return n - r;
 430         }
 431     }
 432 
 433     /**
 434      * Tells whether this stream is ready to be read.  A buffered character
 435      * stream is ready if the buffer is not empty, or if the underlying
 436      * character stream is ready.
 437      *
 438      * @exception  IOException  If an I/O error occurs
 439      */
 440     public boolean ready() throws IOException {
 441         synchronized (lock) {
 442             ensureOpen();
 443 
 444             /*
 445              * If newline needs to be skipped and the next char to be read
 446              * is a newline character, then just skip it right away.
 447              */
 448             if (skipLF) {
 449                 /* Note that in.ready() will return true if and only if the next
 450                  * read on the stream will not block.
 451                  */
 452                 if (nextChar >= nChars && in.ready()) {
 453                     fill();
 454                 }
 455                 if (nextChar < nChars) {
 456                     if (cb[nextChar] == '\n')
 457                         nextChar++;
 458                     skipLF = false;
 459                 }
 460             }
 461             return (nextChar < nChars) || in.ready();
 462         }
 463     }
 464 
 465     /**
 466      * Tells whether this stream supports the mark() operation, which it does.
 467      */
 468     public boolean markSupported() {
 469         return true;
 470     }
 471 
 472     /**
 473      * Marks the present position in the stream.  Subsequent calls to reset()
 474      * will attempt to reposition the stream to this point.
 475      *
 476      * @param readAheadLimit   Limit on the number of characters that may be
 477      *                         read while still preserving the mark. An attempt
 478      *                         to reset the stream after reading characters
 479      *                         up to this limit or beyond may fail.
 480      *                         A limit value larger than the size of the input
 481      *                         buffer will cause a new buffer to be allocated
 482      *                         whose size is no smaller than limit.
 483      *                         Therefore large values should be used with care.
 484      *
 485      * @exception  IllegalArgumentException  If readAheadLimit is < 0
 486      * @exception  IOException  If an I/O error occurs
 487      */
 488     public void mark(int readAheadLimit) throws IOException {
 489         if (readAheadLimit < 0) {
 490             throw new IllegalArgumentException("Read-ahead limit < 0");
 491         }
 492         synchronized (lock) {
 493             ensureOpen();
 494             this.readAheadLimit = readAheadLimit;
 495             markedChar = nextChar;
 496             markedSkipLF = skipLF;
 497         }
 498     }
 499 
 500     /**
 501      * Resets the stream to the most recent mark.
 502      *
 503      * @exception  IOException  If the stream has never been marked,
 504      *                          or if the mark has been invalidated
 505      */
 506     public void reset() throws IOException {
 507         synchronized (lock) {
 508             ensureOpen();
 509             if (markedChar < 0)
 510                 throw new IOException((markedChar == INVALIDATED)
 511                                       ? "Mark invalid"
 512                                       : "Stream not marked");
 513             nextChar = markedChar;
 514             skipLF = markedSkipLF;
 515         }
 516     }
 517 
 518     public void close() throws IOException {
 519         synchronized (lock) {
 520             if (in == null)
 521                 return;
 522             try {
 523                 in.close();
 524             } finally {
 525                 in = null;
 526                 cb = null;
 527             }
 528         }
 529     }
 530 
 531     /**
 532      * Returns a {@code Stream} describing lines read from this {@code
 533      * BufferedReader}.  The {@link Stream} is lazily populated via calls to
 534      * {@link #readLine()}.
 535      * <p>
 536      * If an {@link IOException} is thrown when accessing the underlying {@code
 537      * BufferedReader}, it is wrapped in an {@link UncheckedIOException} which
 538      * will be thrown from the {@code Stream} method that caused the read to
 539      * take place.
 540      *
 541      * @since 1.8
 542      *
 543      * @return a {@code Stream&lt;String&gt;} containing the lines of text
 544      *         described by this {@code BufferedReader}
 545      */
 546     public Stream<String> lines() {
 547         Iterator<String> iter = new Iterator<String>() {
 548             String nextLine = null;
 549 
 550             @Override
 551             public boolean hasNext() {
 552                 if (nextLine != null) {
 553                     return true;
 554                 } else {
 555                     try {
 556                         nextLine = readLine();
 557                         return (nextLine != null);
 558                     } catch (IOException e) {
 559                         throw new UncheckedIOException(e);
 560                     }
 561                 }
 562             }
 563 
 564             @Override
 565             public String next() {
 566                 if (nextLine != null || hasNext()) {
 567                     try {
 568                         return nextLine;
 569                     } finally {
 570                         nextLine = null;
 571                     }
 572                 } else {
 573                     throw new NoSuchElementException();
 574                 }
 575             }
 576         };
 577         return Streams.stream(Streams.spliteratorUnknownSize(iter), Streams.STREAM_IS_ORDERED);
 578     }
 579 }