Frames | No Frames |
1: /* PushbackReader.java -- An character stream that can unread chars 2: Copyright (C) 1998, 2000, 2001, 2003, 2005 Free Software Foundation, Inc. 3: 4: This file is part of GNU Classpath. 5: 6: GNU Classpath is free software; you can redistribute it and/or modify 7: it under the terms of the GNU General Public License as published by 8: the Free Software Foundation; either version 2, or (at your option) 9: any later version. 10: 11: GNU Classpath is distributed in the hope that it will be useful, but 12: WITHOUT ANY WARRANTY; without even the implied warranty of 13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14: General Public License for more details. 15: 16: You should have received a copy of the GNU General Public License 17: along with GNU Classpath; see the file COPYING. If not, write to the 18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 19: 02110-1301 USA. 20: 21: Linking this library statically or dynamically with other modules is 22: making a combined work based on this library. Thus, the terms and 23: conditions of the GNU General Public License cover the whole 24: combination. 25: 26: As a special exception, the copyright holders of this library give you 27: permission to link this library with independent modules to produce an 28: executable, regardless of the license terms of these independent 29: modules, and to copy and distribute the resulting executable under 30: terms of your choice, provided that you also meet, for each linked 31: independent module, the terms and conditions of the license of that 32: module. An independent module is a module which is not derived from 33: or based on this library. If you modify this library, you may extend 34: this exception to your version of the library, but you are not 35: obligated to do so. If you do not wish to do so, delete this 36: exception statement from your version. */ 37: 38: 39: package java.io; 40: 41: /** 42: * This subclass of <code>FilterReader</code> provides the ability to 43: * unread data from a stream. It maintains an internal buffer of unread 44: * data that is supplied to the next read operation. This is conceptually 45: * similar to mark/reset functionality, except that in this case the 46: * position to reset the stream to does not need to be known in advance. 47: * <p> 48: * The default pushback buffer size one char, but this can be overridden 49: * by the creator of the stream. 50: * 51: * @author Aaron M. Renn (arenn@urbanophile.com) 52: * @author Warren Levy (warrenl@cygnus.com) 53: */ 54: public class PushbackReader extends FilterReader 55: { 56: /** 57: * This is the default buffer size 58: */ 59: private static final int DEFAULT_BUFFER_SIZE = 1; 60: 61: /** 62: * This is the buffer that is used to store the pushed back data 63: */ 64: private char[] buf; 65: 66: /** 67: * This is the position in the buffer from which the next char will be 68: * read. Bytes are stored in reverse order in the buffer, starting from 69: * <code>buf[buf.length - 1]</code> to <code>buf[0]</code>. Thus when 70: * <code>pos</code> is 0 the buffer is full and <code>buf.length</code> when 71: * it is empty 72: */ 73: private int pos; 74: 75: /** 76: * This method initializes a <code>PushbackReader</code> to read from the 77: * specified subordinate <code>Reader</code> with a default pushback buffer 78: * size of 1. 79: * 80: * @param in The subordinate stream to read from 81: */ 82: public PushbackReader(Reader in) 83: { 84: this(in, DEFAULT_BUFFER_SIZE); 85: } 86: 87: /** 88: * This method initializes a <code>PushbackReader</code> to read from the 89: * specified subordinate <code>Reader</code> with the specified buffer 90: * size 91: * 92: * @param in The subordinate <code>Reader</code> to read from 93: * @param bufsize The pushback buffer size to use 94: */ 95: public PushbackReader(Reader in, int bufsize) 96: { 97: super(in); 98: 99: if (bufsize < 0) 100: throw new IllegalArgumentException("buffer size must be positive"); 101: 102: buf = new char[bufsize]; 103: pos = bufsize; 104: } 105: 106: /** 107: * This method closes the stream and frees any associated resources. 108: * 109: * @exception IOException If an error occurs. 110: */ 111: public void close() throws IOException 112: { 113: synchronized (lock) 114: { 115: buf = null; 116: super.close(); 117: } 118: } 119: 120: /** 121: * This method throws an exception when called since this class does 122: * not support mark/reset. 123: * 124: * @param read_limit Not used. 125: * 126: * @exception IOException Always thrown to indicate mark/reset not supported. 127: */ 128: public void mark(int read_limit) throws IOException 129: { 130: throw new IOException("mark not supported in this class"); 131: } 132: 133: /** 134: * This method returns <code>false</code> to indicate that it does not support 135: * mark/reset functionality. 136: * 137: * @return This method returns <code>false</code> to indicate that this 138: * class does not support mark/reset functionality 139: * 140: */ 141: public boolean markSupported() 142: { 143: return(false); 144: } 145: 146: /** 147: * This method always throws an IOException in this class because 148: * mark/reset functionality is not supported. 149: * 150: * @exception IOException Always thrown for this class 151: */ 152: public void reset() throws IOException 153: { 154: throw new IOException("reset not supported in this class"); 155: } 156: 157: /** 158: * This method determines whether or not this stream is ready to be read. 159: * If it returns <code>false</code> to indicate that the stream is not 160: * ready, any attempt to read from the stream could (but is not 161: * guaranteed to) block. 162: * <p> 163: * This stream is ready to read if there are either chars waiting to be 164: * read in the pushback buffer or if the underlying stream is ready to 165: * be read. 166: * 167: * @return <code>true</code> if this stream is ready to be read, 168: * <code>false</code> otherwise 169: * 170: * @exception IOException If an error occurs 171: */ 172: public boolean ready() throws IOException 173: { 174: synchronized (lock) 175: { 176: if (buf == null) 177: throw new IOException ("stream closed"); 178: 179: if (((buf.length - pos) > 0) || super.ready()) 180: return(true); 181: else 182: return(false); 183: } 184: } 185: 186: // Don't delete this method just because the spec says it shouldn't be there! 187: // See the CVS log for details. 188: /** 189: * This method skips the specified number of chars in the stream. It 190: * returns the actual number of chars skipped, which may be less than the 191: * requested amount. 192: * <p> 193: * This method first discards chars from the buffer, then calls the 194: * <code>skip</code> method on the underlying <code>Reader</code> to 195: * skip additional chars if necessary. 196: * 197: * @param num_chars The requested number of chars to skip 198: * 199: * @return The actual number of chars skipped. 200: * 201: * @exception IOException If an error occurs 202: */ 203: public long skip(long num_chars) throws IOException 204: { 205: synchronized (lock) 206: { 207: if (num_chars <= 0) 208: return(0); 209: 210: if ((buf.length - pos) >= num_chars) 211: { 212: pos += num_chars; 213: return(num_chars); 214: } 215: 216: int chars_discarded = buf.length - pos; 217: pos = buf.length; 218: 219: long chars_skipped = in.skip(num_chars - chars_discarded); 220: 221: return(chars_discarded + chars_skipped); 222: } 223: } 224: 225: /** 226: * This method reads an unsigned char from the input stream and returns it 227: * as an int in the range of 0-65535. This method also will return -1 if 228: * the end of the stream has been reached. The char returned will be read 229: * from the pushback buffer, unless the buffer is empty, in which case 230: * the char will be read from the underlying stream. 231: * <p> 232: * This method will block until the char can be read. 233: * 234: * @return The char read or -1 if end of stream 235: * 236: * @exception IOException If an error occurs 237: */ 238: public int read() throws IOException 239: { 240: synchronized (lock) 241: { 242: if (buf == null) 243: throw new IOException("stream closed"); 244: 245: if (pos == buf.length) 246: return(super.read()); 247: 248: ++pos; 249: return((buf[pos - 1] & 0xFFFF)); 250: } 251: } 252: 253: /** 254: * This method read chars from a stream and stores them into a caller 255: * supplied buffer. It starts storing the data at index <code>offset</code> 256: * into 257: * the buffer and attempts to read <code>len</code> chars. This method can 258: * return before reading the number of chars requested. The actual number 259: * of chars read is returned as an int. A -1 is returned to indicate the 260: * end of the stream. 261: * <p> 262: * This method will block until some data can be read. 263: * <p> 264: * This method first reads chars from the pushback buffer in order to 265: * satisfy the read request. If the pushback buffer cannot provide all 266: * of the chars requested, the remaining chars are read from the 267: * underlying stream. 268: * 269: * @param buffer The array into which the chars read should be stored 270: * @param offset The offset into the array to start storing chars 271: * @param length The requested number of chars to read 272: * 273: * @return The actual number of chars read, or -1 if end of stream. 274: * 275: * @exception IOException If an error occurs. 276: */ 277: public synchronized int read(char[] buffer, int offset, int length) 278: throws IOException 279: { 280: synchronized (lock) 281: { 282: if (buf == null) 283: throw new IOException("stream closed"); 284: 285: if (offset < 0 || length < 0 || offset + length > buffer.length) 286: throw new ArrayIndexOutOfBoundsException(); 287: 288: int numBytes = Math.min(buf.length - pos, length); 289: if (numBytes > 0) 290: { 291: System.arraycopy (buf, pos, buffer, offset, numBytes); 292: pos += numBytes; 293: return numBytes; 294: } 295: 296: return super.read(buffer, offset, length); 297: } 298: } 299: 300: /** 301: * This method pushes a single char of data into the pushback buffer. 302: * The char pushed back is the one that will be returned as the first char 303: * of the next read. 304: * <p> 305: * If the pushback buffer is full, this method throws an exception. 306: * <p> 307: * The argument to this method is an <code>int</code>. Only the low eight 308: * bits of this value are pushed back. 309: * 310: * @param b The char to be pushed back, passed as an int 311: * 312: * @exception IOException If the pushback buffer is full. 313: */ 314: public void unread(int b) throws IOException 315: { 316: synchronized (lock) 317: { 318: if (buf == null) 319: throw new IOException("stream closed"); 320: if (pos == 0) 321: throw new IOException("Pushback buffer is full"); 322: 323: --pos; 324: buf[pos] = (char)(b & 0xFFFF); 325: } 326: } 327: 328: /** 329: * This method pushes all of the chars in the passed char array into 330: * the pushback buffer. These chars are pushed in reverse order so that 331: * the next char read from the stream after this operation will be 332: * <code>buf[0]</code> followed by <code>buf[1]</code>, etc. 333: * <p> 334: * If the pushback buffer cannot hold all of the requested chars, an 335: * exception is thrown. 336: * 337: * @param buf The char array to be pushed back 338: * 339: * @exception IOException If the pushback buffer is full 340: */ 341: public synchronized void unread(char[] buf) throws IOException 342: { 343: unread(buf, 0, buf.length); 344: } 345: 346: /** 347: * This method pushed back chars from the passed in array into the pushback 348: * buffer. The chars from <code>buf[offset]</code> to 349: * <code>buf[offset + len]</code> 350: * are pushed in reverse order so that the next char read from the stream 351: * after this operation will be <code>buf[offset]</code> followed by 352: * <code>buf[offset + 1]</code>, etc. 353: * <p> 354: * If the pushback buffer cannot hold all of the requested chars, an 355: * exception is thrown. 356: * 357: * @param buffer The char array to be pushed back 358: * @param offset The index into the array where the chars to be push start 359: * @param length The number of chars to be pushed. 360: * 361: * @exception IOException If the pushback buffer is full 362: */ 363: public synchronized void unread(char[] buffer, int offset, int length) 364: throws IOException 365: { 366: synchronized (lock) 367: { 368: if (buf == null) 369: throw new IOException("stream closed"); 370: if (pos < length) 371: throw new IOException("Pushback buffer is full"); 372: 373: // Note the order that these chars are being added is the opposite 374: // of what would be done if they were added to the buffer one at a time. 375: // See the Java Class Libraries book p. 1397. 376: System.arraycopy(buffer, offset, buf, pos - length, length); 377: 378: // Don't put this into the arraycopy above, an exception might be thrown 379: // and in that case we don't want to modify pos. 380: pos -= length; 381: } 382: } 383: }