]> source.dussan.org Git - poi.git/commitdiff
Refactor DocumentInputStream so that it can transparently handle both old style and...
authorNick Burch <nick@apache.org>
Wed, 29 Dec 2010 07:28:10 +0000 (07:28 +0000)
committerNick Burch <nick@apache.org>
Wed, 29 Dec 2010 07:28:10 +0000 (07:28 +0000)
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1053562 13f79535-47bb-0310-9956-ffa450edef68

src/java/org/apache/poi/POIDocument.java
src/java/org/apache/poi/poifs/filesystem/DirectoryNode.java
src/java/org/apache/poi/poifs/filesystem/DocumentInputStream.java
src/java/org/apache/poi/poifs/filesystem/NDocumentInputStream.java
src/java/org/apache/poi/poifs/filesystem/ODocumentInputStream.java [new file with mode: 0644]

index 0bd6b1395c4e5dc698ba3ea0649a322b52a080c7..6e1527e8588866fdc6b1bf5edaf4c5f37cf6de73 100644 (file)
@@ -144,7 +144,7 @@ public abstract class POIDocument {
           //directory can be null when creating new documents
           if(directory == null) return null;
 
-          InputStream dis;
+          DocumentInputStream dis;
           try {
              // Find the entry, and get an input stream for it
              dis = directory.createDocumentInputStream( directory.getEntry(setName) );
index 29be005b8b3aa68e03e1bb534cb4c88463956443..ea733ea879490e52134883b075a17afaf9bb733f 100644 (file)
@@ -171,14 +171,7 @@ public class DirectoryNode
             final String documentName)
         throws IOException
     {
-        Entry document = getEntry(documentName);
-
-        if (!document.isDocumentEntry())
-        {
-            throw new IOException("Entry '" + documentName
-                                  + "' is not a DocumentEntry");
-        }
-        return new DocumentInputStream(( DocumentEntry ) document);
+        return createDocumentInputStream(getEntry(documentName));
     }
 
     /**
@@ -191,7 +184,7 @@ public class DirectoryNode
      * @exception IOException if the document does not exist or the
      *            name is that of a DirectoryEntry
      */
-    public InputStream createDocumentInputStream(
+    public DocumentInputStream createDocumentInputStream(
             final Entry document)
         throws IOException
     {
@@ -201,11 +194,7 @@ public class DirectoryNode
         }
         
         DocumentEntry entry = (DocumentEntry)document;
-        if(_ofilesystem != null) {
-           return new DocumentInputStream(entry);
-        } else {
-           return new NDocumentInputStream(entry);
-        }
+        return new DocumentInputStream(entry);
     }
 
     /**
index 577f3d93b1191d576b896f076537e9e577fab7ce..d39582f7ad196c445ca8923d26e2e37bfce14a61 100644 (file)
@@ -20,40 +20,26 @@ package org.apache.poi.poifs.filesystem;
 import java.io.IOException;
 import java.io.InputStream;
 
-import org.apache.poi.poifs.storage.DataInputBlock;
 import org.apache.poi.util.LittleEndianInput;
 
 /**
  * This class provides methods to read a DocumentEntry managed by a
- * {@link POIFSFileSystem} instance.
- *
- * @author Marc Johnson (mjohnson at apache dot org)
+ *  {@link POIFSFileSystem} or {@link NPOIFSFileSystem} instance.
+ * It creates the appropriate one, and delegates, allowing us to
+ *  work transparently with the two.
  */
-public final class DocumentInputStream extends InputStream implements LittleEndianInput {
+public class DocumentInputStream extends InputStream implements LittleEndianInput {
        /** returned by read operations if we're at end of document */
-       private static final int EOF = -1;
+       protected static final int EOF = -1;
 
-       private static final int SIZE_SHORT = 2;
-       private static final int SIZE_INT = 4;
-       private static final int SIZE_LONG = 8;
-
-       /** current offset into the Document */
-       private int _current_offset;
-
-       /** current marked offset into the Document (used by mark and reset) */
-       private int _marked_offset;
-
-       /** the Document's size */
-       private int _document_size;
-
-       /** have we been closed? */
-       private boolean _closed;
-
-       /** the actual Document */
-       private POIFSDocument _document;
-
-       /** the data block containing the current stream pointer */
-       private DataInputBlock _currentBlock;
+       protected static final int SIZE_SHORT = 2;
+       protected static final int SIZE_INT = 4;
+       protected static final int SIZE_LONG = 8;
+       
+       private DocumentInputStream delegate;
+       
+       /** For use by downstream implementations */
+       protected DocumentInputStream() {}
 
        /**
         * Create an InputStream from the specified DocumentEntry
@@ -64,20 +50,21 @@ public final class DocumentInputStream extends InputStream implements LittleEndi
         *                been deleted?)
         */
        public DocumentInputStream(DocumentEntry document) throws IOException {
-               if (!(document instanceof DocumentNode)) {
-                       throw new IOException("Cannot open internal document storage");
-               }
-               DocumentNode documentNode = (DocumentNode)document;
-               if(documentNode.getDocument() == null) {
-         throw new IOException("Cannot open internal document storage");
-               }
-                     
-               _current_offset = 0;
-               _marked_offset = 0;
-               _document_size = document.getSize();
-               _closed = false;
-               _document = documentNode.getDocument();
-               _currentBlock = getDataInputBlock(0);
+          if (!(document instanceof DocumentNode)) {
+             throw new IOException("Cannot open internal document storage");
+          }
+          DocumentNode documentNode = (DocumentNode)document;
+          DirectoryNode parentNode = (DirectoryNode)document.getParent();
+
+          if(documentNode.getDocument() != null) {
+             delegate = new ODocumentInputStream(document);
+          } else if(parentNode.getFileSystem() != null) {
+             delegate = new ODocumentInputStream(document);
+          } else if(parentNode.getNFileSystem() != null) {
+             delegate = new NDocumentInputStream(document);
+          } else {
+             throw new IOException("No FileSystem bound on the parent, can't read contents");
+          }
        }
 
        /**
@@ -86,27 +73,28 @@ public final class DocumentInputStream extends InputStream implements LittleEndi
         * @param document the Document to be read
         */
        public DocumentInputStream(POIFSDocument document) {
-               _current_offset = 0;
-               _marked_offset = 0;
-               _document_size = document.getSize();
-               _closed = false;
-               _document = document;
-               _currentBlock = getDataInputBlock(0);
+          delegate = new ODocumentInputStream(document);
        }
 
+   /**
+    * Create an InputStream from the specified Document
+    * 
+    * @param document the Document to be read
+    */
+   public DocumentInputStream(NPOIFSDocument document) {
+      delegate = new NDocumentInputStream(document);
+   }
+
        public int available() {
-               if (_closed) {
-                       throw new IllegalStateException("cannot perform requested operation on a closed stream");
-               }
-               return _document_size - _current_offset;
+          return delegate.available();
        }
 
        public void close() {
-               _closed = true;
+          delegate.close();
        }
 
        public void mark(int ignoredReadlimit) {
-               _marked_offset = _current_offset;
+               delegate.mark(ignoredReadlimit);
        }
 
        /**
@@ -118,21 +106,8 @@ public final class DocumentInputStream extends InputStream implements LittleEndi
                return true;
        }
 
-       private DataInputBlock getDataInputBlock(int offset) {
-               return _document.getDataInputBlock(offset);
-       }
-
        public int read() throws IOException {
-               dieIfClosed();
-               if (atEOD()) {
-                       return EOF;
-               }
-               int result = _currentBlock.readUByte();
-               _current_offset++;
-               if (_currentBlock.available() < 1) {
-                       _currentBlock = getDataInputBlock(_current_offset);
-               }
-               return result;
+          return delegate.read();
        }
 
        public int read(byte[] b) throws IOException {
@@ -140,22 +115,7 @@ public final class DocumentInputStream extends InputStream implements LittleEndi
        }
 
        public int read(byte[] b, int off, int len) throws IOException {
-               dieIfClosed();
-               if (b == null) {
-                       throw new IllegalArgumentException("buffer must not be null");
-               }
-               if (off < 0 || len < 0 || b.length < off + len) {
-                       throw new IndexOutOfBoundsException("can't read past buffer boundaries");
-               }
-               if (len == 0) {
-                       return 0;
-               }
-               if (atEOD()) {
-                       return EOF;
-               }
-               int limit = Math.min(available(), len);
-               readFully(b, off, limit);
-               return limit;
+          return delegate.read(b, off, len);
        }
 
        /**
@@ -164,169 +124,46 @@ public final class DocumentInputStream extends InputStream implements LittleEndi
         * method repositions the stream to its beginning.
         */
        public void reset() {
-               _current_offset = _marked_offset;
-               _currentBlock = getDataInputBlock(_current_offset);
+          delegate.reset();
        }
 
        public long skip(long n) throws IOException {
-               dieIfClosed();
-               if (n < 0) {
-                       return 0;
-               }
-               int new_offset = _current_offset + (int) n;
-
-               if (new_offset < _current_offset) {
-
-                       // wrap around in converting a VERY large long to an int
-                       new_offset = _document_size;
-               } else if (new_offset > _document_size) {
-                       new_offset = _document_size;
-               }
-               long rval = new_offset - _current_offset;
-
-               _current_offset = new_offset;
-               _currentBlock = getDataInputBlock(_current_offset);
-               return rval;
-       }
-
-       private void dieIfClosed() throws IOException {
-               if (_closed) {
-                       throw new IOException("cannot perform requested operation on a closed stream");
-               }
-       }
-
-       private boolean atEOD() {
-               return _current_offset == _document_size;
-       }
-
-       private void checkAvaliable(int requestedSize) {
-               if (_closed) {
-                       throw new IllegalStateException("cannot perform requested operation on a closed stream");
-               }
-               if (requestedSize > _document_size - _current_offset) {
-                       throw new RuntimeException("Buffer underrun - requested " + requestedSize
-                                       + " bytes but " + (_document_size - _current_offset) + " was available");
-               }
+          return delegate.skip(n);
        }
 
        public byte readByte() {
-               return (byte) readUByte();
+          return delegate.readByte();
        }
 
        public double readDouble() {
-               return Double.longBitsToDouble(readLong());
-       }
-
-       public void readFully(byte[] buf) {
-               readFully(buf, 0, buf.length);
+          return delegate.readDouble();
        }
 
        public short readShort() {
                return (short) readUShort();
        }
 
+   public void readFully(byte[] buf) {
+      readFully(buf, 0, buf.length);
+   }
+
        public void readFully(byte[] buf, int off, int len) {
-               checkAvaliable(len);
-               int blockAvailable = _currentBlock.available();
-               if (blockAvailable > len) {
-                       _currentBlock.readFully(buf, off, len);
-                       _current_offset += len;
-                       return;
-               }
-               // else read big amount in chunks
-               int remaining = len;
-               int writePos = off;
-               while (remaining > 0) {
-                       boolean blockIsExpiring = remaining >= blockAvailable;
-                       int reqSize;
-                       if (blockIsExpiring) {
-                               reqSize = blockAvailable;
-                       } else {
-                               reqSize = remaining;
-                       }
-                       _currentBlock.readFully(buf, writePos, reqSize);
-                       remaining -= reqSize;
-                       writePos += reqSize;
-                       _current_offset += reqSize;
-                       if (blockIsExpiring) {
-                               if (_current_offset == _document_size) {
-                                       if (remaining > 0) {
-                                               throw new IllegalStateException(
-                                                               "reached end of document stream unexpectedly");
-                                       }
-                                       _currentBlock = null;
-                                       break;
-                               }
-                               _currentBlock = getDataInputBlock(_current_offset);
-                               blockAvailable = _currentBlock.available();
-                       }
-               }
+          delegate.readFully(buf, off, len);
        }
 
        public long readLong() {
-               checkAvaliable(SIZE_LONG);
-               int blockAvailable = _currentBlock.available();
-               long result;
-               if (blockAvailable > SIZE_LONG) {
-                       result = _currentBlock.readLongLE();
-               } else {
-                       DataInputBlock nextBlock = getDataInputBlock(_current_offset + blockAvailable);
-                       if (blockAvailable == SIZE_LONG) {
-                               result = _currentBlock.readLongLE();
-                       } else {
-                               result = nextBlock.readLongLE(_currentBlock, blockAvailable);
-                       }
-                       _currentBlock = nextBlock;
-               }
-               _current_offset += SIZE_LONG;
-               return result;
+          return delegate.readLong();
        }
 
        public int readInt() {
-               checkAvaliable(SIZE_INT);
-               int blockAvailable = _currentBlock.available();
-               int result;
-               if (blockAvailable > SIZE_INT) {
-                       result = _currentBlock.readIntLE();
-               } else {
-                       DataInputBlock nextBlock = getDataInputBlock(_current_offset + blockAvailable);
-                       if (blockAvailable == SIZE_INT) {
-                               result = _currentBlock.readIntLE();
-                       } else {
-                               result = nextBlock.readIntLE(_currentBlock, blockAvailable);
-                       }
-                       _currentBlock = nextBlock;
-               }
-               _current_offset += SIZE_INT;
-               return result;
+          return delegate.readInt();
        }
 
        public int readUShort() {
-               checkAvaliable(SIZE_SHORT);
-               int blockAvailable = _currentBlock.available();
-               int result;
-               if (blockAvailable > SIZE_SHORT) {
-                       result = _currentBlock.readUShortLE();
-               } else {
-                       DataInputBlock nextBlock = getDataInputBlock(_current_offset + blockAvailable);
-                       if (blockAvailable == SIZE_SHORT) {
-                               result = _currentBlock.readUShortLE();
-                       } else {
-                               result = nextBlock.readUShortLE(_currentBlock);
-                       }
-                       _currentBlock = nextBlock;
-               }
-               _current_offset += SIZE_SHORT;
-               return result;
+          return delegate.readUShort();
        }
 
        public int readUByte() {
-               checkAvaliable(1);
-               int result = _currentBlock.readUByte();
-               _current_offset++;
-               if (_currentBlock.available() < 1) {
-                       _currentBlock = getDataInputBlock(_current_offset);
-               }
-               return result;
+          return delegate.readUByte();
        }
 }
index 2595ba025917a4fdaa6ec9688341a034f817d3e6..602cb5ff56a8db13d0e97841393429d6d60d92bc 100644 (file)
 package org.apache.poi.poifs.filesystem;
 
 import java.io.IOException;
-import java.io.InputStream;
 import java.nio.ByteBuffer;
 import java.util.Iterator;
 
 import org.apache.poi.poifs.property.DocumentProperty;
 import org.apache.poi.util.LittleEndian;
-import org.apache.poi.util.LittleEndianInput;
 
 /**
  * This class provides methods to read a DocumentEntry managed by a
- * {@link POIFSFileSystem} instance.
+ * {@link NPOIFSFileSystem} instance.
  */
-public final class NDocumentInputStream extends InputStream implements LittleEndianInput {
-       /** returned by read operations if we're at end of document */
-       private static final int EOF = -1;
-
-       private static final int SIZE_SHORT = 2;
-       private static final int SIZE_INT = 4;
-       private static final int SIZE_LONG = 8;
-
+public final class NDocumentInputStream extends DocumentInputStream {
        /** current offset into the Document */
        private int _current_offset;
        /** current block count */
@@ -104,6 +95,7 @@ public final class NDocumentInputStream extends InputStream implements LittleEnd
       _data = _document.getBlockIterator();
        }
 
+       @Override
        public int available() {
                if (_closed) {
                        throw new IllegalStateException("cannot perform requested operation on a closed stream");
@@ -111,24 +103,18 @@ public final class NDocumentInputStream extends InputStream implements LittleEnd
                return _document_size - _current_offset;
        }
 
+   @Override
        public void close() {
                _closed = true;
        }
 
+   @Override
        public void mark(int ignoredReadlimit) {
                _marked_offset = _current_offset;
                _marked_offset_count = _current_block_count;
        }
 
-       /**
-        * Tests if this input stream supports the mark and reset methods.
-        * 
-        * @return <code>true</code> always
-        */
-       public boolean markSupported() {
-               return true;
-       }
-
+   @Override
        public int read() throws IOException {
                dieIfClosed();
                if (atEOD()) {
@@ -145,10 +131,7 @@ public final class NDocumentInputStream extends InputStream implements LittleEnd
                return result;
        }
 
-       public int read(byte[] b) throws IOException {
-               return read(b, 0, b.length);
-       }
-
+   @Override
        public int read(byte[] b, int off, int len) throws IOException {
                dieIfClosed();
                if (b == null) {
@@ -173,6 +156,7 @@ public final class NDocumentInputStream extends InputStream implements LittleEnd
         * last called on this input stream. If mark() has not been called this
         * method repositions the stream to its beginning.
         */
+   @Override
        public void reset() {
           // Special case for reset to the start
           if(_marked_offset == 0 && _marked_offset_count == 0) {
@@ -207,6 +191,7 @@ public final class NDocumentInputStream extends InputStream implements LittleEnd
       _current_offset = _marked_offset;
        }
 
+   @Override
        public long skip(long n) throws IOException {
                dieIfClosed();
                if (n < 0) {
@@ -249,22 +234,7 @@ public final class NDocumentInputStream extends InputStream implements LittleEnd
                }
        }
 
-       public byte readByte() {
-               return (byte) readUByte();
-       }
-
-       public double readDouble() {
-               return Double.longBitsToDouble(readLong());
-       }
-
-       public void readFully(byte[] buf) {
-               readFully(buf, 0, buf.length);
-       }
-
-       public short readShort() {
-               return (short) readUShort();
-       }
-
+   @Override
        public void readFully(byte[] buf, int off, int len) {
                checkAvaliable(len);
 
@@ -282,6 +252,17 @@ public final class NDocumentInputStream extends InputStream implements LittleEnd
                }
        }
 
+   @Override
+   public byte readByte() {
+      return (byte) readUByte();
+   }
+
+   @Override
+   public double readDouble() {
+      return Double.longBitsToDouble(readLong());
+   }
+
+   @Override
        public long readLong() {
                checkAvaliable(SIZE_LONG);
                byte[] data = new byte[SIZE_LONG];
@@ -289,6 +270,12 @@ public final class NDocumentInputStream extends InputStream implements LittleEnd
                return LittleEndian.getLong(data, 0);
        }
 
+   @Override
+   public short readShort() {
+      return (short) readUShort();
+   }
+
+   @Override
        public int readInt() {
                checkAvaliable(SIZE_INT);
       byte[] data = new byte[SIZE_INT];
@@ -296,6 +283,7 @@ public final class NDocumentInputStream extends InputStream implements LittleEnd
       return LittleEndian.getInt(data);
        }
 
+   @Override
        public int readUShort() {
                checkAvaliable(SIZE_SHORT);
       byte[] data = new byte[SIZE_SHORT];
@@ -303,6 +291,7 @@ public final class NDocumentInputStream extends InputStream implements LittleEnd
       return LittleEndian.getShort(data);
        }
 
+   @Override
        public int readUByte() {
                checkAvaliable(1);
       byte[] data = new byte[1];
diff --git a/src/java/org/apache/poi/poifs/filesystem/ODocumentInputStream.java b/src/java/org/apache/poi/poifs/filesystem/ODocumentInputStream.java
new file mode 100644 (file)
index 0000000..a1bc5da
--- /dev/null
@@ -0,0 +1,321 @@
+/* ====================================================================
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+==================================================================== */
+
+package org.apache.poi.poifs.filesystem;
+
+import java.io.IOException;
+
+import org.apache.poi.poifs.storage.DataInputBlock;
+
+/**
+ * This class provides methods to read a DocumentEntry managed by a
+ * {@link POIFSFileSystem} instance.
+ *
+ * @author Marc Johnson (mjohnson at apache dot org)
+ */
+public final class ODocumentInputStream extends DocumentInputStream {
+       /** current offset into the Document */
+       private int _current_offset;
+
+       /** current marked offset into the Document (used by mark and reset) */
+       private int _marked_offset;
+
+       /** the Document's size */
+       private int _document_size;
+
+       /** have we been closed? */
+       private boolean _closed;
+
+       /** the actual Document */
+       private POIFSDocument _document;
+
+       /** the data block containing the current stream pointer */
+       private DataInputBlock _currentBlock;
+
+       /**
+        * Create an InputStream from the specified DocumentEntry
+        * 
+        * @param document the DocumentEntry to be read
+        * 
+        * @exception IOException if the DocumentEntry cannot be opened (like, maybe it has
+        *                been deleted?)
+        */
+       public ODocumentInputStream(DocumentEntry document) throws IOException {
+               if (!(document instanceof DocumentNode)) {
+                       throw new IOException("Cannot open internal document storage");
+               }
+               DocumentNode documentNode = (DocumentNode)document;
+               if(documentNode.getDocument() == null) {
+         throw new IOException("Cannot open internal document storage");
+               }
+                     
+               _current_offset = 0;
+               _marked_offset = 0;
+               _document_size = document.getSize();
+               _closed = false;
+               _document = documentNode.getDocument();
+               _currentBlock = getDataInputBlock(0);
+       }
+
+       /**
+        * Create an InputStream from the specified Document
+        * 
+        * @param document the Document to be read
+        */
+       public ODocumentInputStream(POIFSDocument document) {
+               _current_offset = 0;
+               _marked_offset = 0;
+               _document_size = document.getSize();
+               _closed = false;
+               _document = document;
+               _currentBlock = getDataInputBlock(0);
+       }
+
+       @Override
+       public int available() {
+               if (_closed) {
+                       throw new IllegalStateException("cannot perform requested operation on a closed stream");
+               }
+               return _document_size - _current_offset;
+       }
+
+   @Override
+       public void close() {
+               _closed = true;
+       }
+
+   @Override
+       public void mark(int ignoredReadlimit) {
+               _marked_offset = _current_offset;
+       }
+
+       private DataInputBlock getDataInputBlock(int offset) {
+               return _document.getDataInputBlock(offset);
+       }
+
+   @Override
+       public int read() throws IOException {
+               dieIfClosed();
+               if (atEOD()) {
+                       return EOF;
+               }
+               int result = _currentBlock.readUByte();
+               _current_offset++;
+               if (_currentBlock.available() < 1) {
+                       _currentBlock = getDataInputBlock(_current_offset);
+               }
+               return result;
+       }
+
+   @Override
+       public int read(byte[] b, int off, int len) throws IOException {
+               dieIfClosed();
+               if (b == null) {
+                       throw new IllegalArgumentException("buffer must not be null");
+               }
+               if (off < 0 || len < 0 || b.length < off + len) {
+                       throw new IndexOutOfBoundsException("can't read past buffer boundaries");
+               }
+               if (len == 0) {
+                       return 0;
+               }
+               if (atEOD()) {
+                       return EOF;
+               }
+               int limit = Math.min(available(), len);
+               readFully(b, off, limit);
+               return limit;
+       }
+
+       /**
+        * Repositions this stream to the position at the time the mark() method was
+        * last called on this input stream. If mark() has not been called this
+        * method repositions the stream to its beginning.
+        */
+   @Override
+       public void reset() {
+               _current_offset = _marked_offset;
+               _currentBlock = getDataInputBlock(_current_offset);
+       }
+
+   @Override
+       public long skip(long n) throws IOException {
+               dieIfClosed();
+               if (n < 0) {
+                       return 0;
+               }
+               int new_offset = _current_offset + (int) n;
+
+               if (new_offset < _current_offset) {
+
+                       // wrap around in converting a VERY large long to an int
+                       new_offset = _document_size;
+               } else if (new_offset > _document_size) {
+                       new_offset = _document_size;
+               }
+               long rval = new_offset - _current_offset;
+
+               _current_offset = new_offset;
+               _currentBlock = getDataInputBlock(_current_offset);
+               return rval;
+       }
+
+       private void dieIfClosed() throws IOException {
+               if (_closed) {
+                       throw new IOException("cannot perform requested operation on a closed stream");
+               }
+       }
+
+       private boolean atEOD() {
+               return _current_offset == _document_size;
+       }
+
+       private void checkAvaliable(int requestedSize) {
+               if (_closed) {
+                       throw new IllegalStateException("cannot perform requested operation on a closed stream");
+               }
+               if (requestedSize > _document_size - _current_offset) {
+                       throw new RuntimeException("Buffer underrun - requested " + requestedSize
+                                       + " bytes but " + (_document_size - _current_offset) + " was available");
+               }
+       }
+
+   @Override
+       public byte readByte() {
+               return (byte) readUByte();
+       }
+
+   @Override
+       public double readDouble() {
+               return Double.longBitsToDouble(readLong());
+       }
+
+   @Override
+       public short readShort() {
+               return (short) readUShort();
+       }
+
+   @Override
+       public void readFully(byte[] buf, int off, int len) {
+               checkAvaliable(len);
+               int blockAvailable = _currentBlock.available();
+               if (blockAvailable > len) {
+                       _currentBlock.readFully(buf, off, len);
+                       _current_offset += len;
+                       return;
+               }
+               // else read big amount in chunks
+               int remaining = len;
+               int writePos = off;
+               while (remaining > 0) {
+                       boolean blockIsExpiring = remaining >= blockAvailable;
+                       int reqSize;
+                       if (blockIsExpiring) {
+                               reqSize = blockAvailable;
+                       } else {
+                               reqSize = remaining;
+                       }
+                       _currentBlock.readFully(buf, writePos, reqSize);
+                       remaining -= reqSize;
+                       writePos += reqSize;
+                       _current_offset += reqSize;
+                       if (blockIsExpiring) {
+                               if (_current_offset == _document_size) {
+                                       if (remaining > 0) {
+                                               throw new IllegalStateException(
+                                                               "reached end of document stream unexpectedly");
+                                       }
+                                       _currentBlock = null;
+                                       break;
+                               }
+                               _currentBlock = getDataInputBlock(_current_offset);
+                               blockAvailable = _currentBlock.available();
+                       }
+               }
+       }
+
+   @Override
+       public long readLong() {
+               checkAvaliable(SIZE_LONG);
+               int blockAvailable = _currentBlock.available();
+               long result;
+               if (blockAvailable > SIZE_LONG) {
+                       result = _currentBlock.readLongLE();
+               } else {
+                       DataInputBlock nextBlock = getDataInputBlock(_current_offset + blockAvailable);
+                       if (blockAvailable == SIZE_LONG) {
+                               result = _currentBlock.readLongLE();
+                       } else {
+                               result = nextBlock.readLongLE(_currentBlock, blockAvailable);
+                       }
+                       _currentBlock = nextBlock;
+               }
+               _current_offset += SIZE_LONG;
+               return result;
+       }
+
+   @Override
+       public int readInt() {
+               checkAvaliable(SIZE_INT);
+               int blockAvailable = _currentBlock.available();
+               int result;
+               if (blockAvailable > SIZE_INT) {
+                       result = _currentBlock.readIntLE();
+               } else {
+                       DataInputBlock nextBlock = getDataInputBlock(_current_offset + blockAvailable);
+                       if (blockAvailable == SIZE_INT) {
+                               result = _currentBlock.readIntLE();
+                       } else {
+                               result = nextBlock.readIntLE(_currentBlock, blockAvailable);
+                       }
+                       _currentBlock = nextBlock;
+               }
+               _current_offset += SIZE_INT;
+               return result;
+       }
+
+   @Override
+       public int readUShort() {
+               checkAvaliable(SIZE_SHORT);
+               int blockAvailable = _currentBlock.available();
+               int result;
+               if (blockAvailable > SIZE_SHORT) {
+                       result = _currentBlock.readUShortLE();
+               } else {
+                       DataInputBlock nextBlock = getDataInputBlock(_current_offset + blockAvailable);
+                       if (blockAvailable == SIZE_SHORT) {
+                               result = _currentBlock.readUShortLE();
+                       } else {
+                               result = nextBlock.readUShortLE(_currentBlock);
+                       }
+                       _currentBlock = nextBlock;
+               }
+               _current_offset += SIZE_SHORT;
+               return result;
+       }
+
+   @Override
+       public int readUByte() {
+               checkAvaliable(1);
+               int result = _currentBlock.readUByte();
+               _current_offset++;
+               if (_currentBlock.available() < 1) {
+                       _currentBlock = getDataInputBlock(_current_offset);
+               }
+               return result;
+       }
+}