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