2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
21 * Wraps an existing {@link InputStream} and adds functionality to "push back"
22 * bytes that have been read, so that they can be read again. Parsers may find
23 * this useful. The number of bytes which may be pushed back can be specified
24 * during construction. If the buffer of pushed back bytes is empty, bytes are
25 * read from the underlying input stream.
27 public class PushbackInputStream extends FilterInputStream {
29 * The buffer that contains pushed-back bytes.
34 * The current position within {@code buf}. A value equal to
35 * {@code buf.length} indicates that no bytes are available. A value of 0
36 * indicates that the buffer is full.
41 * Constructs a new {@code PushbackInputStream} with the specified input
42 * stream as source. The size of the pushback buffer is set to the default
45 * <p><strong>Warning:</strong> passing a null source creates an invalid
46 * {@code PushbackInputStream}. All read operations on such a stream will
50 * the source input stream.
52 public PushbackInputStream(InputStream in) {
54 buf = (in == null) ? null : new byte[1];
59 * Constructs a new {@code PushbackInputStream} with {@code in} as source
60 * input stream. The size of the pushback buffer is set to {@code size}.
62 * <p><strong>Warning:</strong> passing a null source creates an invalid
63 * {@code PushbackInputStream}. All read operations on such a stream will
67 * the source input stream.
69 * the size of the pushback buffer.
70 * @throws IllegalArgumentException
71 * if {@code size} is negative.
73 public PushbackInputStream(InputStream in, int size) {
76 throw new IllegalArgumentException("size <= 0");
78 buf = (in == null) ? null : new byte[size];
83 public int available() throws IOException {
85 throw new IOException();
87 return buf.length - pos + in.available();
91 * Closes this stream. This implementation closes the source stream
92 * and releases the pushback buffer.
95 * if an error occurs while closing this stream.
98 public void close() throws IOException {
107 * Indicates whether this stream supports the {@code mark(int)} and
108 * {@code reset()} methods. {@code PushbackInputStream} does not support
109 * them, so it returns {@code false}.
111 * @return always {@code false}.
116 public boolean markSupported() {
121 * Reads a single byte from this stream and returns it as an integer in the
122 * range from 0 to 255. If the pushback buffer does not contain any
123 * available bytes then a byte from the source input stream is returned.
124 * Blocks until one byte has been read, the end of the source stream is
125 * detected or an exception is thrown.
127 * @return the byte read or -1 if the end of the source stream has been
129 * @throws IOException
130 * if this stream is closed or an I/O error occurs while reading
134 public int read() throws IOException {
136 throw new IOException();
138 // Is there a pushback byte available?
139 if (pos < buf.length) {
140 return (buf[pos++] & 0xFF);
142 // Assume read() in the InputStream will return low-order byte or -1
148 * Reads at most {@code length} bytes from this stream and stores them in
149 * the byte array {@code buffer} starting at {@code offset}. Bytes are read
150 * from the pushback buffer first, then from the source stream if more bytes
151 * are required. Blocks until {@code count} bytes have been read, the end of
152 * the source stream is detected or an exception is thrown.
155 * the array in which to store the bytes read from this stream.
157 * the initial position in {@code buffer} to store the bytes read
160 * the maximum number of bytes to store in {@code buffer}.
161 * @return the number of bytes read or -1 if the end of the source stream
163 * @throws IndexOutOfBoundsException
164 * if {@code offset < 0} or {@code length < 0}, or if
165 * {@code offset + length} is greater than the length of
167 * @throws IOException
168 * if this stream is closed or another I/O error occurs while
169 * reading from this stream.
170 * @throws NullPointerException
171 * if {@code buffer} is {@code null}.
174 public int read(byte[] buffer, int offset, int length) throws IOException {
176 throw streamClosed();
178 // Force buffer null check first!
179 if (offset > buffer.length || offset < 0) {
180 throw new ArrayIndexOutOfBoundsException("Offset out of bounds: " + offset);
182 if (length < 0 || length > buffer.length - offset) {
183 throw new ArrayIndexOutOfBoundsException("Length out of bounds: " + length);
186 int copiedBytes = 0, copyLength = 0, newOffset = offset;
187 // Are there pushback bytes available?
188 if (pos < buf.length) {
189 copyLength = (buf.length - pos >= length) ? length : buf.length
191 System.arraycopy(buf, pos, buffer, newOffset, copyLength);
192 newOffset += copyLength;
193 copiedBytes += copyLength;
194 // Use up the bytes in the local buffer
197 // Have we copied enough?
198 if (copyLength == length) {
201 int inCopied = in.read(buffer, newOffset, length - copiedBytes);
203 return inCopied + copiedBytes;
205 if (copiedBytes == 0) {
211 private IOException streamClosed() throws IOException {
212 throw new IOException("PushbackInputStream is closed");
216 * Skips {@code count} bytes in this stream. This implementation skips bytes
217 * in the pushback buffer first and then in the source stream if necessary.
220 * the number of bytes to skip.
221 * @return the number of bytes actually skipped.
222 * @throws IOException
223 * if this stream is closed or another I/O error occurs.
226 public long skip(long count) throws IOException {
228 throw streamClosed();
234 if (pos < buf.length) {
235 numSkipped += (count < buf.length - pos) ? count : buf.length - pos;
238 if (numSkipped < count) {
239 numSkipped += in.skip(count - numSkipped);
245 * Pushes all the bytes in {@code buffer} back to this stream. The bytes are
246 * pushed back in such a way that the next byte read from this stream is
247 * buffer[0], then buffer[1] and so on.
249 * If this stream's internal pushback buffer cannot store the entire
250 * contents of {@code buffer}, an {@code IOException} is thrown. Parts of
251 * {@code buffer} may have already been copied to the pushback buffer when
252 * the exception is thrown.
255 * the buffer containing the bytes to push back to this stream.
256 * @throws IOException
257 * if the free space in the internal pushback buffer is not
258 * sufficient to store the contents of {@code buffer}.
260 public void unread(byte[] buffer) throws IOException {
261 unread(buffer, 0, buffer.length);
265 * Pushes a subset of the bytes in {@code buffer} back to this stream. The
266 * subset is defined by the start position {@code offset} within
267 * {@code buffer} and the number of bytes specified by {@code length}. The
268 * bytes are pushed back in such a way that the next byte read from this
269 * stream is {@code buffer[offset]}, then {@code buffer[1]} and so on.
271 * If this stream's internal pushback buffer cannot store the selected
272 * subset of {@code buffer}, an {@code IOException} is thrown. Parts of
273 * {@code buffer} may have already been copied to the pushback buffer when
274 * the exception is thrown.
277 * the buffer containing the bytes to push back to this stream.
279 * the index of the first byte in {@code buffer} to push back.
281 * the number of bytes to push back.
282 * @throws IndexOutOfBoundsException
283 * if {@code offset < 0} or {@code length < 0}, or if
284 * {@code offset + length} is greater than the length of
286 * @throws IOException
287 * if the free space in the internal pushback buffer is not
288 * sufficient to store the selected contents of {@code buffer}.
290 public void unread(byte[] buffer, int offset, int length) throws IOException {
292 throw new IOException("Pushback buffer full");
294 if (offset > buffer.length || offset < 0) {
295 throw new ArrayIndexOutOfBoundsException("Offset out of bounds: " + offset);
297 if (length < 0 || length > buffer.length - offset) {
298 throw new ArrayIndexOutOfBoundsException("Length out of bounds: " + length);
301 throw streamClosed();
304 System.arraycopy(buffer, offset, buf, pos - length, length);
309 * Pushes the specified byte {@code oneByte} back to this stream. Only the
310 * least significant byte of the integer {@code oneByte} is pushed back.
311 * This is done in such a way that the next byte read from this stream is
312 * {@code (byte) oneByte}.
314 * If this stream's internal pushback buffer cannot store the byte, an
315 * {@code IOException} is thrown.
318 * the byte to push back to this stream.
319 * @throws IOException
320 * if this stream is closed or the internal pushback buffer is
323 public void unread(int oneByte) throws IOException {
325 throw new IOException();
328 throw new IOException("Pushback buffer full");
330 buf[--pos] = (byte) oneByte;
334 * Marks the current position in this stream. Setting a mark is not
335 * supported in this class; this implementation does nothing.
338 * the number of bytes that can be read from this stream before
339 * the mark is invalidated; this parameter is ignored.
342 public void mark(int readlimit) {
347 * Resets this stream to the last marked position. Resetting the stream is
348 * not supported in this class; this implementation always throws an
349 * {@code IOException}.
351 * @throws IOException
352 * if this method is called.
355 public void reset() throws IOException {
356 throw new IOException();