/* ====================================================================
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.
==================================================================== */
/* ================================================================
* About NPOI
* Author: Tony Qu
* Author's email: tonyqus (at) gmail.com
* Author's Blog: tonyqus.wordpress.com.cn (wp.tonyqus.cn)
* HomePage: http://www.codeplex.com/npoi
* Contributors:
*
* ==============================================================*/
using System;
using System.Collections;
using System.Text;
using System.IO;
using HH.WMS.Utils.NPOI.POIFS.Common;
using System.Collections.Generic;
namespace HH.WMS.Utils.NPOI.POIFS.Storage
{
///
/// Storage for documents that are too small to use regular
/// DocumentBlocks for their data
/// @author Marc Johnson (mjohnson at apache dot org)
///
public class SmallDocumentBlock : BlockWritable, ListManagedBlock
{
private static int BLOCK_SHIFT = 6;
private byte[] _data;
private static byte _default_fill = ( byte ) 0xff;
private static int _block_size = 1 << BLOCK_SHIFT;
private static int BLOCK_MASK = _block_size - 1;
private static int _blocks_per_big_block =
POIFSConstants.BIG_BLOCK_SIZE / _block_size;
private POIFSBigBlockSize _bigBlockSize;
public SmallDocumentBlock(POIFSBigBlockSize bigBlockSize, byte[] data, int index)
{
_bigBlockSize = bigBlockSize;
_blocks_per_big_block = GetBlocksPerBigBlock(bigBlockSize);
_data = new byte[_block_size];
System.Array.Copy(data, index*_block_size, _data, 0, _block_size);
}
public SmallDocumentBlock(POIFSBigBlockSize bigBlockSize)
{
_bigBlockSize = bigBlockSize;
_blocks_per_big_block = GetBlocksPerBigBlock(bigBlockSize);
_data = new byte[_block_size];
}
private static int GetBlocksPerBigBlock(POIFSBigBlockSize bigBlockSize)
{
return bigBlockSize.GetBigBlockSize()/_block_size;
}
///
/// convert a single long array into an array of SmallDocumentBlock
/// instances
///
/// the byte array to be converted
/// the intended size of the array (which may be smaller)
/// an array of SmallDocumentBlock instances, filled from
/// the array
public static SmallDocumentBlock [] Convert(POIFSBigBlockSize bigBlockSize,
byte [] array,
int size)
{
SmallDocumentBlock[] rval = new SmallDocumentBlock[ (size + _block_size - 1) / _block_size ];
int offset = 0;
for (int k = 0; k < rval.Length; k++)
{
rval[ k ] = new SmallDocumentBlock(bigBlockSize);
if (offset < array.Length)
{
int length = Math.Min(_block_size, array.Length - offset);
Array.Copy(array, offset, rval[ k ]._data, 0, length);
if (length != _block_size)
{
for (int i = length; i < _block_size; i++)
rval[k]._data[i] = _default_fill;
}
}
else
{
for (int j = 0; j < rval[k]._data.Length; j++)
{
rval[k]._data[j] = _default_fill;
}
}
offset += _block_size;
}
return rval;
}
///
/// fill out a List of SmallDocumentBlocks so that it fully occupies
/// a Set of big blocks
///
/// the List to be filled out.
/// number of big blocks the list encompasses
public static int Fill(POIFSBigBlockSize bigBlockSize,IList blocks)
{
int _blocks_per_big_block = GetBlocksPerBigBlock(bigBlockSize);
int count = blocks.Count;
int big_block_count = (count + _blocks_per_big_block - 1) / _blocks_per_big_block;
int full_count = big_block_count * _blocks_per_big_block;
for (; count < full_count; count++)
{
blocks.Add(MakeEmptySmallDocumentBlock(bigBlockSize));
}
return big_block_count;
}
///
/// Factory for creating SmallDocumentBlocks from DocumentBlocks
///
/// the original DocumentBlocks
/// the total document size
/// an array of new SmallDocumentBlocks instances
public static SmallDocumentBlock [] Convert(POIFSBigBlockSize bigBlocksSize,
BlockWritable [] store,
int size)
{
using (MemoryStream stream = new MemoryStream())
{
for (int j = 0; j < store.Length; j++)
{
store[j].WriteBlocks(stream);
}
byte[] data = stream.ToArray();
SmallDocumentBlock[] rval = new SmallDocumentBlock[ ConvertToBlockCount(size) ];
for (int index = 0; index < rval.Length; index++)
{
rval[index] = new SmallDocumentBlock(bigBlocksSize,data, index);
}
return rval;
}
}
///
/// create a list of SmallDocumentBlock's from raw data
///
/// the raw data containing the SmallDocumentBlock
/// a List of SmallDocumentBlock's extracted from the input
public static List Extract(POIFSBigBlockSize bigBlockSize, ListManagedBlock [] blocks)
{
int _blocks_per_big_block = GetBlocksPerBigBlock(bigBlockSize);
List sdbs = new List();
for (int j = 0; j < blocks.Length; j++)
{
byte[] data = blocks[ j ].Data;
for (int k = 0; k < _blocks_per_big_block; k++)
{
sdbs.Add(new SmallDocumentBlock(bigBlockSize, data, k));
}
}
return sdbs;
}
///
/// Read data from an array of SmallDocumentBlocks
///
/// the blocks to Read from.
/// the buffer to Write the data into.
/// the offset into the array of blocks to Read from
public static void Read(BlockWritable[] blocks, byte[] buffer, int offset)
{
int firstBlockIndex = offset / _block_size;
int firstBlockOffSet = offset % _block_size;
int lastBlockIndex = (offset + buffer.Length - 1) / _block_size;
if (firstBlockIndex == lastBlockIndex)
{
Array.Copy(
(( SmallDocumentBlock ) blocks[ firstBlockIndex ])._data,
firstBlockOffSet, buffer, 0, buffer.Length);
}
else
{
int buffer_offset = 0;
Array.Copy(
(( SmallDocumentBlock ) blocks[ firstBlockIndex ])._data,
firstBlockOffSet, buffer, buffer_offset,
_block_size - firstBlockOffSet);
buffer_offset += _block_size - firstBlockOffSet;
for (int j = firstBlockIndex + 1; j < lastBlockIndex; j++)
{
Array.Copy((( SmallDocumentBlock ) blocks[ j ])._data,
0, buffer, buffer_offset, _block_size);
buffer_offset += _block_size;
}
Array.Copy(
(( SmallDocumentBlock ) blocks[ lastBlockIndex ])._data, 0,
buffer, buffer_offset, buffer.Length - buffer_offset);
}
}
public static DataInputBlock GetDataInputBlock(SmallDocumentBlock[] blocks, int offset)
{
int firstBlockIndex = offset >> BLOCK_SHIFT;
int firstBlockOffset = offset & BLOCK_MASK;
return new DataInputBlock(blocks[firstBlockIndex]._data, firstBlockOffset);
}
///
/// Calculate the storage size of a Set of SmallDocumentBlocks
///
/// number of SmallDocumentBlocks
/// total size
public static int CalcSize(int size)
{
return size * _block_size;
}
///
/// Makes the empty small document block.
///
///
private static SmallDocumentBlock MakeEmptySmallDocumentBlock(POIFSBigBlockSize bigBlockSize)
{
SmallDocumentBlock block = new SmallDocumentBlock(bigBlockSize);
for (int i = 0; i < block._data.Length; i++)
{
block._data[i] = _default_fill;
}
return block;
}
///
/// Converts to block count.
///
/// The size.
///
private static int ConvertToBlockCount(int size)
{
return (size + _block_size - 1) / _block_size;
}
///
/// Write the storage to an OutputStream
///
/// the OutputStream to which the stored data should
/// be written
public void WriteBlocks(Stream stream)
{
stream.Write(_data,0,_data.Length);
}
///
/// Get the data from the block
///
/// the block's data as a byte array
public byte [] Data
{
get
{
return _data;
}
}
public POIFSBigBlockSize BigBlockSize
{
get { return _bigBlockSize; }
}
}
}