001 /* PushbackReader.java -- An character stream that can unread chars 002 Copyright (C) 1998, 2000, 2001, 2003, 2005 Free Software Foundation, Inc. 003 004 This file is part of GNU Classpath. 005 006 GNU Classpath is free software; you can redistribute it and/or modify 007 it under the terms of the GNU General Public License as published by 008 the Free Software Foundation; either version 2, or (at your option) 009 any later version. 010 011 GNU Classpath 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 for more details. 015 016 You should have received a copy of the GNU General Public License 017 along with GNU Classpath; see the file COPYING. If not, write to the 018 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 019 02110-1301 USA. 020 021 Linking this library statically or dynamically with other modules is 022 making a combined work based on this library. Thus, the terms and 023 conditions of the GNU General Public License cover the whole 024 combination. 025 026 As a special exception, the copyright holders of this library give you 027 permission to link this library with independent modules to produce an 028 executable, regardless of the license terms of these independent 029 modules, and to copy and distribute the resulting executable under 030 terms of your choice, provided that you also meet, for each linked 031 independent module, the terms and conditions of the license of that 032 module. An independent module is a module which is not derived from 033 or based on this library. If you modify this library, you may extend 034 this exception to your version of the library, but you are not 035 obligated to do so. If you do not wish to do so, delete this 036 exception statement from your version. */ 037 038 039 package java.io; 040 041 /** 042 * This subclass of <code>FilterReader</code> provides the ability to 043 * unread data from a stream. It maintains an internal buffer of unread 044 * data that is supplied to the next read operation. This is conceptually 045 * similar to mark/reset functionality, except that in this case the 046 * position to reset the stream to does not need to be known in advance. 047 * <p> 048 * The default pushback buffer size one char, but this can be overridden 049 * by the creator of the stream. 050 * 051 * @author Aaron M. Renn (arenn@urbanophile.com) 052 * @author Warren Levy (warrenl@cygnus.com) 053 */ 054 public class PushbackReader extends FilterReader 055 { 056 /** 057 * This is the default buffer size 058 */ 059 private static final int DEFAULT_BUFFER_SIZE = 1; 060 061 /** 062 * This is the buffer that is used to store the pushed back data 063 */ 064 private char[] buf; 065 066 /** 067 * This is the position in the buffer from which the next char will be 068 * read. Bytes are stored in reverse order in the buffer, starting from 069 * <code>buf[buf.length - 1]</code> to <code>buf[0]</code>. Thus when 070 * <code>pos</code> is 0 the buffer is full and <code>buf.length</code> when 071 * it is empty 072 */ 073 private int pos; 074 075 /** 076 * This method initializes a <code>PushbackReader</code> to read from the 077 * specified subordinate <code>Reader</code> with a default pushback buffer 078 * size of 1. 079 * 080 * @param in The subordinate stream to read from 081 */ 082 public PushbackReader(Reader in) 083 { 084 this(in, DEFAULT_BUFFER_SIZE); 085 } 086 087 /** 088 * This method initializes a <code>PushbackReader</code> to read from the 089 * specified subordinate <code>Reader</code> with the specified buffer 090 * size 091 * 092 * @param in The subordinate <code>Reader</code> to read from 093 * @param bufsize The pushback buffer size to use 094 */ 095 public PushbackReader(Reader in, int bufsize) 096 { 097 super(in); 098 099 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 }