mirror of https://github.com/apache/poi.git
Refactor DocumentInputStream so that it can transparently handle both old style and new style POIFS Documents
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1053562 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
3a0d6d3f8e
commit
d004673137
|
@ -144,7 +144,7 @@ public abstract class POIDocument {
|
||||||
//directory can be null when creating new documents
|
//directory can be null when creating new documents
|
||||||
if(directory == null) return null;
|
if(directory == null) return null;
|
||||||
|
|
||||||
InputStream dis;
|
DocumentInputStream dis;
|
||||||
try {
|
try {
|
||||||
// Find the entry, and get an input stream for it
|
// Find the entry, and get an input stream for it
|
||||||
dis = directory.createDocumentInputStream( directory.getEntry(setName) );
|
dis = directory.createDocumentInputStream( directory.getEntry(setName) );
|
||||||
|
|
|
@ -171,14 +171,7 @@ public class DirectoryNode
|
||||||
final String documentName)
|
final String documentName)
|
||||||
throws IOException
|
throws IOException
|
||||||
{
|
{
|
||||||
Entry document = getEntry(documentName);
|
return createDocumentInputStream(getEntry(documentName));
|
||||||
|
|
||||||
if (!document.isDocumentEntry())
|
|
||||||
{
|
|
||||||
throw new IOException("Entry '" + documentName
|
|
||||||
+ "' is not a DocumentEntry");
|
|
||||||
}
|
|
||||||
return new DocumentInputStream(( DocumentEntry ) document);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -191,7 +184,7 @@ public class DirectoryNode
|
||||||
* @exception IOException if the document does not exist or the
|
* @exception IOException if the document does not exist or the
|
||||||
* name is that of a DirectoryEntry
|
* name is that of a DirectoryEntry
|
||||||
*/
|
*/
|
||||||
public InputStream createDocumentInputStream(
|
public DocumentInputStream createDocumentInputStream(
|
||||||
final Entry document)
|
final Entry document)
|
||||||
throws IOException
|
throws IOException
|
||||||
{
|
{
|
||||||
|
@ -201,11 +194,7 @@ public class DirectoryNode
|
||||||
}
|
}
|
||||||
|
|
||||||
DocumentEntry entry = (DocumentEntry)document;
|
DocumentEntry entry = (DocumentEntry)document;
|
||||||
if(_ofilesystem != null) {
|
return new DocumentInputStream(entry);
|
||||||
return new DocumentInputStream(entry);
|
|
||||||
} else {
|
|
||||||
return new NDocumentInputStream(entry);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -20,40 +20,26 @@ package org.apache.poi.poifs.filesystem;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
|
||||||
import org.apache.poi.poifs.storage.DataInputBlock;
|
|
||||||
import org.apache.poi.util.LittleEndianInput;
|
import org.apache.poi.util.LittleEndianInput;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class provides methods to read a DocumentEntry managed by a
|
* This class provides methods to read a DocumentEntry managed by a
|
||||||
* {@link POIFSFileSystem} instance.
|
* {@link POIFSFileSystem} or {@link NPOIFSFileSystem} instance.
|
||||||
*
|
* It creates the appropriate one, and delegates, allowing us to
|
||||||
* @author Marc Johnson (mjohnson at apache dot org)
|
* 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 */
|
/** 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;
|
protected static final int SIZE_SHORT = 2;
|
||||||
private static final int SIZE_INT = 4;
|
protected static final int SIZE_INT = 4;
|
||||||
private static final int SIZE_LONG = 8;
|
protected static final int SIZE_LONG = 8;
|
||||||
|
|
||||||
/** current offset into the Document */
|
private DocumentInputStream delegate;
|
||||||
private int _current_offset;
|
|
||||||
|
/** For use by downstream implementations */
|
||||||
/** current marked offset into the Document (used by mark and reset) */
|
protected DocumentInputStream() {}
|
||||||
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
|
* Create an InputStream from the specified DocumentEntry
|
||||||
|
@ -64,20 +50,21 @@ public final class DocumentInputStream extends InputStream implements LittleEndi
|
||||||
* been deleted?)
|
* been deleted?)
|
||||||
*/
|
*/
|
||||||
public DocumentInputStream(DocumentEntry document) throws IOException {
|
public DocumentInputStream(DocumentEntry document) throws IOException {
|
||||||
if (!(document instanceof DocumentNode)) {
|
if (!(document instanceof DocumentNode)) {
|
||||||
throw new IOException("Cannot open internal document storage");
|
throw new IOException("Cannot open internal document storage");
|
||||||
}
|
}
|
||||||
DocumentNode documentNode = (DocumentNode)document;
|
DocumentNode documentNode = (DocumentNode)document;
|
||||||
if(documentNode.getDocument() == null) {
|
DirectoryNode parentNode = (DirectoryNode)document.getParent();
|
||||||
throw new IOException("Cannot open internal document storage");
|
|
||||||
}
|
if(documentNode.getDocument() != null) {
|
||||||
|
delegate = new ODocumentInputStream(document);
|
||||||
_current_offset = 0;
|
} else if(parentNode.getFileSystem() != null) {
|
||||||
_marked_offset = 0;
|
delegate = new ODocumentInputStream(document);
|
||||||
_document_size = document.getSize();
|
} else if(parentNode.getNFileSystem() != null) {
|
||||||
_closed = false;
|
delegate = new NDocumentInputStream(document);
|
||||||
_document = documentNode.getDocument();
|
} else {
|
||||||
_currentBlock = getDataInputBlock(0);
|
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
|
* @param document the Document to be read
|
||||||
*/
|
*/
|
||||||
public DocumentInputStream(POIFSDocument document) {
|
public DocumentInputStream(POIFSDocument document) {
|
||||||
_current_offset = 0;
|
delegate = new ODocumentInputStream(document);
|
||||||
_marked_offset = 0;
|
|
||||||
_document_size = document.getSize();
|
|
||||||
_closed = false;
|
|
||||||
_document = document;
|
|
||||||
_currentBlock = getDataInputBlock(0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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() {
|
public int available() {
|
||||||
if (_closed) {
|
return delegate.available();
|
||||||
throw new IllegalStateException("cannot perform requested operation on a closed stream");
|
|
||||||
}
|
|
||||||
return _document_size - _current_offset;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void close() {
|
public void close() {
|
||||||
_closed = true;
|
delegate.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void mark(int ignoredReadlimit) {
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private DataInputBlock getDataInputBlock(int offset) {
|
|
||||||
return _document.getDataInputBlock(offset);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int read() throws IOException {
|
public int read() throws IOException {
|
||||||
dieIfClosed();
|
return delegate.read();
|
||||||
if (atEOD()) {
|
|
||||||
return EOF;
|
|
||||||
}
|
|
||||||
int result = _currentBlock.readUByte();
|
|
||||||
_current_offset++;
|
|
||||||
if (_currentBlock.available() < 1) {
|
|
||||||
_currentBlock = getDataInputBlock(_current_offset);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public int read(byte[] b) throws IOException {
|
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 {
|
public int read(byte[] b, int off, int len) throws IOException {
|
||||||
dieIfClosed();
|
return delegate.read(b, off, len);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -164,169 +124,46 @@ public final class DocumentInputStream extends InputStream implements LittleEndi
|
||||||
* method repositions the stream to its beginning.
|
* method repositions the stream to its beginning.
|
||||||
*/
|
*/
|
||||||
public void reset() {
|
public void reset() {
|
||||||
_current_offset = _marked_offset;
|
delegate.reset();
|
||||||
_currentBlock = getDataInputBlock(_current_offset);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public long skip(long n) throws IOException {
|
public long skip(long n) throws IOException {
|
||||||
dieIfClosed();
|
return delegate.skip(n);
|
||||||
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");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte readByte() {
|
public byte readByte() {
|
||||||
return (byte) readUByte();
|
return delegate.readByte();
|
||||||
}
|
}
|
||||||
|
|
||||||
public double readDouble() {
|
public double readDouble() {
|
||||||
return Double.longBitsToDouble(readLong());
|
return delegate.readDouble();
|
||||||
}
|
|
||||||
|
|
||||||
public void readFully(byte[] buf) {
|
|
||||||
readFully(buf, 0, buf.length);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public short readShort() {
|
public short readShort() {
|
||||||
return (short) readUShort();
|
return (short) readUShort();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void readFully(byte[] buf) {
|
||||||
|
readFully(buf, 0, buf.length);
|
||||||
|
}
|
||||||
|
|
||||||
public void readFully(byte[] buf, int off, int len) {
|
public void readFully(byte[] buf, int off, int len) {
|
||||||
checkAvaliable(len);
|
delegate.readFully(buf, off, 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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public long readLong() {
|
public long readLong() {
|
||||||
checkAvaliable(SIZE_LONG);
|
return delegate.readLong();
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public int readInt() {
|
public int readInt() {
|
||||||
checkAvaliable(SIZE_INT);
|
return delegate.readInt();
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public int readUShort() {
|
public int readUShort() {
|
||||||
checkAvaliable(SIZE_SHORT);
|
return delegate.readUShort();
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public int readUByte() {
|
public int readUByte() {
|
||||||
checkAvaliable(1);
|
return delegate.readUByte();
|
||||||
int result = _currentBlock.readUByte();
|
|
||||||
_current_offset++;
|
|
||||||
if (_currentBlock.available() < 1) {
|
|
||||||
_currentBlock = getDataInputBlock(_current_offset);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,26 +18,17 @@
|
||||||
package org.apache.poi.poifs.filesystem;
|
package org.apache.poi.poifs.filesystem;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
|
|
||||||
import org.apache.poi.poifs.property.DocumentProperty;
|
import org.apache.poi.poifs.property.DocumentProperty;
|
||||||
import org.apache.poi.util.LittleEndian;
|
import org.apache.poi.util.LittleEndian;
|
||||||
import org.apache.poi.util.LittleEndianInput;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class provides methods to read a DocumentEntry managed by a
|
* 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 {
|
public final class NDocumentInputStream extends DocumentInputStream {
|
||||||
/** 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;
|
|
||||||
|
|
||||||
/** current offset into the Document */
|
/** current offset into the Document */
|
||||||
private int _current_offset;
|
private int _current_offset;
|
||||||
/** current block count */
|
/** current block count */
|
||||||
|
@ -104,6 +95,7 @@ public final class NDocumentInputStream extends InputStream implements LittleEnd
|
||||||
_data = _document.getBlockIterator();
|
_data = _document.getBlockIterator();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public int available() {
|
public int available() {
|
||||||
if (_closed) {
|
if (_closed) {
|
||||||
throw new IllegalStateException("cannot perform requested operation on a closed stream");
|
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;
|
return _document_size - _current_offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void close() {
|
public void close() {
|
||||||
_closed = true;
|
_closed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void mark(int ignoredReadlimit) {
|
public void mark(int ignoredReadlimit) {
|
||||||
_marked_offset = _current_offset;
|
_marked_offset = _current_offset;
|
||||||
_marked_offset_count = _current_block_count;
|
_marked_offset_count = _current_block_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* Tests if this input stream supports the mark and reset methods.
|
|
||||||
*
|
|
||||||
* @return <code>true</code> always
|
|
||||||
*/
|
|
||||||
public boolean markSupported() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int read() throws IOException {
|
public int read() throws IOException {
|
||||||
dieIfClosed();
|
dieIfClosed();
|
||||||
if (atEOD()) {
|
if (atEOD()) {
|
||||||
|
@ -145,10 +131,7 @@ public final class NDocumentInputStream extends InputStream implements LittleEnd
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int read(byte[] b) throws IOException {
|
@Override
|
||||||
return read(b, 0, b.length);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int read(byte[] b, int off, int len) throws IOException {
|
public int read(byte[] b, int off, int len) throws IOException {
|
||||||
dieIfClosed();
|
dieIfClosed();
|
||||||
if (b == null) {
|
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
|
* last called on this input stream. If mark() has not been called this
|
||||||
* method repositions the stream to its beginning.
|
* method repositions the stream to its beginning.
|
||||||
*/
|
*/
|
||||||
|
@Override
|
||||||
public void reset() {
|
public void reset() {
|
||||||
// Special case for reset to the start
|
// Special case for reset to the start
|
||||||
if(_marked_offset == 0 && _marked_offset_count == 0) {
|
if(_marked_offset == 0 && _marked_offset_count == 0) {
|
||||||
|
@ -207,6 +191,7 @@ public final class NDocumentInputStream extends InputStream implements LittleEnd
|
||||||
_current_offset = _marked_offset;
|
_current_offset = _marked_offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public long skip(long n) throws IOException {
|
public long skip(long n) throws IOException {
|
||||||
dieIfClosed();
|
dieIfClosed();
|
||||||
if (n < 0) {
|
if (n < 0) {
|
||||||
|
@ -249,22 +234,7 @@ public final class NDocumentInputStream extends InputStream implements LittleEnd
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte readByte() {
|
@Override
|
||||||
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();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void readFully(byte[] buf, int off, int len) {
|
public void readFully(byte[] buf, int off, int len) {
|
||||||
checkAvaliable(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() {
|
public long readLong() {
|
||||||
checkAvaliable(SIZE_LONG);
|
checkAvaliable(SIZE_LONG);
|
||||||
byte[] data = new byte[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);
|
return LittleEndian.getLong(data, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public short readShort() {
|
||||||
|
return (short) readUShort();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public int readInt() {
|
public int readInt() {
|
||||||
checkAvaliable(SIZE_INT);
|
checkAvaliable(SIZE_INT);
|
||||||
byte[] data = new byte[SIZE_INT];
|
byte[] data = new byte[SIZE_INT];
|
||||||
|
@ -296,6 +283,7 @@ public final class NDocumentInputStream extends InputStream implements LittleEnd
|
||||||
return LittleEndian.getInt(data);
|
return LittleEndian.getInt(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public int readUShort() {
|
public int readUShort() {
|
||||||
checkAvaliable(SIZE_SHORT);
|
checkAvaliable(SIZE_SHORT);
|
||||||
byte[] data = new byte[SIZE_SHORT];
|
byte[] data = new byte[SIZE_SHORT];
|
||||||
|
@ -303,6 +291,7 @@ public final class NDocumentInputStream extends InputStream implements LittleEnd
|
||||||
return LittleEndian.getShort(data);
|
return LittleEndian.getShort(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public int readUByte() {
|
public int readUByte() {
|
||||||
checkAvaliable(1);
|
checkAvaliable(1);
|
||||||
byte[] data = new byte[1];
|
byte[] data = new byte[1];
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue