Add 'src/be/nikiroo/utils/' from commit '46add0670fdee4bd936a13fe2448c5e20a7ffd0a'
[fanfix.git] / src / be / nikiroo / utils / streams / NextableInputStream.java
index 4a6e0abc672abf441ef6acf51f9db9ed1d625585..dcab472efa0569bf6763425567330a536ac3eeeb 100644 (file)
@@ -2,6 +2,8 @@ package be.nikiroo.utils.streams;
 
 import java.io.IOException;
 import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
+import java.util.Arrays;
 
 /**
  * This {@link InputStream} can be separated into sub-streams (you can process
@@ -79,14 +81,14 @@ public class NextableInputStream extends BufferedInputStream {
         * It can only be called when the "current" stream is spent (i.e., you must
         * first process the stream until it is spent).
         * <p>
-        * We consider that when the under-laying {@link InputStream} is also spent,
-        * we cannot have a next sub-stream (it will thus return FALSE).
-        * <p>
         * {@link IOException}s can happen when we have no data available in the
         * buffer; in that case, we fetch more data to know if we can have a next
         * sub-stream or not.
+        * <p>
+        * This is can be a blocking call when data need to be fetched.
         * 
-        * @return TRUE if we unblocked the next sub-stream, FALSE if not
+        * @return TRUE if we unblocked the next sub-stream, FALSE if not (i.e.,
+        *         FALSE when there are no more sub-streams to fetch)
         * 
         * @throws IOException
         *             in case of I/O error or if the stream is closed
@@ -101,6 +103,8 @@ public class NextableInputStream extends BufferedInputStream {
         * <p>
         * That is, the next stream, if any, will be the last one and will not be
         * subject to the {@link NextableInputStreamStep}.
+        * <p>
+        * This is can be a blocking call when data need to be fetched.
         * 
         * @return TRUE if we unblocked the next sub-stream, FALSE if not
         * 
@@ -115,16 +119,19 @@ public class NextableInputStream extends BufferedInputStream {
         * Check if this stream is totally spent (no more data to read or to
         * process).
         * <p>
-        * Note: an empty stream that is still not started will return FALSE, as we
-        * don't know yet if it is empty.
+        * Note: when the stream is divided into sub-streams, each sub-stream will
+        * report its own eof when spent.
         * 
         * @return TRUE if it is
+        * 
+        * @throws IOException
+        *             in case of I/O error
         */
        @Override
-       public boolean eof() {
+       public boolean eof() throws IOException {
                return super.eof();
        }
-       
+
        /**
         * Check if we still have some data in the buffer and, if not, fetch some.
         * 
@@ -138,7 +145,7 @@ public class NextableInputStream extends BufferedInputStream {
        protected boolean preRead() throws IOException {
                if (!stopped) {
                        boolean bufferChanged = super.preRead();
-                       checkBuffer(true);
+                       checkBuffer(bufferChanged);
                        return bufferChanged;
                }
 
@@ -149,11 +156,6 @@ public class NextableInputStream extends BufferedInputStream {
                return false;
        }
 
-       /**
-        * We have more data available in the buffer or we can fetch more.
-        * 
-        * @return TRUE if it is the case, FALSE if not
-        */
        @Override
        protected boolean hasMoreData() {
                return started && super.hasMoreData();
@@ -172,12 +174,12 @@ public class NextableInputStream extends BufferedInputStream {
         *            the {@link NextableInputStreamStep}
         */
        private void checkBuffer(boolean newBuffer) {
-               if (step != null && stop > 0) {
+               if (step != null && stop >= 0) {
                        if (newBuffer) {
                                step.clearBuffer();
                        }
 
-                       int stopAt = step.stop(buffer, start, stop);
+                       int stopAt = step.stop(buffer, start, stop, eof);
                        if (stopAt >= 0) {
                                stop = stopAt;
                                eof = true;
@@ -189,12 +191,15 @@ public class NextableInputStream extends BufferedInputStream {
        /**
         * The implementation of {@link NextableInputStream#next()} and
         * {@link NextableInputStream#nextAll()}.
+        * <p>
+        * This is can be a blocking call when data need to be fetched.
         * 
         * @param all
         *            TRUE for {@link NextableInputStream#nextAll()}, FALSE for
         *            {@link NextableInputStream#next()}
         * 
-        * @return TRUE if we unblocked the next sub-stream, FALSE if not
+        * @return TRUE if we unblocked the next sub-stream, FALSE if not (i.e.,
+        *         FALSE when there are no more sub-streams to fetch)
         * 
         * @throws IOException
         *             in case of I/O error or if the stream is closed
@@ -213,23 +218,62 @@ public class NextableInputStream extends BufferedInputStream {
                        return true;
                }
 
-               if (step != null && !hasMoreData() && stopped) {
+               // If started, must be stopped and no more data to continue
+               // i.e., sub-stream must be spent
+               if (!stopped || hasMoreData()) {
+                       return false;
+               }
+
+               if (step != null) {
                        stop = step.getResumeLen();
                        start += step.getResumeSkip();
-                       eof = false;
+                       eof = step.getResumeEof();
+                       stopped = false;
 
                        if (all) {
                                step = null;
                        }
 
-                       if (!preRead()) {
-                               checkBuffer(false);
-                       }
+                       checkBuffer(false);
 
-                       // consider that if EOF, there is no next
-                       return hasMoreData();
+                       return true;
                }
 
                return false;
+
+               // // consider that if EOF, there is no next
+               // if (start >= stop) {
+               // // Make sure, block if necessary
+               // preRead();
+               //
+               // return hasMoreData();
+               // }
+               //
+               // return true;
+       }
+
+       /**
+        * Display a DEBUG {@link String} representation of this object.
+        * <p>
+        * Do <b>not</b> use for release code.
+        */
+       @Override
+       public String toString() {
+               String data = "";
+               if (stop > 0) {
+                       try {
+                               data = new String(Arrays.copyOfRange(buffer, 0, stop), "UTF-8");
+                       } catch (UnsupportedEncodingException e) {
+                       }
+                       if (data.length() > 200) {
+                               data = data.substring(0, 197) + "...";
+                       }
+               }
+               String rep = String.format(
+                               "Nextable %s: %d -> %d [eof: %s] [more data: %s]: %s",
+                               (stopped ? "stopped" : "running"), start, stop, "" + eof, ""
+                                               + hasMoreData(), data);
+
+               return rep;
        }
 }