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