Home | History | Annotate | Download | only in io
      1 /*
      2  *  Licensed to the Apache Software Foundation (ASF) under one or more
      3  *  contributor license agreements.  See the NOTICE file distributed with
      4  *  this work for additional information regarding copyright ownership.
      5  *  The ASF licenses this file to You under the Apache License, Version 2.0
      6  *  (the "License"); you may not use this file except in compliance with
      7  *  the License.  You may obtain a copy of the License at
      8  *
      9  *     http://www.apache.org/licenses/LICENSE-2.0
     10  *
     11  *  Unless required by applicable law or agreed to in writing, software
     12  *  distributed under the License is distributed on an "AS IS" BASIS,
     13  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14  *  See the License for the specific language governing permissions and
     15  *  limitations under the License.
     16  */
     17 
     18 package java.io;
     19 
     20 import java.util.Arrays;
     21 
     22 /**
     23  * Wraps an existing {@link Reader} and <em>buffers</em> the input. Expensive
     24  * interaction with the underlying reader is minimized, since most (smaller)
     25  * requests can be satisfied by accessing the buffer alone. The drawback is that
     26  * some extra space is required to hold the buffer and that copying takes place
     27  * when filling that buffer, but this is usually outweighed by the performance
     28  * benefits.
     29  *
     30  * <p/>A typical application pattern for the class looks like this:<p/>
     31  *
     32  * <pre>
     33  * BufferedReader buf = new BufferedReader(new FileReader(&quot;file.java&quot;));
     34  * </pre>
     35  *
     36  * @see BufferedWriter
     37  * @since 1.1
     38  */
     39 public class BufferedReader extends Reader {
     40 
     41     private Reader in;
     42 
     43     /**
     44      * The characters that can be read and refilled in bulk. We maintain three
     45      * indices into this buffer:<pre>
     46      *     { X X X X X X X X X X X X - - }
     47      *           ^     ^             ^
     48      *           |     |             |
     49      *         mark   pos           end</pre>
     50      * Pos points to the next readable character. End is one greater than the
     51      * last readable character. When {@code pos == end}, the buffer is empty and
     52      * must be {@link #fillBuf() filled} before characters can be read.
     53      *
     54      * <p>Mark is the value pos will be set to on calls to {@link #reset}. Its
     55      * value is in the range {@code [0...pos]}. If the mark is {@code -1}, the
     56      * buffer cannot be reset.
     57      *
     58      * <p>MarkLimit limits the distance between the mark and the pos. When this
     59      * limit is exceeded, {@link #reset} is permitted (but not required) to
     60      * throw an exception. For shorter distances, {@link #reset} shall not throw
     61      * (unless the reader is closed).
     62      */
     63     private char[] buf;
     64 
     65     private int pos;
     66 
     67     private int end;
     68 
     69     private int mark = -1;
     70 
     71     private int markLimit = -1;
     72 
     73     /**
     74      * Constructs a new {@code BufferedReader}, providing {@code in} with a buffer
     75      * of 8192 characters.
     76      *
     77      * @param in the {@code Reader} the buffer reads from.
     78      */
     79     public BufferedReader(Reader in) {
     80         this(in, 8192);
     81     }
     82 
     83     /**
     84      * Constructs a new {@code BufferedReader}, providing {@code in} with {@code size} characters
     85      * of buffer.
     86      *
     87      * @param in the {@code InputStream} the buffer reads from.
     88      * @param size the size of buffer in characters.
     89      * @throws IllegalArgumentException if {@code size <= 0}.
     90      */
     91     public BufferedReader(Reader in, int size) {
     92         super(in);
     93         if (size <= 0) {
     94             throw new IllegalArgumentException("size <= 0");
     95         }
     96         this.in = in;
     97         buf = new char[size];
     98     }
     99 
    100     /**
    101      * Closes this reader. This implementation closes the buffered source reader
    102      * and releases the buffer. Nothing is done if this reader has already been
    103      * closed.
    104      *
    105      * @throws IOException
    106      *             if an error occurs while closing this reader.
    107      */
    108     @Override
    109     public void close() throws IOException {
    110         synchronized (lock) {
    111             if (!isClosed()) {
    112                 in.close();
    113                 buf = null;
    114             }
    115         }
    116     }
    117 
    118     /**
    119      * Populates the buffer with data. It is an error to call this method when
    120      * the buffer still contains data; ie. if {@code pos < end}.
    121      *
    122      * @return the number of bytes read into the buffer, or -1 if the end of the
    123      *      source stream has been reached.
    124      */
    125     private int fillBuf() throws IOException {
    126         // assert(pos == end);
    127 
    128         if (mark == -1 || (pos - mark >= markLimit)) {
    129             /* mark isn't set or has exceeded its limit. use the whole buffer */
    130             int result = in.read(buf, 0, buf.length);
    131             if (result > 0) {
    132                 mark = -1;
    133                 pos = 0;
    134                 end = result;
    135             }
    136             return result;
    137         }
    138 
    139         if (mark == 0 && markLimit > buf.length) {
    140             /* the only way to make room when mark=0 is by growing the buffer */
    141             int newLength = buf.length * 2;
    142             if (newLength > markLimit) {
    143                 newLength = markLimit;
    144             }
    145             char[] newbuf = new char[newLength];
    146             System.arraycopy(buf, 0, newbuf, 0, buf.length);
    147             buf = newbuf;
    148         } else if (mark > 0) {
    149             /* make room by shifting the buffered data to left mark positions */
    150             System.arraycopy(buf, mark, buf, 0, buf.length - mark);
    151             pos -= mark;
    152             end -= mark;
    153             mark = 0;
    154         }
    155 
    156         /* Set the new position and mark position */
    157         int count = in.read(buf, pos, buf.length - pos);
    158         if (count != -1) {
    159             end += count;
    160         }
    161         return count;
    162     }
    163 
    164     /**
    165      * Indicates whether or not this reader is closed.
    166      *
    167      * @return {@code true} if this reader is closed, {@code false}
    168      *         otherwise.
    169      */
    170     private boolean isClosed() {
    171         return buf == null;
    172     }
    173 
    174     /**
    175      * Sets a mark position in this reader. The parameter {@code markLimit}
    176      * indicates how many characters can be read before the mark is invalidated.
    177      * Calling {@code reset()} will reposition the reader back to the marked
    178      * position if {@code markLimit} has not been surpassed.
    179      *
    180      * @param markLimit
    181      *            the number of characters that can be read before the mark is
    182      *            invalidated.
    183      * @throws IllegalArgumentException
    184      *             if {@code markLimit < 0}.
    185      * @throws IOException
    186      *             if an error occurs while setting a mark in this reader.
    187      * @see #markSupported()
    188      * @see #reset()
    189      */
    190     @Override
    191     public void mark(int markLimit) throws IOException {
    192         if (markLimit < 0) {
    193             throw new IllegalArgumentException();
    194         }
    195         synchronized (lock) {
    196             checkNotClosed();
    197             this.markLimit = markLimit;
    198             mark = pos;
    199         }
    200     }
    201 
    202     private void checkNotClosed() throws IOException {
    203         if (isClosed()) {
    204             throw new IOException("BufferedReader is closed");
    205         }
    206     }
    207 
    208     /**
    209      * Indicates whether this reader supports the {@code mark()} and
    210      * {@code reset()} methods. This implementation returns {@code true}.
    211      *
    212      * @return {@code true} for {@code BufferedReader}.
    213      * @see #mark(int)
    214      * @see #reset()
    215      */
    216     @Override
    217     public boolean markSupported() {
    218         return true;
    219     }
    220 
    221     /**
    222      * Reads a single character from this reader and returns it with the two
    223      * higher-order bytes set to 0. If possible, BufferedReader returns a
    224      * character from the buffer. If there are no characters available in the
    225      * buffer, it fills the buffer and then returns a character. It returns -1
    226      * if there are no more characters in the source reader.
    227      *
    228      * @return the character read or -1 if the end of the source reader has been
    229      *         reached.
    230      * @throws IOException
    231      *             if this reader is closed or some other I/O error occurs.
    232      */
    233     @Override
    234     public int read() throws IOException {
    235         synchronized (lock) {
    236             checkNotClosed();
    237             /* Are there buffered characters available? */
    238             if (pos < end || fillBuf() != -1) {
    239                 return buf[pos++];
    240             }
    241             return -1;
    242         }
    243     }
    244 
    245     /**
    246      * Reads at most {@code length} characters from this reader and stores them
    247      * at {@code offset} in the character array {@code buffer}. Returns the
    248      * number of characters actually read or -1 if the end of the source reader
    249      * has been reached. If all the buffered characters have been used, a mark
    250      * has not been set and the requested number of characters is larger than
    251      * this readers buffer size, BufferedReader bypasses the buffer and simply
    252      * places the results directly into {@code buffer}.
    253      *
    254      * @param buffer
    255      *            the character array to store the characters read.
    256      * @param offset
    257      *            the initial position in {@code buffer} to store the bytes read
    258      *            from this reader.
    259      * @param length
    260      *            the maximum number of characters to read, must be
    261      *            non-negative.
    262      * @return number of characters read or -1 if the end of the source reader
    263      *         has been reached.
    264      * @throws IndexOutOfBoundsException
    265      *             if {@code offset < 0} or {@code length < 0}, or if
    266      *             {@code offset + length} is greater than the size of
    267      *             {@code buffer}.
    268      * @throws IOException
    269      *             if this reader is closed or some other I/O error occurs.
    270      */
    271     @Override
    272     public int read(char[] buffer, int offset, int length) throws IOException {
    273         synchronized (lock) {
    274             checkNotClosed();
    275             Arrays.checkOffsetAndCount(buffer.length, offset, length);
    276             int outstanding = length;
    277             while (outstanding > 0) {
    278 
    279                 /*
    280                  * If there are bytes in the buffer, grab those first.
    281                  */
    282                 int available = end - pos;
    283                 if (available > 0) {
    284                     int count = available >= outstanding ? outstanding : available;
    285                     System.arraycopy(buf, pos, buffer, offset, count);
    286                     pos += count;
    287                     offset += count;
    288                     outstanding -= count;
    289                 }
    290 
    291                 /*
    292                  * Before attempting to read from the underlying stream, make
    293                  * sure we really, really want to. We won't bother if we're
    294                  * done, or if we've already got some bytes and reading from the
    295                  * underlying stream would block.
    296                  */
    297                 if (outstanding == 0 || (outstanding < length && !in.ready())) {
    298                     break;
    299                 }
    300 
    301                 // assert(pos == end);
    302 
    303                 /*
    304                  * If we're unmarked and the requested size is greater than our
    305                  * buffer, read the bytes directly into the caller's buffer. We
    306                  * don't read into smaller buffers because that could result in
    307                  * a many reads.
    308                  */
    309                 if ((mark == -1 || (pos - mark >= markLimit)) && outstanding >= buf.length) {
    310                     int count = in.read(buffer, offset, outstanding);
    311                     if (count > 0) {
    312                         outstanding -= count;
    313                         mark = -1;
    314                     }
    315                     break; // assume the source stream gave us all that it could
    316                 }
    317 
    318                 if (fillBuf() == -1) {
    319                     break; // source is exhausted
    320                 }
    321             }
    322 
    323             int count = length - outstanding;
    324             return (count > 0 || count == length) ? count : -1;
    325         }
    326     }
    327 
    328     /**
    329      * Peeks at the next input character, refilling the buffer if necessary. If
    330      * this character is a newline character ("\n"), it is discarded.
    331      */
    332     final void chompNewline() throws IOException {
    333         if ((pos != end || fillBuf() != -1)
    334                 && buf[pos] == '\n') {
    335             pos++;
    336         }
    337     }
    338 
    339     /**
    340      * Returns the next line of text available from this reader. A line is
    341      * represented by zero or more characters followed by {@code '\n'},
    342      * {@code '\r'}, {@code "\r\n"} or the end of the reader. The string does
    343      * not include the newline sequence.
    344      *
    345      * @return the contents of the line or {@code null} if no characters were
    346      *         read before the end of the reader has been reached.
    347      * @throws IOException
    348      *             if this reader is closed or some other I/O error occurs.
    349      */
    350     public String readLine() throws IOException {
    351         synchronized (lock) {
    352             checkNotClosed();
    353             /* has the underlying stream been exhausted? */
    354             if (pos == end && fillBuf() == -1) {
    355                 return null;
    356             }
    357             for (int charPos = pos; charPos < end; charPos++) {
    358                 char ch = buf[charPos];
    359                 if (ch > '\r') {
    360                     continue;
    361                 }
    362                 if (ch == '\n') {
    363                     String res = new String(buf, pos, charPos - pos);
    364                     pos = charPos + 1;
    365                     return res;
    366                 } else if (ch == '\r') {
    367                     String res = new String(buf, pos, charPos - pos);
    368                     pos = charPos + 1;
    369                     if (((pos < end) || (fillBuf() != -1))
    370                             && (buf[pos] == '\n')) {
    371                         pos++;
    372                     }
    373                     return res;
    374                 }
    375             }
    376 
    377             char eol = '\0';
    378             StringBuilder result = new StringBuilder(80);
    379             /* Typical Line Length */
    380 
    381             result.append(buf, pos, end - pos);
    382             while (true) {
    383                 pos = end;
    384 
    385                 /* Are there buffered characters available? */
    386                 if (eol == '\n') {
    387                     return result.toString();
    388                 }
    389                 // attempt to fill buffer
    390                 if (fillBuf() == -1) {
    391                     // characters or null.
    392                     return result.length() > 0 || eol != '\0'
    393                             ? result.toString()
    394                             : null;
    395                 }
    396                 for (int charPos = pos; charPos < end; charPos++) {
    397                     char c = buf[charPos];
    398                     if (eol == '\0') {
    399                         if ((c == '\n' || c == '\r')) {
    400                             eol = c;
    401                         }
    402                     } else if (eol == '\r' && c == '\n') {
    403                         if (charPos > pos) {
    404                             result.append(buf, pos, charPos - pos - 1);
    405                         }
    406                         pos = charPos + 1;
    407                         return result.toString();
    408                     } else {
    409                         if (charPos > pos) {
    410                             result.append(buf, pos, charPos - pos - 1);
    411                         }
    412                         pos = charPos;
    413                         return result.toString();
    414                     }
    415                 }
    416                 if (eol == '\0') {
    417                     result.append(buf, pos, end - pos);
    418                 } else {
    419                     result.append(buf, pos, end - pos - 1);
    420                 }
    421             }
    422         }
    423 
    424     }
    425 
    426     /**
    427      * Indicates whether this reader is ready to be read without blocking.
    428      *
    429      * @return {@code true} if this reader will not block when {@code read} is
    430      *         called, {@code false} if unknown or blocking will occur.
    431      * @throws IOException
    432      *             if this reader is closed or some other I/O error occurs.
    433      * @see #read()
    434      * @see #read(char[], int, int)
    435      * @see #readLine()
    436      */
    437     @Override
    438     public boolean ready() throws IOException {
    439         synchronized (lock) {
    440             checkNotClosed();
    441             return ((end - pos) > 0) || in.ready();
    442         }
    443     }
    444 
    445     /**
    446      * Resets this reader's position to the last {@code mark()} location.
    447      * Invocations of {@code read()} and {@code skip()} will occur from this new
    448      * location.
    449      *
    450      * @throws IOException
    451      *             if this reader is closed or no mark has been set.
    452      * @see #mark(int)
    453      * @see #markSupported()
    454      */
    455     @Override
    456     public void reset() throws IOException {
    457         synchronized (lock) {
    458             checkNotClosed();
    459             if (mark == -1) {
    460                 throw new IOException("Invalid mark");
    461             }
    462             pos = mark;
    463         }
    464     }
    465 
    466     /**
    467      * Skips {@code byteCount} bytes in this stream. Subsequent calls to
    468      * {@code read} will not return these bytes unless {@code reset} is
    469      * used.
    470      * Skipping characters may invalidate a mark if {@code markLimit}
    471      * is surpassed.
    472      *
    473      * @param byteCount
    474      *            the maximum number of characters to skip.
    475      * @return the number of characters actually skipped.
    476      * @throws IllegalArgumentException
    477      *             if {@code byteCount < 0}.
    478      * @throws IOException
    479      *             if this reader is closed or some other I/O error occurs.
    480      * @see #mark(int)
    481      * @see #markSupported()
    482      * @see #reset()
    483      */
    484     @Override
    485     public long skip(long byteCount) throws IOException {
    486         if (byteCount < 0) {
    487             throw new IllegalArgumentException("byteCount < 0: " + byteCount);
    488         }
    489         synchronized (lock) {
    490             checkNotClosed();
    491             if (byteCount < 1) {
    492                 return 0;
    493             }
    494             if (end - pos >= byteCount) {
    495                 pos += byteCount;
    496                 return byteCount;
    497             }
    498 
    499             long read = end - pos;
    500             pos = end;
    501             while (read < byteCount) {
    502                 if (fillBuf() == -1) {
    503                     return read;
    504                 }
    505                 if (end - pos >= byteCount - read) {
    506                     pos += byteCount - read;
    507                     return byteCount;
    508                 }
    509                 // Couldn't get all the characters, skip what we read
    510                 read += (end - pos);
    511                 pos = end;
    512             }
    513             return byteCount;
    514         }
    515     }
    516 }
    517