]> source.dussan.org Git - poi.git/commitdiff
Start to merge the POIFS classes HeaderBlockReader and HeaderBlockWriter into a commo...
authorNick Burch <nick@apache.org>
Sun, 19 Dec 2010 05:54:11 +0000 (05:54 +0000)
committerNick Burch <nick@apache.org>
Sun, 19 Dec 2010 05:54:11 +0000 (05:54 +0000)
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1050764 13f79535-47bb-0310-9956-ffa450edef68

12 files changed:
src/java/org/apache/poi/poifs/dev/POIFSHeaderDumper.java
src/java/org/apache/poi/poifs/eventfilesystem/POIFSReader.java
src/java/org/apache/poi/poifs/filesystem/POIFSFileSystem.java
src/java/org/apache/poi/poifs/storage/HeaderBlock.java [new file with mode: 0644]
src/java/org/apache/poi/poifs/storage/HeaderBlockReader.java [deleted file]
src/testcases/org/apache/poi/poifs/filesystem/TestPOIFSFileSystem.java
src/testcases/org/apache/poi/poifs/storage/AllPOIFSStorageTests.java
src/testcases/org/apache/poi/poifs/storage/TestBlockAllocationTableReader.java
src/testcases/org/apache/poi/poifs/storage/TestHeaderBlockReader.java [deleted file]
src/testcases/org/apache/poi/poifs/storage/TestHeaderBlockReading.java [new file with mode: 0644]
src/testcases/org/apache/poi/poifs/storage/TestHeaderBlockWriter.java [deleted file]
src/testcases/org/apache/poi/poifs/storage/TestHeaderBlockWriting.java [new file with mode: 0644]

index 7a6b187fe0fbbcce60b4be9a53f4a6e5dec40443..cd183b3ba4d641c8b1660382cba5d889182c577f 100644 (file)
 package org.apache.poi.poifs.dev;
 
 import java.io.FileInputStream;
-import java.io.IOException;
 import java.io.InputStream;
 import java.lang.reflect.Field;
 import java.lang.reflect.Method;
-import java.util.Iterator;
 
 import org.apache.poi.poifs.common.POIFSBigBlockSize;
 import org.apache.poi.poifs.common.POIFSConstants;
-import org.apache.poi.poifs.filesystem.DirectoryNode;
-import org.apache.poi.poifs.filesystem.DocumentNode;
-import org.apache.poi.poifs.filesystem.POIFSFileSystem;
 import org.apache.poi.poifs.property.PropertyTable;
 import org.apache.poi.poifs.storage.BlockAllocationTableReader;
 import org.apache.poi.poifs.storage.BlockList;
-import org.apache.poi.poifs.storage.HeaderBlockReader;
+import org.apache.poi.poifs.storage.HeaderBlock;
 import org.apache.poi.poifs.storage.ListManagedBlock;
 import org.apache.poi.poifs.storage.RawDataBlockList;
 import org.apache.poi.poifs.storage.SmallBlockTableReader;
@@ -67,51 +62,50 @@ public class POIFSHeaderDumper {
                InputStream inp = new FileInputStream(filename);
                
                // Header
-               HeaderBlockReader header_block_reader = 
-                  new HeaderBlockReader(inp);
-               displayHeader(header_block_reader);
+               HeaderBlock header_block = new HeaderBlock(inp);
+               displayHeader(header_block);
                
                // Raw blocks
-      POIFSBigBlockSize bigBlockSize = header_block_reader.getBigBlockSize();
+      POIFSBigBlockSize bigBlockSize = header_block.getBigBlockSize();
       RawDataBlockList data_blocks = new RawDataBlockList(inp, bigBlockSize);
       displayRawBlocksSummary(data_blocks);
       
       // Main FAT Table
       BlockAllocationTableReader batReader =
          new BlockAllocationTableReader(
-            header_block_reader.getBigBlockSize(),
-            header_block_reader.getBATCount(),
-            header_block_reader.getBATArray(),
-            header_block_reader.getXBATCount(),
-            header_block_reader.getXBATIndex(),
+            header_block.getBigBlockSize(),
+            header_block.getBATCount(),
+            header_block.getBATArray(),
+            header_block.getXBATCount(),
+            header_block.getXBATIndex(),
             data_blocks);
       displayBATReader(batReader);
 
       // Properties Table
       PropertyTable properties =
          new PropertyTable(
-               header_block_reader.getBigBlockSize(),
-               header_block_reader.getPropertyStart(),
+               header_block.getBigBlockSize(),
+               header_block.getPropertyStart(),
                data_blocks);
       
       // Mini Fat
       BlockList sbat = 
          SmallBlockTableReader.getSmallDocumentBlocks(
                bigBlockSize, data_blocks, properties.getRoot(),
-               header_block_reader.getSBATStart()
+               header_block.getSBATStart()
          );
    }
 
-       public static void displayHeader(HeaderBlockReader header_block_reader) throws Exception {
+       public static void displayHeader(HeaderBlock header_block) throws Exception {
           System.out.println("Header Details:");
-          System.out.println(" Block size: " + header_block_reader.getBigBlockSize());
-      System.out.println(" BAT (FAT) header blocks: " + header_block_reader.getBATArray().length);
-      System.out.println(" BAT (FAT) block count: " + header_block_reader.getBATCount());
-      System.out.println(" XBAT (FAT) block count: " + header_block_reader.getXBATCount());
-      System.out.println(" XBAT (FAT) block 1 at: " + header_block_reader.getXBATIndex());
-      System.out.println(" SBAT (MiniFAT) block count: " + header_block_reader.getSBATCount());
-      System.out.println(" SBAT (MiniFAT) block 1 at: " + header_block_reader.getSBATStart());
-      System.out.println(" Property table at: " + header_block_reader.getPropertyStart());
+          System.out.println(" Block size: " + header_block.getBigBlockSize());
+      System.out.println(" BAT (FAT) header blocks: " + header_block.getBATArray().length);
+      System.out.println(" BAT (FAT) block count: " + header_block.getBATCount());
+      System.out.println(" XBAT (FAT) block count: " + header_block.getXBATCount());
+      System.out.println(" XBAT (FAT) block 1 at: " + header_block.getXBATIndex());
+      System.out.println(" SBAT (MiniFAT) block count: " + header_block.getSBATCount());
+      System.out.println(" SBAT (MiniFAT) block 1 at: " + header_block.getSBATStart());
+      System.out.println(" Property table at: " + header_block.getPropertyStart());
       System.out.println("");
        }
 
index bb83bf815fabac826951cd039405347c74fb2393..450422622794d66d5e0a678b52b73179f8ec04b1 100644 (file)
@@ -31,7 +31,7 @@ import org.apache.poi.poifs.property.Property;
 import org.apache.poi.poifs.property.PropertyTable;
 import org.apache.poi.poifs.storage.BlockAllocationTableReader;
 import org.apache.poi.poifs.storage.BlockList;
-import org.apache.poi.poifs.storage.HeaderBlockReader;
+import org.apache.poi.poifs.storage.HeaderBlock;
 import org.apache.poi.poifs.storage.RawDataBlockList;
 import org.apache.poi.poifs.storage.SmallBlockTableReader;
 
@@ -75,32 +75,32 @@ public class POIFSReader
         registryClosed = true;
 
         // read the header block from the stream
-        HeaderBlockReader header_block_reader = new HeaderBlockReader(stream);
+        HeaderBlock header_block = new HeaderBlock(stream);
 
         // read the rest of the stream into blocks
-        RawDataBlockList  data_blocks         = new RawDataBlockList(stream, header_block_reader.getBigBlockSize());
+        RawDataBlockList  data_blocks         = new RawDataBlockList(stream, header_block.getBigBlockSize());
 
         // set up the block allocation table (necessary for the
         // data_blocks to be manageable
-        new BlockAllocationTableReader(header_block_reader.getBigBlockSize(),
-                                       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.getBigBlockSize(),
-                              header_block_reader.getPropertyStart(),
+            new PropertyTable(header_block.getBigBlockSize(),
+                              header_block.getPropertyStart(),
                               data_blocks);
 
         // process documents
         processProperties(SmallBlockTableReader
             .getSmallDocumentBlocks(
-                  header_block_reader.getBigBlockSize(),
+                  header_block.getBigBlockSize(),
                   data_blocks, properties.getRoot(), 
-                  header_block_reader.getSBATStart()), 
+                  header_block.getSBATStart()), 
                   data_blocks, properties.getRoot()
                         .getChildren(), new POIFSDocumentPath());
     }
index 0a5f97844a8faaff93d326879e7093600f7bde21..bbc22515dddb15a12592dd78eb938988fc48bed4 100644 (file)
@@ -43,7 +43,7 @@ import org.apache.poi.poifs.storage.BlockAllocationTableWriter;
 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;
@@ -146,12 +146,12 @@ 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);
@@ -163,29 +163,29 @@ public class POIFSFileSystem
 
         // set up the block allocation table (necessary for the
         // data_blocks to be manageable
-        new BlockAllocationTableReader(header_block_reader.getBigBlockSize(),
-                                       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(bigBlockSize,
-                              header_block_reader.getPropertyStart(),
+                              header_block.getPropertyStart(),
                               data_blocks);
 
         // init documents
         processProperties(
                        SmallBlockTableReader.getSmallDocumentBlocks(
                              bigBlockSize, data_blocks, properties.getRoot(),
-                                       header_block_reader.getSBATStart()
+                                       header_block.getSBATStart()
                        ),
                        data_blocks,
                        properties.getRoot().getChildren(),
                        null,
-                       header_block_reader.getPropertyStart()
+                       header_block.getPropertyStart()
         );
 
         // For whatever reason CLSID of root is always 0.
diff --git a/src/java/org/apache/poi/poifs/storage/HeaderBlock.java b/src/java/org/apache/poi/poifs/storage/HeaderBlock.java
new file mode 100644 (file)
index 0000000..e12ec44
--- /dev/null
@@ -0,0 +1,343 @@
+/* ====================================================================
+   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.storage;
+
+import static org.apache.poi.poifs.storage.HeaderBlockConstants._bat_array_offset;
+import static org.apache.poi.poifs.storage.HeaderBlockConstants._bat_count_offset;
+import static org.apache.poi.poifs.storage.HeaderBlockConstants._max_bats_in_header;
+import static org.apache.poi.poifs.storage.HeaderBlockConstants._property_start_offset;
+import static org.apache.poi.poifs.storage.HeaderBlockConstants._sbat_start_offset;
+import static org.apache.poi.poifs.storage.HeaderBlockConstants._sbat_block_count_offset;
+import static org.apache.poi.poifs.storage.HeaderBlockConstants._signature;
+import static org.apache.poi.poifs.storage.HeaderBlockConstants._signature_offset;
+import static org.apache.poi.poifs.storage.HeaderBlockConstants._xbat_count_offset;
+import static org.apache.poi.poifs.storage.HeaderBlockConstants._xbat_start_offset;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+
+import org.apache.poi.poifs.common.POIFSBigBlockSize;
+import org.apache.poi.poifs.common.POIFSConstants;
+import org.apache.poi.poifs.filesystem.OfficeXmlFileException;
+import org.apache.poi.poifs.filesystem.POIFSFileSystem;
+import org.apache.poi.util.HexDump;
+import org.apache.poi.util.IOUtils;
+import org.apache.poi.util.IntegerField;
+import org.apache.poi.util.LittleEndian;
+import org.apache.poi.util.LittleEndianConsts;
+import org.apache.poi.util.LongField;
+import org.apache.poi.util.POILogFactory;
+import org.apache.poi.util.POILogger;
+import org.apache.poi.util.ShortField;
+
+/**
+ * The block containing the archive header
+ */
+public final class HeaderBlock {
+   private static final POILogger _logger =
+      POILogFactory.getLogger(HeaderBlock.class);
+   
+       /**
+        * What big block size the file uses. Most files
+        *  use 512 bytes, but a few use 4096
+        */
+       private final POIFSBigBlockSize bigBlockSize;
+
+       /** 
+        * number of big block allocation table blocks (int).
+        * (Number of FAT Sectors in Microsoft parlance) 
+        */
+       private int _bat_count;
+
+       /** 
+        * Start of the property set block (int index of the property set
+        * chain's first big block).
+        */
+       private int _property_start;
+
+       /** 
+        * start of the small block allocation table (int index of small
+        * block allocation table's first big block)
+        */
+       private int _sbat_start;
+       /**
+        * Number of small block allocation table blocks (int)
+        * (Number of MiniFAT Sectors in Microsoft parlance)
+        */
+       private int _sbat_count;
+
+       /** 
+        * Big block index for extension to the big block allocation table
+        */
+       private int _xbat_start;
+       /**
+        * Number of big block allocation table blocks (int)
+        * (Number of DIFAT Sectors in Microsoft parlance)
+        */
+       private int _xbat_count;
+       
+       /**
+        * The data. Only ever 512 bytes, because 4096 byte
+        *  files use zeros for the extra header space.
+        */
+       private final byte[] _data;
+       
+   private static final byte _default_value = ( byte ) 0xFF;
+
+       /**
+        * create a new HeaderBlockReader from an InputStream
+        *
+        * @param stream the source InputStream
+        *
+        * @exception IOException on errors or bad data
+        */
+       public HeaderBlock(InputStream stream) throws IOException {
+               // Grab the first 512 bytes
+          // (For 4096 sized blocks, the remaining 3584 bytes are zero)
+               // Then, process the contents
+               this(readFirst512(stream));
+               
+               // Fetch the rest of the block if needed
+               if(bigBlockSize.getBigBlockSize() != 512) {
+                  int rest = bigBlockSize.getBigBlockSize() - 512;
+                  byte[] tmp = new byte[rest];
+                  IOUtils.readFully(stream, tmp);
+               }
+       }
+       
+       public HeaderBlock(ByteBuffer buffer) throws IOException {
+          this(buffer.array());
+       }
+       
+       private HeaderBlock(byte[] data) throws IOException {
+          this._data = data;
+          
+               // verify signature
+               long signature = LittleEndian.getLong(_data, _signature_offset);
+
+               if (signature != _signature) {
+                       // Is it one of the usual suspects?
+                       byte[] OOXML_FILE_HEADER = POIFSConstants.OOXML_FILE_HEADER;
+                       if(_data[0] == OOXML_FILE_HEADER[0] &&
+                               _data[1] == OOXML_FILE_HEADER[1] &&
+                               _data[2] == OOXML_FILE_HEADER[2] &&
+                               _data[3] == OOXML_FILE_HEADER[3]) {
+                               throw new OfficeXmlFileException("The supplied data appears to be in the Office 2007+ XML. You are calling the part of POI that deals with OLE2 Office Documents. You need to call a different part of POI to process this data (eg XSSF instead of HSSF)");
+                       }
+                       if ((signature & 0xFF8FFFFFFFFFFFFFL) == 0x0010000200040009L) {
+                               // BIFF2 raw stream starts with BOF (sid=0x0009, size=0x0004, data=0x00t0)
+                               throw new IllegalArgumentException("The supplied data appears to be in BIFF2 format.  "
+                                               + "POI only supports BIFF8 format");
+                       }
+
+                       // Give a generic error
+                       throw new IOException("Invalid header signature; read "
+                                                 + longToHex(signature) + ", expected "
+                                                 + longToHex(_signature));
+               }
+
+
+               // Figure out our block size
+               if (_data[30] == 12) {
+                       this.bigBlockSize = POIFSConstants.LARGER_BIG_BLOCK_SIZE_DETAILS;
+               } else if(_data[30] == 9) {
+                       this.bigBlockSize = POIFSConstants.SMALLER_BIG_BLOCK_SIZE_DETAILS;
+               } else {
+                  throw new IOException("Unsupported blocksize  (2^"+ _data[30] + "). Expected 2^9 or 2^12.");
+               }
+
+          // Setup the fields to read and write the counts and starts
+      _bat_count      = new IntegerField(_bat_count_offset, data).get();
+      _property_start = new IntegerField(_property_start_offset,_data).get();
+      _sbat_start = new IntegerField(_sbat_start_offset, _data).get();
+      _sbat_count = new IntegerField(_sbat_block_count_offset, _data).get();
+      _xbat_start = new IntegerField(_xbat_start_offset, _data).get();
+      _xbat_count = new IntegerField(_xbat_count_offset, _data).get();
+      
+      // Sanity check values
+      if(_bat_count > _max_bats_in_header) {
+         _logger.log(POILogger.WARN, "Too many BAT blocks listed in header, found " 
+                     + _bat_count + " but the maximum is " + _max_bats_in_header);
+         _bat_count = _max_bats_in_header;
+      }
+       }
+       
+   /**
+    * Create a single instance initialized with default values
+    */
+   public HeaderBlock(POIFSBigBlockSize bigBlockSize) throws IOException
+   {
+      this.bigBlockSize = bigBlockSize;
+
+      // Our data is always 512 big no matter what
+      _data = new byte[ POIFSConstants.SMALLER_BIG_BLOCK_SIZE ];
+      Arrays.fill(_data, _default_value);
+      
+      // Set all the default values
+      new LongField(_signature_offset, _signature, _data);
+      new IntegerField(0x08, 0, _data);
+      new IntegerField(0x0c, 0, _data);
+      new IntegerField(0x10, 0, _data);
+      new IntegerField(0x14, 0, _data);
+      new ShortField(0x18, ( short ) 0x3b, _data);
+      new ShortField(0x1a, ( short ) 0x3, _data);
+      new ShortField(0x1c, ( short ) -2, _data);
+       
+      new ShortField(0x1e, bigBlockSize.getHeaderValue(), _data);
+      new IntegerField(0x20, 0x6, _data);
+      new IntegerField(0x24, 0, _data);
+      new IntegerField(0x28, 0, _data);
+      new IntegerField(0x34, 0, _data);
+      new IntegerField(0x38, 0x1000, _data);
+      
+      // Initialise the variables
+      _bat_count = 0;
+      _sbat_count = 0;
+      _xbat_count = 0;
+      _property_start = POIFSConstants.END_OF_CHAIN;
+      _sbat_start = POIFSConstants.END_OF_CHAIN;
+      _xbat_start = POIFSConstants.END_OF_CHAIN;
+   }
+   
+       private static byte[] readFirst512(InputStream stream) throws IOException {
+      // Grab the first 512 bytes
+      // (For 4096 sized blocks, the remaining 3584 bytes are zero)
+      byte[] data = new byte[512];
+      int bsCount = IOUtils.readFully(stream, data);
+      if(bsCount != 512) {
+         throw alertShortRead(bsCount, 512);
+      }
+      return data;
+       }
+
+       private static String longToHex(long value) {
+               return new String(HexDump.longToHex(value));
+       }
+
+       private static IOException alertShortRead(int pRead, int expectedReadSize) {
+               int read;
+               if (pRead < 0) {
+                       //Can't have -1 bytes read in the error message!
+                       read = 0;
+               } else {
+                       read = pRead;
+               }
+               String type = " byte" + (read == 1 ? (""): ("s"));
+
+               return new IOException("Unable to read entire header; "
+                               + read + type + " read; expected "
+                               + expectedReadSize + " bytes");
+       }
+
+       /**
+        * get start of Property Table
+        *
+        * @return the index of the first block of the Property Table
+        */
+       public int getPropertyStart() {
+               return _property_start;
+       }
+
+       /**
+        * @return start of small block (MiniFAT) allocation table
+        */
+       public int getSBATStart() {
+               return _sbat_start;
+       }
+       public int getSBATCount() {
+          return _sbat_count;
+       }
+
+       /**
+        * @return number of BAT blocks
+        */
+       public int getBATCount() {
+               return _bat_count;
+       }
+
+       /**
+        * Returns the offsets to the first (up to) 109
+        *  BAT sectors.
+        * Any additional BAT sectors are held in the XBAT (DIFAT)
+        *  sectors in a chain.
+        * @return BAT offset array
+        */
+       public int[] getBATArray() {
+      // Read them in
+               int[] result = new int[ _bat_count ];
+               int offset = _bat_array_offset;
+               for (int j = 0; j < _bat_count; j++) {
+                       result[ j ] = LittleEndian.getInt(_data, offset);
+                       offset     += LittleEndianConsts.INT_SIZE;
+               }
+               return result;
+       }
+
+       /**
+        * @return XBAT (DIFAT) count
+        */
+       public int getXBATCount() {
+               return _xbat_count;
+       }
+
+       /**
+        * @return XBAT (DIFAT) index
+        */
+       public int getXBATIndex() {
+               return _xbat_start;
+       }
+
+       /**
+        * @return The Big Block size, normally 512 bytes, sometimes 4096 bytes
+        */
+       public POIFSBigBlockSize getBigBlockSize() {
+               return bigBlockSize;
+       }
+       
+   /**
+    * Write the block's data to an OutputStream
+    *
+    * @param stream the OutputStream to which the stored data should
+    *               be written
+    *
+    * @exception IOException on problems writing to the specified
+    *            stream
+    */
+   void writeData(final OutputStream stream)
+       throws IOException
+   {
+      // Update the counts and start positions 
+      new IntegerField(_bat_count_offset,      _bat_count, _data);
+      new IntegerField(_property_start_offset, _property_start, _data);
+      new IntegerField(_sbat_start_offset,     _sbat_start, _data);
+      new IntegerField(_sbat_block_count_offset, _sbat_count, _data);
+      new IntegerField(_xbat_start_offset,      _xbat_start, _data);
+      new IntegerField(_xbat_count_offset,      _xbat_count, _data);
+      
+      // Write the main data out
+      stream.write(_data, 0, 512);
+      
+      // Now do the padding if needed
+      for(int i=POIFSConstants.SMALLER_BIG_BLOCK_SIZE; i<POIFSConstants.LARGER_BIG_BLOCK_SIZE; i++) {
+         stream.write(0);
+      }
+   }
+}
diff --git a/src/java/org/apache/poi/poifs/storage/HeaderBlockReader.java b/src/java/org/apache/poi/poifs/storage/HeaderBlockReader.java
deleted file mode 100644 (file)
index 3569dfc..0000000
+++ /dev/null
@@ -1,262 +0,0 @@
-/* ====================================================================
-   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.storage;
-
-import static org.apache.poi.poifs.storage.HeaderBlockConstants._bat_array_offset;
-import static org.apache.poi.poifs.storage.HeaderBlockConstants._bat_count_offset;
-import static org.apache.poi.poifs.storage.HeaderBlockConstants._max_bats_in_header;
-import static org.apache.poi.poifs.storage.HeaderBlockConstants._property_start_offset;
-import static org.apache.poi.poifs.storage.HeaderBlockConstants._sbat_start_offset;
-import static org.apache.poi.poifs.storage.HeaderBlockConstants._sbat_block_count_offset;
-import static org.apache.poi.poifs.storage.HeaderBlockConstants._signature;
-import static org.apache.poi.poifs.storage.HeaderBlockConstants._signature_offset;
-import static org.apache.poi.poifs.storage.HeaderBlockConstants._xbat_count_offset;
-import static org.apache.poi.poifs.storage.HeaderBlockConstants._xbat_start_offset;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.nio.ByteBuffer;
-
-import org.apache.poi.poifs.common.POIFSBigBlockSize;
-import org.apache.poi.poifs.common.POIFSConstants;
-import org.apache.poi.poifs.filesystem.OfficeXmlFileException;
-import org.apache.poi.util.HexDump;
-import org.apache.poi.util.IOUtils;
-import org.apache.poi.util.LittleEndian;
-import org.apache.poi.util.LittleEndianConsts;
-
-/**
- * The block containing the archive header
- *
- * @author Marc Johnson (mjohnson at apache dot org)
- */
-public final class HeaderBlockReader {
-       /**
-        * What big block size the file uses. Most files
-        *  use 512 bytes, but a few use 4096
-        */
-       private final POIFSBigBlockSize bigBlockSize;
-
-       /** 
-        * number of big block allocation table blocks (int).
-        * (Number of FAT Sectors in Microsoft parlance) 
-        */
-       private final int _bat_count;
-
-       /** 
-        * Start of the property set block (int index of the property set
-        * chain's first big block).
-        */
-       private final int _property_start;
-
-       /** 
-        * start of the small block allocation table (int index of small
-        * block allocation table's first big block)
-        */
-       private final int _sbat_start;
-       /**
-        * Number of small block allocation table blocks (int)
-        * (Number of MiniFAT Sectors in Microsoft parlance)
-        */
-       private final int _sbat_count;
-
-       /** 
-        * Big block index for extension to the big block allocation table
-        */
-       private final int _xbat_start;
-       /**
-        * Number of big block allocation table blocks (int)
-        * (Number of DIFAT Sectors in Microsoft parlance)
-        */
-       private final int _xbat_count;
-       
-       /**
-        * The data
-        */
-       private final byte[] _data;
-
-       /**
-        * create a new HeaderBlockReader from an InputStream
-        *
-        * @param stream the source InputStream
-        *
-        * @exception IOException on errors or bad data
-        */
-       public HeaderBlockReader(InputStream stream) throws IOException {
-               // Grab the first 512 bytes
-          // (For 4096 sized blocks, the remaining 3584 bytes are zero)
-               // Then, process the contents
-               this(readFirst512(stream));
-               
-               // Fetch the rest of the block if needed
-               if(bigBlockSize.getBigBlockSize() != 512) {
-                  int rest = bigBlockSize.getBigBlockSize() - 512;
-                  byte[] tmp = new byte[rest];
-                  IOUtils.readFully(stream, tmp);
-               }
-       }
-       
-       public HeaderBlockReader(ByteBuffer buffer) throws IOException {
-          this(buffer.array());
-       }
-       
-       private HeaderBlockReader(byte[] data) throws IOException {
-          this._data = data;
-          
-               // verify signature
-               long signature = LittleEndian.getLong(_data, _signature_offset);
-
-               if (signature != _signature) {
-                       // Is it one of the usual suspects?
-                       byte[] OOXML_FILE_HEADER = POIFSConstants.OOXML_FILE_HEADER;
-                       if(_data[0] == OOXML_FILE_HEADER[0] &&
-                               _data[1] == OOXML_FILE_HEADER[1] &&
-                               _data[2] == OOXML_FILE_HEADER[2] &&
-                               _data[3] == OOXML_FILE_HEADER[3]) {
-                               throw new OfficeXmlFileException("The supplied data appears to be in the Office 2007+ XML. You are calling the part of POI that deals with OLE2 Office Documents. You need to call a different part of POI to process this data (eg XSSF instead of HSSF)");
-                       }
-                       if ((signature & 0xFF8FFFFFFFFFFFFFL) == 0x0010000200040009L) {
-                               // BIFF2 raw stream starts with BOF (sid=0x0009, size=0x0004, data=0x00t0)
-                               throw new IllegalArgumentException("The supplied data appears to be in BIFF2 format.  "
-                                               + "POI only supports BIFF8 format");
-                       }
-
-                       // Give a generic error
-                       throw new IOException("Invalid header signature; read "
-                                                 + longToHex(signature) + ", expected "
-                                                 + longToHex(_signature));
-               }
-
-
-               // Figure out our block size
-               switch (_data[30]) {
-                       case 12:
-                               bigBlockSize = POIFSConstants.LARGER_BIG_BLOCK_SIZE_DETAILS; break;
-                       case  9:
-                               bigBlockSize = POIFSConstants.SMALLER_BIG_BLOCK_SIZE_DETAILS; break;
-                       default:
-                               throw new IOException("Unsupported blocksize  (2^"
-                                               + _data[30] + "). Expected 2^9 or 2^12.");
-               }
-
-               _bat_count      = getInt(_bat_count_offset, _data);
-               _property_start = getInt(_property_start_offset, _data);
-               _sbat_start     = getInt(_sbat_start_offset, _data);
-               _sbat_count     = getInt(_sbat_block_count_offset, _data);
-               _xbat_start     = getInt(_xbat_start_offset, _data);
-               _xbat_count     = getInt(_xbat_count_offset, _data);
-       }
-       
-       private static byte[] readFirst512(InputStream stream) throws IOException {
-      // Grab the first 512 bytes
-      // (For 4096 sized blocks, the remaining 3584 bytes are zero)
-      byte[] data = new byte[512];
-      int bsCount = IOUtils.readFully(stream, data);
-      if(bsCount != 512) {
-         throw alertShortRead(bsCount, 512);
-      }
-      return data;
-       }
-
-       private static int getInt(int offset, byte[] data) {
-               return LittleEndian.getInt(data, offset);
-       }
-
-       private static String longToHex(long value) {
-               return new String(HexDump.longToHex(value));
-       }
-
-       private static IOException alertShortRead(int pRead, int expectedReadSize) {
-               int read;
-               if (pRead < 0) {
-                       //Can't have -1 bytes read in the error message!
-                       read = 0;
-               } else {
-                       read = pRead;
-               }
-               String type = " byte" + (read == 1 ? (""): ("s"));
-
-               return new IOException("Unable to read entire header; "
-                               + read + type + " read; expected "
-                               + expectedReadSize + " bytes");
-       }
-
-       /**
-        * get start of Property Table
-        *
-        * @return the index of the first block of the Property Table
-        */
-       public int getPropertyStart() {
-               return _property_start;
-       }
-
-       /**
-        * @return start of small block (MiniFAT) allocation table
-        */
-       public int getSBATStart() {
-               return _sbat_start;
-       }
-       public int getSBATCount() {
-          return _sbat_count;
-       }
-
-       /**
-        * @return number of BAT blocks
-        */
-       public int getBATCount() {
-               return _bat_count;
-       }
-
-       /**
-        * Returns the offsets to the first (up to) 109
-        *  BAT sectors.
-        * Any additional BAT sectors 
-        * @return BAT offset array
-        */
-       public int[] getBATArray() {
-               int[] result = new int[ _max_bats_in_header ];
-               int   offset = _bat_array_offset;
-
-               for (int j = 0; j < _max_bats_in_header; j++) {
-                       result[ j ] = LittleEndian.getInt(_data, offset);
-                       offset     += LittleEndianConsts.INT_SIZE;
-               }
-               return result;
-       }
-
-       /**
-        * @return XBAT (DIFAT) count
-        */
-       public int getXBATCount() {
-               return _xbat_count;
-       }
-
-       /**
-        * @return XBAT (DIFAT) index
-        */
-       public int getXBATIndex() {
-               return _xbat_start;
-       }
-
-       /**
-        * @return The Big Block size, normally 512 bytes, sometimes 4096 bytes
-        */
-       public POIFSBigBlockSize getBigBlockSize() {
-               return bigBlockSize;
-       }
-}
index ae9ac644a411762c73f0edb708a65f7309bf1356..0389a18a1bd6ca9c1b40536a45cac666252dff27 100644 (file)
@@ -29,7 +29,7 @@ import junit.framework.TestCase;
 import org.apache.poi.POIDataSamples;
 import org.apache.poi.hssf.HSSFTestDataSamples;
 import org.apache.poi.poifs.common.POIFSBigBlockSize;
-import org.apache.poi.poifs.storage.HeaderBlockReader;
+import org.apache.poi.poifs.storage.HeaderBlock;
 import org.apache.poi.poifs.storage.RawDataBlockList;
 
 /**
@@ -184,14 +184,14 @@ public final class TestPOIFSFileSystem extends TestCase {
           InputStream inp = _samples.openResourceAsStream("BlockSize4096.zvi");
           
           // First up, check that we can process the header properly
-      HeaderBlockReader header_block_reader = new HeaderBlockReader(inp);
-      POIFSBigBlockSize bigBlockSize = header_block_reader.getBigBlockSize();
+      HeaderBlock header_block = new HeaderBlock(inp);
+      POIFSBigBlockSize bigBlockSize = header_block.getBigBlockSize();
       assertEquals(4096, bigBlockSize.getBigBlockSize());
       
       // Check the fat info looks sane
-      assertEquals(109, header_block_reader.getBATArray().length);
-      assertTrue(header_block_reader.getBATCount() > 0);
-      assertEquals(0, header_block_reader.getXBATCount());
+      assertEquals(1, header_block.getBATArray().length);
+      assertEquals(1, header_block.getBATCount());
+      assertEquals(0, header_block.getXBATCount());
       
       // Now check we can get the basic fat
       RawDataBlockList data_blocks = new RawDataBlockList(inp, bigBlockSize);
index b589922587c1dcd9c08cd010301291a402d3bc9d..46011916eeb288405fb8a361356a11f6f4719aa8 100644 (file)
@@ -33,8 +33,8 @@ public final class AllPOIFSStorageTests {
                result.addTestSuite(TestBlockAllocationTableWriter.class);
                result.addTestSuite(TestBlockListImpl.class);
                result.addTestSuite(TestDocumentBlock.class);
-               result.addTestSuite(TestHeaderBlockReader.class);
-               result.addTestSuite(TestHeaderBlockWriter.class);
+               result.addTestSuite(TestHeaderBlockReading.class);
+               result.addTestSuite(TestHeaderBlockWriting.class);
                result.addTestSuite(TestPropertyBlock.class);
                result.addTestSuite(TestRawDataBlock.class);
                result.addTestSuite(TestRawDataBlockList.class);
index 45dd34c26eaf57e97aecd1e97280b5948d2584a1..79f7bd76def29cece062a30a9aa3db7e7f6e75e4 100644 (file)
@@ -405,10 +405,10 @@ public final class TestBlockAllocationTableReader extends TestCase {
 
                // similar code to POIFSFileSystem.<init>:
                InputStream stream = new ByteArrayInputStream(data);
-               HeaderBlockReader hb;
+               HeaderBlock hb;
                RawDataBlockList dataBlocks;
                try {
-                       hb = new HeaderBlockReader(stream);
+                       hb = new HeaderBlock(stream);
                        dataBlocks = new RawDataBlockList(stream, bigBlockSize);
                } catch (IOException e) {
                        throw new RuntimeException(e);
@@ -419,7 +419,11 @@ public final class TestBlockAllocationTableReader extends TestCase {
                                        hb.getXBATIndex(), dataBlocks);
                } catch (IOException e) {
                        // expected during successful test
-                       assertEquals("Block count 538976257 is too high. POI maximum is 65535.", e.getMessage());
+                       assertEquals(
+                             "Your file contains 0 sectors, but the initial DIFAT array at index 0 referenced block # 538976288. This isn't allowed and  your file is corrupt", 
+                             e.getMessage()
+                       );
+//       assertEquals("Block count 538976257 is too high. POI maximum is 65535.", e.getMessage());
                } catch (OutOfMemoryError e) {
                        if (e.getStackTrace()[1].getMethodName().equals("testBadSectorAllocationTableSize")) {
                                throw new AssertionFailedError("Identified bug 48085");
diff --git a/src/testcases/org/apache/poi/poifs/storage/TestHeaderBlockReader.java b/src/testcases/org/apache/poi/poifs/storage/TestHeaderBlockReader.java
deleted file mode 100644 (file)
index f49ef12..0000000
+++ /dev/null
@@ -1,83 +0,0 @@
-/* ====================================================================
-   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.storage;
-
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-
-import junit.framework.TestCase;
-
-/**
- * Class to test HeaderBlockReader functionality
- *
- * @author Marc Johnson
- */
-public final class TestHeaderBlockReader extends TestCase {
-
-       public void testConstructors() throws IOException {
-               String[] hexData = {
-                       "D0 CF 11 E0 A1 B1 1A E1 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 3B 00 03 00 FE FF 09 00",
-                       "06 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 FE FF FF FF 00 00 00 00 00 10 00 00 FE FF FF FF",
-                       "01 00 00 00 FE FF FF FF 00 00 00 00 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
-                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
-                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
-                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
-                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
-                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
-                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
-                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
-                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
-                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
-                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
-                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
-                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
-                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
-               };
-               byte[] content = RawDataUtil.decode(hexData);
-               HeaderBlockReader block = new HeaderBlockReader(new ByteArrayInputStream(content));
-
-               assertEquals(-2, block.getPropertyStart());
-
-               // verify we can't read a short block
-               byte[] shortblock = new byte[511];
-
-               System.arraycopy(content, 0, shortblock, 0, 511);
-               try {
-                       block = new HeaderBlockReader(new ByteArrayInputStream(shortblock));
-                       fail("Should have caught IOException reading a short block");
-               } catch (IOException ignored) {
-
-                       // as expected
-               }
-
-               // try various forms of corruption
-               for (int index = 0; index < 8; index++) {
-                       content[index] = (byte) (content[index] - 1);
-                       try {
-                               block = new HeaderBlockReader(new ByteArrayInputStream(content));
-                               fail("Should have caught IOException corrupting byte " + index);
-                       } catch (IOException ignored) {
-
-                               // as expected
-                       }
-
-                       // restore byte value
-                       content[index] = (byte) (content[index] + 1);
-               }
-       }
-}
diff --git a/src/testcases/org/apache/poi/poifs/storage/TestHeaderBlockReading.java b/src/testcases/org/apache/poi/poifs/storage/TestHeaderBlockReading.java
new file mode 100644 (file)
index 0000000..15077c4
--- /dev/null
@@ -0,0 +1,83 @@
+/* ====================================================================
+   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.storage;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+
+import junit.framework.TestCase;
+
+/**
+ * Class to test HeaderBlockReader functionality
+ *
+ * @author Marc Johnson
+ */
+public final class TestHeaderBlockReading extends TestCase {
+
+       public void testConstructors() throws IOException {
+               String[] hexData = {
+                       "D0 CF 11 E0 A1 B1 1A E1 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 3B 00 03 00 FE FF 09 00",
+                       "06 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 FE FF FF FF 00 00 00 00 00 10 00 00 FE FF FF FF",
+                       "01 00 00 00 FE FF FF FF 00 00 00 00 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
+                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
+                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
+                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
+                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
+                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
+                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
+                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
+                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
+                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
+                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
+                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
+                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
+                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
+               };
+               byte[] content = RawDataUtil.decode(hexData);
+               HeaderBlock block = new HeaderBlock(new ByteArrayInputStream(content));
+
+               assertEquals(-2, block.getPropertyStart());
+
+               // verify we can't read a short block
+               byte[] shortblock = new byte[511];
+
+               System.arraycopy(content, 0, shortblock, 0, 511);
+               try {
+                       block = new HeaderBlock(new ByteArrayInputStream(shortblock));
+                       fail("Should have caught IOException reading a short block");
+               } catch (IOException ignored) {
+
+                       // as expected
+               }
+
+               // try various forms of corruption
+               for (int index = 0; index < 8; index++) {
+                       content[index] = (byte) (content[index] - 1);
+                       try {
+                               block = new HeaderBlock(new ByteArrayInputStream(content));
+                               fail("Should have caught IOException corrupting byte " + index);
+                       } catch (IOException ignored) {
+
+                               // as expected
+                       }
+
+                       // restore byte value
+                       content[index] = (byte) (content[index] + 1);
+               }
+       }
+}
diff --git a/src/testcases/org/apache/poi/poifs/storage/TestHeaderBlockWriter.java b/src/testcases/org/apache/poi/poifs/storage/TestHeaderBlockWriter.java
deleted file mode 100644 (file)
index 456d30c..0000000
+++ /dev/null
@@ -1,270 +0,0 @@
-/* ====================================================================
-   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.storage;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-
-import junit.framework.TestCase;
-
-import org.apache.poi.poifs.common.POIFSConstants;
-import org.apache.poi.util.LittleEndian;
-import org.apache.poi.util.LittleEndianConsts;
-
-/**
- * Class to test HeaderBlockWriter functionality
- *
- * @author Marc Johnson
- */
-public final class TestHeaderBlockWriter extends TestCase {
-
-       private static void confirmEqual(String[] expectedDataHexDumpLines, byte[] actual) {
-               byte[] expected = RawDataUtil.decode(expectedDataHexDumpLines);
-
-               assertEquals(expected.length, actual.length);
-               for (int j = 0; j < expected.length; j++) {
-                       assertEquals("testing byte " + j, expected[j], actual[j]);
-               }
-       }
-
-       /**
-        * Test creating a HeaderBlockWriter
-        */
-       public void testConstructors() throws IOException {
-               HeaderBlockWriter block = new HeaderBlockWriter(POIFSConstants.SMALLER_BIG_BLOCK_SIZE_DETAILS);
-               ByteArrayOutputStream output = new ByteArrayOutputStream(512);
-
-               block.writeBlocks(output);
-               byte[] copy = output.toByteArray();
-               String[] expected = {
-                       "D0 CF 11 E0 A1 B1 1A E1 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 3B 00 03 00 FE FF 09 00",
-                       "06 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 FE FF FF FF 00 00 00 00 00 10 00 00 FE FF FF FF",
-                       "00 00 00 00 FE FF FF FF 00 00 00 00 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
-                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
-                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
-                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
-                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
-                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
-                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
-                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
-                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
-                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
-                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
-                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
-                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
-                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
-               };
-
-               confirmEqual(expected, copy);
-
-               // verify we can read a 'good' HeaderBlockWriter (also test
-               // getPropertyStart)
-               block.setPropertyStart(0x87654321);
-               output = new ByteArrayOutputStream(512);
-               block.writeBlocks(output);
-               assertEquals(0x87654321, new HeaderBlockReader(new ByteArrayInputStream(output
-                               .toByteArray())).getPropertyStart());
-       }
-
-       /**
-        * Test setting the SBAT start block
-        */
-       public void testSetSBATStart() throws IOException {
-          HeaderBlockWriter block = new HeaderBlockWriter(POIFSConstants.SMALLER_BIG_BLOCK_SIZE_DETAILS);
-
-               block.setSBATStart(0x01234567);
-               ByteArrayOutputStream output = new ByteArrayOutputStream(512);
-
-               block.writeBlocks(output);
-               byte[] copy = output.toByteArray();
-               String[] expected = {
-                       "D0 CF 11 E0 A1 B1 1A E1 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 3B 00 03 00 FE FF 09 00",
-                       "06 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 FE FF FF FF 00 00 00 00 00 10 00 00 67 45 23 01",
-                       "00 00 00 00 FE FF FF FF 00 00 00 00 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
-                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
-                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
-                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
-                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
-                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
-                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
-                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
-                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
-                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
-                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
-                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
-                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
-                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
-               };
-               confirmEqual(expected, copy);
-       }
-
-       /**
-        * test setPropertyStart and getPropertyStart
-        */
-       public void testSetPropertyStart() throws IOException {
-          HeaderBlockWriter block = new HeaderBlockWriter(POIFSConstants.SMALLER_BIG_BLOCK_SIZE_DETAILS);
-
-               block.setPropertyStart(0x01234567);
-               ByteArrayOutputStream output = new ByteArrayOutputStream(512);
-
-               block.writeBlocks(output);
-               byte[] copy = output.toByteArray();
-               String[] expected = {
-                       "D0 CF 11 E0 A1 B1 1A E1 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 3B 00 03 00 FE FF 09 00",
-                       "06 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 67 45 23 01 00 00 00 00 00 10 00 00 FE FF FF FF",
-                       "00 00 00 00 FE FF FF FF 00 00 00 00 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
-                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
-                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
-                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
-                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
-                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
-                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
-                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
-                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
-                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
-                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
-                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
-                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
-                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
-               };
-               confirmEqual(expected, copy);
-       }
-
-       /**
-        * test setting the BAT blocks; also tests getBATCount, getBATArray,
-        * getXBATCount
-        */
-       public void testSetBATBlocks() throws IOException {
-
-               // first, a small set of blocks
-          HeaderBlockWriter block = new HeaderBlockWriter(POIFSConstants.SMALLER_BIG_BLOCK_SIZE_DETAILS);
-               BATBlock[] xbats = block.setBATBlocks(5, 0x01234567);
-
-               assertEquals(0, xbats.length);
-               assertEquals(0, HeaderBlockWriter.calculateXBATStorageRequirements(POIFSConstants.SMALLER_BIG_BLOCK_SIZE_DETAILS,5));
-               ByteArrayOutputStream output = new ByteArrayOutputStream(512);
-
-               block.writeBlocks(output);
-               byte[] copy = output.toByteArray();
-               String[] expected = {
-                       "D0 CF 11 E0 A1 B1 1A E1 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 3B 00 03 00 FE FF 09 00",
-                       "06 00 00 00 00 00 00 00 00 00 00 00 05 00 00 00 FE FF FF FF 00 00 00 00 00 10 00 00 FE FF FF FF",
-                       "00 00 00 00 FE FF FF FF 00 00 00 00 67 45 23 01 68 45 23 01 69 45 23 01 6A 45 23 01 6B 45 23 01",
-                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
-                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
-                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
-                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
-                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
-                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
-                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
-                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
-                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
-                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
-                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
-                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
-                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
-               };
-
-               confirmEqual(expected, copy);
-
-               // second, a full set of blocks (109 blocks)
-               block = new HeaderBlockWriter(POIFSConstants.SMALLER_BIG_BLOCK_SIZE_DETAILS);
-               xbats = block.setBATBlocks(109, 0x01234567);
-               assertEquals(0, xbats.length);
-               assertEquals(0, HeaderBlockWriter.calculateXBATStorageRequirements(POIFSConstants.SMALLER_BIG_BLOCK_SIZE_DETAILS,109));
-               output = new ByteArrayOutputStream(512);
-               block.writeBlocks(output);
-               copy = output.toByteArray();
-               String[] expected2 = {
-                       "D0 CF 11 E0 A1 B1 1A E1 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 3B 00 03 00 FE FF 09 00",
-                       "06 00 00 00 00 00 00 00 00 00 00 00 6D 00 00 00 FE FF FF FF 00 00 00 00 00 10 00 00 FE FF FF FF",
-                       "00 00 00 00 FE FF FF FF 00 00 00 00 67 45 23 01 68 45 23 01 69 45 23 01 6A 45 23 01 6B 45 23 01",
-                       "6C 45 23 01 6D 45 23 01 6E 45 23 01 6F 45 23 01 70 45 23 01 71 45 23 01 72 45 23 01 73 45 23 01",
-                       "74 45 23 01 75 45 23 01 76 45 23 01 77 45 23 01 78 45 23 01 79 45 23 01 7A 45 23 01 7B 45 23 01",
-                       "7C 45 23 01 7D 45 23 01 7E 45 23 01 7F 45 23 01 80 45 23 01 81 45 23 01 82 45 23 01 83 45 23 01",
-                       "84 45 23 01 85 45 23 01 86 45 23 01 87 45 23 01 88 45 23 01 89 45 23 01 8A 45 23 01 8B 45 23 01",
-                       "8C 45 23 01 8D 45 23 01 8E 45 23 01 8F 45 23 01 90 45 23 01 91 45 23 01 92 45 23 01 93 45 23 01",
-                       "94 45 23 01 95 45 23 01 96 45 23 01 97 45 23 01 98 45 23 01 99 45 23 01 9A 45 23 01 9B 45 23 01",
-                       "9C 45 23 01 9D 45 23 01 9E 45 23 01 9F 45 23 01 A0 45 23 01 A1 45 23 01 A2 45 23 01 A3 45 23 01",
-                       "A4 45 23 01 A5 45 23 01 A6 45 23 01 A7 45 23 01 A8 45 23 01 A9 45 23 01 AA 45 23 01 AB 45 23 01",
-                       "AC 45 23 01 AD 45 23 01 AE 45 23 01 AF 45 23 01 B0 45 23 01 B1 45 23 01 B2 45 23 01 B3 45 23 01",
-                       "B4 45 23 01 B5 45 23 01 B6 45 23 01 B7 45 23 01 B8 45 23 01 B9 45 23 01 BA 45 23 01 BB 45 23 01",
-                       "BC 45 23 01 BD 45 23 01 BE 45 23 01 BF 45 23 01 C0 45 23 01 C1 45 23 01 C2 45 23 01 C3 45 23 01",
-                       "C4 45 23 01 C5 45 23 01 C6 45 23 01 C7 45 23 01 C8 45 23 01 C9 45 23 01 CA 45 23 01 CB 45 23 01",
-                       "CC 45 23 01 CD 45 23 01 CE 45 23 01 CF 45 23 01 D0 45 23 01 D1 45 23 01 D2 45 23 01 D3 45 23 01",
-               };
-               confirmEqual(expected2, copy);
-
-               // finally, a really large set of blocks (256 blocks)
-               block = new HeaderBlockWriter(POIFSConstants.SMALLER_BIG_BLOCK_SIZE_DETAILS);
-               xbats = block.setBATBlocks(256, 0x01234567);
-               assertEquals(2, xbats.length);
-               assertEquals(2, HeaderBlockWriter.calculateXBATStorageRequirements(POIFSConstants.SMALLER_BIG_BLOCK_SIZE_DETAILS,256));
-               output = new ByteArrayOutputStream(512);
-               block.writeBlocks(output);
-               copy = output.toByteArray();
-               String[] expected3 = {
-                       "D0 CF 11 E0 A1 B1 1A E1 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 3B 00 03 00 FE FF 09 00",
-                       "06 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 FE FF FF FF 00 00 00 00 00 10 00 00 FE FF FF FF",
-                       "00 00 00 00 67 46 23 01 02 00 00 00 67 45 23 01 68 45 23 01 69 45 23 01 6A 45 23 01 6B 45 23 01",
-                       "6C 45 23 01 6D 45 23 01 6E 45 23 01 6F 45 23 01 70 45 23 01 71 45 23 01 72 45 23 01 73 45 23 01",
-                       "74 45 23 01 75 45 23 01 76 45 23 01 77 45 23 01 78 45 23 01 79 45 23 01 7A 45 23 01 7B 45 23 01",
-                       "7C 45 23 01 7D 45 23 01 7E 45 23 01 7F 45 23 01 80 45 23 01 81 45 23 01 82 45 23 01 83 45 23 01",
-                       "84 45 23 01 85 45 23 01 86 45 23 01 87 45 23 01 88 45 23 01 89 45 23 01 8A 45 23 01 8B 45 23 01",
-                       "8C 45 23 01 8D 45 23 01 8E 45 23 01 8F 45 23 01 90 45 23 01 91 45 23 01 92 45 23 01 93 45 23 01",
-                       "94 45 23 01 95 45 23 01 96 45 23 01 97 45 23 01 98 45 23 01 99 45 23 01 9A 45 23 01 9B 45 23 01",
-                       "9C 45 23 01 9D 45 23 01 9E 45 23 01 9F 45 23 01 A0 45 23 01 A1 45 23 01 A2 45 23 01 A3 45 23 01",
-                       "A4 45 23 01 A5 45 23 01 A6 45 23 01 A7 45 23 01 A8 45 23 01 A9 45 23 01 AA 45 23 01 AB 45 23 01",
-                       "AC 45 23 01 AD 45 23 01 AE 45 23 01 AF 45 23 01 B0 45 23 01 B1 45 23 01 B2 45 23 01 B3 45 23 01",
-                       "B4 45 23 01 B5 45 23 01 B6 45 23 01 B7 45 23 01 B8 45 23 01 B9 45 23 01 BA 45 23 01 BB 45 23 01",
-                       "BC 45 23 01 BD 45 23 01 BE 45 23 01 BF 45 23 01 C0 45 23 01 C1 45 23 01 C2 45 23 01 C3 45 23 01",
-                       "C4 45 23 01 C5 45 23 01 C6 45 23 01 C7 45 23 01 C8 45 23 01 C9 45 23 01 CA 45 23 01 CB 45 23 01",
-                       "CC 45 23 01 CD 45 23 01 CE 45 23 01 CF 45 23 01 D0 45 23 01 D1 45 23 01 D2 45 23 01 D3 45 23 01",
-               };
-
-               confirmEqual(expected3, copy);
-
-               output = new ByteArrayOutputStream(1028);
-               xbats[0].writeBlocks(output);
-               xbats[1].writeBlocks(output);
-               copy = output.toByteArray();
-               int correct = 0x012345D4;
-               int offset = 0;
-               int k = 0;
-
-               for (; k < 127; k++) {
-                       assertEquals("XBAT entry " + k, correct, LittleEndian.getInt(copy, offset));
-                       correct++;
-                       offset += LittleEndianConsts.INT_SIZE;
-               }
-               assertEquals("XBAT Chain", 0x01234567 + 257, LittleEndian.getInt(copy, offset));
-               offset += LittleEndianConsts.INT_SIZE;
-               k++;
-               for (; k < 148; k++) {
-                       assertEquals("XBAT entry " + k, correct, LittleEndian.getInt(copy, offset));
-                       correct++;
-                       offset += LittleEndianConsts.INT_SIZE;
-               }
-               for (; k < 255; k++) {
-                       assertEquals("XBAT entry " + k, -1, LittleEndian.getInt(copy, offset));
-                       offset += LittleEndianConsts.INT_SIZE;
-               }
-               assertEquals("XBAT End of chain", -2, LittleEndian.getInt(copy, offset));
-       }
-}
diff --git a/src/testcases/org/apache/poi/poifs/storage/TestHeaderBlockWriting.java b/src/testcases/org/apache/poi/poifs/storage/TestHeaderBlockWriting.java
new file mode 100644 (file)
index 0000000..e01c788
--- /dev/null
@@ -0,0 +1,270 @@
+/* ====================================================================
+   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.storage;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+import junit.framework.TestCase;
+
+import org.apache.poi.poifs.common.POIFSConstants;
+import org.apache.poi.util.LittleEndian;
+import org.apache.poi.util.LittleEndianConsts;
+
+/**
+ * Class to test HeaderBlockWriter functionality
+ *
+ * @author Marc Johnson
+ */
+public final class TestHeaderBlockWriting extends TestCase {
+
+       private static void confirmEqual(String[] expectedDataHexDumpLines, byte[] actual) {
+               byte[] expected = RawDataUtil.decode(expectedDataHexDumpLines);
+
+               assertEquals(expected.length, actual.length);
+               for (int j = 0; j < expected.length; j++) {
+                       assertEquals("testing byte " + j, expected[j], actual[j]);
+               }
+       }
+
+       /**
+        * Test creating a HeaderBlockWriter
+        */
+       public void testConstructors() throws IOException {
+               HeaderBlockWriter block = new HeaderBlockWriter(POIFSConstants.SMALLER_BIG_BLOCK_SIZE_DETAILS);
+               ByteArrayOutputStream output = new ByteArrayOutputStream(512);
+
+               block.writeBlocks(output);
+               byte[] copy = output.toByteArray();
+               String[] expected = {
+                       "D0 CF 11 E0 A1 B1 1A E1 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 3B 00 03 00 FE FF 09 00",
+                       "06 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 FE FF FF FF 00 00 00 00 00 10 00 00 FE FF FF FF",
+                       "00 00 00 00 FE FF FF FF 00 00 00 00 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
+                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
+                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
+                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
+                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
+                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
+                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
+                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
+                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
+                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
+                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
+                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
+                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
+                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
+               };
+
+               confirmEqual(expected, copy);
+
+               // verify we can read a 'good' HeaderBlockWriter (also test
+               // getPropertyStart)
+               block.setPropertyStart(0x87654321);
+               output = new ByteArrayOutputStream(512);
+               block.writeBlocks(output);
+               assertEquals(0x87654321, new HeaderBlock(
+                     new ByteArrayInputStream(output.toByteArray())).getPropertyStart());
+       }
+
+       /**
+        * Test setting the SBAT start block
+        */
+       public void testSetSBATStart() throws IOException {
+          HeaderBlockWriter block = new HeaderBlockWriter(POIFSConstants.SMALLER_BIG_BLOCK_SIZE_DETAILS);
+
+               block.setSBATStart(0x01234567);
+               ByteArrayOutputStream output = new ByteArrayOutputStream(512);
+
+               block.writeBlocks(output);
+               byte[] copy = output.toByteArray();
+               String[] expected = {
+                       "D0 CF 11 E0 A1 B1 1A E1 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 3B 00 03 00 FE FF 09 00",
+                       "06 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 FE FF FF FF 00 00 00 00 00 10 00 00 67 45 23 01",
+                       "00 00 00 00 FE FF FF FF 00 00 00 00 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
+                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
+                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
+                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
+                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
+                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
+                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
+                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
+                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
+                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
+                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
+                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
+                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
+                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
+               };
+               confirmEqual(expected, copy);
+       }
+
+       /**
+        * test setPropertyStart and getPropertyStart
+        */
+       public void testSetPropertyStart() throws IOException {
+          HeaderBlockWriter block = new HeaderBlockWriter(POIFSConstants.SMALLER_BIG_BLOCK_SIZE_DETAILS);
+
+               block.setPropertyStart(0x01234567);
+               ByteArrayOutputStream output = new ByteArrayOutputStream(512);
+
+               block.writeBlocks(output);
+               byte[] copy = output.toByteArray();
+               String[] expected = {
+                       "D0 CF 11 E0 A1 B1 1A E1 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 3B 00 03 00 FE FF 09 00",
+                       "06 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 67 45 23 01 00 00 00 00 00 10 00 00 FE FF FF FF",
+                       "00 00 00 00 FE FF FF FF 00 00 00 00 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
+                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
+                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
+                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
+                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
+                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
+                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
+                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
+                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
+                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
+                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
+                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
+                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
+                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
+               };
+               confirmEqual(expected, copy);
+       }
+
+       /**
+        * test setting the BAT blocks; also tests getBATCount, getBATArray,
+        * getXBATCount
+        */
+       public void testSetBATBlocks() throws IOException {
+
+               // first, a small set of blocks
+          HeaderBlockWriter block = new HeaderBlockWriter(POIFSConstants.SMALLER_BIG_BLOCK_SIZE_DETAILS);
+               BATBlock[] xbats = block.setBATBlocks(5, 0x01234567);
+
+               assertEquals(0, xbats.length);
+               assertEquals(0, HeaderBlockWriter.calculateXBATStorageRequirements(POIFSConstants.SMALLER_BIG_BLOCK_SIZE_DETAILS,5));
+               ByteArrayOutputStream output = new ByteArrayOutputStream(512);
+
+               block.writeBlocks(output);
+               byte[] copy = output.toByteArray();
+               String[] expected = {
+                       "D0 CF 11 E0 A1 B1 1A E1 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 3B 00 03 00 FE FF 09 00",
+                       "06 00 00 00 00 00 00 00 00 00 00 00 05 00 00 00 FE FF FF FF 00 00 00 00 00 10 00 00 FE FF FF FF",
+                       "00 00 00 00 FE FF FF FF 00 00 00 00 67 45 23 01 68 45 23 01 69 45 23 01 6A 45 23 01 6B 45 23 01",
+                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
+                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
+                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
+                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
+                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
+                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
+                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
+                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
+                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
+                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
+                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
+                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
+                       "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
+               };
+
+               confirmEqual(expected, copy);
+
+               // second, a full set of blocks (109 blocks)
+               block = new HeaderBlockWriter(POIFSConstants.SMALLER_BIG_BLOCK_SIZE_DETAILS);
+               xbats = block.setBATBlocks(109, 0x01234567);
+               assertEquals(0, xbats.length);
+               assertEquals(0, HeaderBlockWriter.calculateXBATStorageRequirements(POIFSConstants.SMALLER_BIG_BLOCK_SIZE_DETAILS,109));
+               output = new ByteArrayOutputStream(512);
+               block.writeBlocks(output);
+               copy = output.toByteArray();
+               String[] expected2 = {
+                       "D0 CF 11 E0 A1 B1 1A E1 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 3B 00 03 00 FE FF 09 00",
+                       "06 00 00 00 00 00 00 00 00 00 00 00 6D 00 00 00 FE FF FF FF 00 00 00 00 00 10 00 00 FE FF FF FF",
+                       "00 00 00 00 FE FF FF FF 00 00 00 00 67 45 23 01 68 45 23 01 69 45 23 01 6A 45 23 01 6B 45 23 01",
+                       "6C 45 23 01 6D 45 23 01 6E 45 23 01 6F 45 23 01 70 45 23 01 71 45 23 01 72 45 23 01 73 45 23 01",
+                       "74 45 23 01 75 45 23 01 76 45 23 01 77 45 23 01 78 45 23 01 79 45 23 01 7A 45 23 01 7B 45 23 01",
+                       "7C 45 23 01 7D 45 23 01 7E 45 23 01 7F 45 23 01 80 45 23 01 81 45 23 01 82 45 23 01 83 45 23 01",
+                       "84 45 23 01 85 45 23 01 86 45 23 01 87 45 23 01 88 45 23 01 89 45 23 01 8A 45 23 01 8B 45 23 01",
+                       "8C 45 23 01 8D 45 23 01 8E 45 23 01 8F 45 23 01 90 45 23 01 91 45 23 01 92 45 23 01 93 45 23 01",
+                       "94 45 23 01 95 45 23 01 96 45 23 01 97 45 23 01 98 45 23 01 99 45 23 01 9A 45 23 01 9B 45 23 01",
+                       "9C 45 23 01 9D 45 23 01 9E 45 23 01 9F 45 23 01 A0 45 23 01 A1 45 23 01 A2 45 23 01 A3 45 23 01",
+                       "A4 45 23 01 A5 45 23 01 A6 45 23 01 A7 45 23 01 A8 45 23 01 A9 45 23 01 AA 45 23 01 AB 45 23 01",
+                       "AC 45 23 01 AD 45 23 01 AE 45 23 01 AF 45 23 01 B0 45 23 01 B1 45 23 01 B2 45 23 01 B3 45 23 01",
+                       "B4 45 23 01 B5 45 23 01 B6 45 23 01 B7 45 23 01 B8 45 23 01 B9 45 23 01 BA 45 23 01 BB 45 23 01",
+                       "BC 45 23 01 BD 45 23 01 BE 45 23 01 BF 45 23 01 C0 45 23 01 C1 45 23 01 C2 45 23 01 C3 45 23 01",
+                       "C4 45 23 01 C5 45 23 01 C6 45 23 01 C7 45 23 01 C8 45 23 01 C9 45 23 01 CA 45 23 01 CB 45 23 01",
+                       "CC 45 23 01 CD 45 23 01 CE 45 23 01 CF 45 23 01 D0 45 23 01 D1 45 23 01 D2 45 23 01 D3 45 23 01",
+               };
+               confirmEqual(expected2, copy);
+
+               // finally, a really large set of blocks (256 blocks)
+               block = new HeaderBlockWriter(POIFSConstants.SMALLER_BIG_BLOCK_SIZE_DETAILS);
+               xbats = block.setBATBlocks(256, 0x01234567);
+               assertEquals(2, xbats.length);
+               assertEquals(2, HeaderBlockWriter.calculateXBATStorageRequirements(POIFSConstants.SMALLER_BIG_BLOCK_SIZE_DETAILS,256));
+               output = new ByteArrayOutputStream(512);
+               block.writeBlocks(output);
+               copy = output.toByteArray();
+               String[] expected3 = {
+                       "D0 CF 11 E0 A1 B1 1A E1 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 3B 00 03 00 FE FF 09 00",
+                       "06 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 FE FF FF FF 00 00 00 00 00 10 00 00 FE FF FF FF",
+                       "00 00 00 00 67 46 23 01 02 00 00 00 67 45 23 01 68 45 23 01 69 45 23 01 6A 45 23 01 6B 45 23 01",
+                       "6C 45 23 01 6D 45 23 01 6E 45 23 01 6F 45 23 01 70 45 23 01 71 45 23 01 72 45 23 01 73 45 23 01",
+                       "74 45 23 01 75 45 23 01 76 45 23 01 77 45 23 01 78 45 23 01 79 45 23 01 7A 45 23 01 7B 45 23 01",
+                       "7C 45 23 01 7D 45 23 01 7E 45 23 01 7F 45 23 01 80 45 23 01 81 45 23 01 82 45 23 01 83 45 23 01",
+                       "84 45 23 01 85 45 23 01 86 45 23 01 87 45 23 01 88 45 23 01 89 45 23 01 8A 45 23 01 8B 45 23 01",
+                       "8C 45 23 01 8D 45 23 01 8E 45 23 01 8F 45 23 01 90 45 23 01 91 45 23 01 92 45 23 01 93 45 23 01",
+                       "94 45 23 01 95 45 23 01 96 45 23 01 97 45 23 01 98 45 23 01 99 45 23 01 9A 45 23 01 9B 45 23 01",
+                       "9C 45 23 01 9D 45 23 01 9E 45 23 01 9F 45 23 01 A0 45 23 01 A1 45 23 01 A2 45 23 01 A3 45 23 01",
+                       "A4 45 23 01 A5 45 23 01 A6 45 23 01 A7 45 23 01 A8 45 23 01 A9 45 23 01 AA 45 23 01 AB 45 23 01",
+                       "AC 45 23 01 AD 45 23 01 AE 45 23 01 AF 45 23 01 B0 45 23 01 B1 45 23 01 B2 45 23 01 B3 45 23 01",
+                       "B4 45 23 01 B5 45 23 01 B6 45 23 01 B7 45 23 01 B8 45 23 01 B9 45 23 01 BA 45 23 01 BB 45 23 01",
+                       "BC 45 23 01 BD 45 23 01 BE 45 23 01 BF 45 23 01 C0 45 23 01 C1 45 23 01 C2 45 23 01 C3 45 23 01",
+                       "C4 45 23 01 C5 45 23 01 C6 45 23 01 C7 45 23 01 C8 45 23 01 C9 45 23 01 CA 45 23 01 CB 45 23 01",
+                       "CC 45 23 01 CD 45 23 01 CE 45 23 01 CF 45 23 01 D0 45 23 01 D1 45 23 01 D2 45 23 01 D3 45 23 01",
+               };
+
+               confirmEqual(expected3, copy);
+
+               output = new ByteArrayOutputStream(1028);
+               xbats[0].writeBlocks(output);
+               xbats[1].writeBlocks(output);
+               copy = output.toByteArray();
+               int correct = 0x012345D4;
+               int offset = 0;
+               int k = 0;
+
+               for (; k < 127; k++) {
+                       assertEquals("XBAT entry " + k, correct, LittleEndian.getInt(copy, offset));
+                       correct++;
+                       offset += LittleEndianConsts.INT_SIZE;
+               }
+               assertEquals("XBAT Chain", 0x01234567 + 257, LittleEndian.getInt(copy, offset));
+               offset += LittleEndianConsts.INT_SIZE;
+               k++;
+               for (; k < 148; k++) {
+                       assertEquals("XBAT entry " + k, correct, LittleEndian.getInt(copy, offset));
+                       correct++;
+                       offset += LittleEndianConsts.INT_SIZE;
+               }
+               for (; k < 255; k++) {
+                       assertEquals("XBAT entry " + k, -1, LittleEndian.getInt(copy, offset));
+                       offset += LittleEndianConsts.INT_SIZE;
+               }
+               assertEquals("XBAT End of chain", -2, LittleEndian.getInt(copy, offset));
+       }
+}