BufferedReader.java revision fdb2704414a9ed92394ada0d1395e4db86889465
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 * BufferedReader is a buffered character input reader. Buffering allows reading
28 * from character streams more efficiently. If the default size of the buffer is
29 * not practical, another size may be specified. Reading a character from a
30 * Reader class usually involves reading a character from its Stream or
31 * subsequent Reader. It is advisable to wrap a BufferedReader around those
32 * Readers whose read operations may have high latency. For example, the
33 * following code
34 *
35 * <pre>
36 * BufferedReader inReader = new BufferedReader(new FileReader(&quot;file.java&quot;));
37 * </pre>
38 *
39 * will buffer input for the file <code>file.java</code>.
40 *
41 * @see BufferedWriter
42 * @since 1.1
43 */
44public class BufferedReader extends Reader {
45
46    private Reader in;
47
48    private char[] buf;
49
50    private int marklimit = -1;
51
52    private int count;
53
54    private int markpos = -1;
55
56    private int pos;
57
58    /**
59     * Constructs a new BufferedReader on the Reader <code>in</code>. The
60     * default buffer size (8K) is allocated and all reads can now be filtered
61     * through this BufferedReader.
62     *
63     * @param in
64     *            the Reader to buffer reads on.
65     */
66    public BufferedReader(Reader in) {
67        super(in);
68        this.in = in;
69        buf = new char[8192];
70
71        // BEGIN android-added
72        /*
73         * For Android, we want to discourage the use of this
74         * constructor (with its arguably too-large default), so we
75         * note its use in the log. We don't disable it, nor do we
76         * alter the default, however, because we still aim to behave
77         * compatibly, and the default value, though not documented,
78         * is established by convention.
79         */
80        Logger.global.info(
81                "Default buffer size used in BufferedReader " +
82                "constructor. It would be " +
83                "better to be explicit if a 8k-char buffer is required.");
84        // END android-added
85    }
86
87    /**
88     * Constructs a new BufferedReader on the Reader <code>in</code>. The
89     * buffer size is specified by the parameter <code>size</code> and all
90     * reads can now be filtered through this BufferedReader.
91     *
92     * @param in
93     *            the Reader to buffer reads on.
94     * @param size
95     *            the size of buffer to allocate.
96     * @throws IllegalArgumentException
97     *             if the size is <= 0
98     */
99    public BufferedReader(Reader in, int size) {
100        super(in);
101        if (size <= 0) {
102            throw new IllegalArgumentException(Msg.getString("K0058")); //$NON-NLS-1$
103        }
104        this.in = in;
105        buf = new char[size];
106    }
107
108    /**
109     * Close the Reader. This implementation closes the Reader being filtered
110     * and releases the buffer used by this reader. If this BufferedReader has
111     * already been closed, nothing is done.
112     *
113     * @throws IOException
114     *             If an error occurs attempting to close this BufferedReader.
115     */
116    @Override
117    public void close() throws IOException {
118        synchronized (lock) {
119            if (!isClosed()) {
120                in.close();
121                buf = null;
122            }
123        }
124    }
125
126    private int fillbuf() throws IOException {
127        if (markpos == -1 || (pos - markpos >= marklimit)) {
128            /* Mark position not set or exceeded readlimit */
129            int result = in.read(buf, 0, buf.length);
130            if (result > 0) {
131                markpos = -1;
132                pos = 0;
133                count = result == -1 ? 0 : result;
134            }
135            return result;
136        }
137        if (markpos == 0 && marklimit > buf.length) {
138            /* Increase buffer size to accommodate the readlimit */
139            int newLength = buf.length * 2;
140            if (newLength > marklimit) {
141                newLength = marklimit;
142            }
143            char[] newbuf = new char[newLength];
144            System.arraycopy(buf, 0, newbuf, 0, buf.length);
145            buf = newbuf;
146        } else if (markpos > 0) {
147            System.arraycopy(buf, markpos, buf, 0, buf.length - markpos);
148        }
149
150        /* Set the new position and mark position */
151        pos -= markpos;
152        count = markpos = 0;
153        int charsread = in.read(buf, pos, buf.length - pos);
154        count = charsread == -1 ? pos : pos + charsread;
155        return charsread;
156    }
157
158    /**
159     * Answer a boolean indicating whether or not this BufferedReader is closed.
160     *
161     * @return <code>true</code> if this reader is closed, <code>false</code>
162     *         otherwise
163     */
164    private boolean isClosed() {
165        return buf == null;
166    }
167
168    /**
169     * Set a Mark position in this BufferedReader. The parameter
170     * <code>readLimit</code> indicates how many characters can be read before
171     * a mark is invalidated. Sending reset() will reposition the reader back to
172     * the marked position provided <code>readLimit</code> has not been
173     * surpassed.
174     *
175     * @param readlimit
176     *            an int representing how many characters must be read before
177     *            invalidating the mark.
178     *
179     * @throws IOException
180     *             If an error occurs attempting mark this BufferedReader.
181     * @throws IllegalArgumentException
182     *             If readlimit is < 0
183     */
184    @Override
185    public void mark(int readlimit) throws IOException {
186        if (readlimit < 0) {
187            throw new IllegalArgumentException();
188        }
189        synchronized (lock) {
190            if (isClosed()) {
191                throw new IOException(Msg.getString("K005b")); //$NON-NLS-1$
192            }
193            marklimit = readlimit;
194            markpos = pos;
195        }
196    }
197
198    /**
199     * Returns a boolean indicating whether or not this Reader supports mark()
200     * and reset(). This implementation returns <code>true</code>.
201     *
202     * @return <code>true</code> if mark() and reset() are supported,
203     *         <code>false</code> otherwise
204     */
205    @Override
206    public boolean markSupported() {
207        return true;
208    }
209
210    /**
211     * Reads a single character from this reader and returns the result as an
212     * int. The 2 higher-order characters are set to 0. If the end of reader was
213     * encountered then return -1. This implementation either returns a
214     * character from the buffer or if there are no characters available, fill
215     * the buffer then return a character or -1.
216     *
217     * @return the character read or -1 if end of reader.
218     *
219     * @throws IOException
220     *             If the BufferedReader is already closed or some other IO
221     *             error occurs.
222     */
223    @Override
224    public int read() throws IOException {
225        synchronized (lock) {
226            if (isClosed()) {
227                throw new IOException(Msg.getString("K005b")); //$NON-NLS-1$
228            }
229            /* Are there buffered characters available? */
230            if (pos < count || fillbuf() != -1) {
231                return buf[pos++];
232            }
233            return -1;
234        }
235    }
236
237    /**
238     * Reads at most <code>length</code> characters from this BufferedReader
239     * and stores them at <code>offset</code> in the character array
240     * <code>buffer</code>. Returns the number of characters actually read or
241     * -1 if the end of reader was encountered. If all the buffered characters
242     * have been used, a mark has not been set, and the requested number of
243     * characters is larger than this Readers buffer size, this implementation
244     * bypasses the buffer and simply places the results directly into
245     * <code>buffer</code>.
246     *
247     * @param buffer
248     *            character array to store the read characters
249     * @param offset
250     *            offset in buf to store the read characters
251     * @param length
252     *            maximum number of characters to read
253     * @return number of characters read or -1 if end of reader.
254     *
255     * @throws IOException
256     *             If the BufferedReader is already closed or some other IO
257     *             error occurs.
258     */
259    @Override
260    public int read(char[] buffer, int offset, int length) throws IOException {
261        synchronized (lock) {
262            if (isClosed()) {
263                throw new IOException(Msg.getString("K005b")); //$NON-NLS-1$
264            }
265            if (offset < 0 || offset > buffer.length - length || length < 0) {
266                throw new IndexOutOfBoundsException();
267            }
268            if (length == 0) {
269                return 0;
270            }
271            int required;
272            if (pos < count) {
273                /* There are bytes available in the buffer. */
274                int copylength = count - pos >= length ? length : count - pos;
275                System.arraycopy(buf, pos, buffer, offset, copylength);
276                pos += copylength;
277                if (copylength == length || !in.ready()) {
278                    return copylength;
279                }
280                offset += copylength;
281                required = length - copylength;
282            } else {
283                required = length;
284            }
285
286            while (true) {
287                int read;
288                /*
289                 * If we're not marked and the required size is greater than the
290                 * buffer, simply read the bytes directly bypassing the buffer.
291                 */
292                if (markpos == -1 && required >= buf.length) {
293                    read = in.read(buffer, offset, required);
294                    if (read == -1) {
295                        return required == length ? -1 : length - required;
296                    }
297                } else {
298                    if (fillbuf() == -1) {
299                        return required == length ? -1 : length - required;
300                    }
301                    read = count - pos >= required ? required : count - pos;
302                    System.arraycopy(buf, pos, buffer, offset, read);
303                    pos += read;
304                }
305                required -= read;
306                if (required == 0) {
307                    return length;
308                }
309                if (!in.ready()) {
310                    return length - required;
311                }
312                offset += read;
313            }
314        }
315    }
316
317    /**
318     * Returns a <code>String</code> representing the next line of text
319     * available in this BufferedReader. A line is represented by 0 or more
320     * characters followed by <code>'\n'</code>, <code>'\r'</code>,
321     * <code>'\r\n'</code> or end of stream. The <code>String</code> does
322     * not include the newline sequence.
323     *
324     * @return the contents of the line or null if no characters were read
325     *         before end of stream.
326     *
327     * @throws IOException
328     *             If the BufferedReader is already closed or some other IO
329     *             error occurs.
330     */
331    public String readLine() throws IOException {
332        synchronized (lock) {
333            if (isClosed()) {
334                throw new IOException(Msg.getString("K005b")); //$NON-NLS-1$
335            }
336            /* Are there buffered characters available? */
337            if ((pos >= count) && (fillbuf() == -1)) {
338                return null;
339            }
340            for (int charPos = pos; charPos < count; charPos++) {
341                char ch = buf[charPos];
342                if (ch > '\r') {
343                    continue;
344                }
345                if (ch == '\n') {
346                    String res = new String(buf, pos, charPos - pos);
347                    pos = charPos + 1;
348                    return res;
349                } else if (ch == '\r') {
350                    String res = new String(buf, pos, charPos - pos);
351                    pos = charPos + 1;
352                    if (((pos < count) || (fillbuf() != -1))
353                            && (buf[pos] == '\n')) {
354                        pos++;
355                    }
356                    return res;
357                }
358            }
359
360            char eol = '\0';
361            StringBuilder result = new StringBuilder(80);
362            /* Typical Line Length */
363
364            result.append(buf, pos, count - pos);
365            pos = count;
366            while (true) {
367                /* Are there buffered characters available? */
368                if (pos >= count) {
369                    if (eol == '\n') {
370                        return result.toString();
371                    }
372                    // attempt to fill buffer
373                    if (fillbuf() == -1) {
374                        // characters or null.
375                        return result.length() > 0 || eol != '\0' ? result
376                                .toString() : null;
377                    }
378                }
379                for (int charPos = pos; charPos < count; charPos++) {
380                    if (eol == '\0') {
381                        if ((buf[charPos] == '\n' || buf[charPos] == '\r')) {
382                            eol = buf[charPos];
383                        }
384                    } else if (eol == '\r' && (buf[charPos] == '\n')) {
385                        if (charPos > pos) {
386                            result.append(buf, pos, charPos - pos - 1);
387                        }
388                        pos = charPos + 1;
389                        return result.toString();
390                    } else if (eol != '\0') {
391                        if (charPos > pos) {
392                            result.append(buf, pos, charPos - pos - 1);
393                        }
394                        pos = charPos;
395                        return result.toString();
396                    }
397                }
398                if (eol == '\0') {
399                    result.append(buf, pos, count - pos);
400                } else {
401                    result.append(buf, pos, count - pos - 1);
402                }
403                pos = count;
404            }
405        }
406
407    }
408
409    /**
410     * Returns a <code>boolean</code> indicating whether or not this Reader is
411     * ready to be read without blocking. If the result is <code>true</code>,
412     * the next <code>read()</code> will not block. If the result is
413     * <code>false</code> this Reader may or may not block when
414     * <code>read()</code> is sent.
415     *
416     * @return <code>true</code> if the receiver will not block when
417     *         <code>read()</code> is called, <code>false</code> if unknown
418     *         or blocking will occur.
419     *
420     * @throws IOException
421     *             If the BufferedReader is already closed or some other IO
422     *             error occurs.
423     */
424    @Override
425    public boolean ready() throws IOException {
426        synchronized (lock) {
427            if (isClosed()) {
428                throw new IOException(Msg.getString("K005b")); //$NON-NLS-1$
429            }
430            return ((count - pos) > 0) || in.ready();
431        }
432    }
433
434    /**
435     * Reset this BufferedReader's position to the last <code>mark()</code>
436     * location. Invocations of <code>read()/skip()</code> will occur from
437     * this new location. If this Reader was not marked, throw IOException.
438     *
439     * @throws IOException
440     *             If a problem occurred, the receiver does not support
441     *             <code>mark()/reset()</code>, or no mark has been set.
442     */
443    @Override
444    public void reset() throws IOException {
445        synchronized (lock) {
446            if (isClosed()) {
447                throw new IOException(Msg.getString("K005b")); //$NON-NLS-1$
448            }
449            if (markpos == -1) {
450                throw new IOException(Msg.getString("K005c")); //$NON-NLS-1$
451            }
452            pos = markpos;
453        }
454    }
455
456    /**
457     * Skips <code>amount</code> number of characters in this Reader.
458     * Subsequent <code>read()</code>'s will not return these characters
459     * unless <code>reset()</code> is used. Skipping characters may invalidate
460     * a mark if marklimit is surpassed.
461     *
462     * @param amount
463     *            the maximum number of characters to skip.
464     * @return the number of characters actually skipped.
465     *
466     * @throws IOException
467     *             If the BufferedReader is already closed or some other IO
468     *             error occurs.
469     * @throws IllegalArgumentException
470     *             If amount is negative
471     */
472    @Override
473    public long skip(long amount) throws IOException {
474        if (amount < 0) {
475            throw new IllegalArgumentException();
476        }
477        synchronized (lock) {
478            if (isClosed()) {
479                throw new IOException(Msg.getString("K005b")); //$NON-NLS-1$
480            }
481            if (amount < 1) {
482                return 0;
483            }
484            if (count - pos >= amount) {
485                pos += amount;
486                return amount;
487            }
488
489            long read = count - pos;
490            pos = count;
491            while (read < amount) {
492                if (fillbuf() == -1) {
493                    return read;
494                }
495                if (count - pos >= amount - read) {
496                    pos += amount - read;
497                    return amount;
498                }
499                // Couldn't get all the characters, skip what we read
500                read += (count - pos);
501                pos = count;
502            }
503            return amount;
504        }
505    }
506}
507