001: /*
002: * @(#)PushbackInputStream.java 1.39 06/10/10
003: *
004: * Copyright 1990-2006 Sun Microsystems, Inc. All Rights Reserved.
005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License version
009: * 2 only, as published by the Free Software Foundation.
010: *
011: * This program is distributed in the hope that it will be useful, but
012: * WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * General Public License version 2 for more details (a copy is
015: * included at /legal/license.txt).
016: *
017: * You should have received a copy of the GNU General Public License
018: * version 2 along with this work; if not, write to the Free Software
019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA
021: *
022: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
023: * Clara, CA 95054 or visit www.sun.com if you need additional
024: * information or have any questions.
025: *
026: */
027:
028: package java.io;
029:
030: /**
031: * A <code>PushbackInputStream</code> adds
032: * functionality to another input stream, namely
033: * the ability to "push back" or "unread"
034: * one byte. This is useful in situations where
035: * it is convenient for a fragment of code
036: * to read an indefinite number of data bytes
037: * that are delimited by a particular byte
038: * value; after reading the terminating byte,
039: * the code fragment can "unread" it, so that
040: * the next read operation on the input stream
041: * will reread the byte that was pushed back.
042: * For example, bytes representing the characters
043: * constituting an identifier might be terminated
044: * by a byte representing an operator character;
045: * a method whose job is to read just an identifier
046: * can read until it sees the operator and
047: * then push the operator back to be re-read.
048: *
049: * @version 1.31, 02/02/00
050: * @since JDK1.0
051: */
052: public class PushbackInputStream extends FilterInputStream {
053: /**
054: * The pushback buffer.
055: * @since JDK1.1
056: */
057: protected byte[] buf;
058:
059: /**
060: * The position within the pushback buffer from which the next byte will
061: * be read. When the buffer is empty, <code>pos</code> is equal to
062: * <code>buf.length</code>; when the buffer is full, <code>pos</code> is
063: * equal to zero.
064: *
065: * @since JDK1.1
066: */
067: protected int pos;
068:
069: /**
070: * Check to make sure that this stream has not been closed
071: */
072: private void ensureOpen() throws IOException {
073: if (in == null)
074: throw new IOException("Stream closed");
075: }
076:
077: /**
078: * Creates a <code>PushbackInputStream</code>
079: * with a pushback buffer of the specified <code>size</code>,
080: * and saves its argument, the input stream
081: * <code>in</code>, for later use. Initially,
082: * there is no pushed-back byte (the field
083: * <code>pushBack</code> is initialized to
084: * <code>-1</code>).
085: *
086: * @param in the input stream from which bytes will be read.
087: * @param size the size of the pushback buffer.
088: * @exception IllegalArgumentException if size is <= 0
089: * @since JDK1.1
090: */
091: public PushbackInputStream(InputStream in, int size) {
092: super (in);
093: if (size <= 0) {
094: throw new IllegalArgumentException("size <= 0");
095: }
096: this .buf = new byte[size];
097: this .pos = size;
098: }
099:
100: /**
101: * Creates a <code>PushbackInputStream</code>
102: * and saves its argument, the input stream
103: * <code>in</code>, for later use. Initially,
104: * there is no pushed-back byte (the field
105: * <code>pushBack</code> is initialized to
106: * <code>-1</code>).
107: *
108: * @param in the input stream from which bytes will be read.
109: */
110: public PushbackInputStream(InputStream in) {
111: this (in, 1);
112: }
113:
114: /**
115: * Reads the next byte of data from this input stream. The value
116: * byte is returned as an <code>int</code> in the range
117: * <code>0</code> to <code>255</code>. If no byte is available
118: * because the end of the stream has been reached, the value
119: * <code>-1</code> is returned. This method blocks until input data
120: * is available, the end of the stream is detected, or an exception
121: * is thrown.
122: *
123: * <p> This method returns the most recently pushed-back byte, if there is
124: * one, and otherwise calls the <code>read</code> method of its underlying
125: * input stream and returns whatever value that method returns.
126: *
127: * @return the next byte of data, or <code>-1</code> if the end of the
128: * stream has been reached.
129: * @exception IOException if an I/O error occurs.
130: * @see java.io.InputStream#read()
131: */
132: public int read() throws IOException {
133: ensureOpen();
134: if (pos < buf.length) {
135: return buf[pos++] & 0xff;
136: }
137: return super .read();
138: }
139:
140: /**
141: * Reads up to <code>len</code> bytes of data from this input stream into
142: * an array of bytes. This method first reads any pushed-back bytes; after
143: * that, if fewer than than <code>len</code> bytes have been read then it
144: * reads from the underlying input stream. This method blocks until at
145: * least 1 byte of input is available.
146: *
147: * @param b the buffer into which the data is read.
148: * @param off the start offset of the data.
149: * @param len the maximum number of bytes read.
150: * @return the total number of bytes read into the buffer, or
151: * <code>-1</code> if there is no more data because the end of
152: * the stream has been reached.
153: * @exception IOException if an I/O error occurs.
154: * @see java.io.InputStream#read(byte[], int, int)
155: */
156: public int read(byte[] b, int off, int len) throws IOException {
157: ensureOpen();
158: if ((off < 0) || (off > b.length) || (len < 0)
159: || ((off + len) > b.length) || ((off + len) < 0)) {
160: throw new IndexOutOfBoundsException();
161: }
162:
163: if (len == 0) {
164: return 0;
165: }
166:
167: int avail = buf.length - pos;
168: if (avail > 0) {
169: if (len < avail) {
170: avail = len;
171: }
172: System.arraycopy(buf, pos, b, off, avail);
173: pos += avail;
174: off += avail;
175: len -= avail;
176: }
177: if (len > 0) {
178: len = super .read(b, off, len);
179: if (len == -1) {
180: return avail == 0 ? -1 : avail;
181: }
182: return avail + len;
183: }
184: return avail;
185: }
186:
187: /**
188: * Pushes back a byte by copying it to the front of the pushback buffer.
189: * After this method returns, the next byte to be read will have the value
190: * <code>(byte)b</code>.
191: *
192: * @param b the <code>int</code> value whose low-order
193: * byte is to be pushed back.
194: * @exception IOException If there is not enough room in the pushback
195: * buffer for the byte.
196: */
197: public void unread(int b) throws IOException {
198: ensureOpen();
199: if (pos == 0) {
200: throw new IOException("Push back buffer is full");
201: }
202: buf[--pos] = (byte) b;
203: }
204:
205: /**
206: * Pushes back a portion of an array of bytes by copying it to the front
207: * of the pushback buffer. After this method returns, the next byte to be
208: * read will have the value <code>b[off]</code>, the byte after that will
209: * have the value <code>b[off+1]</code>, and so forth.
210: *
211: * @param b the byte array to push back.
212: * @param off the start offset of the data.
213: * @param len the number of bytes to push back.
214: * @exception IOException If there is not enough room in the pushback
215: * buffer for the specified number of bytes.
216: * @since JDK1.1
217: */
218: public void unread(byte[] b, int off, int len) throws IOException {
219: ensureOpen();
220: if (len > pos) {
221: throw new IOException("Push back buffer is full");
222: }
223: pos -= len;
224: System.arraycopy(b, off, buf, pos, len);
225: }
226:
227: /**
228: * Pushes back an array of bytes by copying it to the front of the
229: * pushback buffer. After this method returns, the next byte to be read
230: * will have the value <code>b[0]</code>, the byte after that will have the
231: * value <code>b[1]</code>, and so forth.
232: *
233: * @param b the byte array to push back
234: * @exception IOException If there is not enough room in the pushback
235: * buffer for the specified number of bytes.
236: * @since JDK1.1
237: */
238: public void unread(byte[] b) throws IOException {
239: unread(b, 0, b.length);
240: }
241:
242: /**
243: * Returns the number of bytes that can be read from this input stream
244: * without blocking. This method calls the <code>available</code> method
245: * of the underlying input stream; it returns that value plus the number of
246: * bytes that have been pushed back.
247: *
248: * @return the number of bytes that can be read from the input stream
249: * without blocking.
250: * @exception IOException if an I/O error occurs.
251: * @see java.io.FilterInputStream#in
252: * @see java.io.InputStream#available()
253: */
254: public int available() throws IOException {
255: ensureOpen();
256: return (buf.length - pos) + super .available();
257: }
258:
259: /**
260: * Skips over and discards <code>n</code> bytes of data from this
261: * input stream. The <code>skip</code> method may, for a variety of
262: * reasons, end up skipping over some smaller number of bytes,
263: * possibly zero. If <code>n</code> is negative, no bytes are skipped.
264: *
265: * <p> The <code>skip</code> method of <code>PushbackInputStream</code>
266: * first skips over the bytes in the pushback buffer, if any. It then
267: * calls the <code>skip</code> method of the underlying input stream if
268: * more bytes need to be skipped. The actual number of bytes skipped
269: * is returned.
270: *
271: * @param n the number of bytes to be skipped.
272: * @return the actual number of bytes skipped.
273: * @exception IOException if an I/O error occurs.
274: * @see java.io.FilterInputStream#in
275: * @see java.io.InputStream#skip(long n)
276: * @since 1.2
277: */
278: public long skip(long n) throws IOException {
279: ensureOpen();
280: if (n <= 0) {
281: return 0;
282: }
283:
284: long pskip = buf.length - pos;
285: if (pskip > 0) {
286: if (n < pskip) {
287: pskip = n;
288: }
289: pos += pskip;
290: n -= pskip;
291: }
292: if (n > 0) {
293: pskip += super .skip(n);
294: }
295: return pskip;
296: }
297:
298: /**
299: * Tests if this input stream supports the <code>mark</code> and
300: * <code>reset</code> methods, which it does not.
301: *
302: * @return <code>false</code>, since this class does not support the
303: * <code>mark</code> and <code>reset</code> methods.
304: * @see java.io.InputStream#mark(int)
305: * @see java.io.InputStream#reset()
306: */
307: public boolean markSupported() {
308: return false;
309: }
310:
311: /**
312: * Closes this input stream and releases any system resources
313: * associated with the stream.
314: *
315: * @exception IOException if an I/O error occurs.
316: */
317: public synchronized void close() throws IOException {
318: if (in == null)
319: return;
320: in.close();
321: in = null;
322: buf = null;
323: }
324:
325: }
|