OSDN Git Service

original
[gb-231r1-is01/Gingerbread_2.3.3_r1_IS01.git] / libcore / luni / src / main / java / java / io / PushbackInputStream.java
1 /*
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
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
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.
16  */
17
18 package java.io;
19
20 /**
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.
26  */
27 public class PushbackInputStream extends FilterInputStream {
28     /**
29      * The buffer that contains pushed-back bytes.
30      */
31     protected byte[] buf;
32
33     /**
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.
37      */
38     protected int pos;
39
40     /**
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
43      * value of 1 byte.
44      *
45      * <p><strong>Warning:</strong> passing a null source creates an invalid
46      * {@code PushbackInputStream}. All read operations on such a stream will
47      * fail.
48      *
49      * @param in
50      *            the source input stream.
51      */
52     public PushbackInputStream(InputStream in) {
53         super(in);
54         buf = (in == null) ? null : new byte[1];
55         pos = 1;
56     }
57
58     /**
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}.
61      *
62      * <p><strong>Warning:</strong> passing a null source creates an invalid
63      * {@code PushbackInputStream}. All read operations on such a stream will
64      * fail.
65      *
66      * @param in
67      *            the source input stream.
68      * @param size
69      *            the size of the pushback buffer.
70      * @throws IllegalArgumentException
71      *             if {@code size} is negative.
72      */
73     public PushbackInputStream(InputStream in, int size) {
74         super(in);
75         if (size <= 0) {
76             throw new IllegalArgumentException("size <= 0");
77         }
78         buf = (in == null) ? null : new byte[size];
79         pos = size;
80     }
81
82     @Override
83     public int available() throws IOException {
84         if (buf == null) {
85             throw new IOException();
86         }
87         return buf.length - pos + in.available();
88     }
89
90     /**
91      * Closes this stream. This implementation closes the source stream
92      * and releases the pushback buffer.
93      *
94      * @throws IOException
95      *             if an error occurs while closing this stream.
96      */
97     @Override
98     public void close() throws IOException {
99         if (in != null) {
100             in.close();
101             in = null;
102             buf = null;
103         }
104     }
105
106     /**
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}.
110      *
111      * @return always {@code false}.
112      * @see #mark(int)
113      * @see #reset()
114      */
115     @Override
116     public boolean markSupported() {
117         return false;
118     }
119
120     /**
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.
126      *
127      * @return the byte read or -1 if the end of the source stream has been
128      *         reached.
129      * @throws IOException
130      *             if this stream is closed or an I/O error occurs while reading
131      *             from this stream.
132      */
133     @Override
134     public int read() throws IOException {
135         if (buf == null) {
136             throw new IOException();
137         }
138         // Is there a pushback byte available?
139         if (pos < buf.length) {
140             return (buf[pos++] & 0xFF);
141         }
142         // Assume read() in the InputStream will return low-order byte or -1
143         // if end of stream.
144         return in.read();
145     }
146
147     /**
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.
153      *
154      * @param buffer
155      *            the array in which to store the bytes read from this stream.
156      * @param offset
157      *            the initial position in {@code buffer} to store the bytes read
158      *            from this stream.
159      * @param length
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
162      *         has been reached.
163      * @throws IndexOutOfBoundsException
164      *             if {@code offset < 0} or {@code length < 0}, or if
165      *             {@code offset + length} is greater than the length of
166      *             {@code buffer}.
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}.
172      */
173     @Override
174     public int read(byte[] buffer, int offset, int length) throws IOException {
175         if (buf == null) {
176             throw streamClosed();
177         }
178         // Force buffer null check first!
179         if (offset > buffer.length || offset < 0) {
180             throw new ArrayIndexOutOfBoundsException("Offset out of bounds: " + offset);
181         }
182         if (length < 0 || length > buffer.length - offset) {
183             throw new ArrayIndexOutOfBoundsException("Length out of bounds: " + length);
184         }
185
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
190                     - pos;
191             System.arraycopy(buf, pos, buffer, newOffset, copyLength);
192             newOffset += copyLength;
193             copiedBytes += copyLength;
194             // Use up the bytes in the local buffer
195             pos += copyLength;
196         }
197         // Have we copied enough?
198         if (copyLength == length) {
199             return length;
200         }
201         int inCopied = in.read(buffer, newOffset, length - copiedBytes);
202         if (inCopied > 0) {
203             return inCopied + copiedBytes;
204         }
205         if (copiedBytes == 0) {
206             return inCopied;
207         }
208         return copiedBytes;
209     }
210
211     private IOException streamClosed() throws IOException  {
212         throw new IOException("PushbackInputStream is closed");
213     }
214
215     /**
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.
218      *
219      * @param count
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.
224      */
225     @Override
226     public long skip(long count) throws IOException {
227         if (in == null) {
228             throw streamClosed();
229         }
230         if (count <= 0) {
231             return 0;
232         }
233         int numSkipped = 0;
234         if (pos < buf.length) {
235             numSkipped += (count < buf.length - pos) ? count : buf.length - pos;
236             pos += numSkipped;
237         }
238         if (numSkipped < count) {
239             numSkipped += in.skip(count - numSkipped);
240         }
241         return numSkipped;
242     }
243
244     /**
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.
248      * <p>
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.
253      *
254      * @param buffer
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}.
259      */
260     public void unread(byte[] buffer) throws IOException {
261         unread(buffer, 0, buffer.length);
262     }
263
264     /**
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.
270      * <p>
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.
275      *
276      * @param buffer
277      *            the buffer containing the bytes to push back to this stream.
278      * @param offset
279      *            the index of the first byte in {@code buffer} to push back.
280      * @param length
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
285      *             {@code buffer}.
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}.
289      */
290     public void unread(byte[] buffer, int offset, int length) throws IOException {
291         if (length > pos) {
292             throw new IOException("Pushback buffer full");
293         }
294         if (offset > buffer.length || offset < 0) {
295             throw new ArrayIndexOutOfBoundsException("Offset out of bounds: " + offset);
296         }
297         if (length < 0 || length > buffer.length - offset) {
298             throw new ArrayIndexOutOfBoundsException("Length out of bounds: " + length);
299         }
300         if (buf == null) {
301             throw streamClosed();
302         }
303
304         System.arraycopy(buffer, offset, buf, pos - length, length);
305         pos = pos - length;
306     }
307
308     /**
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}.
313      * <p>
314      * If this stream's internal pushback buffer cannot store the byte, an
315      * {@code IOException} is thrown.
316      *
317      * @param oneByte
318      *            the byte to push back to this stream.
319      * @throws IOException
320      *             if this stream is closed or the internal pushback buffer is
321      *             full.
322      */
323     public void unread(int oneByte) throws IOException {
324         if (buf == null) {
325             throw new IOException();
326         }
327         if (pos == 0) {
328             throw new IOException("Pushback buffer full");
329         }
330         buf[--pos] = (byte) oneByte;
331     }
332
333     /**
334      * Marks the current position in this stream. Setting a mark is not
335      * supported in this class; this implementation does nothing.
336      *
337      * @param readlimit
338      *            the number of bytes that can be read from this stream before
339      *            the mark is invalidated; this parameter is ignored.
340      */
341     @Override
342     public void mark(int readlimit) {
343         return;
344     }
345
346     /**
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}.
350      *
351      * @throws IOException
352      *             if this method is called.
353      */
354     @Override
355     public void reset() throws IOException {
356         throw new IOException();
357     }
358 }