BufferedReader.java revision b1396870f92135aa140bd2b86221768dea5bc11d
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
18package java.io;
19
20import java.util.logging.Logger;
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 */
39public 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            if (offset < 0 || offset > buffer.length - length || length < 0) {
276                throw new IndexOutOfBoundsException();
277            }
278            int outstanding = length;
279            while (outstanding > 0) {
280
281                /*
282                 * If there are bytes in the buffer, grab those first.
283                 */
284                int available = end - pos;
285                if (available > 0) {
286                    int count = available >= outstanding ? outstanding : available;
287                    System.arraycopy(buf, pos, buffer, offset, count);
288                    pos += count;
289                    offset += count;
290                    outstanding -= count;
291                }
292
293                /*
294                 * Before attempting to read from the underlying stream, make
295                 * sure we really, really want to. We won't bother if we're
296                 * done, or if we've already got some bytes and reading from the
297                 * underlying stream would block.
298                 */
299                if (outstanding == 0 || (outstanding < length && !in.ready())) {
300                    break;
301                }
302
303                // assert(pos == end);
304
305                /*
306                 * If we're unmarked and the requested size is greater than our
307                 * buffer, read the bytes directly into the caller's buffer. We
308                 * don't read into smaller buffers because that could result in
309                 * a many reads.
310                 */
311                if ((mark == -1 || (pos - mark >= markLimit))
312                        && outstanding >= buf.length) {
313                    int count = in.read(buffer, offset, outstanding);
314                    if (count > 0) {
315                        offset += count;
316                        outstanding -= count;
317                        mark = -1;
318                    }
319
320                    break; // assume the source stream gave us all that it could
321                }
322
323                if (fillBuf() == -1) {
324                    break; // source is exhausted
325                }
326            }
327
328            int count = length - outstanding;
329            return (count > 0 || count == length) ? count : -1;
330        }
331    }
332
333    /**
334     * Peeks at the next input character, refilling the buffer if necessary. If
335     * this character is a newline character ("\n"), it is discarded.
336     */
337    final void chompNewline() throws IOException {
338        if ((pos != end || fillBuf() != -1)
339                && buf[pos] == '\n') {
340            pos++;
341        }
342    }
343
344    /**
345     * Returns the next line of text available from this reader. A line is
346     * represented by zero or more characters followed by {@code '\n'},
347     * {@code '\r'}, {@code "\r\n"} or the end of the reader. The string does
348     * not include the newline sequence.
349     *
350     * @return the contents of the line or {@code null} if no characters were
351     *         read before the end of the reader has been reached.
352     * @throws IOException
353     *             if this reader is closed or some other I/O error occurs.
354     */
355    public String readLine() throws IOException {
356        synchronized (lock) {
357            checkNotClosed();
358            /* has the underlying stream been exhausted? */
359            if (pos == end && fillBuf() == -1) {
360                return null;
361            }
362            for (int charPos = pos; charPos < end; charPos++) {
363                char ch = buf[charPos];
364                // BEGIN android-note
365                // a switch statement may be more efficient
366                // END android-note
367                if (ch > '\r') {
368                    continue;
369                }
370                if (ch == '\n') {
371                    String res = new String(buf, pos, charPos - pos);
372                    pos = charPos + 1;
373                    return res;
374                } else if (ch == '\r') {
375                    String res = new String(buf, pos, charPos - pos);
376                    pos = charPos + 1;
377                    if (((pos < end) || (fillBuf() != -1))
378                            && (buf[pos] == '\n')) {
379                        pos++;
380                    }
381                    return res;
382                }
383            }
384
385            char eol = '\0';
386            StringBuilder result = new StringBuilder(80);
387            /* Typical Line Length */
388
389            result.append(buf, pos, end - pos);
390            while (true) {
391                pos = end;
392
393                /* Are there buffered characters available? */
394                if (eol == '\n') {
395                    return result.toString();
396                }
397                // attempt to fill buffer
398                if (fillBuf() == -1) {
399                    // characters or null.
400                    return result.length() > 0 || eol != '\0'
401                            ? result.toString()
402                            : null;
403                }
404                for (int charPos = pos; charPos < end; charPos++) {
405                    char c = buf[charPos];
406                    if (eol == '\0') {
407                        if ((c == '\n' || c == '\r')) {
408                            eol = c;
409                        }
410                    } else if (eol == '\r' && c == '\n') {
411                        if (charPos > pos) {
412                            result.append(buf, pos, charPos - pos - 1);
413                        }
414                        pos = charPos + 1;
415                        return result.toString();
416                    } else {
417                        if (charPos > pos) {
418                            result.append(buf, pos, charPos - pos - 1);
419                        }
420                        pos = charPos;
421                        return result.toString();
422                    }
423                }
424                if (eol == '\0') {
425                    result.append(buf, pos, end - pos);
426                } else {
427                    result.append(buf, pos, end - pos - 1);
428                }
429            }
430        }
431
432    }
433
434    /**
435     * Indicates whether this reader is ready to be read without blocking.
436     *
437     * @return {@code true} if this reader will not block when {@code read} is
438     *         called, {@code false} if unknown or blocking will occur.
439     * @throws IOException
440     *             if this reader is closed or some other I/O error occurs.
441     * @see #read()
442     * @see #read(char[], int, int)
443     * @see #readLine()
444     */
445    @Override
446    public boolean ready() throws IOException {
447        synchronized (lock) {
448            checkNotClosed();
449            return ((end - pos) > 0) || in.ready();
450        }
451    }
452
453    /**
454     * Resets this reader's position to the last {@code mark()} location.
455     * Invocations of {@code read()} and {@code skip()} will occur from this new
456     * location.
457     *
458     * @throws IOException
459     *             if this reader is closed or no mark has been set.
460     * @see #mark(int)
461     * @see #markSupported()
462     */
463    @Override
464    public void reset() throws IOException {
465        synchronized (lock) {
466            checkNotClosed();
467            if (mark == -1) {
468                throw new IOException("Invalid mark");
469            }
470            pos = mark;
471        }
472    }
473
474    /**
475     * Skips {@code amount} characters in this reader. Subsequent
476     * {@code read()}s will not return these characters unless {@code reset()}
477     * is used. Skipping characters may invalidate a mark if {@code markLimit}
478     * is surpassed.
479     *
480     * @param amount
481     *            the maximum number of characters to skip.
482     * @return the number of characters actually skipped.
483     * @throws IllegalArgumentException
484     *             if {@code amount < 0}.
485     * @throws IOException
486     *             if this reader is closed or some other I/O error occurs.
487     * @see #mark(int)
488     * @see #markSupported()
489     * @see #reset()
490     */
491    @Override
492    public long skip(long amount) throws IOException {
493        if (amount < 0) {
494            throw new IllegalArgumentException();
495        }
496        synchronized (lock) {
497            checkNotClosed();
498            if (amount < 1) {
499                return 0;
500            }
501            if (end - pos >= amount) {
502                pos += amount;
503                return amount;
504            }
505
506            long read = end - pos;
507            pos = end;
508            while (read < amount) {
509                if (fillBuf() == -1) {
510                    return read;
511                }
512                if (end - pos >= amount - read) {
513                    pos += amount - read;
514                    return amount;
515                }
516                // Couldn't get all the characters, skip what we read
517                read += (end - pos);
518                pos = end;
519            }
520            return amount;
521        }
522    }
523}
524