001: /*
002: * Copyright 1994-2006 Sun Microsystems, Inc. All Rights Reserved.
003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004: *
005: * This code is free software; you can redistribute it and/or modify it
006: * under the terms of the GNU General Public License version 2 only, as
007: * published by the Free Software Foundation. Sun designates this
008: * particular file as subject to the "Classpath" exception as provided
009: * by Sun in the LICENSE file that accompanied this code.
010: *
011: * This code is distributed in the hope that it will be useful, but WITHOUT
012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014: * version 2 for more details (a copy is included in the LICENSE file that
015: * accompanied this code).
016: *
017: * You should have received a copy of the GNU General Public License version
018: * 2 along with this work; if not, write to the Free Software Foundation,
019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020: *
021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022: * CA 95054 USA or visit www.sun.com if you need additional information or
023: * have any questions.
024: */
025:
026: package java.io;
027:
028: /**
029: * A <code>PushbackInputStream</code> adds
030: * functionality to another input stream, namely
031: * the ability to "push back" or "unread"
032: * one byte. This is useful in situations where
033: * it is convenient for a fragment of code
034: * to read an indefinite number of data bytes
035: * that are delimited by a particular byte
036: * value; after reading the terminating byte,
037: * the code fragment can "unread" it, so that
038: * the next read operation on the input stream
039: * will reread the byte that was pushed back.
040: * For example, bytes representing the characters
041: * constituting an identifier might be terminated
042: * by a byte representing an operator character;
043: * a method whose job is to read just an identifier
044: * can read until it sees the operator and
045: * then push the operator back to be re-read.
046: *
047: * @author David Connelly
048: * @author Jonathan Payne
049: * @version 1.49, 05/05/07
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 this input stream has been closed by
130: * invoking its {@link #close()} method,
131: * or an I/O error occurs.
132: * @see java.io.InputStream#read()
133: */
134: public int read() throws IOException {
135: ensureOpen();
136: if (pos < buf.length) {
137: return buf[pos++] & 0xff;
138: }
139: return super .read();
140: }
141:
142: /**
143: * Reads up to <code>len</code> bytes of data from this input stream into
144: * an array of bytes. This method first reads any pushed-back bytes; after
145: * that, if fewer than <code>len</code> bytes have been read then it
146: * reads from the underlying input stream. If <code>len</code> is not zero, the method
147: * blocks until at least 1 byte of input is available; otherwise, no
148: * bytes are read and <code>0</code> is returned.
149: *
150: * @param b the buffer into which the data is read.
151: * @param off the start offset in the destination array <code>b</code>
152: * @param len the maximum number of bytes read.
153: * @return the total number of bytes read into the buffer, or
154: * <code>-1</code> if there is no more data because the end of
155: * the stream has been reached.
156: * @exception NullPointerException If <code>b</code> is <code>null</code>.
157: * @exception IndexOutOfBoundsException If <code>off</code> is negative,
158: * <code>len</code> is negative, or <code>len</code> is greater than
159: * <code>b.length - off</code>
160: * @exception IOException if this input stream has been closed by
161: * invoking its {@link #close()} method,
162: * or an I/O error occurs.
163: * @see java.io.InputStream#read(byte[], int, int)
164: */
165: public int read(byte[] b, int off, int len) throws IOException {
166: ensureOpen();
167: if (b == null) {
168: throw new NullPointerException();
169: } else if (off < 0 || len < 0 || len > b.length - off) {
170: throw new IndexOutOfBoundsException();
171: } else if (len == 0) {
172: return 0;
173: }
174:
175: int avail = buf.length - pos;
176: if (avail > 0) {
177: if (len < avail) {
178: avail = len;
179: }
180: System.arraycopy(buf, pos, b, off, avail);
181: pos += avail;
182: off += avail;
183: len -= avail;
184: }
185: if (len > 0) {
186: len = super .read(b, off, len);
187: if (len == -1) {
188: return avail == 0 ? -1 : avail;
189: }
190: return avail + len;
191: }
192: return avail;
193: }
194:
195: /**
196: * Pushes back a byte by copying it to the front of the pushback buffer.
197: * After this method returns, the next byte to be read will have the value
198: * <code>(byte)b</code>.
199: *
200: * @param b the <code>int</code> value whose low-order
201: * byte is to be pushed back.
202: * @exception IOException If there is not enough room in the pushback
203: * buffer for the byte, or this input stream has been closed by
204: * invoking its {@link #close()} method.
205: */
206: public void unread(int b) throws IOException {
207: ensureOpen();
208: if (pos == 0) {
209: throw new IOException("Push back buffer is full");
210: }
211: buf[--pos] = (byte) b;
212: }
213:
214: /**
215: * Pushes back a portion of an array of bytes by copying it to the front
216: * of the pushback buffer. After this method returns, the next byte to be
217: * read will have the value <code>b[off]</code>, the byte after that will
218: * have the value <code>b[off+1]</code>, and so forth.
219: *
220: * @param b the byte array to push back.
221: * @param off the start offset of the data.
222: * @param len the number of bytes to push back.
223: * @exception IOException If there is not enough room in the pushback
224: * buffer for the specified number of bytes,
225: * or this input stream has been closed by
226: * invoking its {@link #close()} method.
227: * @since JDK1.1
228: */
229: public void unread(byte[] b, int off, int len) throws IOException {
230: ensureOpen();
231: if (len > pos) {
232: throw new IOException("Push back buffer is full");
233: }
234: pos -= len;
235: System.arraycopy(b, off, buf, pos, len);
236: }
237:
238: /**
239: * Pushes back an array of bytes by copying it to the front of the
240: * pushback buffer. After this method returns, the next byte to be read
241: * will have the value <code>b[0]</code>, the byte after that will have the
242: * value <code>b[1]</code>, and so forth.
243: *
244: * @param b the byte array to push back
245: * @exception IOException If there is not enough room in the pushback
246: * buffer for the specified number of bytes,
247: * or this input stream has been closed by
248: * invoking its {@link #close()} method.
249: * @since JDK1.1
250: */
251: public void unread(byte[] b) throws IOException {
252: unread(b, 0, b.length);
253: }
254:
255: /**
256: * Returns an estimate of the number of bytes that can be read (or
257: * skipped over) from this input stream without blocking by the next
258: * invocation of a method for this input stream. The next invocation might be
259: * the same thread or another thread. A single read or skip of this
260: * many bytes will not block, but may read or skip fewer bytes.
261: *
262: * <p> The method returns the sum of the number of bytes that have been
263: * pushed back and the value returned by {@link
264: * java.io.FilterInputStream#available available}.
265: *
266: * @return the number of bytes that can be read (or skipped over) from
267: * the input stream without blocking.
268: * @exception IOException if this input stream has been closed by
269: * invoking its {@link #close()} method,
270: * or an I/O error occurs.
271: * @see java.io.FilterInputStream#in
272: * @see java.io.InputStream#available()
273: */
274: public int available() throws IOException {
275: ensureOpen();
276: return (buf.length - pos) + super .available();
277: }
278:
279: /**
280: * Skips over and discards <code>n</code> bytes of data from this
281: * input stream. The <code>skip</code> method may, for a variety of
282: * reasons, end up skipping over some smaller number of bytes,
283: * possibly zero. If <code>n</code> is negative, no bytes are skipped.
284: *
285: * <p> The <code>skip</code> method of <code>PushbackInputStream</code>
286: * first skips over the bytes in the pushback buffer, if any. It then
287: * calls the <code>skip</code> method of the underlying input stream if
288: * more bytes need to be skipped. The actual number of bytes skipped
289: * is returned.
290: *
291: * @param n {@inheritDoc}
292: * @return {@inheritDoc}
293: * @exception IOException if the stream does not support seek,
294: * or the stream has been closed by
295: * invoking its {@link #close()} method,
296: * or an I/O error occurs.
297: * @see java.io.FilterInputStream#in
298: * @see java.io.InputStream#skip(long n)
299: * @since 1.2
300: */
301: public long skip(long n) throws IOException {
302: ensureOpen();
303: if (n <= 0) {
304: return 0;
305: }
306:
307: long pskip = buf.length - pos;
308: if (pskip > 0) {
309: if (n < pskip) {
310: pskip = n;
311: }
312: pos += pskip;
313: n -= pskip;
314: }
315: if (n > 0) {
316: pskip += super .skip(n);
317: }
318: return pskip;
319: }
320:
321: /**
322: * Tests if this input stream supports the <code>mark</code> and
323: * <code>reset</code> methods, which it does not.
324: *
325: * @return <code>false</code>, since this class does not support the
326: * <code>mark</code> and <code>reset</code> methods.
327: * @see java.io.InputStream#mark(int)
328: * @see java.io.InputStream#reset()
329: */
330: public boolean markSupported() {
331: return false;
332: }
333:
334: /**
335: * Marks the current position in this input stream.
336: *
337: * <p> The <code>mark</code> method of <code>PushbackInputStream</code>
338: * does nothing.
339: *
340: * @param readlimit the maximum limit of bytes that can be read before
341: * the mark position becomes invalid.
342: * @see java.io.InputStream#reset()
343: */
344: public synchronized void mark(int readlimit) {
345: }
346:
347: /**
348: * Repositions this stream to the position at the time the
349: * <code>mark</code> method was last called on this input stream.
350: *
351: * <p> The method <code>reset</code> for class
352: * <code>PushbackInputStream</code> does nothing except throw an
353: * <code>IOException</code>.
354: *
355: * @exception IOException if this method is invoked.
356: * @see java.io.InputStream#mark(int)
357: * @see java.io.IOException
358: */
359: public synchronized void reset() throws IOException {
360: throw new IOException("mark/reset not supported");
361: }
362:
363: /**
364: * Closes this input stream and releases any system resources
365: * associated with the stream.
366: * Once the stream has been closed, further read(), unread(),
367: * available(), reset(), or skip() invocations will throw an IOException.
368: * Closing a previously closed stream has no effect.
369: *
370: * @exception IOException if an I/O error occurs.
371: */
372: public synchronized void close() throws IOException {
373: if (in == null)
374: return;
375: in.close();
376: in = null;
377: buf = null;
378: }
379: }
|