001: /*
002: * @(#)BufferedReader.java 1.35 06/10/10
003: *
004: * Copyright 1990-2006 Sun Microsystems, Inc. All Rights Reserved.
005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License version
009: * 2 only, as published by the Free Software Foundation.
010: *
011: * This program is distributed in the hope that it will be useful, but
012: * WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * General Public License version 2 for more details (a copy is
015: * included at /legal/license.txt).
016: *
017: * You should have received a copy of the GNU General Public License
018: * version 2 along with this work; if not, write to the Free Software
019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA
021: *
022: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
023: * Clara, CA 95054 or visit www.sun.com if you need additional
024: * information or have any questions.
025: *
026: */
027:
028: package java.io;
029:
030: /**
031: * Read text from a character-input stream, buffering characters so as to
032: * provide for the efficient reading of characters, arrays, and lines.
033: *
034: * <p> The buffer size may be specified, or the default size may be used. The
035: * default is large enough for most purposes.
036: *
037: * <p> In general, each read request made of a Reader causes a corresponding
038: * read request to be made of the underlying character or byte stream. It is
039: * therefore advisable to wrap a BufferedReader around any Reader whose read()
040: * operations may be costly, such as FileReaders and InputStreamReaders. For
041: * example,
042: *
043: * <pre>
044: * BufferedReader in
045: * = new BufferedReader(new FileReader("foo.in"));
046: * </pre>
047: *
048: * will buffer the input from the specified file. Without buffering, each
049: * invocation of read() or readLine() could cause bytes to be read from the
050: * file, converted into characters, and then returned, which can be very
051: * inefficient.
052: *
053: * <p> Programs that use DataInputStreams for textual input can be localized by
054: * replacing each DataInputStream with an appropriate BufferedReader.
055: *
056: * @see FileReader
057: * @see InputStreamReader
058: *
059: * @version 1.29 10/10/01
060: * @author Mark Reinhold
061: * @since JDK1.1
062: */
063:
064: public class BufferedReader extends Reader {
065:
066: private Reader in;
067:
068: private char cb[];
069: private int nChars, nextChar;
070:
071: private static final int INVALIDATED = -2;
072: private static final int UNMARKED = -1;
073: private int markedChar = UNMARKED;
074: private int readAheadLimit = 0; /* Valid only when markedChar > 0 */
075:
076: /** If the next character is a line feed, skip it */
077: private boolean skipLF = false;
078:
079: /** The skipLF flag when the mark was set */
080: private boolean markedSkipLF = false;
081:
082: private static int defaultCharBufferSize = 8192;
083: private static int defaultExpectedLineLength = 80;
084:
085: /**
086: * Create a buffering character-input stream that uses an input buffer of
087: * the specified size.
088: *
089: * @param in A Reader
090: * @param sz Input-buffer size
091: *
092: * @exception IllegalArgumentException If sz is <= 0
093: */
094: public BufferedReader(Reader in, int sz) {
095: super (in);
096: if (sz <= 0)
097: throw new IllegalArgumentException("Buffer size <= 0");
098: this .in = in;
099: cb = new char[sz];
100: nextChar = nChars = 0;
101: }
102:
103: /**
104: * Create a buffering character-input stream that uses a default-sized
105: * input buffer.
106: *
107: * @param in A Reader
108: */
109: public BufferedReader(Reader in) {
110: this (in, defaultCharBufferSize);
111: }
112:
113: /** Check to make sure that the stream has not been closed */
114: private void ensureOpen() throws IOException {
115: if (in == null)
116: throw new IOException("Stream closed");
117: }
118:
119: /**
120: * Fill the input buffer, taking the mark into account if it is valid.
121: */
122: private void fill() throws IOException {
123: int dst;
124: if (markedChar <= UNMARKED) {
125: /* No mark */
126: dst = 0;
127: } else {
128: /* Marked */
129: int delta = nextChar - markedChar;
130: if (delta >= readAheadLimit) {
131: /* Gone past read-ahead limit: Invalidate mark */
132: markedChar = INVALIDATED;
133: readAheadLimit = 0;
134: dst = 0;
135: } else {
136: if (readAheadLimit <= cb.length) {
137: /* Shuffle in the current buffer */
138: System.arraycopy(cb, markedChar, cb, 0, delta);
139: markedChar = 0;
140: dst = delta;
141: } else {
142: /* Reallocate buffer to accomodate read-ahead limit */
143: char ncb[] = new char[readAheadLimit];
144: System.arraycopy(cb, markedChar, ncb, 0, delta);
145: cb = ncb;
146: markedChar = 0;
147: dst = delta;
148: }
149: nextChar = nChars = delta;
150: }
151: }
152:
153: int n;
154: do {
155: n = in.read(cb, dst, cb.length - dst);
156: } while (n == 0);
157: if (n > 0) {
158: nChars = dst + n;
159: nextChar = dst;
160: }
161: }
162:
163: /**
164: * Read a single character.
165: *
166: * @return The character read, as an integer in the range
167: * 0 to 65535 (<tt>0x00-0xffff</tt>), or -1 if the
168: * end of the stream has been reached
169: * @exception IOException If an I/O error occurs
170: */
171: public int read() throws IOException {
172: synchronized (lock) {
173: ensureOpen();
174: for (;;) {
175: if (nextChar >= nChars) {
176: fill();
177: if (nextChar >= nChars)
178: return -1;
179: }
180: if (skipLF) {
181: skipLF = false;
182: if (cb[nextChar] == '\n') {
183: nextChar++;
184: continue;
185: }
186: }
187: return cb[nextChar++];
188: }
189: }
190: }
191:
192: /**
193: * Read characters into a portion of an array, reading from the underlying
194: * stream if necessary.
195: */
196: private int read1(char[] cbuf, int off, int len) throws IOException {
197: if (nextChar >= nChars) {
198: /* If the requested length is at least as large as the buffer, and
199: if there is no mark/reset activity, and if line feeds are not
200: being skipped, do not bother to copy the characters into the
201: local buffer. In this way buffered streams will cascade
202: harmlessly. */
203: if (len >= cb.length && markedChar <= UNMARKED && !skipLF) {
204: return in.read(cbuf, off, len);
205: }
206: fill();
207: }
208: if (nextChar >= nChars)
209: return -1;
210: if (skipLF) {
211: skipLF = false;
212: if (cb[nextChar] == '\n') {
213: nextChar++;
214: if (nextChar >= nChars)
215: fill();
216: if (nextChar >= nChars)
217: return -1;
218: }
219: }
220: int n = Math.min(len, nChars - nextChar);
221: System.arraycopy(cb, nextChar, cbuf, off, n);
222: nextChar += n;
223: return n;
224: }
225:
226: /**
227: * Read characters into a portion of an array.
228: *
229: * <p> This method implements the general contract of the corresponding
230: * <code>{@link Reader#read(char[], int, int) read}</code> method of the
231: * <code>{@link Reader}</code> class. As an additional convenience, it
232: * attempts to read as many characters as possible by repeatedly invoking
233: * the <code>read</code> method of the underlying stream. This iterated
234: * <code>read</code> continues until one of the following conditions becomes
235: * true: <ul>
236: *
237: * <li> The specified number of characters have been read,
238: *
239: * <li> The <code>read</code> method of the underlying stream returns
240: * <code>-1</code>, indicating end-of-file, or
241: *
242: * <li> The <code>ready</code> method of the underlying stream
243: * returns <code>false</code>, indicating that further input requests
244: * would block.
245: *
246: * </ul> If the first <code>read</code> on the underlying stream returns
247: * <code>-1</code> to indicate end-of-file then this method returns
248: * <code>-1</code>. Otherwise this method returns the number of characters
249: * actually read.
250: *
251: * <p> Subclasses of this class are encouraged, but not required, to
252: * attempt to read as many characters as possible in the same fashion.
253: *
254: * <p> Ordinarily this method takes characters from this stream's character
255: * buffer, filling it from the underlying stream as necessary. If,
256: * however, the buffer is empty, the mark is not valid, and the requested
257: * length is at least as large as the buffer, then this method will read
258: * characters directly from the underlying stream into the given array.
259: * Thus redundant <code>BufferedReader</code>s will not copy data
260: * unnecessarily.
261: *
262: * @param cbuf Destination buffer
263: * @param off Offset at which to start storing characters
264: * @param len Maximum number of characters to read
265: *
266: * @return The number of characters read, or -1 if the end of the
267: * stream has been reached
268: *
269: * @exception IOException If an I/O error occurs
270: */
271: public int read(char cbuf[], int off, int len) throws IOException {
272: synchronized (lock) {
273: ensureOpen();
274: if ((off < 0) || (off > cbuf.length) || (len < 0)
275: || ((off + len) > cbuf.length) || ((off + len) < 0)) {
276: throw new IndexOutOfBoundsException();
277: } else if (len == 0) {
278: return 0;
279: }
280:
281: int n = read1(cbuf, off, len);
282: if (n <= 0)
283: return n;
284: while ((n < len) && in.ready()) {
285: int n1 = read1(cbuf, off + n, len - n);
286: if (n1 <= 0)
287: break;
288: n += n1;
289: }
290: return n;
291: }
292: }
293:
294: /**
295: * Read a line of text. A line is considered to be terminated by any one
296: * of a line feed ('\n'), a carriage return ('\r'), or a carriage return
297: * followed immediately by a linefeed.
298: *
299: * @param ignoreLF If true, the next '\n' will be skipped
300: *
301: * @return A String containing the contents of the line, not including
302: * any line-termination characters, or null if the end of the
303: * stream has been reached
304: *
305: * @see java.io.LineNumberReader#readLine()
306: *
307: * @exception IOException If an I/O error occurs
308: */
309: String readLine(boolean ignoreLF) throws IOException {
310: StringBuffer s = null;
311: int startChar;
312: boolean omitLF = ignoreLF || skipLF;
313:
314: synchronized (lock) {
315: ensureOpen();
316:
317: bufferLoop: for (;;) {
318:
319: if (nextChar >= nChars)
320: fill();
321: if (nextChar >= nChars) { /* EOF */
322: if (s != null && s.length() > 0)
323: return s.toString();
324: else
325: return null;
326: }
327: boolean eol = false;
328: char c = 0;
329: int i;
330:
331: /* Skip a leftover '\n', if necessary */
332: if (omitLF && (cb[nextChar] == '\n'))
333: nextChar++;
334: skipLF = false;
335: omitLF = false;
336:
337: charLoop: for (i = nextChar; i < nChars; i++) {
338: c = cb[i];
339: if ((c == '\n') || (c == '\r')) {
340: eol = true;
341: break charLoop;
342: }
343: }
344:
345: startChar = nextChar;
346: nextChar = i;
347:
348: if (eol) {
349: String str;
350: if (s == null) {
351: str = new String(cb, startChar, i - startChar);
352: } else {
353: s.append(cb, startChar, i - startChar);
354: str = s.toString();
355: }
356: nextChar++;
357: if (c == '\r') {
358: skipLF = true;
359: }
360: return str;
361: }
362:
363: if (s == null)
364: s = new StringBuffer(defaultExpectedLineLength);
365: s.append(cb, startChar, i - startChar);
366: }
367: }
368: }
369:
370: /**
371: * Read a line of text. A line is considered to be terminated by any one
372: * of a line feed ('\n'), a carriage return ('\r'), or a carriage return
373: * followed immediately by a linefeed.
374: *
375: * @return A String containing the contents of the line, not including
376: * any line-termination characters, or null if the end of the
377: * stream has been reached
378: *
379: * @exception IOException If an I/O error occurs
380: */
381: public String readLine() throws IOException {
382: return readLine(false);
383: }
384:
385: /**
386: * Skip characters.
387: *
388: * @param n The number of characters to skip
389: *
390: * @return The number of characters actually skipped
391: *
392: * @exception IllegalArgumentException If <code>n</code> is negative.
393: * @exception IOException If an I/O error occurs
394: */
395: public long skip(long n) throws IOException {
396: if (n < 0L) {
397: throw new IllegalArgumentException("skip value is negative");
398: }
399: synchronized (lock) {
400: ensureOpen();
401: long r = n;
402: while (r > 0) {
403: if (nextChar >= nChars)
404: fill();
405: if (nextChar >= nChars) /* EOF */
406: break;
407: if (skipLF) {
408: skipLF = false;
409: if (cb[nextChar] == '\n') {
410: nextChar++;
411: }
412: }
413: long d = nChars - nextChar;
414: if (r <= d) {
415: nextChar += r;
416: r = 0;
417: break;
418: } else {
419: r -= d;
420: nextChar = nChars;
421: }
422: }
423: return n - r;
424: }
425: }
426:
427: /**
428: * Tell whether this stream is ready to be read. A buffered character
429: * stream is ready if the buffer is not empty, or if the underlying
430: * character stream is ready.
431: *
432: * @exception IOException If an I/O error occurs
433: */
434: public boolean ready() throws IOException {
435: synchronized (lock) {
436: ensureOpen();
437:
438: /*
439: * If newline needs to be skipped and the next char to be read
440: * is a newline character, then just skip it right away.
441: */
442: if (skipLF) {
443: /* Note that in.ready() will return true if and only if the next
444: * read on the stream will not block.
445: */
446: if (nextChar >= nChars && in.ready()) {
447: fill();
448: }
449: if (nextChar < nChars) {
450: if (cb[nextChar] == '\n')
451: nextChar++;
452: skipLF = false;
453: }
454: }
455: return (nextChar < nChars) || in.ready();
456: }
457: }
458:
459: /**
460: * Tell whether this stream supports the mark() operation, which it does.
461: */
462: public boolean markSupported() {
463: return true;
464: }
465:
466: /**
467: * Mark the present position in the stream. Subsequent calls to reset()
468: * will attempt to reposition the stream to this point.
469: *
470: * @param readAheadLimit Limit on the number of characters that may be
471: * read while still preserving the mark. After
472: * reading this many characters, attempting to
473: * reset the stream may fail. A limit value larger
474: * than the size of the input buffer will cause a
475: * new buffer to be allocated whose size is no
476: * smaller than limit. Therefore large values
477: * should be used with care.
478: *
479: * @exception IllegalArgumentException If readAheadLimit is < 0
480: * @exception IOException If an I/O error occurs
481: */
482: public void mark(int readAheadLimit) throws IOException {
483: if (readAheadLimit < 0) {
484: throw new IllegalArgumentException("Read-ahead limit < 0");
485: }
486: synchronized (lock) {
487: ensureOpen();
488: this .readAheadLimit = readAheadLimit;
489: markedChar = nextChar;
490: markedSkipLF = skipLF;
491: }
492: }
493:
494: /**
495: * Reset the stream to the most recent mark.
496: *
497: * @exception IOException If the stream has never been marked,
498: * or if the mark has been invalidated
499: */
500: public void reset() throws IOException {
501: synchronized (lock) {
502: ensureOpen();
503: if (markedChar < 0)
504: throw new IOException(
505: (markedChar == INVALIDATED) ? "Mark invalid"
506: : "Stream not marked");
507: nextChar = markedChar;
508: skipLF = markedSkipLF;
509: }
510: }
511:
512: /**
513: * Close the stream.
514: *
515: * @exception IOException If an I/O error occurs
516: */
517: public void close() throws IOException {
518: synchronized (lock) {
519: if (in == null)
520: return;
521: in.close();
522: in = null;
523: cb = null;
524: }
525: }
526:
527: }
|