1 /*
   2  * Copyright (c) 1996, 2015, 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      * @exception  IndexOutOfBoundsException {@inheritDoc}
 106      */
 107     public int read(char[] cbuf, int off, int len) throws IOException {
 108         synchronized (lock) {
 109             ensureOpen();
 110             try {
 111                 if (len <= 0) {
 112                     if (len < 0) {
 113                         throw new IndexOutOfBoundsException();
 114                     } else if ((off < 0) || (off > cbuf.length)) {
 115                         throw new IndexOutOfBoundsException();
 116                     }
 117                     return 0;
 118                 }
 119                 int avail = buf.length - pos;
 120                 if (avail > 0) {
 121                     if (len < avail)
 122                         avail = len;
 123                     System.arraycopy(buf, pos, cbuf, off, avail);
 124                     pos += avail;
 125                     off += avail;
 126                     len -= avail;
 127                 }
 128                 if (len > 0) {
 129                     len = super.read(cbuf, off, len);
 130                     if (len == -1) {
 131                         return (avail == 0) ? -1 : avail;
 132                     }
 133                     return avail + len;
 134                 }
 135                 return avail;
 136             } catch (ArrayIndexOutOfBoundsException e) {
 137                 throw new IndexOutOfBoundsException();
 138             }
 139         }
 140     }
 141 
 142     /**
 143      * Pushes back a single character by copying it to the front of the
 144      * pushback buffer. After this method returns, the next character to be read
 145      * will have the value <code>(char)c</code>.
 146      *
 147      * @param  c  The int value representing a character to be pushed back
 148      *
 149      * @exception  IOException  If the pushback buffer is full,
 150      *                          or if some other I/O error occurs
 151      */
 152     public void unread(int c) throws IOException {
 153         synchronized (lock) {
 154             ensureOpen();
 155             if (pos == 0)
 156                 throw new IOException("Pushback buffer overflow");
 157             buf[--pos] = (char) c;
 158         }
 159     }
 160 
 161     /**
 162      * Pushes back a portion of an array of characters by copying it to the
 163      * front of the pushback buffer.  After this method returns, the next
 164      * character to be read will have the value <code>cbuf[off]</code>, the
 165      * character after that will have the value <code>cbuf[off+1]</code>, and
 166      * so forth.
 167      *
 168      * @param  cbuf  Character array
 169      * @param  off   Offset of first character to push back
 170      * @param  len   Number of characters to push back
 171      *
 172      * @exception  IOException  If there is insufficient room in the pushback
 173      *                          buffer, or if some other I/O error occurs
 174      */
 175     public void unread(char[] cbuf, int off, int len) throws IOException {
 176         synchronized (lock) {
 177             ensureOpen();
 178             if (len > pos)
 179                 throw new IOException("Pushback buffer overflow");
 180             pos -= len;
 181             System.arraycopy(cbuf, off, buf, pos, len);
 182         }
 183     }
 184 
 185     /**
 186      * Pushes back an array of characters by copying it to the front of the
 187      * pushback buffer.  After this method returns, the next character to be
 188      * read will have the value <code>cbuf[0]</code>, the character after that
 189      * will have the value <code>cbuf[1]</code>, and so forth.
 190      *
 191      * @param  cbuf  Character array to push back
 192      *
 193      * @exception  IOException  If there is insufficient room in the pushback
 194      *                          buffer, or if some other I/O error occurs
 195      */
 196     public void unread(char[] cbuf) throws IOException {
 197         unread(cbuf, 0, cbuf.length);
 198     }
 199 
 200     /**
 201      * Tells whether this stream is ready to be read.
 202      *
 203      * @exception  IOException  If an I/O error occurs
 204      */
 205     public boolean ready() throws IOException {
 206         synchronized (lock) {
 207             ensureOpen();
 208             return (pos < buf.length) || super.ready();
 209         }
 210     }
 211 
 212     /**
 213      * Marks the present position in the stream. The <code>mark</code>
 214      * for class <code>PushbackReader</code> always throws an exception.
 215      *
 216      * @exception  IOException  Always, since mark is not supported
 217      */
 218     public void mark(int readAheadLimit) throws IOException {
 219         throw new IOException("mark/reset not supported");
 220     }
 221 
 222     /**
 223      * Resets the stream. The <code>reset</code> method of
 224      * <code>PushbackReader</code> always throws an exception.
 225      *
 226      * @exception  IOException  Always, since reset is not supported
 227      */
 228     public void reset() throws IOException {
 229         throw new IOException("mark/reset not supported");
 230     }
 231 
 232     /**
 233      * Tells whether this stream supports the mark() operation, which it does
 234      * not.
 235      */
 236     public boolean markSupported() {
 237         return false;
 238     }
 239 
 240     /**
 241      * Closes the stream and releases any system resources associated with
 242      * it. Once the stream has been closed, further read(),
 243      * unread(), ready(), or skip() invocations will throw an IOException.
 244      * Closing a previously closed stream has no effect. This method will block
 245      * while there is another thread blocking on the reader.
 246      *
 247      * @exception  IOException  If an I/O error occurs
 248      */
 249     public void close() throws IOException {
 250         synchronized (lock) {
 251             super.close();
 252             buf = null;
 253         }
 254     }
 255 
 256     /**
 257      * Skips characters.  This method will block until some characters are
 258      * available, an I/O error occurs, or the end of the stream is reached.
 259      *
 260      * @param  n  The number of characters to skip
 261      *
 262      * @return    The number of characters actually skipped
 263      *
 264      * @exception  IllegalArgumentException  If <code>n</code> is negative.
 265      * @exception  IOException  If an I/O error occurs
 266      */
 267     public long skip(long n) throws IOException {
 268         if (n < 0L)
 269             throw new IllegalArgumentException("skip value is negative");
 270         synchronized (lock) {
 271             ensureOpen();
 272             int avail = buf.length - pos;
 273             if (avail > 0) {
 274                 if (n <= avail) {
 275                     pos += n;
 276                     return n;
 277                 } else {
 278                     pos = buf.length;
 279                     n -= avail;
 280                 }
 281             }
 282             return avail + super.skip(n);
 283         }
 284     }
 285 }