1 package be
.nikiroo
.utils
.streams
;
3 import java
.io
.IOException
;
4 import java
.io
.InputStream
;
7 * This {@link InputStream} can be separated into sub-streams (you can process
8 * it as a normal {@link InputStream} but, when it is spent, you can call
9 * {@link NextableInputStream#next()} on it to unlock new data).
11 * The separation in sub-streams is done via {@link NextableInputStreamStep}.
15 public class NextableInputStream
extends BufferedInputStream
{
16 private NextableInputStreamStep step
;
17 private boolean started
;
18 private boolean stopped
;
21 * Create a new {@link NextableInputStream} that wraps the given
22 * {@link InputStream}.
25 * the {@link InputStream} to wrap
27 * how to separate it into sub-streams (can be NULL, but in that
28 * case it will behave as a normal {@link InputStream})
30 public NextableInputStream(InputStream in
, NextableInputStreamStep step
) {
36 * Create a new {@link NextableInputStream} that wraps the given bytes array
40 * the array to wrap, cannot be NULL
42 * how to separate it into sub-streams (can be NULL, but in that
43 * case it will behave as a normal {@link InputStream})
45 public NextableInputStream(byte[] in
, NextableInputStreamStep step
) {
46 this(in
, step
, 0, in
.length
);
50 * Create a new {@link NextableInputStream} that wraps the given bytes array
54 * the array to wrap, cannot be NULL
56 * how to separate it into sub-streams (can be NULL, but in that
57 * case it will behave as a normal {@link InputStream})
59 * the offset to start the reading at
61 * the number of bytes to take into account in the array,
62 * starting from the offset
64 * @throws NullPointerException
65 * if the array is NULL
66 * @throws IndexOutOfBoundsException
67 * if the offset and length do not correspond to the given array
69 public NextableInputStream(byte[] in
, NextableInputStreamStep step
,
70 int offset
, int length
) {
71 super(in
, offset
, length
);
77 * Unblock the processing of the next sub-stream.
79 * It can only be called when the "current" stream is spent (i.e., you must
80 * first process the stream until it is spent).
82 * We consider that when the under-laying {@link InputStream} is also spent,
83 * we cannot have a next sub-stream (it will thus return FALSE).
85 * {@link IOException}s can happen when we have no data available in the
86 * buffer; in that case, we fetch more data to know if we can have a next
89 * @return TRUE if we unblocked the next sub-stream, FALSE if not
92 * in case of I/O error or if the stream is closed
94 public boolean next() throws IOException
{
99 * Unblock the next sub-stream as would have done
100 * {@link NextableInputStream#next()}, but disable the sub-stream systems.
102 * That is, the next stream, if any, will be the last one and will not be
103 * subject to the {@link NextableInputStreamStep}.
105 * @return TRUE if we unblocked the next sub-stream, FALSE if not
107 * @throws IOException
108 * in case of I/O error or if the stream is closed
110 public boolean nextAll() throws IOException
{
115 * Check if this stream is totally spent (no more data to read or to
118 * Note: when the stream is divided into sub-streams, each sub-stream will
119 * report it is eof when emptied.
121 * @return TRUE if it is
123 * @throws IOException
124 * in case of I/O error
127 public boolean eof() throws IOException
{
132 * Check if we still have some data in the buffer and, if not, fetch some.
134 * @return TRUE if we fetched some data, FALSE if there are still some in
137 * @throws IOException
138 * in case of I/O error
141 protected boolean preRead() throws IOException
{
143 boolean bufferChanged
= super.preRead();
144 checkBuffer(bufferChanged
);
145 return bufferChanged
;
156 * We have more data available in the buffer or we can fetch more.
158 * @return TRUE if it is the case, FALSE if not
161 protected boolean hasMoreData() {
162 return started
&& super.hasMoreData();
166 * Check that the buffer didn't overshot to the next item, and fix
167 * {@link NextableInputStream#stop} if needed.
169 * If {@link NextableInputStream#stop} is fixed,
170 * {@link NextableInputStream#eof} and {@link NextableInputStream#stopped}
174 * we changed the buffer, we need to clear some information in
175 * the {@link NextableInputStreamStep}
177 private void checkBuffer(boolean newBuffer
) {
178 if (step
!= null && stop
>= 0) {
183 int stopAt
= step
.stop(buffer
, start
, stop
, eof
);
193 * The implementation of {@link NextableInputStream#next()} and
194 * {@link NextableInputStream#nextAll()}.
197 * TRUE for {@link NextableInputStream#nextAll()}, FALSE for
198 * {@link NextableInputStream#next()}
200 * @return TRUE if we unblocked the next sub-stream, FALSE if not
202 * @throws IOException
203 * in case of I/O error or if the stream is closed
205 private boolean next(boolean all
) throws IOException
{
209 // First call before being allowed to read
219 if (step
!= null && !hasMoreData() && stopped
) {
220 stop
= step
.getResumeLen();
221 start
+= step
.getResumeSkip();
222 eof
= step
.getResumeEof();
231 // consider that if EOF, there is no next
232 return hasMoreData();