| /* |
| * 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; |
| } |
| } |