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.*;
021:
022: import java.util.*;
023:
024: import org.apache.poi.poifs.common.POIFSConstants;
025:
026: /**
027: * Storage for documents that are too small to use regular
028: * DocumentBlocks for their data
029: *
030: * @author Marc Johnson (mjohnson at apache dot org)
031: */
032:
033: public class SmallDocumentBlock implements BlockWritable,
034: ListManagedBlock {
035: private byte[] _data;
036: private static final byte _default_fill = (byte) 0xff;
037: private static final int _block_size = 64;
038: private static final int _blocks_per_big_block = POIFSConstants.BIG_BLOCK_SIZE
039: / _block_size;
040:
041: private SmallDocumentBlock(final byte[] data, final int index) {
042: this ();
043: System.arraycopy(data, index * _block_size, _data, 0,
044: _block_size);
045: }
046:
047: private SmallDocumentBlock() {
048: _data = new byte[_block_size];
049: }
050:
051: /**
052: * convert a single long array into an array of SmallDocumentBlock
053: * instances
054: *
055: * @param array the byte array to be converted
056: * @param size the intended size of the array (which may be smaller)
057: *
058: * @return an array of SmallDocumentBlock instances, filled from
059: * the array
060: */
061:
062: public static SmallDocumentBlock[] convert(final byte[] array,
063: final int size) {
064: SmallDocumentBlock[] rval = new SmallDocumentBlock[(size
065: + _block_size - 1)
066: / _block_size];
067: int offset = 0;
068:
069: for (int k = 0; k < rval.length; k++) {
070: rval[k] = new SmallDocumentBlock();
071: if (offset < array.length) {
072: int length = Math.min(_block_size, array.length
073: - offset);
074:
075: System.arraycopy(array, offset, rval[k]._data, 0,
076: length);
077: if (length != _block_size) {
078: Arrays.fill(rval[k]._data, length, _block_size,
079: _default_fill);
080: }
081: } else {
082: Arrays.fill(rval[k]._data, _default_fill);
083: }
084: offset += _block_size;
085: }
086: return rval;
087: }
088:
089: /**
090: * fill out a List of SmallDocumentBlocks so that it fully occupies
091: * a set of big blocks
092: *
093: * @param blocks the List to be filled out
094: *
095: * @return number of big blocks the list encompasses
096: */
097:
098: public static int fill(final List blocks) {
099: int count = blocks.size();
100: int big_block_count = (count + _blocks_per_big_block - 1)
101: / _blocks_per_big_block;
102: int full_count = big_block_count * _blocks_per_big_block;
103:
104: for (; count < full_count; count++) {
105: blocks.add(makeEmptySmallDocumentBlock());
106: }
107: return big_block_count;
108: }
109:
110: /**
111: * Factory for creating SmallDocumentBlocks from DocumentBlocks
112: *
113: * @param store the original DocumentBlocks
114: * @param size the total document size
115: *
116: * @return an array of new SmallDocumentBlocks instances
117: *
118: * @exception IOException on errors reading from the DocumentBlocks
119: * @exception ArrayIndexOutOfBoundsException if, somehow, the store
120: * contains less data than size indicates
121: */
122:
123: public static SmallDocumentBlock[] convert(
124: final BlockWritable[] store, final int size)
125: throws IOException, ArrayIndexOutOfBoundsException {
126: ByteArrayOutputStream stream = new ByteArrayOutputStream();
127:
128: for (int j = 0; j < store.length; j++) {
129: store[j].writeBlocks(stream);
130: }
131: byte[] data = stream.toByteArray();
132: SmallDocumentBlock[] rval = new SmallDocumentBlock[convertToBlockCount(size)];
133:
134: for (int index = 0; index < rval.length; index++) {
135: rval[index] = new SmallDocumentBlock(data, index);
136: }
137: return rval;
138: }
139:
140: /**
141: * create a list of SmallDocumentBlock's from raw data
142: *
143: * @param blocks the raw data containing the SmallDocumentBlock
144: * data
145: *
146: * @return a List of SmallDocumentBlock's extracted from the input
147: *
148: * @exception IOException
149: */
150:
151: public static List extract(ListManagedBlock[] blocks)
152: throws IOException {
153: List sdbs = new ArrayList();
154:
155: for (int j = 0; j < blocks.length; j++) {
156: byte[] data = blocks[j].getData();
157:
158: for (int k = 0; k < _blocks_per_big_block; k++) {
159: sdbs.add(new SmallDocumentBlock(data, k));
160: }
161: }
162: return sdbs;
163: }
164:
165: /**
166: * read data from an array of SmallDocumentBlocks
167: *
168: * @param blocks the blocks to read from
169: * @param buffer the buffer to write the data into
170: * @param offset the offset into the array of blocks to read from
171: */
172:
173: public static void read(final BlockWritable[] blocks,
174: final byte[] buffer, final int offset) {
175: int firstBlockIndex = offset / _block_size;
176: int firstBlockOffset = offset % _block_size;
177: int lastBlockIndex = (offset + buffer.length - 1) / _block_size;
178:
179: if (firstBlockIndex == lastBlockIndex) {
180: System
181: .arraycopy(
182: ((SmallDocumentBlock) blocks[firstBlockIndex])._data,
183: firstBlockOffset, buffer, 0, buffer.length);
184: } else {
185: int buffer_offset = 0;
186:
187: System
188: .arraycopy(
189: ((SmallDocumentBlock) blocks[firstBlockIndex])._data,
190: firstBlockOffset, buffer, buffer_offset,
191: _block_size - firstBlockOffset);
192: buffer_offset += _block_size - firstBlockOffset;
193: for (int j = firstBlockIndex + 1; j < lastBlockIndex; j++) {
194: System.arraycopy(
195: ((SmallDocumentBlock) blocks[j])._data, 0,
196: buffer, buffer_offset, _block_size);
197: buffer_offset += _block_size;
198: }
199: System
200: .arraycopy(
201: ((SmallDocumentBlock) blocks[lastBlockIndex])._data,
202: 0, buffer, buffer_offset, buffer.length
203: - buffer_offset);
204: }
205: }
206:
207: /**
208: * Calculate the storage size of a set of SmallDocumentBlocks
209: *
210: * @param size number of SmallDocumentBlocks
211: *
212: * @return total size
213: */
214:
215: public static int calcSize(int size) {
216: return size * _block_size;
217: }
218:
219: private static SmallDocumentBlock makeEmptySmallDocumentBlock() {
220: SmallDocumentBlock block = new SmallDocumentBlock();
221:
222: Arrays.fill(block._data, _default_fill);
223: return block;
224: }
225:
226: private static int convertToBlockCount(final int size) {
227: return (size + _block_size - 1) / _block_size;
228: }
229:
230: /* ********** START implementation of BlockWritable ********** */
231:
232: /**
233: * Write the storage to an OutputStream
234: *
235: * @param stream the OutputStream to which the stored data should
236: * be written
237: *
238: * @exception IOException on problems writing to the specified
239: * stream
240: */
241:
242: public void writeBlocks(final OutputStream stream)
243: throws IOException {
244: stream.write(_data);
245: }
246:
247: /* ********** END implementation of BlockWritable ********** */
248: /* ********** START implementation of ListManagedBlock ********** */
249:
250: /**
251: * Get the data from the block
252: *
253: * @return the block's data as a byte array
254: *
255: * @exception IOException if there is no data
256: */
257:
258: public byte[] getData() throws IOException {
259: return _data;
260: }
261:
262: /* ********** END implementation of ListManagedBlock ********** */
263: } // end public class SmallDocumentBlock
|