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 * This is can be a blocking call when data need to be fetched.
91 * @return TRUE if we unblocked the next sub-stream, FALSE if not
94 * in case of I/O error or if the stream is closed
96 public boolean next() throws IOException
{
101 * Unblock the next sub-stream as would have done
102 * {@link NextableInputStream#next()}, but disable the sub-stream systems.
104 * That is, the next stream, if any, will be the last one and will not be
105 * subject to the {@link NextableInputStreamStep}.
107 * This is can be a blocking call when data need to be fetched.
109 * @return TRUE if we unblocked the next sub-stream, FALSE if not
111 * @throws IOException
112 * in case of I/O error or if the stream is closed
114 public boolean nextAll() throws IOException
{
119 * Check if this stream is totally spent (no more data to read or to
122 * Note: when the stream is divided into sub-streams, each sub-stream will
123 * report it is eof when emptied.
125 * @return TRUE if it is
127 * @throws IOException
128 * in case of I/O error
131 public boolean eof() throws IOException
{
136 * Check if we still have some data in the buffer and, if not, fetch some.
138 * @return TRUE if we fetched some data, FALSE if there are still some in
141 * @throws IOException
142 * in case of I/O error
145 protected boolean preRead() throws IOException
{
147 boolean bufferChanged
= super.preRead();
148 checkBuffer(bufferChanged
);
149 return bufferChanged
;
160 * We have more data available in the buffer or we can fetch more.
162 * @return TRUE if it is the case, FALSE if not
165 protected boolean hasMoreData() {
166 return started
&& super.hasMoreData();
170 * Check that the buffer didn't overshot to the next item, and fix
171 * {@link NextableInputStream#stop} if needed.
173 * If {@link NextableInputStream#stop} is fixed,
174 * {@link NextableInputStream#eof} and {@link NextableInputStream#stopped}
178 * we changed the buffer, we need to clear some information in
179 * the {@link NextableInputStreamStep}
181 private void checkBuffer(boolean newBuffer
) {
182 if (step
!= null && stop
>= 0) {
187 int stopAt
= step
.stop(buffer
, start
, stop
, eof
);
197 * The implementation of {@link NextableInputStream#next()} and
198 * {@link NextableInputStream#nextAll()}.
200 * This is can be a blocking call when data need to be fetched.
203 * TRUE for {@link NextableInputStream#nextAll()}, FALSE for
204 * {@link NextableInputStream#next()}
206 * @return TRUE if we unblocked the next sub-stream, FALSE if not
208 * @throws IOException
209 * in case of I/O error or if the stream is closed
211 private boolean next(boolean all
) throws IOException
{
215 // First call before being allowed to read
223 if (step
!= null && !hasMoreData() && stopped
) {
224 stop
= step
.getResumeLen();
225 start
+= step
.getResumeSkip();
226 eof
= step
.getResumeEof();
236 // consider that if EOF, there is no next
238 // Make sure, block if necessary
241 return hasMoreData();
247 public String
DEBUG() {
248 String rep
= String
.format(
249 "Nextable %s: %d -> %d [eof: %s] [more data: %s]",
250 (stopped ?
"stopped" : "running"), start
, stop
, "" + eof
, ""