| /* | 
 |  * Copyright (c) 1994, 2013, Oracle and/or its affiliates. All rights reserved. | 
 |  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | 
 |  * | 
 |  * This code is free software; you can redistribute it and/or modify it | 
 |  * under the terms of the GNU General Public License version 2 only, as | 
 |  * published by the Free Software Foundation.  Oracle designates this | 
 |  * particular file as subject to the "Classpath" exception as provided | 
 |  * by Oracle in the LICENSE file that accompanied this code. | 
 |  * | 
 |  * This code is distributed in the hope that it will be useful, but WITHOUT | 
 |  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | 
 |  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License | 
 |  * version 2 for more details (a copy is included in the LICENSE file that | 
 |  * accompanied this code). | 
 |  * | 
 |  * You should have received a copy of the GNU General Public License version | 
 |  * 2 along with this work; if not, write to the Free Software Foundation, | 
 |  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | 
 |  * | 
 |  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | 
 |  * or visit www.oracle.com if you need additional information or have any | 
 |  * questions. | 
 |  */ | 
 |  | 
 | package java.io; | 
 |  | 
 | /** | 
 |  * A <code>PushbackInputStream</code> adds | 
 |  * functionality to another input stream, namely | 
 |  * the  ability to "push back" or "unread" | 
 |  * one byte. This is useful in situations where | 
 |  * it is  convenient for a fragment of code | 
 |  * to read an indefinite number of data bytes | 
 |  * that  are delimited by a particular byte | 
 |  * value; after reading the terminating byte, | 
 |  * the  code fragment can "unread" it, so that | 
 |  * the next read operation on the input stream | 
 |  * will reread the byte that was pushed back. | 
 |  * For example, bytes representing the  characters | 
 |  * constituting an identifier might be terminated | 
 |  * by a byte representing an  operator character; | 
 |  * a method whose job is to read just an identifier | 
 |  * can read until it  sees the operator and | 
 |  * then push the operator back to be re-read. | 
 |  * | 
 |  * @author  David Connelly | 
 |  * @author  Jonathan Payne | 
 |  * @since   JDK1.0 | 
 |  */ | 
 | public | 
 | class PushbackInputStream extends FilterInputStream { | 
 |     /** | 
 |      * The pushback buffer. | 
 |      * @since   JDK1.1 | 
 |      */ | 
 |     protected byte[] buf; | 
 |  | 
 |     /** | 
 |      * The position within the pushback buffer from which the next byte will | 
 |      * be read.  When the buffer is empty, <code>pos</code> is equal to | 
 |      * <code>buf.length</code>; when the buffer is full, <code>pos</code> is | 
 |      * equal to zero. | 
 |      * | 
 |      * @since   JDK1.1 | 
 |      */ | 
 |     protected int pos; | 
 |  | 
 |     /** | 
 |      * Check to make sure that this stream has not been closed | 
 |      */ | 
 |     private void ensureOpen() throws IOException { | 
 |         if (in == null) | 
 |             throw new IOException("Stream closed"); | 
 |     } | 
 |  | 
 |     /** | 
 |      * Creates a <code>PushbackInputStream</code> | 
 |      * with a pushback buffer of the specified <code>size</code>, | 
 |      * and saves its  argument, the input stream | 
 |      * <code>in</code>, for later use. Initially, | 
 |      * there is no pushed-back byte  (the field | 
 |      * <code>pushBack</code> is initialized to | 
 |      * <code>-1</code>). | 
 |      * | 
 |      * @param  in    the input stream from which bytes will be read. | 
 |      * @param  size  the size of the pushback buffer. | 
 |      * @exception IllegalArgumentException if {@code size <= 0} | 
 |      * @since  JDK1.1 | 
 |      */ | 
 |     public PushbackInputStream(InputStream in, int size) { | 
 |         super(in); | 
 |         if (size <= 0) { | 
 |             throw new IllegalArgumentException("size <= 0"); | 
 |         } | 
 |         this.buf = new byte[size]; | 
 |         this.pos = size; | 
 |     } | 
 |  | 
 |     /** | 
 |      * Creates a <code>PushbackInputStream</code> | 
 |      * and saves its  argument, the input stream | 
 |      * <code>in</code>, for later use. Initially, | 
 |      * there is no pushed-back byte  (the field | 
 |      * <code>pushBack</code> is initialized to | 
 |      * <code>-1</code>). | 
 |      * | 
 |      * @param   in   the input stream from which bytes will be read. | 
 |      */ | 
 |     public PushbackInputStream(InputStream in) { | 
 |         this(in, 1); | 
 |     } | 
 |  | 
 |     /** | 
 |      * Reads the next byte of data from this input stream. The value | 
 |      * byte is returned as an <code>int</code> in the range | 
 |      * <code>0</code> to <code>255</code>. If no byte is available | 
 |      * because the end of the stream has been reached, the value | 
 |      * <code>-1</code> is returned. This method blocks until input data | 
 |      * is available, the end of the stream is detected, or an exception | 
 |      * is thrown. | 
 |      * | 
 |      * <p> This method returns the most recently pushed-back byte, if there is | 
 |      * one, and otherwise calls the <code>read</code> method of its underlying | 
 |      * input stream and returns whatever value that method returns. | 
 |      * | 
 |      * @return     the next byte of data, or <code>-1</code> if the end of the | 
 |      *             stream has been reached. | 
 |      * @exception  IOException  if this input stream has been closed by | 
 |      *             invoking its {@link #close()} method, | 
 |      *             or an I/O error occurs. | 
 |      * @see        java.io.InputStream#read() | 
 |      */ | 
 |     public int read() throws IOException { | 
 |         ensureOpen(); | 
 |         if (pos < buf.length) { | 
 |             return buf[pos++] & 0xff; | 
 |         } | 
 |         return super.read(); | 
 |     } | 
 |  | 
 |     /** | 
 |      * Reads up to <code>len</code> bytes of data from this input stream into | 
 |      * an array of bytes.  This method first reads any pushed-back bytes; after | 
 |      * that, if fewer than <code>len</code> bytes have been read then it | 
 |      * reads from the underlying input stream. If <code>len</code> is not zero, the method | 
 |      * blocks until at least 1 byte of input is available; otherwise, no | 
 |      * bytes are read and <code>0</code> is returned. | 
 |      * | 
 |      * @param      b     the buffer into which the data is read. | 
 |      * @param      off   the start offset in the destination array <code>b</code> | 
 |      * @param      len   the maximum number of bytes read. | 
 |      * @return     the total number of bytes read into the buffer, or | 
 |      *             <code>-1</code> if there is no more data because the end of | 
 |      *             the stream has been reached. | 
 |      * @exception  NullPointerException If <code>b</code> is <code>null</code>. | 
 |      * @exception  IndexOutOfBoundsException If <code>off</code> is negative, | 
 |      * <code>len</code> is negative, or <code>len</code> is greater than | 
 |      * <code>b.length - off</code> | 
 |      * @exception  IOException  if this input stream has been closed by | 
 |      *             invoking its {@link #close()} method, | 
 |      *             or an I/O error occurs. | 
 |      * @see        java.io.InputStream#read(byte[], int, int) | 
 |      */ | 
 |     public int read(byte[] b, int off, int len) throws IOException { | 
 |         ensureOpen(); | 
 |         if (b == null) { | 
 |             throw new NullPointerException(); | 
 |         } else if (off < 0 || len < 0 || len > b.length - off) { | 
 |             throw new IndexOutOfBoundsException(); | 
 |         } else if (len == 0) { | 
 |             return 0; | 
 |         } | 
 |  | 
 |         int avail = buf.length - pos; | 
 |         if (avail > 0) { | 
 |             if (len < avail) { | 
 |                 avail = len; | 
 |             } | 
 |             System.arraycopy(buf, pos, b, off, avail); | 
 |             pos += avail; | 
 |             off += avail; | 
 |             len -= avail; | 
 |         } | 
 |         if (len > 0) { | 
 |             len = super.read(b, off, len); | 
 |             if (len == -1) { | 
 |                 return avail == 0 ? -1 : avail; | 
 |             } | 
 |             return avail + len; | 
 |         } | 
 |         return avail; | 
 |     } | 
 |  | 
 |     /** | 
 |      * Pushes back a byte by copying it to the front of the pushback buffer. | 
 |      * After this method returns, the next byte to be read will have the value | 
 |      * <code>(byte)b</code>. | 
 |      * | 
 |      * @param      b   the <code>int</code> value whose low-order | 
 |      *                  byte is to be pushed back. | 
 |      * @exception IOException If there is not enough room in the pushback | 
 |      *            buffer for the byte, or this input stream has been closed by | 
 |      *            invoking its {@link #close()} method. | 
 |      */ | 
 |     public void unread(int b) throws IOException { | 
 |         ensureOpen(); | 
 |         if (pos == 0) { | 
 |             throw new IOException("Push back buffer is full"); | 
 |         } | 
 |         buf[--pos] = (byte)b; | 
 |     } | 
 |  | 
 |     /** | 
 |      * Pushes back a portion of an array of bytes by copying it to the front | 
 |      * of the pushback buffer.  After this method returns, the next byte to be | 
 |      * read will have the value <code>b[off]</code>, the byte after that will | 
 |      * have the value <code>b[off+1]</code>, and so forth. | 
 |      * | 
 |      * @param b the byte array to push back. | 
 |      * @param off the start offset of the data. | 
 |      * @param len the number of bytes to push back. | 
 |      * @exception IOException If there is not enough room in the pushback | 
 |      *            buffer for the specified number of bytes, | 
 |      *            or this input stream has been closed by | 
 |      *            invoking its {@link #close()} method. | 
 |      * @since     JDK1.1 | 
 |      */ | 
 |     public void unread(byte[] b, int off, int len) throws IOException { | 
 |         ensureOpen(); | 
 |         if (len > pos) { | 
 |             throw new IOException("Push back buffer is full"); | 
 |         } | 
 |         pos -= len; | 
 |         System.arraycopy(b, off, buf, pos, len); | 
 |     } | 
 |  | 
 |     /** | 
 |      * Pushes back an array of bytes by copying it to the front of the | 
 |      * pushback buffer.  After this method returns, the next byte to be read | 
 |      * will have the value <code>b[0]</code>, the byte after that will have the | 
 |      * value <code>b[1]</code>, and so forth. | 
 |      * | 
 |      * @param b the byte array to push back | 
 |      * @exception IOException If there is not enough room in the pushback | 
 |      *            buffer for the specified number of bytes, | 
 |      *            or this input stream has been closed by | 
 |      *            invoking its {@link #close()} method. | 
 |      * @since     JDK1.1 | 
 |      */ | 
 |     public void unread(byte[] b) throws IOException { | 
 |         unread(b, 0, b.length); | 
 |     } | 
 |  | 
 |     /** | 
 |      * Returns an estimate of the number of bytes that can be read (or | 
 |      * skipped over) from this input stream without blocking by the next | 
 |      * invocation of a method for this input stream. The next invocation might be | 
 |      * the same thread or another thread.  A single read or skip of this | 
 |      * many bytes will not block, but may read or skip fewer bytes. | 
 |      * | 
 |      * <p> The method returns the sum of the number of bytes that have been | 
 |      * pushed back and the value returned by {@link | 
 |      * java.io.FilterInputStream#available available}. | 
 |      * | 
 |      * @return     the number of bytes that can be read (or skipped over) from | 
 |      *             the input stream without blocking. | 
 |      * @exception  IOException  if this input stream has been closed by | 
 |      *             invoking its {@link #close()} method, | 
 |      *             or an I/O error occurs. | 
 |      * @see        java.io.FilterInputStream#in | 
 |      * @see        java.io.InputStream#available() | 
 |      */ | 
 |     public int available() throws IOException { | 
 |         ensureOpen(); | 
 |         int n = buf.length - pos; | 
 |         int avail = super.available(); | 
 |         return n > (Integer.MAX_VALUE - avail) | 
 |                     ? Integer.MAX_VALUE | 
 |                     : n + avail; | 
 |     } | 
 |  | 
 |     /** | 
 |      * Skips over and discards <code>n</code> bytes of data from this | 
 |      * input stream. The <code>skip</code> method may, for a variety of | 
 |      * reasons, end up skipping over some smaller number of bytes, | 
 |      * possibly zero.  If <code>n</code> is negative, no bytes are skipped. | 
 |      * | 
 |      * <p> The <code>skip</code> method of <code>PushbackInputStream</code> | 
 |      * first skips over the bytes in the pushback buffer, if any.  It then | 
 |      * calls the <code>skip</code> method of the underlying input stream if | 
 |      * more bytes need to be skipped.  The actual number of bytes skipped | 
 |      * is returned. | 
 |      * | 
 |      * @param      n  {@inheritDoc} | 
 |      * @return     {@inheritDoc} | 
 |      * @exception  IOException  if the stream does not support seek, | 
 |      *            or the stream has been closed by | 
 |      *            invoking its {@link #close()} method, | 
 |      *            or an I/O error occurs. | 
 |      * @see        java.io.FilterInputStream#in | 
 |      * @see        java.io.InputStream#skip(long n) | 
 |      * @since      1.2 | 
 |      */ | 
 |     public long skip(long n) throws IOException { | 
 |         ensureOpen(); | 
 |         if (n <= 0) { | 
 |             return 0; | 
 |         } | 
 |  | 
 |         long pskip = buf.length - pos; | 
 |         if (pskip > 0) { | 
 |             if (n < pskip) { | 
 |                 pskip = n; | 
 |             } | 
 |             pos += pskip; | 
 |             n -= pskip; | 
 |         } | 
 |         if (n > 0) { | 
 |             pskip += super.skip(n); | 
 |         } | 
 |         return pskip; | 
 |     } | 
 |  | 
 |     /** | 
 |      * Tests if this input stream supports the <code>mark</code> and | 
 |      * <code>reset</code> methods, which it does not. | 
 |      * | 
 |      * @return   <code>false</code>, since this class does not support the | 
 |      *           <code>mark</code> and <code>reset</code> methods. | 
 |      * @see     java.io.InputStream#mark(int) | 
 |      * @see     java.io.InputStream#reset() | 
 |      */ | 
 |     public boolean markSupported() { | 
 |         return false; | 
 |     } | 
 |  | 
 |     /** | 
 |      * Marks the current position in this input stream. | 
 |      * | 
 |      * <p> The <code>mark</code> method of <code>PushbackInputStream</code> | 
 |      * does nothing. | 
 |      * | 
 |      * @param   readlimit   the maximum limit of bytes that can be read before | 
 |      *                      the mark position becomes invalid. | 
 |      * @see     java.io.InputStream#reset() | 
 |      */ | 
 |     public synchronized void mark(int readlimit) { | 
 |     } | 
 |  | 
 |     /** | 
 |      * Repositions this stream to the position at the time the | 
 |      * <code>mark</code> method was last called on this input stream. | 
 |      * | 
 |      * <p> The method <code>reset</code> for class | 
 |      * <code>PushbackInputStream</code> does nothing except throw an | 
 |      * <code>IOException</code>. | 
 |      * | 
 |      * @exception  IOException  if this method is invoked. | 
 |      * @see     java.io.InputStream#mark(int) | 
 |      * @see     java.io.IOException | 
 |      */ | 
 |     public synchronized void reset() throws IOException { | 
 |         throw new IOException("mark/reset not supported"); | 
 |     } | 
 |  | 
 |     /** | 
 |      * Closes this input stream and releases any system resources | 
 |      * associated with the stream. | 
 |      * Once the stream has been closed, further read(), unread(), | 
 |      * available(), reset(), or skip() invocations will throw an IOException. | 
 |      * Closing a previously closed stream has no effect. | 
 |      * | 
 |      * @exception  IOException  if an I/O error occurs. | 
 |      */ | 
 |     public synchronized void close() throws IOException { | 
 |         if (in == null) | 
 |             return; | 
 |         in.close(); | 
 |         in = null; | 
 |         buf = null; | 
 |     } | 
 | } |