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