1 /*
   2  * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package java.io;
  27 
  28 
  29 /**
  30  * A character-stream reader that allows characters to be pushed back into the
  31  * stream.
  32  *
  33  * @author      Mark Reinhold
  34  * @since       1.1
  35  */
  36 
  37 public class PushbackReader extends FilterReader {
  38 
  39     /** Pushback buffer */
  40     private char[] buf;
  41 
  42     /** Current position in buffer */
  43     private int pos;
  44 
  45     /**
  46      * Creates a new pushback reader with a pushback buffer of the given size.
  47      *
  48      * @param   in   The reader from which characters will be read
  49      * @param   size The size of the pushback buffer
  50      * @exception IllegalArgumentException if {@code size <= 0}
  51      */
  52     public PushbackReader(Reader in, int size) {
  53         super(in);
  54         if (size <= 0) {
  55             throw new IllegalArgumentException("size <= 0");
  56         }
  57         this.buf = new char[size];
  58         this.pos = size;
  59     }
  60 
  61     /**
  62      * Creates a new pushback reader with a one-character pushback buffer.
  63      *
  64      * @param   in  The reader from which characters will be read
  65      */
  66     public PushbackReader(Reader in) {
  67         this(in, 1);
  68     }
  69 
  70     /** Checks to make sure that the stream has not been closed. */
  71     private void ensureOpen() throws IOException {
  72         if (buf == null)
  73             throw new IOException("Stream closed");
  74     }
  75 
  76     /**
  77      * Reads a single character.
  78      *
  79      * @return     The character read, or -1 if the end of the stream has been
  80      *             reached
  81      *
  82      * @exception  IOException  If an I/O error occurs
  83      */
  84     public int read() throws IOException {
  85         synchronized (lock) {
  86             ensureOpen();
  87             if (pos < buf.length)
  88                 return buf[pos++];
  89             else
  90                 return super.read();
  91         }
  92     }
  93 
  94     /**
  95      * Reads characters into a portion of an array.
  96      *
  97      * @param      cbuf  Destination buffer
  98      * @param      off   Offset at which to start writing characters
  99      * @param      len   Maximum number of characters to read
 100      *
 101      * @return     The number of characters read, or -1 if the end of the
 102      *             stream has been reached
 103      *
 104      * @exception  IOException  If an I/O error occurs
 105      */
 106     public int read(char cbuf[], int off, int len) throws IOException {
 107         synchronized (lock) {
 108             ensureOpen();
 109             try {
 110                 if (len <= 0) {
 111                     if (len < 0) {
 112                         throw new IndexOutOfBoundsException();
 113                     } else if ((off < 0) || (off > cbuf.length)) {
 114                         throw new IndexOutOfBoundsException();
 115                     }
 116                     return 0;
 117                 }
 118                 int avail = buf.length - pos;
 119                 if (avail > 0) {
 120                     if (len < avail)
 121                         avail = len;
 122                     System.arraycopy(buf, pos, cbuf, off, avail);
 123                     pos += avail;
 124                     off += avail;
 125                     len -= avail;
 126                 }
 127                 if (len > 0) {
 128                     len = super.read(cbuf, off, len);
 129                     if (len == -1) {
 130                         return (avail == 0) ? -1 : avail;
 131                     }
 132                     return avail + len;
 133                 }
 134                 return avail;
 135             } catch (ArrayIndexOutOfBoundsException e) {
 136                 throw new IndexOutOfBoundsException();
 137             }
 138         }
 139     }
 140 
 141     /**
 142      * Pushes back a single character by copying it to the front of the
 143      * pushback buffer. After this method returns, the next character to be read
 144      * will have the value <code>(char)c</code>.
 145      *
 146      * @param  c  The int value representing a character to be pushed back
 147      *
 148      * @exception  IOException  If the pushback buffer is full,
 149      *                          or if some other I/O error occurs
 150      */
 151     public void unread(int c) throws IOException {
 152         synchronized (lock) {
 153             ensureOpen();
 154             if (pos == 0)
 155                 throw new IOException("Pushback buffer overflow");
 156             buf[--pos] = (char) c;
 157         }
 158     }
 159 
 160     /**
 161      * Pushes back a portion of an array of characters by copying it to the
 162      * front of the pushback buffer.  After this method returns, the next
 163      * character to be read will have the value <code>cbuf[off]</code>, the
 164      * character after that will have the value <code>cbuf[off+1]</code>, and
 165      * so forth.
 166      *
 167      * @param  cbuf  Character array
 168      * @param  off   Offset of first character to push back
 169      * @param  len   Number of characters to push back
 170      *
 171      * @exception  IOException  If there is insufficient room in the pushback
 172      *                          buffer, or if some other I/O error occurs
 173      */
 174     public void unread(char cbuf[], int off, int len) throws IOException {
 175         synchronized (lock) {
 176             ensureOpen();
 177             if (len > pos)
 178                 throw new IOException("Pushback buffer overflow");
 179             pos -= len;
 180             System.arraycopy(cbuf, off, buf, pos, len);
 181         }
 182     }
 183 
 184     /**
 185      * Pushes back an array of characters by copying it to the front of the
 186      * pushback buffer.  After this method returns, the next character to be
 187      * read will have the value <code>cbuf[0]</code>, the character after that
 188      * will have the value <code>cbuf[1]</code>, and so forth.
 189      *
 190      * @param  cbuf  Character array to push back
 191      *
 192      * @exception  IOException  If there is insufficient room in the pushback
 193      *                          buffer, or if some other I/O error occurs
 194      */
 195     public void unread(char cbuf[]) throws IOException {
 196         unread(cbuf, 0, cbuf.length);
 197     }
 198 
 199     /**
 200      * Tells whether this stream is ready to be read.
 201      *
 202      * @exception  IOException  If an I/O error occurs
 203      */
 204     public boolean ready() throws IOException {
 205         synchronized (lock) {
 206             ensureOpen();
 207             return (pos < buf.length) || super.ready();
 208         }
 209     }
 210 
 211     /**
 212      * Marks the present position in the stream. The <code>mark</code>
 213      * for class <code>PushbackReader</code> always throws an exception.
 214      *
 215      * @exception  IOException  Always, since mark is not supported
 216      */
 217     public void mark(int readAheadLimit) throws IOException {
 218         throw new IOException("mark/reset not supported");
 219     }
 220 
 221     /**
 222      * Resets the stream. The <code>reset</code> method of
 223      * <code>PushbackReader</code> always throws an exception.
 224      *
 225      * @exception  IOException  Always, since reset is not supported
 226      */
 227     public void reset() throws IOException {
 228         throw new IOException("mark/reset not supported");
 229     }
 230 
 231     /**
 232      * Tells whether this stream supports the mark() operation, which it does
 233      * not.
 234      */
 235     public boolean markSupported() {
 236         return false;
 237     }
 238 
 239     /**
 240      * Closes the stream and releases any system resources associated with
 241      * it. Once the stream has been closed, further read(),
 242      * unread(), ready(), or skip() invocations will throw an IOException.
 243      * Closing a previously closed stream has no effect.
 244      *
 245      * @exception  IOException  If an I/O error occurs
 246      */
 247     public void close() throws IOException {
 248         super.close();
 249         buf = null;
 250     }
 251 
 252     /**
 253      * Skips characters.  This method will block until some characters are
 254      * available, an I/O error occurs, or the end of the stream is reached.
 255      *
 256      * @param  n  The number of characters to skip
 257      *
 258      * @return    The number of characters actually skipped
 259      *
 260      * @exception  IllegalArgumentException  If <code>n</code> is negative.
 261      * @exception  IOException  If an I/O error occurs
 262      */
 263     public long skip(long n) throws IOException {
 264         if (n < 0L)
 265             throw new IllegalArgumentException("skip value is negative");
 266         synchronized (lock) {
 267             ensureOpen();
 268             int avail = buf.length - pos;
 269             if (avail > 0) {
 270                 if (n <= avail) {
 271                     pos += n;
 272                     return n;
 273                 } else {
 274                     pos = buf.length;
 275                     n -= avail;
 276                 }
 277             }
 278             return avail + super.skip(n);
 279         }
 280     }
 281 }