Source for java.io.PushbackReader

   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: }