Save This Page
Home » poi-src-3.2-FINAL-20081019 » org.apache.poi.poifs » storage » [javadoc | source]
    1   
    2   /* ====================================================================
    3      Licensed to the Apache Software Foundation (ASF) under one or more
    4      contributor license agreements.  See the NOTICE file distributed with
    5      this work for additional information regarding copyright ownership.
    6      The ASF licenses this file to You under the Apache License, Version 2.0
    7      (the "License"); you may not use this file except in compliance with
    8      the License.  You may obtain a copy of the License at
    9   
   10          http://www.apache.org/licenses/LICENSE-2.0
   11   
   12      Unless required by applicable law or agreed to in writing, software
   13      distributed under the License is distributed on an "AS IS" BASIS,
   14      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   15      See the License for the specific language governing permissions and
   16      limitations under the License.
   17   ==================================================================== */
   18           
   19   
   20   package org.apache.poi.poifs.storage;
   21   
   22   import java.io.IOException;
   23   
   24   import java.util;
   25   
   26   import org.apache.poi.poifs.common.POIFSConstants;
   27   import org.apache.poi.util.IntList;
   28   import org.apache.poi.util.LittleEndian;
   29   import org.apache.poi.util.LittleEndianConsts;
   30   
   31   /**
   32    * This class manages and creates the Block Allocation Table, which is
   33    * basically a set of linked lists of block indices.
   34    * <P>
   35    * Each block of the filesystem has an index. The first block, the
   36    * header, is skipped; the first block after the header is index 0,
   37    * the next is index 1, and so on.
   38    * <P>
   39    * A block's index is also its index into the Block Allocation
   40    * Table. The entry that it finds in the Block Allocation Table is the
   41    * index of the next block in the linked list of blocks making up a
   42    * file, or it is set to -2: end of list.
   43    *
   44    * @author Marc Johnson (mjohnson at apache dot org)
   45    */
   46   
   47   public class BlockAllocationTableReader
   48   {
   49       private IntList _entries;
   50   
   51       /**
   52        * create a BlockAllocationTableReader for an existing filesystem. Side
   53        * effect: when this method finishes, the BAT blocks will have
   54        * been removed from the raw block list, and any blocks labeled as
   55        * 'unused' in the block allocation table will also have been
   56        * removed from the raw block list.
   57        *
   58        * @param block_count the number of BAT blocks making up the block
   59        *                    allocation table
   60        * @param block_array the array of BAT block indices from the
   61        *                    filesystem's header
   62        * @param xbat_count the number of XBAT blocks
   63        * @param xbat_index the index of the first XBAT block
   64        * @param raw_block_list the list of RawDataBlocks
   65        *
   66        * @exception IOException if, in trying to create the table, we
   67        *            encounter logic errors
   68        */
   69   
   70       public BlockAllocationTableReader(final int block_count,
   71                                         final int [] block_array,
   72                                         final int xbat_count,
   73                                         final int xbat_index,
   74                                         final BlockList raw_block_list)
   75           throws IOException
   76       {
   77           this();
   78           if (block_count <= 0)
   79           {
   80               throw new IOException(
   81                   "Illegal block count; minimum count is 1, got " + block_count
   82                   + " instead");
   83           }
   84   
   85           // acquire raw data blocks containing the BAT block data
   86           RawDataBlock blocks[] = new RawDataBlock[ block_count ];
   87           int          limit    = Math.min(block_count, block_array.length);
   88           int          block_index;
   89   
   90           for (block_index = 0; block_index < limit; block_index++)
   91           {
   92               blocks[ block_index ] =
   93                   ( RawDataBlock ) raw_block_list
   94                       .remove(block_array[ block_index ]);
   95           }
   96           if (block_index < block_count)
   97           {
   98   
   99               // must have extended blocks
  100               if (xbat_index < 0)
  101               {
  102                   throw new IOException(
  103                       "BAT count exceeds limit, yet XBAT index indicates no valid entries");
  104               }
  105               int chain_index           = xbat_index;
  106               int max_entries_per_block = BATBlock.entriesPerXBATBlock();
  107               int chain_index_offset    = BATBlock.getXBATChainOffset();
  108   
  109               for (int j = 0; j < xbat_count; j++)
  110               {
  111                   limit = Math.min(block_count - block_index,
  112                                    max_entries_per_block);
  113                   byte[] data   = raw_block_list.remove(chain_index).getData();
  114                   int    offset = 0;
  115   
  116                   for (int k = 0; k < limit; k++)
  117                   {
  118                       blocks[ block_index++ ] =
  119                           ( RawDataBlock ) raw_block_list
  120                               .remove(LittleEndian.getInt(data, offset));
  121                       offset                  += LittleEndianConsts.INT_SIZE;
  122                   }
  123                   chain_index = LittleEndian.getInt(data, chain_index_offset);
  124                   if (chain_index == POIFSConstants.END_OF_CHAIN)
  125                   {
  126                       break;
  127                   }
  128               }
  129           }
  130           if (block_index != block_count)
  131           {
  132               throw new IOException("Could not find all blocks");
  133           }
  134   
  135           // now that we have all of the raw data blocks, go through and
  136           // create the indices
  137           setEntries(blocks, raw_block_list);
  138       }
  139   
  140       /**
  141        * create a BlockAllocationTableReader from an array of raw data blocks
  142        *
  143        * @param blocks the raw data
  144        * @param raw_block_list the list holding the managed blocks
  145        *
  146        * @exception IOException
  147        */
  148   
  149       BlockAllocationTableReader(final ListManagedBlock [] blocks,
  150                                  final BlockList raw_block_list)
  151           throws IOException
  152       {
  153           this();
  154           setEntries(blocks, raw_block_list);
  155       }
  156   
  157       /**
  158        * Constructor BlockAllocationTableReader
  159        *
  160        *
  161        */
  162   
  163       BlockAllocationTableReader()
  164       {
  165           _entries = new IntList();
  166       }
  167   
  168       /**
  169        * walk the entries from a specified point and return the
  170        * associated blocks. The associated blocks are removed from the
  171        * block list
  172        *
  173        * @param startBlock the first block in the chain
  174        * @param blockList the raw data block list
  175        *
  176        * @return array of ListManagedBlocks, in their correct order
  177        *
  178        * @exception IOException if there is a problem acquiring the blocks
  179        */
  180   
  181       ListManagedBlock [] fetchBlocks(final int startBlock,
  182                                       final BlockList blockList)
  183           throws IOException
  184       {
  185           List blocks       = new ArrayList();
  186           int  currentBlock = startBlock;
  187   
  188           while (currentBlock != POIFSConstants.END_OF_CHAIN)
  189           {
  190               blocks.add(blockList.remove(currentBlock));
  191               currentBlock = _entries.get(currentBlock);
  192           }
  193           return ( ListManagedBlock [] ) blocks
  194               .toArray(new ListManagedBlock[ 0 ]);
  195       }
  196   
  197       // methods for debugging reader
  198   
  199       /**
  200        * determine whether the block specified by index is used or not
  201        *
  202        * @param index index of block in question
  203        *
  204        * @return true if the specific block is used, else false
  205        */
  206   
  207       boolean isUsed(final int index)
  208       {
  209           boolean rval = false;
  210   
  211           try
  212           {
  213               rval = _entries.get(index) != -1;
  214           }
  215           catch (IndexOutOfBoundsException ignored)
  216           {
  217           }
  218           return rval;
  219       }
  220   
  221       /**
  222        * return the next block index
  223        *
  224        * @param index of the current block
  225        *
  226        * @return index of the next block (may be
  227        *         POIFSConstants.END_OF_CHAIN, indicating end of chain
  228        *         (duh))
  229        *
  230        * @exception IOException if the current block is unused
  231        */
  232   
  233       int getNextBlockIndex(final int index)
  234           throws IOException
  235       {
  236           if (isUsed(index))
  237           {
  238               return _entries.get(index);
  239           }
  240           else
  241           {
  242               throw new IOException("index " + index + " is unused");
  243           }
  244       }
  245   
  246       /**
  247        * Convert an array of blocks into a set of integer indices
  248        *
  249        * @param blocks the array of blocks containing the indices
  250        * @param raw_blocks the list of blocks being managed. Unused
  251        *                   blocks will be eliminated from the list
  252        *
  253        * @exception IOException
  254        */
  255   
  256       private void setEntries(final ListManagedBlock [] blocks,
  257                               final BlockList raw_blocks)
  258           throws IOException
  259       {
  260           int limit = BATBlock.entriesPerBlock();
  261   
  262           for (int block_index = 0; block_index < blocks.length; block_index++)
  263           {
  264               byte[] data   = blocks[ block_index ].getData();
  265               int    offset = 0;
  266   
  267               for (int k = 0; k < limit; k++)
  268               {
  269                   int entry = LittleEndian.getInt(data, offset);
  270   
  271                   if (entry == POIFSConstants.UNUSED_BLOCK)
  272                   {
  273                       raw_blocks.zap(_entries.size());
  274                   }
  275                   _entries.add(entry);
  276                   offset += LittleEndianConsts.INT_SIZE;
  277               }
  278   
  279               // discard block
  280               blocks[ block_index ] = null;
  281           }
  282           raw_blocks.setBAT(this);
  283       }
  284   }   // end class BlockAllocationTableReader
  285   

Save This Page
Home » poi-src-3.2-FINAL-20081019 » org.apache.poi.poifs » storage » [javadoc | source]