diff options
Diffstat (limited to 'src/java/org/apache/poi/poifs/filesystem/ODocumentInputStream.java')
-rw-r--r-- | src/java/org/apache/poi/poifs/filesystem/ODocumentInputStream.java | 321 |
1 files changed, 321 insertions, 0 deletions
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 index 0000000000..a1bc5da95f --- /dev/null +++ b/src/java/org/apache/poi/poifs/filesystem/ODocumentInputStream.java @@ -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; + } +} |