GNU Classpath (0.95) | |
Frames | No Frames |
1: /* BufferedReader.java 2: Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005 3: Free Software Foundation, Inc. 4: 5: This file is part of GNU Classpath. 6: 7: GNU Classpath is free software; you can redistribute it and/or modify 8: it under the terms of the GNU General Public License as published by 9: the Free Software Foundation; either version 2, or (at your option) 10: any later version. 11: 12: GNU Classpath is distributed in the hope that it will be useful, but 13: WITHOUT ANY WARRANTY; without even the implied warranty of 14: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15: General Public License for more details. 16: 17: You should have received a copy of the GNU General Public License 18: along with GNU Classpath; see the file COPYING. If not, write to the 19: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 20: 02110-1301 USA. 21: 22: Linking this library statically or dynamically with other modules is 23: making a combined work based on this library. Thus, the terms and 24: conditions of the GNU General Public License cover the whole 25: combination. 26: 27: As a special exception, the copyright holders of this library give you 28: permission to link this library with independent modules to produce an 29: executable, regardless of the license terms of these independent 30: modules, and to copy and distribute the resulting executable under 31: terms of your choice, provided that you also meet, for each linked 32: independent module, the terms and conditions of the license of that 33: module. An independent module is a module which is not derived from 34: or based on this library. If you modify this library, you may extend 35: this exception to your version of the library, but you are not 36: obligated to do so. If you do not wish to do so, delete this 37: exception statement from your version. */ 38: 39: 40: package java.io; 41: 42: /* Written using "Java Class Libraries", 2nd edition, plus online 43: * API docs for JDK 1.2 beta from http://www.javasoft.com. 44: * Status: Believed complete and correct. 45: */ 46: 47: /** 48: * This subclass of <code>FilterReader</code> buffers input from an 49: * underlying implementation to provide a possibly more efficient read 50: * mechanism. It maintains the buffer and buffer state in instance 51: * variables that are available to subclasses. The default buffer size 52: * of 8192 chars can be overridden by the creator of the stream. 53: * <p> 54: * This class also implements mark/reset functionality. It is capable 55: * of remembering any number of input chars, to the limits of 56: * system memory or the size of <code>Integer.MAX_VALUE</code> 57: * 58: * @author Per Bothner (bothner@cygnus.com) 59: * @author Aaron M. Renn (arenn@urbanophile.com) 60: */ 61: public class BufferedReader extends Reader 62: { 63: Reader in; 64: char[] buffer; 65: /* Index of current read position. Must be >= 0 and <= limit. */ 66: /* There is a special case where pos may be equal to limit+1; this 67: * is used as an indicator that a readLine was done with a '\r' was 68: * the very last char in the buffer. Since we don't want to read-ahead 69: * and potentially block, we set pos this way to indicate the situation 70: * and deal with it later. Doing it this way rather than having a 71: * separate boolean field to indicate the condition has the advantage 72: * that it is self-clearing on things like mark/reset. 73: */ 74: int pos; 75: /* Limit of valid data in buffer. Must be >= pos and <= buffer.length. */ 76: /* This can be < pos in the one special case described above. */ 77: int limit; 78: 79: /* The value -1 means there is no mark, or the mark has been invalidated. 80: Otherwise, markPos is the index in the buffer of the marked position. 81: Must be >= 0 and <= pos. 82: Note we do not explicitly store the read-limit. 83: The implicit read-limit is (buffer.length - markPos), which is 84: guaranteed to be >= the read-limit requested in the call to mark. */ 85: int markPos = -1; 86: 87: // The JCL book specifies the default buffer size as 8K characters. 88: // This is package-private because it is used by LineNumberReader. 89: static final int DEFAULT_BUFFER_SIZE = 8192; 90: 91: /** 92: * The line buffer for <code>readLine</code>. 93: */ 94: private StringBuffer sbuf = null; 95: 96: /** 97: * Create a new <code>BufferedReader</code> that will read from the 98: * specified subordinate stream with a default buffer size of 8192 chars. 99: * 100: * @param in The subordinate stream to read from 101: */ 102: public BufferedReader(Reader in) 103: { 104: this(in, DEFAULT_BUFFER_SIZE); 105: } 106: 107: /** 108: * Create a new <code>BufferedReader</code> that will read from the 109: * specified subordinate stream with a buffer size that is specified by the 110: * caller. 111: * 112: * @param in The subordinate stream to read from 113: * @param size The buffer size to use 114: * 115: * @exception IllegalArgumentException if size <= 0 116: */ 117: public BufferedReader(Reader in, int size) 118: { 119: super(in.lock); 120: if (size <= 0) 121: throw new IllegalArgumentException("Illegal buffer size: " + size); 122: this.in = in; 123: buffer = new char[size]; 124: } 125: 126: /** 127: * This method closes the underlying stream and frees any associated 128: * resources. 129: * 130: * @exception IOException If an error occurs 131: */ 132: public void close() throws IOException 133: { 134: synchronized (lock) 135: { 136: if (in != null) 137: in.close(); 138: in = null; 139: buffer = null; 140: } 141: } 142: 143: /** 144: * Returns <code>true</code> to indicate that this class supports mark/reset 145: * functionality. 146: * 147: * @return <code>true</code> 148: */ 149: public boolean markSupported() 150: { 151: return true; 152: } 153: 154: /** 155: * Mark a position in the input to which the stream can be 156: * "reset" by calling the <code>reset()</code> method. The parameter 157: * <code>readLimit</code> is the number of chars that can be read from the 158: * stream after setting the mark before the mark becomes invalid. For 159: * example, if <code>mark()</code> is called with a read limit of 10, then 160: * when 11 chars of data are read from the stream before the 161: * <code>reset()</code> method is called, then the mark is invalid and the 162: * stream object instance is not required to remember the mark. 163: * <p> 164: * Note that the number of chars that can be remembered by this method 165: * can be greater than the size of the internal read buffer. It is also 166: * not dependent on the subordinate stream supporting mark/reset 167: * functionality. 168: * 169: * @param readLimit The number of chars that can be read before the mark 170: * becomes invalid 171: * 172: * @exception IOException If an error occurs 173: * @exception IllegalArgumentException if readLimit is negative. 174: */ 175: public void mark(int readLimit) throws IOException 176: { 177: if (readLimit < 0) 178: throw new IllegalArgumentException("Read-ahead limit is negative"); 179: 180: synchronized (lock) 181: { 182: checkStatus(); 183: // In this method we need to be aware of the special case where 184: // pos + 1 == limit. This indicates that a '\r' was the last char 185: // in the buffer during a readLine. We'll want to maintain that 186: // condition after we shift things around and if a larger buffer is 187: // needed to track readLimit, we'll have to make it one element 188: // larger to ensure we don't invalidate the mark too early, if the 189: // char following the '\r' is NOT a '\n'. This is ok because, per 190: // the spec, we are not required to invalidate when passing readLimit. 191: // 192: // Note that if 'pos > limit', then doing 'limit -= pos' will cause 193: // limit to be negative. This is the only way limit will be < 0. 194: 195: if (pos + readLimit > limit) 196: { 197: char[] old_buffer = buffer; 198: int extraBuffSpace = 0; 199: if (pos > limit) 200: extraBuffSpace = 1; 201: if (readLimit + extraBuffSpace > limit) 202: buffer = new char[readLimit + extraBuffSpace]; 203: limit -= pos; 204: if (limit >= 0) 205: { 206: System.arraycopy(old_buffer, pos, buffer, 0, limit); 207: pos = 0; 208: } 209: } 210: 211: if (limit < 0) 212: { 213: // Maintain the relationship of 'pos > limit'. 214: pos = 1; 215: limit = markPos = 0; 216: } 217: else 218: markPos = pos; 219: // Now pos + readLimit <= buffer.length. thus if we need to read 220: // beyond buffer.length, then we are allowed to invalidate markPos. 221: } 222: } 223: 224: /** 225: * Reset the stream to the point where the <code>mark()</code> method 226: * was called. Any chars that were read after the mark point was set will 227: * be re-read during subsequent reads. 228: * <p> 229: * This method will throw an IOException if the number of chars read from 230: * the stream since the call to <code>mark()</code> exceeds the mark limit 231: * passed when establishing the mark. 232: * 233: * @exception IOException If an error occurs; 234: */ 235: public void reset() throws IOException 236: { 237: synchronized (lock) 238: { 239: checkStatus(); 240: if (markPos < 0) 241: throw new IOException("mark never set or invalidated"); 242: 243: // Need to handle the extremely unlikely case where a readLine was 244: // done with a '\r' as the last char in the buffer; which was then 245: // immediately followed by a mark and a reset with NO intervening 246: // read of any sort. In that case, setting pos to markPos would 247: // lose that info and a subsequent read would thus not skip a '\n' 248: // (if one exists). The value of limit in this rare case is zero. 249: // We can assume that if limit is zero for other reasons, then 250: // pos is already set to zero and doesn't need to be readjusted. 251: if (limit > 0) 252: pos = markPos; 253: } 254: } 255: 256: /** 257: * This method determines whether or not a stream is ready to be read. If 258: * this method returns <code>false</code> then this stream could (but is 259: * not guaranteed to) block on the next read attempt. 260: * 261: * @return <code>true</code> if this stream is ready to be read, 262: * <code>false</code> otherwise 263: * 264: * @exception IOException If an error occurs 265: */ 266: public boolean ready() throws IOException 267: { 268: synchronized (lock) 269: { 270: checkStatus(); 271: return pos < limit || in.ready(); 272: } 273: } 274: 275: /** 276: * This method read chars from a stream and stores them into a caller 277: * supplied buffer. It starts storing the data at index 278: * <code>offset</code> into 279: * the buffer and attempts to read <code>len</code> chars. This method can 280: * return before reading the number of chars requested. The actual number 281: * of chars read is returned as an int. A -1 is returned to indicate the 282: * end of the stream. 283: * <p> 284: * This method will block until some data can be read. 285: * 286: * @param buf The array into which the chars read should be stored 287: * @param offset The offset into the array to start storing chars 288: * @param count The requested number of chars to read 289: * 290: * @return The actual number of chars read, or -1 if end of stream. 291: * 292: * @exception IOException If an error occurs. 293: * @exception IndexOutOfBoundsException If offset and count are not 294: * valid regarding buf. 295: */ 296: public int read(char[] buf, int offset, int count) throws IOException 297: { 298: if (offset < 0 || offset + count > buf.length || count < 0) 299: throw new IndexOutOfBoundsException(); 300: 301: synchronized (lock) 302: { 303: checkStatus(); 304: // Once again, we need to handle the special case of a readLine 305: // that has a '\r' at the end of the buffer. In this case, we'll 306: // need to skip a '\n' if it is the next char to be read. 307: // This special case is indicated by 'pos > limit'. 308: boolean retAtEndOfBuffer = false; 309: 310: int avail = limit - pos; 311: if (count > avail) 312: { 313: if (avail > 0) 314: count = avail; 315: else // pos >= limit 316: { 317: if (limit == buffer.length) 318: markPos = -1; // read too far - invalidate the mark. 319: if (pos > limit) 320: { 321: // Set a boolean and make pos == limit to simplify things. 322: retAtEndOfBuffer = true; 323: --pos; 324: } 325: if (markPos < 0) 326: { 327: // Optimization: can read directly into buf. 328: if (count >= buffer.length && !retAtEndOfBuffer) 329: return in.read(buf, offset, count); 330: pos = limit = 0; 331: } 332: avail = in.read(buffer, limit, buffer.length - limit); 333: if (retAtEndOfBuffer && avail > 0 && buffer[limit] == '\n') 334: { 335: --avail; 336: limit++; 337: } 338: if (avail < count) 339: { 340: if (avail <= 0) 341: return avail; 342: count = avail; 343: } 344: limit += avail; 345: } 346: } 347: System.arraycopy(buffer, pos, buf, offset, count); 348: pos += count; 349: return count; 350: } 351: } 352: 353: /* Read more data into the buffer. Update pos and limit appropriately. 354: Assumes pos==limit initially. May invalidate the mark if read too much. 355: Return number of chars read (never 0), or -1 on eof. */ 356: private int fill() throws IOException 357: { 358: checkStatus(); 359: // Handle the special case of a readLine that has a '\r' at the end of 360: // the buffer. In this case, we'll need to skip a '\n' if it is the 361: // next char to be read. This special case is indicated by 'pos > limit'. 362: boolean retAtEndOfBuffer = false; 363: if (pos > limit) 364: { 365: retAtEndOfBuffer = true; 366: --pos; 367: } 368: 369: if (markPos >= 0 && limit == buffer.length) 370: markPos = -1; 371: if (markPos < 0) 372: pos = limit = 0; 373: int count = in.read(buffer, limit, buffer.length - limit); 374: if (count > 0) 375: limit += count; 376: 377: if (retAtEndOfBuffer && buffer[pos] == '\n') 378: { 379: --count; 380: // If the mark was set to the location of the \n, then we 381: // must change it to fully pretend that the \n does not 382: // exist. 383: if (markPos == pos) 384: ++markPos; 385: ++pos; 386: } 387: 388: return count; 389: } 390: 391: public int read() throws IOException 392: { 393: synchronized (lock) 394: { 395: checkStatus(); 396: if (pos >= limit && fill () <= 0) 397: return -1; 398: return buffer[pos++]; 399: } 400: } 401: 402: /* Return the end of the line starting at this.pos and ending at limit. 403: * The index returns is *before* any line terminators, or limit 404: * if no line terminators were found. 405: */ 406: private int lineEnd(int limit) 407: { 408: int i = pos; 409: for (; i < limit; i++) 410: { 411: char ch = buffer[i]; 412: if (ch == '\n' || ch == '\r') 413: break; 414: } 415: return i; 416: } 417: 418: /** 419: * This method reads a single line of text from the input stream, returning 420: * it as a <code>String</code>. A line is terminated by "\n", a "\r", or 421: * an "\r\n" sequence. The system dependent line separator is not used. 422: * The line termination characters are not returned in the resulting 423: * <code>String</code>. 424: * 425: * @return The line of text read, or <code>null</code> if end of stream. 426: * 427: * @exception IOException If an error occurs 428: */ 429: public String readLine() throws IOException 430: { 431: checkStatus(); 432: // Handle the special case where a previous readLine (with no intervening 433: // reads/skips) had a '\r' at the end of the buffer. 434: // In this case, we'll need to skip a '\n' if it's the next char to be read. 435: // This special case is indicated by 'pos > limit'. 436: if (pos > limit) 437: { 438: int ch = read(); 439: if (ch < 0) 440: return null; 441: if (ch != '\n') 442: --pos; 443: } 444: int i = lineEnd(limit); 445: if (i < limit) 446: { 447: String str = String.valueOf(buffer, pos, i - pos); 448: pos = i + 1; 449: // If the last char in the buffer is a '\r', we must remember 450: // to check if the next char to be read after the buffer is refilled 451: // is a '\n'. If so, skip it. To indicate this condition, we set pos 452: // to be limit + 1, which normally is never possible. 453: if (buffer[i] == '\r') 454: if (pos == limit || buffer[pos] == '\n') 455: pos++; 456: return str; 457: } 458: if (sbuf == null) 459: sbuf = new StringBuffer(200); 460: else 461: sbuf.setLength(0); 462: sbuf.append(buffer, pos, i - pos); 463: pos = i; 464: // We only want to return null when no characters were read before 465: // EOF. So we must keep track of this separately. Otherwise we 466: // would treat an empty `sbuf' as an EOF condition, which is wrong 467: // when there is just a newline. 468: boolean eof = false; 469: for (;;) 470: { 471: // readLine should block. So we must not return until a -1 is reached. 472: if (pos >= limit) 473: { 474: // here count == 0 isn't sufficient to give a failure. 475: int count = fill(); 476: if (count < 0) 477: { 478: eof = true; 479: break; 480: } 481: continue; 482: } 483: int ch = buffer[pos++]; 484: if (ch == '\n' || ch == '\r') 485: { 486: // Check here if a '\r' was the last char in the buffer; if so, 487: // mark it as in the comment above to indicate future reads 488: // should skip a newline that is the next char read after 489: // refilling the buffer. 490: if (ch == '\r') 491: if (pos == limit || buffer[pos] == '\n') 492: pos++; 493: break; 494: } 495: i = lineEnd(limit); 496: sbuf.append(buffer, pos - 1, i - (pos - 1)); 497: pos = i; 498: } 499: return (sbuf.length() == 0 && eof) ? null : sbuf.toString(); 500: } 501: 502: /** 503: * This method skips the specified number of chars in the stream. It 504: * returns the actual number of chars skipped, which may be less than the 505: * requested amount. 506: * <p> 507: * This method first discards chars in the buffer, then calls the 508: * <code>skip</code> method on the underlying stream to skip the 509: * remaining chars. 510: * 511: * @param count The requested number of chars to skip 512: * 513: * @return The actual number of chars skipped. 514: * 515: * @exception IOException If an error occurs. 516: * @exception IllegalArgumentException If count is negative. 517: */ 518: public long skip(long count) throws IOException 519: { 520: synchronized (lock) 521: { 522: checkStatus(); 523: if (count < 0) 524: throw new IllegalArgumentException("skip value is negative"); 525: if (count == 0) 526: return 0; 527: // Yet again, we need to handle the special case of a readLine 528: // that has a '\r' at the end of the buffer. In this case, we need 529: // to ignore a '\n' if it is the next char to be read. 530: // This special case is indicated by 'pos > limit' (i.e. avail < 0). 531: // To simplify things, if we're dealing with the special case for 532: // readLine, just read the next char (since the fill method will 533: // skip the '\n' for us). By doing this, we'll have to back up pos. 534: // That's easier than trying to keep track of whether we've skipped 535: // one element or not. 536: if (pos > limit) 537: { 538: if (read() < 0) 539: return 0; 540: else 541: --pos; 542: } 543: 544: int avail = limit - pos; 545: 546: if (count < avail) 547: { 548: pos += count; 549: return count; 550: } 551: 552: pos = limit; 553: long todo = count - avail; 554: if (todo > buffer.length) 555: { 556: markPos = -1; 557: todo -= in.skip(todo); 558: } 559: else 560: { 561: while (todo > 0) 562: { 563: avail = fill(); 564: if (avail <= 0) 565: break; 566: if (avail > todo) 567: avail = (int) todo; 568: pos += avail; 569: todo -= avail; 570: } 571: } 572: return count - todo; 573: } 574: } 575: 576: private void checkStatus() throws IOException 577: { 578: if (in == null) 579: throw new IOException("Stream closed"); 580: } 581: }
GNU Classpath (0.95) |