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