You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@poi.apache.org by ni...@apache.org on 2010/12/30 03:35:11 UTC

svn commit: r1053791 [4/18] - in /poi/branches/NIO_32_BRANCH: ./ src/contrib/src/org/apache/poi/contrib/poibrowser/ src/java/org/apache/poi/ src/java/org/apache/poi/hpsf/ src/java/org/apache/poi/hpsf/extractor/ src/java/org/apache/poi/hssf/record/ src/...

Added: poi/branches/NIO_32_BRANCH/src/java/org/apache/poi/poifs/filesystem/NPOIFSStream.java
URL: http://svn.apache.org/viewvc/poi/branches/NIO_32_BRANCH/src/java/org/apache/poi/poifs/filesystem/NPOIFSStream.java?rev=1053791&view=auto
==============================================================================
--- poi/branches/NIO_32_BRANCH/src/java/org/apache/poi/poifs/filesystem/NPOIFSStream.java (added)
+++ poi/branches/NIO_32_BRANCH/src/java/org/apache/poi/poifs/filesystem/NPOIFSStream.java Thu Dec 30 02:35:06 2010
@@ -0,0 +1,224 @@
+
+/* ====================================================================
+   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 java.nio.ByteBuffer;
+import java.util.Iterator;
+
+import org.apache.poi.poifs.common.POIFSConstants;
+import org.apache.poi.poifs.filesystem.BlockStore.ChainLoopDetector;
+import org.apache.poi.poifs.property.Property;
+import org.apache.poi.poifs.storage.HeaderBlock;
+
+/**
+ * This handles reading and writing a stream within a
+ *  {@link NPOIFSFileSystem}. It can supply an iterator
+ *  to read blocks, and way to write out to existing and
+ *  new blocks.
+ * Most users will want a higher level version of this, 
+ *  which deals with properties to track which stream
+ *  this is.
+ * This only works on big block streams, it doesn't
+ *  handle small block ones.
+ * This uses the new NIO code
+ * 
+ * TODO Implement a streaming write method, and append
+ */
+
+public class NPOIFSStream implements Iterable<ByteBuffer>
+{
+	private BlockStore blockStore;
+	private int startBlock;
+	
+	/**
+	 * Constructor for an existing stream. It's up to you
+	 *  to know how to get the start block (eg from a 
+	 *  {@link HeaderBlock} or a {@link Property}) 
+	 */
+	public NPOIFSStream(BlockStore blockStore, int startBlock) {
+	   this.blockStore = blockStore;
+	   this.startBlock = startBlock;
+	}
+	
+	/**
+	 * Constructor for a new stream. A start block won't
+	 *  be allocated until you begin writing to it.
+	 */
+	public NPOIFSStream(BlockStore blockStore) {
+      this.blockStore = blockStore;
+	   this.startBlock = POIFSConstants.END_OF_CHAIN;
+	}
+	
+	/**
+	 * What block does this stream start at?
+	 * Will be {@link POIFSConstants#END_OF_CHAIN} for a
+	 *  new stream that hasn't been written to yet.
+	 */
+	public int getStartBlock() {
+	   return startBlock;
+	}
+
+	/**
+	 * Returns an iterator that'll supply one {@link ByteBuffer}
+	 *  per block in the stream.
+	 */
+   public Iterator<ByteBuffer> iterator() {
+      return getBlockIterator();
+   }
+	
+   public Iterator<ByteBuffer> getBlockIterator() {
+      if(startBlock == POIFSConstants.END_OF_CHAIN) {
+         throw new IllegalStateException(
+               "Can't read from a new stream before it has been written to"
+         );
+      }
+      return new StreamBlockByteBufferIterator(startBlock);
+   }
+   
+   /**
+    * Updates the contents of the stream to the new
+    *  set of bytes.
+    * Note - if this is property based, you'll still
+    *  need to update the size in the property yourself
+    */
+   public void updateContents(byte[] contents) throws IOException {
+      // How many blocks are we going to need?
+      int blockSize = blockStore.getBlockStoreBlockSize();
+      int blocks = (int)Math.ceil(contents.length / blockSize);
+      
+      // Make sure we don't encounter a loop whilst overwriting
+      //  the existing blocks
+      ChainLoopDetector loopDetector = blockStore.getChainLoopDetector();
+      
+      // Start writing
+      int prevBlock = POIFSConstants.END_OF_CHAIN;
+      int nextBlock = startBlock;
+      for(int i=0; i<blocks; i++) {
+         int thisBlock = nextBlock;
+         
+         // Allocate a block if needed, otherwise figure
+         //  out what the next block will be
+         if(thisBlock == POIFSConstants.END_OF_CHAIN) {
+            thisBlock = blockStore.getFreeBlock();
+            loopDetector.claim(thisBlock);
+            
+            // We're on the end of the chain
+            nextBlock = POIFSConstants.END_OF_CHAIN;
+            
+            // Mark the previous block as carrying on to us if needed
+            if(prevBlock != POIFSConstants.END_OF_CHAIN) {
+               blockStore.setNextBlock(prevBlock, thisBlock);
+            }
+            blockStore.setNextBlock(thisBlock, POIFSConstants.END_OF_CHAIN);
+            
+            // If we've just written the first block on a 
+            //  new stream, save the start block offset
+            if(this.startBlock == POIFSConstants.END_OF_CHAIN) {
+               this.startBlock = thisBlock;
+            }
+         } else {
+            loopDetector.claim(thisBlock);
+            nextBlock = blockStore.getNextBlock(thisBlock);
+         }
+         
+         // Write it
+         ByteBuffer buffer = blockStore.createBlockIfNeeded(thisBlock);
+         buffer.put(contents, i*blockSize, blockSize);
+         
+         // Update pointers
+         prevBlock = thisBlock;
+      }
+      int lastBlock = prevBlock;
+      
+      // If we're overwriting, free any remaining blocks
+      NPOIFSStream toFree = new NPOIFSStream(blockStore, nextBlock);
+      toFree.free(loopDetector);
+      
+      // Mark the end of the stream
+      blockStore.setNextBlock(lastBlock, POIFSConstants.END_OF_CHAIN);
+   }
+   
+   // TODO Streaming write support
+   // TODO  then convert fixed sized write to use streaming internally
+   // TODO Append write support (probably streaming)
+   
+   /**
+    * Frees all blocks in the stream
+    */
+   public void free() throws IOException {
+      ChainLoopDetector loopDetector = blockStore.getChainLoopDetector();
+      free(loopDetector);
+   }
+   private void free(ChainLoopDetector loopDetector) {
+      int nextBlock = startBlock;
+      while(nextBlock != POIFSConstants.END_OF_CHAIN) {
+         int thisBlock = nextBlock;
+         loopDetector.claim(thisBlock);
+         nextBlock = blockStore.getNextBlock(thisBlock);
+         blockStore.setNextBlock(thisBlock, POIFSConstants.UNUSED_BLOCK);
+      }
+      this.startBlock = POIFSConstants.END_OF_CHAIN;
+   }
+   
+   /**
+    * Class that handles a streaming read of one stream
+    */
+   protected class StreamBlockByteBufferIterator implements Iterator<ByteBuffer> {
+      private ChainLoopDetector loopDetector;
+      private int nextBlock;
+      
+      protected StreamBlockByteBufferIterator(int firstBlock) {
+         this.nextBlock = firstBlock;
+         try {
+            this.loopDetector = blockStore.getChainLoopDetector();
+         } catch(IOException e) {
+            throw new RuntimeException(e);
+         }
+      }
+
+      public boolean hasNext() {
+         if(nextBlock == POIFSConstants.END_OF_CHAIN) {
+            return false;
+         }
+         return true;
+      }
+
+      public ByteBuffer next() {
+         if(nextBlock == POIFSConstants.END_OF_CHAIN) {
+            throw new IndexOutOfBoundsException("Can't read past the end of the stream");
+         }
+         
+         try {
+            loopDetector.claim(nextBlock);
+            ByteBuffer data = blockStore.getBlockAt(nextBlock);
+            nextBlock = blockStore.getNextBlock(nextBlock);
+            return data;
+         } catch(IOException e) {
+            throw new RuntimeException(e);
+         }
+      }
+
+      public void remove() {
+         throw new UnsupportedOperationException();
+      }
+   }
+}
+

Added: poi/branches/NIO_32_BRANCH/src/java/org/apache/poi/poifs/filesystem/ODocumentInputStream.java
URL: http://svn.apache.org/viewvc/poi/branches/NIO_32_BRANCH/src/java/org/apache/poi/poifs/filesystem/ODocumentInputStream.java?rev=1053791&view=auto
==============================================================================
--- poi/branches/NIO_32_BRANCH/src/java/org/apache/poi/poifs/filesystem/ODocumentInputStream.java (added)
+++ poi/branches/NIO_32_BRANCH/src/java/org/apache/poi/poifs/filesystem/ODocumentInputStream.java Thu Dec 30 02:35:06 2010
@@ -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;
+	}
+}

Added: poi/branches/NIO_32_BRANCH/src/java/org/apache/poi/poifs/filesystem/Ole10Native.java
URL: http://svn.apache.org/viewvc/poi/branches/NIO_32_BRANCH/src/java/org/apache/poi/poifs/filesystem/Ole10Native.java?rev=1053791&view=auto
==============================================================================
--- poi/branches/NIO_32_BRANCH/src/java/org/apache/poi/poifs/filesystem/Ole10Native.java (added)
+++ poi/branches/NIO_32_BRANCH/src/java/org/apache/poi/poifs/filesystem/Ole10Native.java Thu Dec 30 02:35:06 2010
@@ -0,0 +1,279 @@
+/* ====================================================================
+   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 org.apache.poi.util.*;
+
+import java.io.ByteArrayOutputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.Arrays;
+
+/**
+ * Represents an Ole10Native record which is wrapped around certain binary
+ * files being embedded in OLE2 documents.
+ *
+ * @author Rainer Schwarze
+ */
+public class Ole10Native {
+  // (the fields as they appear in the raw record:)
+  private final int totalSize;                // 4 bytes, total size of record not including this field
+  private short flags1;                // 2 bytes, unknown, mostly [02 00]
+  private final String label;                // ASCIIZ, stored in this field without the terminating zero
+  private final String fileName;        // ASCIIZ, stored in this field without the terminating zero
+  private short flags2;                // 2 bytes, unknown, mostly [00 00]
+  // private byte unknown1Length;	// 1 byte, specifying the length of the following byte array (unknown1)
+  private byte[] unknown1;        // see below
+  private byte[] unknown2;        // 3 bytes, unknown, mostly [00 00 00]
+  private final String command;                // ASCIIZ, stored in this field without the terminating zero
+  private final int dataSize;                // 4 bytes (if space), size of following buffer
+  private final byte[] dataBuffer;        // varying size, the actual native data
+  private short flags3;                // some final flags? or zero terminators?, sometimes not there
+  public static final String OLE10_NATIVE = "\u0001Ole10Native";
+
+  /**
+   * Creates an instance of this class from an embedded OLE Object. The OLE Object is expected
+   * to include a stream &quot;{01}Ole10Native&quot; which contains the actual
+   * data relevant for this class.
+   *
+   * @param poifs POI Filesystem object
+   * @return Returns an instance of this class
+   * @throws IOException on IO error
+   * @throws Ole10NativeException on invalid or unexcepted data format
+   */
+  public static Ole10Native createFromEmbeddedOleObject(POIFSFileSystem poifs) throws IOException, Ole10NativeException {
+    boolean plain = false;
+
+    try {
+      poifs.getRoot().getEntry("\u0001Ole10ItemName");
+      plain = true;
+    } catch (FileNotFoundException ex) {
+      plain = false;
+    }
+
+    DocumentInputStream dis = poifs.createDocumentInputStream(OLE10_NATIVE);
+    ByteArrayOutputStream bos = new ByteArrayOutputStream();
+    IOUtils.copy(dis, bos);
+    byte[] data = bos.toByteArray();
+
+    return new Ole10Native(data, 0, plain);
+  }
+
+  /**
+   * Creates an instance and fills the fields based on the data in the given buffer.
+   *
+   * @param data   The buffer containing the Ole10Native record
+   * @param offset The start offset of the record in the buffer
+   * @throws Ole10NativeException on invalid or unexcepted data format
+   */
+  public Ole10Native(byte[] data, int offset) throws Ole10NativeException {
+    this(data, offset, false);
+  }
+  /**
+   * Creates an instance and fills the fields based on the data in the given buffer.
+   *
+   * @param data   The buffer containing the Ole10Native record
+   * @param offset The start offset of the record in the buffer
+   * @param plain Specified 'plain' format without filename
+   * @throws Ole10NativeException on invalid or unexcepted data format
+   */
+  public Ole10Native(byte[] data, int offset, boolean plain) throws Ole10NativeException {
+    int ofs = offset;        // current offset, initialized to start
+
+    if (data.length<offset+2) {
+      throw new Ole10NativeException("data is too small");
+    }
+
+    totalSize = LittleEndian.getInt(data, ofs);
+    ofs += LittleEndianConsts.INT_SIZE;
+
+    if (plain) {
+      dataBuffer = new byte[totalSize-4];
+      System.arraycopy(data, 4, dataBuffer, 0, dataBuffer.length);
+      dataSize = totalSize - 4;
+      
+      byte[] oleLabel = new byte[8];
+      System.arraycopy(dataBuffer, 0, oleLabel, 0, Math.min(dataBuffer.length, 8));
+      label = "ole-"+ HexDump.toHex(oleLabel);
+      fileName = label;
+      command = label;
+    } else {
+      flags1 = LittleEndian.getShort(data, ofs);
+      ofs += LittleEndianConsts.SHORT_SIZE;
+      int len = getStringLength(data, ofs);
+      label = StringUtil.getFromCompressedUnicode(data, ofs, len - 1);
+      ofs += len;
+      len = getStringLength(data, ofs);
+      fileName = StringUtil.getFromCompressedUnicode(data, ofs, len - 1);
+      ofs += len;
+      flags2 = LittleEndian.getShort(data, ofs);
+      ofs += LittleEndianConsts.SHORT_SIZE;
+      len = LittleEndian.getUnsignedByte(data, ofs);
+      unknown1 = new byte[len];
+      ofs += len;
+      len = 3;
+      unknown2 = new byte[len];
+      ofs += len;
+      len = getStringLength(data, ofs);
+      command = StringUtil.getFromCompressedUnicode(data, ofs, len - 1);
+      ofs += len;
+
+      if (totalSize + LittleEndianConsts.INT_SIZE - ofs > LittleEndianConsts.INT_SIZE) {
+        dataSize = LittleEndian.getInt(data, ofs);
+        ofs += LittleEndianConsts.INT_SIZE;
+
+        if (dataSize > totalSize || dataSize<0) {
+          throw new Ole10NativeException("Invalid Ole10Native");
+        }
+
+        dataBuffer = new byte[dataSize];
+        System.arraycopy(data, ofs, dataBuffer, 0, dataSize);
+        ofs += dataSize;
+
+        if (unknown1.length > 0) {
+          flags3 = LittleEndian.getShort(data, ofs);
+          ofs += LittleEndianConsts.SHORT_SIZE;
+        } else {
+          flags3 = 0;
+        }
+      } else {
+        throw new Ole10NativeException("Invalid Ole10Native");
+      }
+    }
+  }
+
+  /*
+   * Helper - determine length of zero terminated string (ASCIIZ).
+   */
+  private static int getStringLength(byte[] data, int ofs) {
+    int len = 0;
+    while (len+ofs<data.length && data[ofs + len] != 0) {
+      len++;
+    }
+    len++;
+    return len;
+  }
+
+  /**
+   * Returns the value of the totalSize field - the total length of the structure
+   * is totalSize + 4 (value of this field + size of this field).
+   *
+   * @return the totalSize
+   */
+  public int getTotalSize() {
+    return totalSize;
+  }
+
+  /**
+   * Returns flags1 - currently unknown - usually 0x0002.
+   *
+   * @return the flags1
+   */
+  public short getFlags1() {
+    return flags1;
+  }
+
+  /**
+   * Returns the label field - usually the name of the file (without directory) but
+   * probably may be any name specified during packaging/embedding the data.
+   *
+   * @return the label
+   */
+  public String getLabel() {
+    return label;
+  }
+
+  /**
+   * Returns the fileName field - usually the name of the file being embedded
+   * including the full path.
+   *
+   * @return the fileName
+   */
+  public String getFileName() {
+    return fileName;
+  }
+
+  /**
+   * Returns flags2 - currently unknown - mostly 0x0000.
+   *
+   * @return the flags2
+   */
+  public short getFlags2() {
+    return flags2;
+  }
+
+  /**
+   * Returns unknown1 field - currently unknown.
+   *
+   * @return the unknown1
+   */
+  public byte[] getUnknown1() {
+    return unknown1;
+  }
+
+  /**
+   * Returns the unknown2 field - currently being a byte[3] - mostly {0, 0, 0}.
+   *
+   * @return the unknown2
+   */
+  public byte[] getUnknown2() {
+    return unknown2;
+  }
+
+  /**
+   * Returns the command field - usually the name of the file being embedded
+   * including the full path, may be a command specified during embedding the file.
+   *
+   * @return the command
+   */
+  public String getCommand() {
+    return command;
+  }
+
+  /**
+   * Returns the size of the embedded file. If the size is 0 (zero), no data has been
+   * embedded. To be sure, that no data has been embedded, check whether
+   * {@link #getDataBuffer()} returns <code>null</code>.
+   *
+   * @return the dataSize
+   */
+  public int getDataSize() {
+    return dataSize;
+  }
+
+  /**
+   * Returns the buffer containing the embedded file's data, or <code>null</code>
+   * if no data was embedded. Note that an embedding may provide information about
+   * the data, but the actual data is not included. (So label, filename etc. are
+   * available, but this method returns <code>null</code>.)
+   *
+   * @return the dataBuffer
+   */
+  public byte[] getDataBuffer() {
+    return dataBuffer;
+  }
+
+  /**
+   * Returns the flags3 - currently unknown.
+   *
+   * @return the flags3
+   */
+  public short getFlags3() {
+    return flags3;
+  }
+}

Added: poi/branches/NIO_32_BRANCH/src/java/org/apache/poi/poifs/filesystem/Ole10NativeException.java
URL: http://svn.apache.org/viewvc/poi/branches/NIO_32_BRANCH/src/java/org/apache/poi/poifs/filesystem/Ole10NativeException.java?rev=1053791&view=auto
==============================================================================
--- poi/branches/NIO_32_BRANCH/src/java/org/apache/poi/poifs/filesystem/Ole10NativeException.java (added)
+++ poi/branches/NIO_32_BRANCH/src/java/org/apache/poi/poifs/filesystem/Ole10NativeException.java Thu Dec 30 02:35:06 2010
@@ -0,0 +1,24 @@
+/* ====================================================================
+   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;
+
+public class Ole10NativeException extends Exception {
+    public Ole10NativeException(String message) {
+        super(message);
+    }
+}

Modified: poi/branches/NIO_32_BRANCH/src/java/org/apache/poi/poifs/filesystem/POIFSDocument.java
URL: http://svn.apache.org/viewvc/poi/branches/NIO_32_BRANCH/src/java/org/apache/poi/poifs/filesystem/POIFSDocument.java?rev=1053791&r1=1053790&r2=1053791&view=diff
==============================================================================
--- poi/branches/NIO_32_BRANCH/src/java/org/apache/poi/poifs/filesystem/POIFSDocument.java (original)
+++ poi/branches/NIO_32_BRANCH/src/java/org/apache/poi/poifs/filesystem/POIFSDocument.java Thu Dec 30 02:35:06 2010
@@ -1,4 +1,3 @@
-
 /* ====================================================================
    Licensed to the Apache Software Foundation (ASF) under one or more
    contributor license agreements.  See the NOTICE file distributed with
@@ -15,21 +14,27 @@
    See the License for the specific language governing permissions and
    limitations under the License.
 ==================================================================== */
-        
 
 package org.apache.poi.poifs.filesystem;
 
-import java.io.*;
-
-import java.util.*;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
 
+import org.apache.poi.poifs.common.POIFSBigBlockSize;
 import org.apache.poi.poifs.common.POIFSConstants;
 import org.apache.poi.poifs.dev.POIFSViewable;
 import org.apache.poi.poifs.property.DocumentProperty;
 import org.apache.poi.poifs.property.Property;
 import org.apache.poi.poifs.storage.BlockWritable;
-import org.apache.poi.poifs.storage.ListManagedBlock;
+import org.apache.poi.poifs.storage.DataInputBlock;
 import org.apache.poi.poifs.storage.DocumentBlock;
+import org.apache.poi.poifs.storage.ListManagedBlock;
 import org.apache.poi.poifs.storage.RawDataBlock;
 import org.apache.poi.poifs.storage.SmallDocumentBlock;
 import org.apache.poi.util.HexDump;
@@ -39,595 +44,525 @@ import org.apache.poi.util.HexDump;
  *
  * @author Marc Johnson (mjohnson at apache dot org)
  */
-
-public class POIFSDocument
-    implements BATManaged, BlockWritable, POIFSViewable
-{
-    private DocumentProperty _property;
-    private int              _size;
-
-    // one of these stores will be valid
-    private SmallBlockStore  _small_store;
-    private BigBlockStore    _big_store;
-
-    /**
-     * Constructor from large blocks
-     *
-     * @param name the name of the POIFSDocument
-     * @param blocks the big blocks making up the POIFSDocument
-     * @param length the actual length of the POIFSDocument
-     *
-     * @exception IOException
-     */
-
-    public POIFSDocument(final String name, final RawDataBlock [] blocks,
-                         final int length)
-        throws IOException
-    {
-        _size        = length;
-        _big_store   = new BigBlockStore(blocks);
-        _property    = new DocumentProperty(name, _size);
-        _small_store = new SmallBlockStore(new BlockWritable[ 0 ]);
-        _property.setDocument(this);
-    }
-
-    /**
-     * Constructor from small blocks
-     *
-     * @param name the name of the POIFSDocument
-     * @param blocks the small blocks making up the POIFSDocument
-     * @param length the actual length of the POIFSDocument
-     */
-
-    public POIFSDocument(final String name,
-                         final SmallDocumentBlock [] blocks, final int length)
-    {
-        _size = length;
-        try
-        {
-            _big_store = new BigBlockStore(new RawDataBlock[ 0 ]);
-        }
-        catch (IOException ignored)
-        {
-
-            // can't happen with that constructor
-        }
-        _property    = new DocumentProperty(name, _size);
-        _small_store = new SmallBlockStore(blocks);
-        _property.setDocument(this);
-    }
-
-    /**
-     * Constructor from small blocks
-     *
-     * @param name the name of the POIFSDocument
-     * @param blocks the small blocks making up the POIFSDocument
-     * @param length the actual length of the POIFSDocument
-     *
-     * @exception IOException
-     */
-
-    public POIFSDocument(final String name, final ListManagedBlock [] blocks,
-                         final int length)
-        throws IOException
-    {
-        _size     = length;
-        _property = new DocumentProperty(name, _size);
-        _property.setDocument(this);
-        if (Property.isSmall(_size))
-        {
-            _big_store   = new BigBlockStore(new RawDataBlock[ 0 ]);
-            _small_store = new SmallBlockStore(blocks);
-        }
-        else
-        {
-            _big_store   = new BigBlockStore(blocks);
-            _small_store = new SmallBlockStore(new BlockWritable[ 0 ]);
-        }
-    }
-
-    /**
-     * Constructor
-     *
-     * @param name the name of the POIFSDocument
-     * @param stream the InputStream we read data from
-     *
-     * @exception IOException thrown on read errors
-     */
-
-    public POIFSDocument(final String name, final InputStream stream)
-        throws IOException
-    {
-        List blocks = new ArrayList();
-
-        _size = 0;
-        while (true)
-        {
-            DocumentBlock block     = new DocumentBlock(stream);
-            int           blockSize = block.size();
-
-            if (blockSize > 0)
-            {
-                blocks.add(block);
-                _size += blockSize;
-            }
-            if (block.partiallyRead())
-            {
-                break;
-            }
-        }
-        DocumentBlock[] bigBlocks =
-            ( DocumentBlock [] ) blocks.toArray(new DocumentBlock[ 0 ]);
-
-        _big_store = new BigBlockStore(bigBlocks);
-        _property  = new DocumentProperty(name, _size);
-        _property.setDocument(this);
-        if (_property.shouldUseSmallBlocks())
-        {
-            _small_store =
-                new SmallBlockStore(SmallDocumentBlock.convert(bigBlocks,
-                    _size));
-            _big_store   = new BigBlockStore(new DocumentBlock[ 0 ]);
-        }
-        else
-        {
-            _small_store = new SmallBlockStore(new BlockWritable[ 0 ]);
-        }
-    }
-
-    /**
-     * Constructor
-     *
-     * @param name the name of the POIFSDocument
-     * @param size the length of the POIFSDocument
-     * @param path the path of the POIFSDocument
-     * @param writer the writer who will eventually write the document
-     *               contents
-     *
-     * @exception IOException thrown on read errors
-     */
-
-    public POIFSDocument(final String name, final int size,
-                         final POIFSDocumentPath path,
-                         final POIFSWriterListener writer)
-        throws IOException
-    {
-        _size     = size;
-        _property = new DocumentProperty(name, _size);
-        _property.setDocument(this);
-        if (_property.shouldUseSmallBlocks())
-        {
-            _small_store = new SmallBlockStore(path, name, size, writer);
-            _big_store   = new BigBlockStore(new Object[ 0 ]);
-        }
-        else
-        {
-            _small_store = new SmallBlockStore(new BlockWritable[ 0 ]);
-            _big_store   = new BigBlockStore(path, name, size, writer);
-        }
-    }
-
-    /**
-     * return the array of SmallDocumentBlocks used
-     *
-     * @return array of SmallDocumentBlocks; may be empty, cannot be null
-     */
-
-    public BlockWritable [] getSmallBlocks()
-    {
-        return _small_store.getBlocks();
-    }
-
-    /**
-     * @return size of the document
-     */
-
-    public int getSize()
-    {
-        return _size;
-    }
-
-    /**
-     * read data from the internal stores
-     *
-     * @param buffer the buffer to write to
-     * @param offset the offset into our storage to read from
-     */
-
-    void read(final byte [] buffer, final int offset)
-    {
-        if (_property.shouldUseSmallBlocks())
-        {
-            SmallDocumentBlock.read(_small_store.getBlocks(), buffer, offset);
-        }
-        else
-        {
-            DocumentBlock.read(_big_store.getBlocks(), buffer, offset);
-        }
-    }
-
-    /**
-     * Get the DocumentProperty
-     *
-     * @return the instance's DocumentProperty
-     */
-
-    DocumentProperty getDocumentProperty()
-    {
-        return _property;
-    }
-
-    /* ********** START implementation of BlockWritable ********** */
-
-    /**
-     * Write the storage to an OutputStream
-     *
-     * @param stream the OutputStream to which the stored data should
-     *               be written
-     *
-     * @exception IOException on problems writing to the specified
-     *            stream
-     */
-
-    public void writeBlocks(final OutputStream stream)
-        throws IOException
-    {
-        _big_store.writeBlocks(stream);
-    }
-
-    /* **********  END  implementation of BlockWritable ********** */
-    /* ********** START implementation of BATManaged ********** */
-
-    /**
-     * Return the number of BigBlock's this instance uses
-     *
-     * @return count of BigBlock instances
-     */
-
-    public int countBlocks()
-    {
-        return _big_store.countBlocks();
-    }
-
-    /**
-     * Set the start block for this instance
-     *
-     * @param index index into the array of blocks making up the
-     *        filesystem
-     */
-
-    public void setStartBlock(final int index)
-    {
-        _property.setStartBlock(index);
-    }
-
-    /* **********  END  implementation of BATManaged ********** */
-    /* ********** START begin implementation of POIFSViewable ********** */
-
-    /**
-     * Get an array of objects, some of which may implement
-     * POIFSViewable
-     *
-     * @return an array of Object; may not be null, but may be empty
-     */
-
-    public Object [] getViewableArray()
-    {
-        Object[] results = new Object[ 1 ];
-        String   result;
-
-        try
-        {
-            ByteArrayOutputStream output = new ByteArrayOutputStream();
-            BlockWritable[]       blocks = null;
-
-            if (_big_store.isValid())
-            {
-                blocks = _big_store.getBlocks();
-            }
-            else if (_small_store.isValid())
-            {
-                blocks = _small_store.getBlocks();
-            }
-            if (blocks != null)
-            {
-                for (int k = 0; k < blocks.length; k++)
-                {
-                    blocks[ k ].writeBlocks(output);
-                }
-                byte[] data = output.toByteArray();
-
-                if (data.length > _property.getSize())
-                {
-                    byte[] tmp = new byte[ _property.getSize() ];
-
-                    System.arraycopy(data, 0, tmp, 0, tmp.length);
-                    data = tmp;
-                }
-                output = new ByteArrayOutputStream();
-                HexDump.dump(data, 0, output, 0);
-                result = output.toString();
-            }
-            else
-            {
-                result = "<NO DATA>";
-            }
-        }
-        catch (IOException e)
-        {
-            result = e.getMessage();
-        }
-        results[ 0 ] = result;
-        return results;
-    }
-
-    /**
-     * Get an Iterator of objects, some of which may implement
-     * POIFSViewable
-     *
-     * @return an Iterator; may not be null, but may have an empty
-     * back end store
-     */
-
-    public Iterator getViewableIterator()
-    {
-        return Collections.EMPTY_LIST.iterator();
-    }
-
-    /**
-     * Give viewers a hint as to whether to call getViewableArray or
-     * getViewableIterator
-     *
-     * @return true if a viewer should call getViewableArray, false if
-     *         a viewer should call getViewableIterator
-     */
-
-    public boolean preferArray()
-    {
-        return true;
-    }
-
-    /**
-     * Provides a short description of the object, to be used when a
-     * POIFSViewable object has not provided its contents.
-     *
-     * @return short description
-     */
-
-    public String getShortDescription()
-    {
-        StringBuffer buffer = new StringBuffer();
-
-        buffer.append("Document: \"").append(_property.getName())
-            .append("\"");
-        buffer.append(" size = ").append(getSize());
-        return buffer.toString();
-    }
-
-    /* **********  END  begin implementation of POIFSViewable ********** */
-    private class SmallBlockStore
-    {
-        private SmallDocumentBlock[] smallBlocks;
-        private POIFSDocumentPath    path;
-        private String               name;
-        private int                  size;
-        private POIFSWriterListener  writer;
-
-        /**
-         * Constructor
-         *
-         * @param blocks blocks to construct the store from
-         */
-
-        SmallBlockStore(final Object [] blocks)
-        {
-            smallBlocks = new SmallDocumentBlock[ blocks.length ];
-            for (int j = 0; j < blocks.length; j++)
-            {
-                smallBlocks[ j ] = ( SmallDocumentBlock ) blocks[ j ];
-            }
-            this.path   = null;
-            this.name   = null;
-            this.size   = -1;
-            this.writer = null;
-        }
-
-        /**
-         * Constructor for a small block store that will be written
-         * later
-         *
-         * @param path path of the document
-         * @param name name of the document
-         * @param size length of the document
-         * @param writer the object that will eventually write the document
-         */
-
-        SmallBlockStore(final POIFSDocumentPath path, final String name,
-                        final int size, final POIFSWriterListener writer)
-        {
-            smallBlocks = new SmallDocumentBlock[ 0 ];
-            this.path   = path;
-            this.name   = name;
-            this.size   = size;
-            this.writer = writer;
-        }
-
-        /**
-         * @return true if this store is a valid source of data
-         */
-
-        boolean isValid()
-        {
-            return ((smallBlocks.length > 0) || (writer != null));
-        }
-
-        /**
-         * @return the SmallDocumentBlocks
-         */
-
-        BlockWritable [] getBlocks()
-        {
-            if (isValid() && (writer != null))
-            {
-                ByteArrayOutputStream stream  =
-                    new ByteArrayOutputStream(size);
-                DocumentOutputStream  dstream =
-                    new DocumentOutputStream(stream, size);
-
-                writer.processPOIFSWriterEvent(new POIFSWriterEvent(dstream,
-                        path, name, size));
-                smallBlocks = SmallDocumentBlock.convert(stream.toByteArray(),
-                                                         size);
-            }
-            return smallBlocks;
-        }
-    }   // end private class SmallBlockStore
-
-    private class BigBlockStore
-    {
-        private DocumentBlock[]     bigBlocks;
-        private POIFSDocumentPath   path;
-        private String              name;
-        private int                 size;
-        private POIFSWriterListener writer;
-
-        /**
-         * Constructor
-         *
-         * @param blocks the blocks making up the store
-         *
-         * @exception IOException on I/O error
-         */
-
-        BigBlockStore(final Object [] blocks)
-            throws IOException
-        {
-            bigBlocks = new DocumentBlock[ blocks.length ];
-            for (int j = 0; j < blocks.length; j++)
-            {
-                if (blocks[ j ] instanceof DocumentBlock)
-                {
-                    bigBlocks[ j ] = ( DocumentBlock ) blocks[ j ];
-                }
-                else
-                {
-                    bigBlocks[ j ] =
-                        new DocumentBlock(( RawDataBlock ) blocks[ j ]);
-                }
-            }
-            this.path   = null;
-            this.name   = null;
-            this.size   = -1;
-            this.writer = null;
-        }
-
-        /**
-         * Constructor for a big block store that will be written
-         * later
-         *
-         * @param path path of the document
-         * @param name name of the document
-         * @param size length of the document
-         * @param writer the object that will eventually write the
-         *               document
-         */
-
-        BigBlockStore(final POIFSDocumentPath path, final String name,
-                      final int size, final POIFSWriterListener writer)
-        {
-            bigBlocks   = new DocumentBlock[ 0 ];
-            this.path   = path;
-            this.name   = name;
-            this.size   = size;
-            this.writer = writer;
-        }
-
-        /**
-         * @return true if this store is a valid source of data
-         */
-
-        boolean isValid()
-        {
-            return ((bigBlocks.length > 0) || (writer != null));
-        }
-
-        /**
-         * @return the DocumentBlocks
-         */
-
-        DocumentBlock [] getBlocks()
-        {
-            if (isValid() && (writer != null))
-            {
-                ByteArrayOutputStream stream  =
-                    new ByteArrayOutputStream(size);
-                DocumentOutputStream  dstream =
-                    new DocumentOutputStream(stream, size);
-
-                writer.processPOIFSWriterEvent(new POIFSWriterEvent(dstream,
-                        path, name, size));
-                bigBlocks = DocumentBlock.convert(stream.toByteArray(), size);
-            }
-            return bigBlocks;
-        }
-
-        /**
-         * write the blocks to a stream
-         *
-         * @param stream the stream to which the data is to be written
-         *
-         * @exception IOException on error
-         */
-
-        void writeBlocks(OutputStream stream)
-            throws IOException
-        {
-            if (isValid())
-            {
-                if (writer != null)
-                {
-                    DocumentOutputStream dstream =
-                        new DocumentOutputStream(stream, size);
-
-                    writer.processPOIFSWriterEvent(
-                        new POIFSWriterEvent(dstream, path, name, size));
-                    dstream.writeFiller(countBlocks()
-                                        * POIFSConstants
-                                            .BIG_BLOCK_SIZE, DocumentBlock
-                                            .getFillByte());
-                }
-                else
-                {
-                    for (int k = 0; k < bigBlocks.length; k++)
-                    {
-                        bigBlocks[ k ].writeBlocks(stream);
-                    }
-                }
-            }
-        }
-
-        /**
-         * @return number of big blocks making up this document
-         */
-
-        int countBlocks()
-        {
-            int rval = 0;
-
-            if (isValid())
-            {
-                if (writer != null)
-                {
-                    rval = (size + POIFSConstants.BIG_BLOCK_SIZE - 1)
-                           / POIFSConstants.BIG_BLOCK_SIZE;
-                }
-                else
-                {
-                    rval = bigBlocks.length;
-                }
-            }
-            return rval;
-        }
-    }   // end private class BigBlockStore
-}       // end class POIFSDocument
-
+public final class POIFSDocument implements BATManaged, BlockWritable, POIFSViewable  {
+	private static final DocumentBlock[] EMPTY_BIG_BLOCK_ARRAY = { };
+	private static final SmallDocumentBlock[] EMPTY_SMALL_BLOCK_ARRAY = { };
+	private DocumentProperty _property;
+	private int _size;
+	
+   private final POIFSBigBlockSize _bigBigBlockSize;
+
+	// one of these stores will be valid
+	private SmallBlockStore  _small_store;
+	private BigBlockStore	 _big_store;
+	
+		/**
+	 * Constructor from large blocks
+	 *
+	 * @param name the name of the POIFSDocument
+	 * @param blocks the big blocks making up the POIFSDocument
+	 * @param length the actual length of the POIFSDocument
+	 */
+	public POIFSDocument(String name, RawDataBlock[] blocks, int length) throws IOException {
+		_size = length;
+		if(blocks.length == 0) {
+		   _bigBigBlockSize = POIFSConstants.SMALLER_BIG_BLOCK_SIZE_DETAILS;
+		} else {
+		   _bigBigBlockSize = (blocks[0].getBigBlockSize() == POIFSConstants.SMALLER_BIG_BLOCK_SIZE ?
+		         POIFSConstants.SMALLER_BIG_BLOCK_SIZE_DETAILS : 
+		         POIFSConstants.LARGER_BIG_BLOCK_SIZE_DETAILS
+		   );
+		}
+		
+		_big_store = new BigBlockStore(_bigBigBlockSize, convertRawBlocksToBigBlocks(blocks));
+		_property = new DocumentProperty(name, _size);
+		_small_store = new SmallBlockStore(_bigBigBlockSize, EMPTY_SMALL_BLOCK_ARRAY);
+		_property.setDocument(this);
+	}
+
+	// TODO - awkward typing going on here
+	private static DocumentBlock[] convertRawBlocksToBigBlocks(ListManagedBlock[] blocks) throws IOException {
+		DocumentBlock[] result = new DocumentBlock[blocks.length];
+		for (int i = 0; i < result.length; i++) {
+			result[i] = new DocumentBlock((RawDataBlock)blocks[i]);
+		}
+		return result;
+	}
+	private static SmallDocumentBlock[] convertRawBlocksToSmallBlocks(ListManagedBlock[] blocks) {
+		if (blocks instanceof SmallDocumentBlock[]) {
+			return (SmallDocumentBlock[]) blocks;
+		}
+		SmallDocumentBlock[] result = new SmallDocumentBlock[blocks.length];
+		System.arraycopy(blocks, 0, result, 0, blocks.length);
+		return result;
+	}
+
+	/**
+	 * Constructor from small blocks
+	 *
+	 * @param name the name of the POIFSDocument
+	 * @param blocks the small blocks making up the POIFSDocument
+	 * @param length the actual length of the POIFSDocument
+	 */
+	public POIFSDocument(String name, SmallDocumentBlock[] blocks, int length) {
+		_size = length;
+		
+		if(blocks.length == 0) {
+		   _bigBigBlockSize = POIFSConstants.SMALLER_BIG_BLOCK_SIZE_DETAILS;
+		} else {
+		   _bigBigBlockSize = blocks[0].getBigBlockSize();
+		}
+
+		_big_store = new BigBlockStore(_bigBigBlockSize, EMPTY_BIG_BLOCK_ARRAY);
+		_property = new DocumentProperty(name, _size);
+		_small_store = new SmallBlockStore(_bigBigBlockSize, blocks);
+		_property.setDocument(this);
+	}
+
+	/**
+	 * Constructor from small blocks
+	 *
+	 * @param name the name of the POIFSDocument
+	 * @param blocks the small blocks making up the POIFSDocument
+	 * @param length the actual length of the POIFSDocument
+	 */
+	public POIFSDocument(String name, POIFSBigBlockSize bigBlockSize, ListManagedBlock[] blocks, int length) throws IOException {
+		_size = length;
+		_bigBigBlockSize = bigBlockSize;
+		_property = new DocumentProperty(name, _size);
+		_property.setDocument(this);
+		if (Property.isSmall(_size)) {
+			_big_store = new BigBlockStore(bigBlockSize,EMPTY_BIG_BLOCK_ARRAY);
+			_small_store = new SmallBlockStore(bigBlockSize,convertRawBlocksToSmallBlocks(blocks));
+		} else {
+			_big_store = new BigBlockStore(bigBlockSize,convertRawBlocksToBigBlocks(blocks));
+			_small_store = new SmallBlockStore(bigBlockSize,EMPTY_SMALL_BLOCK_ARRAY);
+		}
+	}
+	public POIFSDocument(String name, ListManagedBlock[] blocks, int length) throws IOException {
+	   this(name, POIFSConstants.SMALLER_BIG_BLOCK_SIZE_DETAILS, blocks, length);
+	}
+
+	/**
+	 * Constructor
+	 *
+	 * @param name the name of the POIFSDocument
+	 * @param stream the InputStream we read data from
+	 */
+	public POIFSDocument(String name, POIFSBigBlockSize bigBlockSize, InputStream stream) throws IOException {
+		List<DocumentBlock> blocks = new ArrayList<DocumentBlock>();
+
+		_size = 0;
+		_bigBigBlockSize = bigBlockSize;
+		while (true) {
+			DocumentBlock block = new DocumentBlock(stream, bigBlockSize);
+			int blockSize = block.size();
+
+			if (blockSize > 0) {
+				blocks.add(block);
+				_size += blockSize;
+			}
+			if (block.partiallyRead()) {
+				break;
+			}
+		}
+		DocumentBlock[] bigBlocks = blocks.toArray(new DocumentBlock[blocks.size()]);
+
+		_big_store = new BigBlockStore(bigBlockSize,bigBlocks);
+		_property = new DocumentProperty(name, _size);
+		_property.setDocument(this);
+		if (_property.shouldUseSmallBlocks()) {
+			_small_store = new SmallBlockStore(bigBlockSize,SmallDocumentBlock.convert(bigBlockSize,bigBlocks, _size));
+			_big_store = new BigBlockStore(bigBlockSize,new DocumentBlock[0]);
+		} else {
+			_small_store = new SmallBlockStore(bigBlockSize,EMPTY_SMALL_BLOCK_ARRAY);
+		}
+	}
+	public POIFSDocument(String name, InputStream stream) throws IOException {
+	   this(name, POIFSConstants.SMALLER_BIG_BLOCK_SIZE_DETAILS, stream);
+	}
+
+	/**
+	 * Constructor
+	 *
+	 * @param name the name of the POIFSDocument
+	 * @param size the length of the POIFSDocument
+	 * @param path the path of the POIFSDocument
+	 * @param writer the writer who will eventually write the document contents
+	 */
+	public POIFSDocument(String name, int size, POIFSBigBlockSize bigBlockSize, POIFSDocumentPath path, POIFSWriterListener writer) {
+		_size = size;
+		_bigBigBlockSize = bigBlockSize;
+		_property = new DocumentProperty(name, _size);
+		_property.setDocument(this);
+		if (_property.shouldUseSmallBlocks()) {
+			_small_store = new SmallBlockStore(_bigBigBlockSize, path, name, size, writer);
+			_big_store = new BigBlockStore(_bigBigBlockSize, EMPTY_BIG_BLOCK_ARRAY);
+		} else {
+			_small_store = new SmallBlockStore(_bigBigBlockSize, EMPTY_SMALL_BLOCK_ARRAY);
+			_big_store = new BigBlockStore(_bigBigBlockSize, path, name, size, writer);
+		}
+	}
+	public POIFSDocument(String name, int size, POIFSDocumentPath path, POIFSWriterListener writer) {
+	   this(name, size, POIFSConstants.SMALLER_BIG_BLOCK_SIZE_DETAILS, path, writer);
+	}
+
+	/**
+	 * @return array of SmallDocumentBlocks; may be empty, cannot be null
+	 */
+	public BlockWritable[] getSmallBlocks() {
+		return _small_store.getBlocks();
+	}
+
+	/**
+	 * @return size of the document
+	 */
+	public int getSize() {
+		return _size;
+	}
+
+	/**
+	 * read data from the internal stores
+	 *
+	 * @param buffer the buffer to write to
+	 * @param offset the offset into our storage to read from
+	 * This method is currently (Oct 2008) only used by test code. Perhaps it can be deleted
+	 */
+	void read(byte[] buffer, int offset) {
+		int len = buffer.length;
+
+		DataInputBlock currentBlock = getDataInputBlock(offset);
+
+		int blockAvailable = currentBlock.available();
+		if (blockAvailable > len) {
+			currentBlock.readFully(buffer, 0, len);
+			return;
+		}
+		// else read big amount in chunks
+		int remaining = len;
+		int writePos = 0;
+		int currentOffset = offset;
+		while (remaining > 0) {
+			boolean blockIsExpiring = remaining >= blockAvailable;
+			int reqSize;
+			if (blockIsExpiring) {
+				reqSize = blockAvailable;
+			} else {
+				reqSize = remaining;
+			}
+			currentBlock.readFully(buffer, writePos, reqSize);
+			remaining-=reqSize;
+			writePos+=reqSize;
+			currentOffset += reqSize;
+			if (blockIsExpiring) {
+				if (currentOffset == _size) {
+					if (remaining > 0) {
+						throw new IllegalStateException("reached end of document stream unexpectedly");
+					}
+					currentBlock = null;
+					break;
+				}
+				currentBlock = getDataInputBlock(currentOffset);
+				blockAvailable = currentBlock.available();
+			}
+		}
+	}
+
+	/**
+	 * @return <code>null</code> if <tt>offset</tt> points to the end of the document stream
+	 */
+	DataInputBlock getDataInputBlock(int offset) {
+		if (offset >= _size) {
+			if (offset > _size) {
+				throw new RuntimeException("Request for Offset " + offset + " doc size is " + _size);
+			}
+			return null;
+		}
+		if (_property.shouldUseSmallBlocks()) {
+			return SmallDocumentBlock.getDataInputBlock(_small_store.getBlocks(), offset);
+		}
+		return DocumentBlock.getDataInputBlock(_big_store.getBlocks(), offset);
+	}
+
+	/**
+	 * @return the instance's DocumentProperty
+	 */
+
+	DocumentProperty getDocumentProperty() {
+		return _property;
+	}
+
+	/* ********** START implementation of BlockWritable ********** */
+
+	/**
+	 * Write the storage to an OutputStream
+	 *
+	 * @param stream the OutputStream to which the stored data should be written
+	 */
+	public void writeBlocks(OutputStream stream) throws IOException {
+		_big_store.writeBlocks(stream);
+	}
+
+	/* **********  END  implementation of BlockWritable ********** */
+	/* ********** START implementation of BATManaged ********** */
+
+	/**
+	 * Return the number of BigBlock's this instance uses
+	 *
+	 * @return count of BigBlock instances
+	 */
+	public int countBlocks() {
+		return _big_store.countBlocks();
+	}
+
+	/**
+	 * Set the start block for this instance
+	 *
+	 * @param index index into the array of blocks making up the filesystem
+	 */
+	public void setStartBlock(int index) {
+		_property.setStartBlock(index);
+	}
+
+	/* **********  END  implementation of BATManaged ********** */
+	/* ********** START begin implementation of POIFSViewable ********** */
+
+	/**
+	 * Get an array of objects, some of which may implement POIFSViewable
+	 *
+	 * @return an array of Object; may not be null, but may be empty
+	 */
+	public Object[] getViewableArray() {
+		Object[] results = new Object[1];
+		String result;
+
+		try {
+			ByteArrayOutputStream output = new ByteArrayOutputStream();
+			BlockWritable[] blocks = null;
+
+			if (_big_store.isValid()) {
+				blocks = _big_store.getBlocks();
+			} else if (_small_store.isValid()) {
+				blocks = _small_store.getBlocks();
+			}
+			if (blocks != null) {
+				for (int k = 0; k < blocks.length; k++) {
+					blocks[k].writeBlocks(output);
+				}
+				byte[] data = output.toByteArray();
+
+				if (data.length > _property.getSize()) {
+					byte[] tmp = new byte[_property.getSize()];
+
+					System.arraycopy(data, 0, tmp, 0, tmp.length);
+					data = tmp;
+				}
+				output = new ByteArrayOutputStream();
+				HexDump.dump(data, 0, output, 0);
+				result = output.toString();
+			} else {
+				result = "<NO DATA>";
+			}
+		} catch (IOException e) {
+			result = e.getMessage();
+		}
+		results[0] = result;
+		return results;
+	}
+
+	/**
+	 * Get an Iterator of objects, some of which may implement POIFSViewable
+	 *
+	 * @return an Iterator; may not be null, but may have an empty back end
+	 *		 store
+	 */
+	public Iterator getViewableIterator() {
+		return Collections.EMPTY_LIST.iterator();
+	}
+
+	/**
+	 * Give viewers a hint as to whether to call getViewableArray or
+	 * getViewableIterator
+	 *
+	 * @return <code>true</code> if a viewer should call getViewableArray,
+	 *		 <code>false</code> if a viewer should call getViewableIterator
+	 */
+	public boolean preferArray() {
+		return true;
+	}
+
+	/**
+	 * Provides a short description of the object, to be used when a
+	 * POIFSViewable object has not provided its contents.
+	 *
+	 * @return short description
+	 */
+	public String getShortDescription() {
+		StringBuffer buffer = new StringBuffer();
+
+		buffer.append("Document: \"").append(_property.getName()).append("\"");
+		buffer.append(" size = ").append(getSize());
+		return buffer.toString();
+	}
+
+	/* **********  END  begin implementation of POIFSViewable ********** */
+	private static final class SmallBlockStore {
+		private SmallDocumentBlock[] _smallBlocks;
+		private final POIFSDocumentPath _path;
+		private final String _name;
+		private final int _size;
+		private final POIFSWriterListener _writer;
+		private final POIFSBigBlockSize _bigBlockSize;
+
+		/**
+		 * Constructor
+		 *
+		 * @param blocks blocks to construct the store from
+		 */
+		SmallBlockStore(POIFSBigBlockSize bigBlockSize, SmallDocumentBlock[] blocks) {
+		   _bigBlockSize = bigBlockSize;
+			_smallBlocks = blocks.clone();
+			this._path = null;
+			this._name = null;
+			this._size = -1;
+			this._writer = null;
+		}
+
+		/**
+		 * Constructor for a small block store that will be written later
+		 *
+		 * @param path path of the document
+		 * @param name name of the document
+		 * @param size length of the document
+		 * @param writer the object that will eventually write the document
+		 */
+		SmallBlockStore(POIFSBigBlockSize bigBlockSize, POIFSDocumentPath path, 
+		                String name, int size, POIFSWriterListener writer) {
+		   _bigBlockSize = bigBlockSize;
+			_smallBlocks = new SmallDocumentBlock[0];
+			this._path = path;
+			this._name = name;
+			this._size = size;
+			this._writer = writer;
+		}
+
+		/**
+		 * @return <code>true</code> if this store is a valid source of data
+		 */
+		boolean isValid() {
+			return _smallBlocks.length > 0 || _writer != null;
+		}
+
+		/**
+		 * @return the SmallDocumentBlocks
+		 */
+		SmallDocumentBlock[] getBlocks() {
+			if (isValid() && _writer != null) {
+				ByteArrayOutputStream stream = new ByteArrayOutputStream(_size);
+				DocumentOutputStream dstream = new DocumentOutputStream(stream, _size);
+
+				_writer.processPOIFSWriterEvent(new POIFSWriterEvent(dstream, _path, _name, _size));
+				_smallBlocks = SmallDocumentBlock.convert(_bigBlockSize, stream.toByteArray(), _size);
+			}
+			return _smallBlocks;
+		}
+	} // end private class SmallBlockStore
+
+	private static final class BigBlockStore {
+		private DocumentBlock[] bigBlocks;
+		private final POIFSDocumentPath _path;
+		private final String _name;
+		private final int _size;
+		private final POIFSWriterListener _writer;
+      private final POIFSBigBlockSize _bigBlockSize;
+
+		/**
+		 * Constructor
+		 *
+		 * @param blocks the blocks making up the store
+		 */
+		BigBlockStore(POIFSBigBlockSize bigBlockSize, DocumentBlock[] blocks) {
+		   _bigBlockSize = bigBlockSize;
+			bigBlocks = blocks.clone();
+			_path = null;
+			_name = null;
+			_size = -1;
+			_writer = null;
+		}
+
+		/**
+		 * Constructor for a big block store that will be written later
+		 *
+		 * @param path path of the document
+		 * @param name name of the document
+		 * @param size length of the document
+		 * @param writer the object that will eventually write the document
+		 */
+		BigBlockStore(POIFSBigBlockSize bigBlockSize, POIFSDocumentPath path, 
+		              String name, int size, POIFSWriterListener writer) {
+		   _bigBlockSize = bigBlockSize;
+			bigBlocks = new DocumentBlock[0];
+			_path = path;
+			_name = name;
+			_size = size;
+			_writer = writer;
+		}
+
+		/**
+		 * @return <code>true</code> if this store is a valid source of data
+		 */
+		boolean isValid() {
+			return bigBlocks.length > 0 || _writer != null;
+		}
+
+		/**
+		 * @return the DocumentBlocks
+		 */
+		DocumentBlock[] getBlocks() {
+			if (isValid() && _writer != null) {
+				ByteArrayOutputStream stream = new ByteArrayOutputStream(_size);
+				DocumentOutputStream dstream = new DocumentOutputStream(stream, _size);
+
+				_writer.processPOIFSWriterEvent(new POIFSWriterEvent(dstream, _path, _name, _size));
+				bigBlocks = DocumentBlock.convert(_bigBlockSize, stream.toByteArray(), _size);
+			}
+			return bigBlocks;
+		}
+
+		/**
+		 * write the blocks to a stream
+		 *
+		 * @param stream the stream to which the data is to be written
+		 */
+		void writeBlocks(OutputStream stream) throws IOException {
+			if (isValid()) {
+				if (_writer != null) {
+					DocumentOutputStream dstream = new DocumentOutputStream(stream, _size);
+
+					_writer.processPOIFSWriterEvent(new POIFSWriterEvent(dstream, _path, _name, _size));
+					dstream.writeFiller(countBlocks() * _bigBlockSize.getBigBlockSize(),
+							DocumentBlock.getFillByte());
+				} else {
+					for (int k = 0; k < bigBlocks.length; k++) {
+						bigBlocks[k].writeBlocks(stream);
+					}
+				}
+			}
+		}
+
+		/**
+		 * @return number of big blocks making up this document
+		 */
+		int countBlocks() {
+
+			if (isValid()) {
+				if (_writer == null) {
+					return bigBlocks.length;
+				}
+				return (_size + _bigBlockSize.getBigBlockSize() - 1)
+							/ _bigBlockSize.getBigBlockSize();
+			}
+			return 0;
+		}
+	} // end private class BigBlockStore
+}

Modified: poi/branches/NIO_32_BRANCH/src/java/org/apache/poi/poifs/filesystem/POIFSDocumentPath.java
URL: http://svn.apache.org/viewvc/poi/branches/NIO_32_BRANCH/src/java/org/apache/poi/poifs/filesystem/POIFSDocumentPath.java?rev=1053791&r1=1053790&r2=1053791&view=diff
==============================================================================
--- poi/branches/NIO_32_BRANCH/src/java/org/apache/poi/poifs/filesystem/POIFSDocumentPath.java (original)
+++ poi/branches/NIO_32_BRANCH/src/java/org/apache/poi/poifs/filesystem/POIFSDocumentPath.java Thu Dec 30 02:35:06 2010
@@ -21,6 +21,9 @@ package org.apache.poi.poifs.filesystem;
 
 import java.io.File;
 
+import org.apache.poi.util.POILogFactory;
+import org.apache.poi.util.POILogger;
+
 /**
  * Class POIFSDocumentPath
  *
@@ -30,6 +33,8 @@ import java.io.File;
 
 public class POIFSDocumentPath
 {
+    private static final POILogger log = POILogFactory.getLogger(POIFSDocumentPath.class);
+          
     private String[] components;
     private int      hashcode = 0;
 
@@ -125,12 +130,17 @@ public class POIFSDocumentPath
         {
             for (int j = 0; j < components.length; j++)
             {
-                if ((components[ j ] == null)
-                        || (components[ j ].length() == 0))
+                if (components[ j ] == null)
                 {
                     throw new IllegalArgumentException(
-                        "components cannot contain null or empty strings");
+                        "components cannot contain null");
+                }
+                if (components[ j ].length() == 0)
+                {
+                    log.log(POILogger.WARN, "Directory under " + path + " has an empty name, " +
+                            "not all OLE2 readers will handle this file correctly!");
                 }
+                
                 this.components[ j + path.components.length ] =
                     components[ j ];
             }

Modified: poi/branches/NIO_32_BRANCH/src/java/org/apache/poi/poifs/filesystem/POIFSFileSystem.java
URL: http://svn.apache.org/viewvc/poi/branches/NIO_32_BRANCH/src/java/org/apache/poi/poifs/filesystem/POIFSFileSystem.java?rev=1053791&r1=1053790&r2=1053791&view=diff
==============================================================================
--- poi/branches/NIO_32_BRANCH/src/java/org/apache/poi/poifs/filesystem/POIFSFileSystem.java (original)
+++ poi/branches/NIO_32_BRANCH/src/java/org/apache/poi/poifs/filesystem/POIFSFileSystem.java Thu Dec 30 02:35:06 2010
@@ -15,7 +15,7 @@
    See the License for the specific language governing permissions and
    limitations under the License.
 ==================================================================== */
-        
+
 
 package org.apache.poi.poifs.filesystem;
 
@@ -31,6 +31,7 @@ import java.util.Collections;
 import java.util.Iterator;
 import java.util.List;
 
+import org.apache.poi.poifs.common.POIFSBigBlockSize;
 import org.apache.poi.poifs.common.POIFSConstants;
 import org.apache.poi.poifs.dev.POIFSViewable;
 import org.apache.poi.poifs.property.DirectoryProperty;
@@ -42,11 +43,12 @@ import org.apache.poi.poifs.storage.Bloc
 import org.apache.poi.poifs.storage.BlockList;
 import org.apache.poi.poifs.storage.BlockWritable;
 import org.apache.poi.poifs.storage.HeaderBlockConstants;
-import org.apache.poi.poifs.storage.HeaderBlockReader;
+import org.apache.poi.poifs.storage.HeaderBlock;
 import org.apache.poi.poifs.storage.HeaderBlockWriter;
 import org.apache.poi.poifs.storage.RawDataBlockList;
 import org.apache.poi.poifs.storage.SmallBlockTableReader;
 import org.apache.poi.poifs.storage.SmallBlockTableWriter;
+import org.apache.poi.util.CloseIgnoringInputStream;
 import org.apache.poi.util.IOUtils;
 import org.apache.poi.util.LongField;
 import org.apache.poi.util.POILogFactory;
@@ -64,47 +66,32 @@ public class POIFSFileSystem
 {
 	private static final POILogger _logger =
 		POILogFactory.getLogger(POIFSFileSystem.class);
-    
-    private static final class CloseIgnoringInputStream extends InputStream {
 
-        private final InputStream _is;
-        public CloseIgnoringInputStream(InputStream is) {
-            _is = is;
-        }
-        public int read() throws IOException {
-            return _is.read();
-        }
-        public int read(byte[] b, int off, int len) throws IOException {
-            return _is.read(b, off, len);
-        }
-        public void close() {
-            // do nothing
-        }
-    }
-    
     /**
      * Convenience method for clients that want to avoid the auto-close behaviour of the constructor.
      */
     public static InputStream createNonClosingInputStream(InputStream is) {
         return new CloseIgnoringInputStream(is);
     }
-    
+
     private PropertyTable _property_table;
     private List          _documents;
     private DirectoryNode _root;
-    
+
     /**
      * What big block size the file uses. Most files
      *  use 512 bytes, but a few use 4096
      */
-    private int bigBlockSize = POIFSConstants.BIG_BLOCK_SIZE;
+    private POIFSBigBlockSize bigBlockSize = 
+       POIFSConstants.SMALLER_BIG_BLOCK_SIZE_DETAILS;
 
     /**
      * Constructor, intended for writing
      */
     public POIFSFileSystem()
     {
-        _property_table = new PropertyTable();
+        HeaderBlock header_block = new HeaderBlock(bigBlockSize);
+        _property_table = new PropertyTable(header_block);
         _documents      = new ArrayList();
         _root           = null;
     }
@@ -112,20 +99,20 @@ public class POIFSFileSystem
     /**
      * Create a POIFSFileSystem from an <tt>InputStream</tt>.  Normally the stream is read until
      * EOF.  The stream is always closed.<p/>
-     * 
-     * Some streams are usable after reaching EOF (typically those that return <code>true</code> 
-     * for <tt>markSupported()</tt>).  In the unlikely case that the caller has such a stream 
+     *
+     * Some streams are usable after reaching EOF (typically those that return <code>true</code>
+     * for <tt>markSupported()</tt>).  In the unlikely case that the caller has such a stream
      * <i>and</i> needs to use it after this constructor completes, a work around is to wrap the
      * stream in order to trap the <tt>close()</tt> call.  A convenience method (
      * <tt>createNonClosingInputStream()</tt>) has been provided for this purpose:
      * <pre>
      * InputStream wrappedStream = POIFSFileSystem.createNonClosingInputStream(is);
      * HSSFWorkbook wb = new HSSFWorkbook(wrappedStream);
-     * is.reset(); 
-     * doSomethingElse(is); 
+     * is.reset();
+     * doSomethingElse(is);
      * </pre>
      * Note also the special case of <tt>ByteArrayInputStream</tt> for which the <tt>close()</tt>
-     * method does nothing. 
+     * method does nothing.
      * <pre>
      * ByteArrayInputStream bais = ...
      * HSSFWorkbook wb = new HSSFWorkbook(bais); // calls bais.close() !
@@ -144,49 +131,57 @@ public class POIFSFileSystem
         this();
         boolean success = false;
 
-        HeaderBlockReader header_block_reader;
+        HeaderBlock header_block;
         RawDataBlockList data_blocks;
         try {
             // read the header block from the stream
-            header_block_reader = new HeaderBlockReader(stream);
-            bigBlockSize = header_block_reader.getBigBlockSize();
-            
+            header_block = new HeaderBlock(stream);
+            bigBlockSize = header_block.getBigBlockSize();
+
             // read the rest of the stream into blocks
             data_blocks = new RawDataBlockList(stream, bigBlockSize);
             success = true;
         } finally {
             closeInputStream(stream, success);
         }
-        
+
 
         // set up the block allocation table (necessary for the
         // data_blocks to be manageable
-        new BlockAllocationTableReader(header_block_reader.getBATCount(),
-                                       header_block_reader.getBATArray(),
-                                       header_block_reader.getXBATCount(),
-                                       header_block_reader.getXBATIndex(),
+        new BlockAllocationTableReader(header_block.getBigBlockSize(),
+                                       header_block.getBATCount(),
+                                       header_block.getBATArray(),
+                                       header_block.getXBATCount(),
+                                       header_block.getXBATIndex(),
                                        data_blocks);
 
         // get property table from the document
         PropertyTable properties =
-            new PropertyTable(header_block_reader.getPropertyStart(),
-                              data_blocks);
+            new PropertyTable(header_block, data_blocks);
 
         // init documents
-        processProperties(SmallBlockTableReader
-            .getSmallDocumentBlocks(data_blocks, properties
-                .getRoot(), header_block_reader
-                    .getSBATStart()), data_blocks, properties.getRoot()
-                        .getChildren(), null);
+        processProperties(
+        		SmallBlockTableReader.getSmallDocumentBlocks(
+        		      bigBlockSize, data_blocks, properties.getRoot(),
+        				header_block.getSBATStart()
+        		),
+        		data_blocks,
+        		properties.getRoot().getChildren(),
+        		null,
+        		header_block.getPropertyStart()
+        );
+
+        // For whatever reason CLSID of root is always 0.
+        getRoot().setStorageClsid(properties.getRoot().getStorageClsid());
     }
     /**
      * @param stream the stream to be closed
      * @param success <code>false</code> if an exception is currently being thrown in the calling method
      */
     private void closeInputStream(InputStream stream, boolean success) {
-        
+
         if(stream.markSupported() && !(stream instanceof ByteArrayInputStream)) {
-            String msg = "POIFS is closing the supplied input stream of type (" 
+            String msg = "POIFS is closing the supplied input stream of type ("
                     + stream.getClass().getName() + ") which supports mark/reset.  "
                     + "This will be a problem for the caller if the stream will still be used.  "
                     + "If that is the case the caller should wrap the input stream to avoid this close logic.  "
@@ -199,7 +194,7 @@ public class POIFSFileSystem
             if(success) {
                 throw new RuntimeException(e);
             }
-            // else not success? Try block did not complete normally 
+            // else not success? Try block did not complete normally
             // just print stack trace and leave original ex to be thrown
             e.printStackTrace();
         }
@@ -207,15 +202,15 @@ public class POIFSFileSystem
 
     /**
      * Checks that the supplied InputStream (which MUST
-     *  support mark and reset, or be a PushbackInputStream) 
+     *  support mark and reset, or be a PushbackInputStream)
      *  has a POIFS (OLE2) header at the start of it.
      * If your InputStream does not support mark / reset,
      *  then wrap it in a PushBackInputStream, then be
      *  sure to always use that, and not the original!
-     * @param inp An InputStream which supports either mark/reset, or is a PushbackInputStream 
+     * @param inp An InputStream which supports either mark/reset, or is a PushbackInputStream
      */
     public static boolean hasPOIFSHeader(InputStream inp) throws IOException {
-        // We want to peek at the first 8 bytes 
+        // We want to peek at the first 8 bytes
         inp.mark(8);
 
         byte[] header = new byte[8];
@@ -229,7 +224,7 @@ public class POIFSFileSystem
         } else {
             inp.reset();
         }
-        
+
         // Did it match the signature?
         return (signature.get() == HeaderBlockConstants._signature);
     }
@@ -288,7 +283,7 @@ public class POIFSFileSystem
     {
         return getRoot().createDirectory(name);
     }
-    
+
     /**
      * Write the filesystem out
      *
@@ -307,11 +302,11 @@ public class POIFSFileSystem
 
         // create the small block store, and the SBAT
         SmallBlockTableWriter      sbtw       =
-            new SmallBlockTableWriter(_documents, _property_table.getRoot());
+            new SmallBlockTableWriter(bigBlockSize, _documents, _property_table.getRoot());
 
         // create the block allocation table
         BlockAllocationTableWriter bat        =
-            new BlockAllocationTableWriter();
+            new BlockAllocationTableWriter(bigBlockSize);
 
         // create a list of BATManaged objects: the documents plus the
         // property table and the small block table
@@ -349,7 +344,7 @@ public class POIFSFileSystem
         int               batStartBlock       = bat.createBlocks();
 
         // get the extended block allocation table blocks
-        HeaderBlockWriter header_block_writer = new HeaderBlockWriter();
+        HeaderBlockWriter header_block_writer = new HeaderBlockWriter(bigBlockSize);
         BATBlock[]        xbat_blocks         =
             header_block_writer.setBATBlocks(bat.countBlocks(),
                                              batStartBlock);
@@ -491,7 +486,8 @@ public class POIFSFileSystem
     private void processProperties(final BlockList small_blocks,
                                    final BlockList big_blocks,
                                    final Iterator properties,
-                                   final DirectoryNode dir)
+                                   final DirectoryNode dir,
+                                   final int headerPropertiesStartAt)
         throws IOException
     {
         while (properties.hasNext())
@@ -511,7 +507,8 @@ public class POIFSFileSystem
 
                 processProperties(
                     small_blocks, big_blocks,
-                    (( DirectoryProperty ) property).getChildren(), new_dir);
+                    (( DirectoryProperty ) property).getChildren(),
+                    new_dir, headerPropertiesStartAt);
             }
             else
             {
@@ -522,14 +519,15 @@ public class POIFSFileSystem
                 if (property.shouldUseSmallBlocks())
                 {
                     document =
-                        new POIFSDocument(name, small_blocks
-                            .fetchBlocks(startBlock), size);
+                        new POIFSDocument(name,
+                                          small_blocks.fetchBlocks(startBlock, headerPropertiesStartAt),
+                                          size);
                 }
                 else
                 {
                     document =
                         new POIFSDocument(name,
-                                          big_blocks.fetchBlocks(startBlock),
+                                          big_blocks.fetchBlocks(startBlock, headerPropertiesStartAt),
                                           size);
                 }
                 parent.createDocument(document);
@@ -552,10 +550,7 @@ public class POIFSFileSystem
         {
             return (( POIFSViewable ) getRoot()).getViewableArray();
         }
-        else
-        {
-            return new Object[ 0 ];
-        }
+        return new Object[ 0 ];
     }
 
     /**
@@ -572,10 +567,7 @@ public class POIFSFileSystem
         {
             return (( POIFSViewable ) getRoot()).getViewableIterator();
         }
-        else
-        {
-            return Collections.EMPTY_LIST.iterator();
-        }
+        return Collections.EMPTY_LIST.iterator();
     }
 
     /**
@@ -607,9 +599,15 @@ public class POIFSFileSystem
      * @return The Big Block size, normally 512 bytes, sometimes 4096 bytes
      */
     public int getBigBlockSize() {
-    	return bigBlockSize;
+    	return bigBlockSize.getBigBlockSize();
+    }
+    /**
+     * @return The Big Block size, normally 512 bytes, sometimes 4096 bytes
+     */
+    public POIFSBigBlockSize getBigBlockSizeDetails() {
+      return bigBlockSize;
     }
-    
+
     /* **********  END  begin implementation of POIFSViewable ********** */
 }   // end public class POIFSFileSystem
 

Added: poi/branches/NIO_32_BRANCH/src/java/org/apache/poi/poifs/nio/ByteArrayBackedDataSource.java
URL: http://svn.apache.org/viewvc/poi/branches/NIO_32_BRANCH/src/java/org/apache/poi/poifs/nio/ByteArrayBackedDataSource.java?rev=1053791&view=auto
==============================================================================
--- poi/branches/NIO_32_BRANCH/src/java/org/apache/poi/poifs/nio/ByteArrayBackedDataSource.java (added)
+++ poi/branches/NIO_32_BRANCH/src/java/org/apache/poi/poifs/nio/ByteArrayBackedDataSource.java Thu Dec 30 02:35:06 2010
@@ -0,0 +1,94 @@
+/* ====================================================================
+   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.nio;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.ByteBuffer;
+
+/**
+ * A POIFS {@link DataSource} backed by a byte array.
+ */
+public class ByteArrayBackedDataSource extends DataSource {
+   private byte[] buffer;
+   private long size;
+   
+   public ByteArrayBackedDataSource(byte[] data, int size) {
+      this.buffer = data;
+      this.size = size;
+   }
+   public ByteArrayBackedDataSource(byte[] data) {
+      this(data, data.length);
+   }
+                
+   public ByteBuffer read(int length, long position) {
+      if(position >= size) {
+         throw new IndexOutOfBoundsException(
+               "Unable to read " + length + " bytes from " +
+               position + " in stream of length " + size
+         );
+      }
+      
+      int toRead = (int)Math.min(length, size - position);
+      return ByteBuffer.wrap(buffer, (int)position, toRead);
+   }
+   
+   public void write(ByteBuffer src, long position) {
+      // Extend if needed
+      long endPosition = position + src.capacity(); 
+      if(endPosition > buffer.length) {
+         extend(endPosition);
+      }
+      
+      // Now copy
+      src.get(buffer, (int)position, src.capacity());
+      
+      // Update size if needed
+      if(endPosition > size) {
+         size = endPosition;
+      }
+   }
+   
+   private void extend(long length) {
+      // Consider extending by a bit more than requested
+      long difference = length - buffer.length;
+      if(difference < buffer.length*0.25) {
+         difference = (long)(buffer.length*0.25);
+      }
+      if(difference < 4096) {
+         difference = 4096;
+      }
+
+      byte[] nb = new byte[(int)(difference+buffer.length)];
+      System.arraycopy(buffer, 0, nb, 0, (int)size);
+      buffer = nb;
+   }
+   
+   public void copyTo(OutputStream stream) throws IOException {
+      stream.write(buffer, 0, (int)size);
+   }
+   
+   public long size() {
+      return size;
+   }
+   
+   public void close() {
+      buffer = null;
+      size = -1;
+   }
+}

Added: poi/branches/NIO_32_BRANCH/src/java/org/apache/poi/poifs/nio/DataSource.java
URL: http://svn.apache.org/viewvc/poi/branches/NIO_32_BRANCH/src/java/org/apache/poi/poifs/nio/DataSource.java?rev=1053791&view=auto
==============================================================================
--- poi/branches/NIO_32_BRANCH/src/java/org/apache/poi/poifs/nio/DataSource.java (added)
+++ poi/branches/NIO_32_BRANCH/src/java/org/apache/poi/poifs/nio/DataSource.java Thu Dec 30 02:35:06 2010
@@ -0,0 +1,35 @@
+/* ====================================================================
+   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.nio;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.ByteBuffer;
+
+/**
+ * Common definition of how we read and write bytes
+ */
+public abstract class DataSource {
+   public abstract ByteBuffer read(int length, long position) throws IOException;
+   public abstract void write(ByteBuffer src, long position) throws IOException;
+   public abstract long size() throws IOException;
+   /** Close the underlying stream */
+   public abstract void close() throws IOException;
+   /** Copies the contents to the specified OutputStream */
+   public abstract void copyTo(OutputStream stream) throws IOException;
+}



---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@poi.apache.org
For additional commands, e-mail: commits-help@poi.apache.org