001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: package java.io;
019:
020: import org.apache.harmony.luni.util.Msg;
021:
022: /**
023: * PushbackInputStream is a filter class which allows bytes read to be pushed
024: * back into the stream so that they can be reread. Parsers may find this
025: * useful. There is a progammable limit to the number of bytes which may be
026: * pushed back. If the buffer of pushed back bytes is empty, bytes are read from
027: * the source input stream.
028: */
029: public class PushbackInputStream extends FilterInputStream {
030: /**
031: * The <code>byte</code> array containing the bytes to read.
032: */
033: protected byte[] buf;
034:
035: /**
036: * The current position within the byte array <code>buf</code>. A value
037: * equal to buf.length indicates no bytes available. A value of 0 indicates
038: * the buffer is full.
039: */
040: protected int pos;
041:
042: /**
043: * Constructs a new PushbackInputStream on the InputStream <code>in</code>.
044: * The size of the pushback buffer is set to the default, or 1 byte.
045: *
046: * @param in
047: * the InputStream to allow pushback operations on.
048: */
049: public PushbackInputStream(InputStream in) {
050: super (in);
051: buf = (in == null) ? null : new byte[1];
052: pos = 1;
053: }
054:
055: /**
056: * Constructs a new PushbackInputStream on the InputStream <code>in</code>.
057: * The size of the pushback buffer is set to <code>size</code>.
058: *
059: * @param in
060: * the InputStream to allow pushback operations on.
061: * @param size
062: * the size of the pushback buffer (<code>size>=0</code>).
063: */
064: public PushbackInputStream(InputStream in, int size) {
065: super (in);
066: if (size <= 0) {
067: throw new IllegalArgumentException(Msg.getString("K0058")); //$NON-NLS-1$
068: }
069: buf = (in == null) ? null : new byte[size];
070: pos = size;
071: }
072:
073: /**
074: * Answers a int representing then number of bytes that are available before
075: * this PushbackInputStream will block. This method returns the number of
076: * bytes available in the pushback buffer plus those available in the target
077: * stream.
078: *
079: * @return int the number of bytes available before blocking.
080: *
081: * @throws java.io.IOException
082: * If an error occurs in this stream.
083: */
084: @Override
085: public int available() throws IOException {
086: if (buf == null) {
087: throw new IOException();
088: }
089: return buf.length - pos + in.available();
090: }
091:
092: /**
093: * Close this PushbackInputStream. This implementation closes the target
094: * stream.
095: *
096: * @throws IOException
097: * If an error occurs attempting to close this stream.
098: */
099: @Override
100: public void close() throws IOException {
101: if (in != null) {
102: in.close();
103: in = null;
104: buf = null;
105: }
106: }
107:
108: /**
109: * Answers a boolean indicating whether or not this PushbackInputStream
110: * supports mark() and reset(). This implementation always answers false
111: * since PushbackInputStreams do not support mark/reset.
112: *
113: * @return boolean indicates whether or not mark() and reset() are
114: * supported.
115: */
116: @Override
117: public boolean markSupported() {
118: return false;
119: }
120:
121: /**
122: * Reads a single byte from this PushbackInputStream and returns the result
123: * as an int. The low-order byte is returned or -1 of the end of stream was
124: * encountered. If the pushback buffer does not contain any available bytes
125: * then a byte from the target input stream is returned.
126: *
127: * @return int The byte read or -1 if end of stream.
128: *
129: * @throws IOException
130: * If an IOException occurs.
131: */
132: @Override
133: public int read() throws IOException {
134: if (buf == null) {
135: throw new IOException();
136: }
137: // Is there a pushback byte available?
138: if (pos < buf.length) {
139: return (buf[pos++] & 0xFF);
140: }
141: // Assume read() in the InputStream will return low-order byte or -1
142: // if end of stream.
143: return in.read();
144: }
145:
146: /**
147: * Reads at most <code>length</code> bytes from this PushbackInputStream
148: * and stores them in byte array <code>buffer</code> starting at
149: * <code>offset</code>. Answer the number of bytes actually read or -1 if
150: * no bytes were read and end of stream was encountered. This implementation
151: * reads bytes from the pushback buffer first, then the target stream if
152: * more bytes are required to satisfy <code>count</code>.
153: *
154: * @param buffer
155: * the byte array in which to store the read bytes.
156: * @param offset
157: * the offset in <code>buffer</code> to store the read bytes.
158: * @param length
159: * the maximum number of bytes to store in <code>buffer</code>.
160: * @return the number of bytes actually read or -1 if end of stream.
161: *
162: * @throws IOException
163: * If an IOException occurs.
164: */
165: @Override
166: public int read(byte[] buffer, int offset, int length)
167: throws IOException {
168: if (buf == null) {
169: throw new IOException();
170: }
171: if (buffer == null) {
172: throw new NullPointerException();
173: }
174: // avoid int overflow
175: if (offset < 0 || offset > buffer.length || length < 0
176: || length > buffer.length - offset) {
177: throw new ArrayIndexOutOfBoundsException();
178: }
179:
180: int copiedBytes = 0, copyLength = 0, newOffset = offset;
181: // Are there pushback bytes available?
182: if (pos < buf.length) {
183: copyLength = (buf.length - pos >= length) ? length
184: : buf.length - pos;
185: System.arraycopy(buf, pos, buffer, newOffset, copyLength);
186: newOffset += copyLength;
187: copiedBytes += copyLength;
188: // Use up the bytes in the local buffer
189: pos += copyLength;
190: }
191: // Have we copied enough?
192: if (copyLength == length) {
193: return length;
194: }
195: int inCopied = in.read(buffer, newOffset, length - copiedBytes);
196: if (inCopied > 0) {
197: return inCopied + copiedBytes;
198: }
199: if (copiedBytes == 0) {
200: return inCopied;
201: }
202: return copiedBytes;
203: }
204:
205: /**
206: * Skips <code>count</code> number of bytes in this PushbackInputStream.
207: * Subsequent <code>read()</code>'s will not return these bytes unless
208: * <code>reset()</code> is used. This implementation skips
209: * <code>count</code> number of bytes in the buffer and/or the target
210: * stream.
211: *
212: * @param count
213: * the number of bytes to skip.
214: * @return the number of bytes actually skipped.
215: *
216: * @throws IOException
217: * If the stream is already closed or another IOException
218: * occurs.
219: */
220: @Override
221: public long skip(long count) throws IOException {
222: if (in == null) {
223: throw new IOException(Msg.getString("K0059")); //$NON-NLS-1$
224: }
225: if (count <= 0) {
226: return 0;
227: }
228: int numSkipped = 0;
229: if (pos < buf.length) {
230: numSkipped += (count < buf.length - pos) ? count
231: : buf.length - pos;
232: pos += numSkipped;
233: }
234: if (numSkipped < count) {
235: numSkipped += in.skip(count - numSkipped);
236: }
237: return numSkipped;
238: }
239:
240: /**
241: * Push back all the bytes in <code>buffer</code>. The bytes are pushed
242: * so that they would be read back buffer[0], buffer[1], etc. If the push
243: * back buffer cannot handle the entire contents of <code>buffer</code>,
244: * an IOException will be thrown. Some of the buffer may already be in the
245: * buffer after the exception is thrown.
246: *
247: * @param buffer
248: * the byte array containing bytes to push back into the stream.
249: *
250: * @throws IOException
251: * If the pushback buffer becomes, or is, full.
252: */
253: public void unread(byte[] buffer) throws IOException {
254: unread(buffer, 0, buffer.length);
255: }
256:
257: /**
258: * Push back <code>length</code> number of bytes in <code>buffer</code>
259: * starting at <code>offset</code>. The bytes are pushed so that they
260: * would be read back buffer[offset], buffer[offset+1], etc. If the push
261: * back buffer cannot handle the bytes copied from <code>buffer</code>,
262: * an IOException will be thrown. Some of the bytes may already be in the
263: * buffer after the exception is thrown.
264: *
265: * @param buffer
266: * the byte array containing bytes to push back into the stream.
267: * @param offset
268: * the location to start taking bytes to push back.
269: * @param length
270: * the number of bytes to push back.
271: *
272: * @throws IOException
273: * If the pushback buffer becomes, or is, full.
274: */
275: public void unread(byte[] buffer, int offset, int length)
276: throws IOException {
277: if (length > pos) {
278: // Pushback buffer full
279: throw new IOException(Msg.getString("K007e")); //$NON-NLS-1$
280: }
281: // avoid int overflow
282: if (offset < 0 || offset > buffer.length || length < 0
283: || length > buffer.length - offset) {
284: throw new ArrayIndexOutOfBoundsException();
285: }
286:
287: for (int i = offset + length - 1; i >= offset; i--) {
288: unread(buffer[i]);
289: }
290: }
291:
292: /**
293: * Push back one <code>byte</code>. Takes the byte <code>oneByte</code>
294: * and puts in in the local buffer of bytes to read back before accessing
295: * the target input stream.
296: *
297: * @param oneByte
298: * the byte to push back into the stream.
299: *
300: * @throws IOException
301: * If the pushback buffer is already full.
302: */
303: public void unread(int oneByte) throws IOException {
304: if (buf == null) {
305: throw new IOException();
306: }
307: if (pos == 0) {
308: throw new IOException(Msg.getString("K007e")); //$NON-NLS-1$
309: }
310: buf[--pos] = (byte) oneByte;
311: }
312:
313: /**
314: * Make a mark of the current position in the stream but the mark method
315: * does nothing.
316: *
317: * @param readlimit
318: * the maximum number of bytes that are able to be read before
319: * the mark becomes invalid
320: * @see FilterInputStream#mark(int)
321: */
322: @Override
323: public void mark(int readlimit) {
324: return;
325: }
326:
327: /**
328: * Reset current position to the mark made previously int the stream, but
329: * the reset method will throw IOException and do nothing else if called.
330: *
331: * @throws IOException
332: * If the method is called
333: * @see FilterInputStream#reset()
334: */
335: @Override
336: public void reset() throws IOException {
337: throw new IOException();
338: }
339: }
|