001: /* ====================================================================
002: Licensed to the Apache Software Foundation (ASF) under one or more
003: contributor license agreements. See the NOTICE file distributed with
004: this work for additional information regarding copyright ownership.
005: The ASF licenses this file to You under the Apache License, Version 2.0
006: (the "License"); you may not use this file except in compliance with
007: the License. You may obtain a copy of the License at
008:
009: http://www.apache.org/licenses/LICENSE-2.0
010:
011: Unless required by applicable law or agreed to in writing, software
012: distributed under the License is distributed on an "AS IS" BASIS,
013: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: See the License for the specific language governing permissions and
015: limitations under the License.
016: ==================================================================== */
017:
018: package org.apache.poi.poifs.storage;
019:
020: import java.io.IOException;
021:
022: import java.util.*;
023:
024: import org.apache.poi.poifs.common.POIFSConstants;
025: import org.apache.poi.util.IntList;
026: import org.apache.poi.util.LittleEndian;
027: import org.apache.poi.util.LittleEndianConsts;
028:
029: /**
030: * This class manages and creates the Block Allocation Table, which is
031: * basically a set of linked lists of block indices.
032: * <P>
033: * Each block of the filesystem has an index. The first block, the
034: * header, is skipped; the first block after the header is index 0,
035: * the next is index 1, and so on.
036: * <P>
037: * A block's index is also its index into the Block Allocation
038: * Table. The entry that it finds in the Block Allocation Table is the
039: * index of the next block in the linked list of blocks making up a
040: * file, or it is set to -2: end of list.
041: *
042: * @author Marc Johnson (mjohnson at apache dot org)
043: */
044:
045: public class BlockAllocationTableReader {
046: private IntList _entries;
047:
048: /**
049: * create a BlockAllocationTableReader for an existing filesystem. Side
050: * effect: when this method finishes, the BAT blocks will have
051: * been removed from the raw block list, and any blocks labeled as
052: * 'unused' in the block allocation table will also have been
053: * removed from the raw block list.
054: *
055: * @param block_count the number of BAT blocks making up the block
056: * allocation table
057: * @param block_array the array of BAT block indices from the
058: * filesystem's header
059: * @param xbat_count the number of XBAT blocks
060: * @param xbat_index the index of the first XBAT block
061: * @param raw_block_list the list of RawDataBlocks
062: *
063: * @exception IOException if, in trying to create the table, we
064: * encounter logic errors
065: */
066:
067: public BlockAllocationTableReader(final int block_count,
068: final int[] block_array, final int xbat_count,
069: final int xbat_index, final BlockList raw_block_list)
070: throws IOException {
071: this ();
072: if (block_count <= 0) {
073: throw new IOException(
074: "Illegal block count; minimum count is 1, got "
075: + block_count + " instead");
076: }
077:
078: // acquire raw data blocks containing the BAT block data
079: RawDataBlock blocks[] = new RawDataBlock[block_count];
080: int limit = Math.min(block_count, block_array.length);
081: int block_index;
082:
083: for (block_index = 0; block_index < limit; block_index++) {
084: blocks[block_index] = (RawDataBlock) raw_block_list
085: .remove(block_array[block_index]);
086: }
087: if (block_index < block_count) {
088:
089: // must have extended blocks
090: if (xbat_index < 0) {
091: throw new IOException(
092: "BAT count exceeds limit, yet XBAT index indicates no valid entries");
093: }
094: int chain_index = xbat_index;
095: int max_entries_per_block = BATBlock.entriesPerXBATBlock();
096: int chain_index_offset = BATBlock.getXBATChainOffset();
097:
098: for (int j = 0; j < xbat_count; j++) {
099: limit = Math.min(block_count - block_index,
100: max_entries_per_block);
101: byte[] data = raw_block_list.remove(chain_index)
102: .getData();
103: int offset = 0;
104:
105: for (int k = 0; k < limit; k++) {
106: blocks[block_index++] = (RawDataBlock) raw_block_list
107: .remove(LittleEndian.getInt(data, offset));
108: offset += LittleEndianConsts.INT_SIZE;
109: }
110: chain_index = LittleEndian.getInt(data,
111: chain_index_offset);
112: if (chain_index == POIFSConstants.END_OF_CHAIN) {
113: break;
114: }
115: }
116: }
117: if (block_index != block_count) {
118: throw new IOException("Could not find all blocks");
119: }
120:
121: // now that we have all of the raw data blocks, go through and
122: // create the indices
123: setEntries(blocks, raw_block_list);
124: }
125:
126: /**
127: * create a BlockAllocationTableReader from an array of raw data blocks
128: *
129: * @param blocks the raw data
130: * @param raw_block_list the list holding the managed blocks
131: *
132: * @exception IOException
133: */
134:
135: BlockAllocationTableReader(final ListManagedBlock[] blocks,
136: final BlockList raw_block_list) throws IOException {
137: this ();
138: setEntries(blocks, raw_block_list);
139: }
140:
141: /**
142: * Constructor BlockAllocationTableReader
143: *
144: *
145: */
146:
147: BlockAllocationTableReader() {
148: _entries = new IntList();
149: }
150:
151: /**
152: * walk the entries from a specified point and return the
153: * associated blocks. The associated blocks are removed from the
154: * block list
155: *
156: * @param startBlock the first block in the chain
157: * @param blockList the raw data block list
158: *
159: * @return array of ListManagedBlocks, in their correct order
160: *
161: * @exception IOException if there is a problem acquiring the blocks
162: */
163:
164: ListManagedBlock[] fetchBlocks(final int startBlock,
165: final BlockList blockList) throws IOException {
166: List blocks = new ArrayList();
167: int currentBlock = startBlock;
168:
169: while (currentBlock != POIFSConstants.END_OF_CHAIN) {
170: blocks.add(blockList.remove(currentBlock));
171: currentBlock = _entries.get(currentBlock);
172: }
173: return (ListManagedBlock[]) blocks
174: .toArray(new ListManagedBlock[0]);
175: }
176:
177: // methods for debugging reader
178:
179: /**
180: * determine whether the block specified by index is used or not
181: *
182: * @param index index of block in question
183: *
184: * @return true if the specific block is used, else false
185: */
186:
187: boolean isUsed(final int index) {
188: boolean rval = false;
189:
190: try {
191: rval = _entries.get(index) != -1;
192: } catch (IndexOutOfBoundsException ignored) {
193: }
194: return rval;
195: }
196:
197: /**
198: * return the next block index
199: *
200: * @param index of the current block
201: *
202: * @return index of the next block (may be
203: * POIFSConstants.END_OF_CHAIN, indicating end of chain
204: * (duh))
205: *
206: * @exception IOException if the current block is unused
207: */
208:
209: int getNextBlockIndex(final int index) throws IOException {
210: if (isUsed(index)) {
211: return _entries.get(index);
212: } else {
213: throw new IOException("index " + index + " is unused");
214: }
215: }
216:
217: /**
218: * Convert an array of blocks into a set of integer indices
219: *
220: * @param blocks the array of blocks containing the indices
221: * @param raw_blocks the list of blocks being managed. Unused
222: * blocks will be eliminated from the list
223: *
224: * @exception IOException
225: */
226:
227: private void setEntries(final ListManagedBlock[] blocks,
228: final BlockList raw_blocks) throws IOException {
229: int limit = BATBlock.entriesPerBlock();
230:
231: for (int block_index = 0; block_index < blocks.length; block_index++) {
232: byte[] data = blocks[block_index].getData();
233: int offset = 0;
234:
235: for (int k = 0; k < limit; k++) {
236: int entry = LittleEndian.getInt(data, offset);
237:
238: if (entry == POIFSConstants.UNUSED_BLOCK) {
239: raw_blocks.zap(_entries.size());
240: }
241: _entries.add(entry);
242: offset += LittleEndianConsts.INT_SIZE;
243: }
244:
245: // discard block
246: blocks[block_index] = null;
247: }
248: raw_blocks.setBAT(this );
249: }
250: } // end class BlockAllocationTableReader
|