BufferedReader.java revision cff1616012dc0d56c2da9af2b9b1183e76c7e044
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.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 */
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 chars 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("markLimit < 0:" + markLimit);
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 chars 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 chars 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 chars 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 chars 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 charCount} chars in this stream. Subsequent calls to
468     * {@code read} will not return these chars unless {@code reset} is
469     * used.
470     *
471     * <p>Skipping characters may invalidate a mark if {@code markLimit}
472     * is surpassed.
473     *
474     * @param charCount the maximum number of characters to skip.
475     * @return the number of characters actually skipped.
476     * @throws IllegalArgumentException if {@code charCount < 0}.
477     * @throws IOException
478     *             if this reader is closed or some other I/O error occurs.
479     * @see #mark(int)
480     * @see #markSupported()
481     * @see #reset()
482     */
483    @Override
484    public long skip(long charCount) throws IOException {
485        if (charCount < 0) {
486            throw new IllegalArgumentException("charCount < 0: " + charCount);
487        }
488        synchronized (lock) {
489            checkNotClosed();
490            if (charCount < 1) {
491                return 0;
492            }
493            if (end - pos >= charCount) {
494                pos += charCount;
495                return charCount;
496            }
497
498            long read = end - pos;
499            pos = end;
500            while (read < charCount) {
501                if (fillBuf() == -1) {
502                    return read;
503                }
504                if (end - pos >= charCount - read) {
505                    pos += charCount - read;
506                    return charCount;
507                }
508                // Couldn't get all the characters, skip what we read
509                read += (end - pos);
510                pos = end;
511            }
512            return charCount;
513        }
514    }
515}
516