📄 compoundfile.java3
字号:
/*********************************************************************
*
* Copyright (C) 2002 Andrew Khan
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
***************************************************************************/
package jxl.write.biff;
import java.io.OutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.HashMap;
import common.Assert;
import common.Logger;
import jxl.biff.BaseCompoundFile;
import jxl.biff.IntegerHelper;
import jxl.read.biff.BiffException;
/**
* Writes out a compound file
*
* Header block is -1
* Excel data is e..n (where is the head extension blocks, normally 0 and
* n is at least 8)
* Summary information (8 blocks)
* Document summary (8 blocks)
* BBD is block p..q (where p=e+n+16 and q-p+1 is the number of BBD blocks)
* Property storage block is q+b...r (normally 1 block) (where b is the number
* of BBD blocks)
*/
final class CompoundFile extends BaseCompoundFile
{
/**
* The logger
*/
private static Logger logger = Logger.getLogger(CompoundFile.class);
/**
* The stream to which the jumbled up data is written to
*/
private OutputStream out;
/**
* The organized biff records which form the actual excel data
*/
private byte[] excelData;
/**
* The size of the array
*/
private int size;
/**
* The size the excel data should be in order to comply with the
* general compound file format
*/
private int requiredSize;
/**
* The number of blocks it takes to store the big block depot
*/
private int numBigBlockDepotBlocks;
/**
* The number of blocks it takes to store the small block depot chain
*/
private int numSmallBlockDepotChainBlocks;
/**
* The number of blocks it takes to store the small block depot
*/
private int numSmallBlockDepotBlocks;
/**
* The number of extension blocks required for the header to describe
* the BBD
*/
private int numExtensionBlocks;
/**
* The extension block for the header
*/
private int extensionBlock;
/**
* The number of blocks it takes to store the excel data
*/
private int excelDataBlocks;
/**
* The start block of the root entry
*/
private int rootStartBlock;
/**
* The start block of the excel data
*/
private int excelDataStartBlock;
/**
* The start block of the big block depot
*/
private int bbdStartBlock;
/**
* The number of big blocks required for additional property sets
*/
private int additionalPropertyBlocks;
/**
* The total number of property sets in this compound file
*/
private int numPropertySets;
/**
* The number of blocks required to store the root entry property sets
* and small block depot
*/
private int numRootEntryBlocks;
/**
* The list of additional, non standard property sets names
*/
private ArrayList additionalPropertySets;
/**
* A hash map of the original property sets keyed on name
*/
private HashMap readPropertySets;
/**
* The array of standard property set mappings
*/
private int[] standardPropertySetMappings;
private ReadPropertyStorage rootEntryPropertySet;
/**
* Structure used to store the property set and the data
*/
private static final class ReadPropertyStorage
{
PropertyStorage propertyStorage;
byte[] data;
int number;
ReadPropertyStorage(PropertyStorage ps, byte[] d, int n)
{
propertyStorage = ps;
data = d;
number = n;
}
}
// The following member variables are used across methods when
// writing out the big block depot
/**
* The current position within the bbd. Used when writing out the
* BBD
*/
private int bbdPos;
/**
* The current bbd block
*/
private byte[] bigBlockDepot;
/**
* Constructor
*
* @param l the length of the data
* @param os the output stream to write to
* @param data the excel data
* @param rcf the read compound
*/
public CompoundFile(byte[] data, int l, OutputStream os,
jxl.read.biff.CompoundFile rcf)
throws CopyAdditionalPropertySetsException, IOException
{
super();
size = l;
excelData = data;
readAdditionalPropertySets(rcf);
numRootEntryBlocks = 1;
numPropertySets = 4 +
(additionalPropertySets != null ? additionalPropertySets.size() : 0);
if (additionalPropertySets != null)
{
try
{
rootEntryPropertySet = new ReadPropertyStorage(rcf.getPropertySet(ROOT_ENTRY_NAME), rcf.getStream(ROOT_ENTRY_NAME), 0);
int blocks = rootEntryPropertySet.data.length >= SMALL_BLOCK_THRESHOLD ?
getBigBlocksRequired(rootEntryPropertySet.data.length) :
SMALL_BLOCK_THRESHOLD / BIG_BLOCK_SIZE;
additionalPropertyBlocks += blocks;
}
catch(BiffException e)
{
e.printStackTrace();
}
numRootEntryBlocks += getBigBlocksRequired
(additionalPropertySets.size() * PROPERTY_STORAGE_BLOCK_SIZE);
}
logger.debug("root entry requires " + numRootEntryBlocks + " blocks");
int blocks = getBigBlocksRequired(l);
// First pad the data out so that it fits nicely into a whole number
// of blocks
if (l < SMALL_BLOCK_THRESHOLD)
{
requiredSize = SMALL_BLOCK_THRESHOLD;
}
else
{
requiredSize = blocks * BIG_BLOCK_SIZE;
}
out = os;
// logger.debug("smallBlockDepot requires " + numSmallBlockDepotBlocks + " big blocks");
// Do the calculations
excelDataBlocks = requiredSize/BIG_BLOCK_SIZE;
numBigBlockDepotBlocks = 1;
int blockChainLength = (BIG_BLOCK_SIZE - BIG_BLOCK_DEPOT_BLOCKS_POS)/4;
int startTotalBlocks = excelDataBlocks +
8 + // summary block
8 + // document information
additionalPropertyBlocks +
numRootEntryBlocks;
int totalBlocks = startTotalBlocks + numBigBlockDepotBlocks;
// Calculate the number of BBD blocks needed to hold this info
numBigBlockDepotBlocks = (int) Math.ceil( (double) totalBlocks /
(double) (BIG_BLOCK_SIZE/4));
// Does this affect the total?
totalBlocks = startTotalBlocks + numBigBlockDepotBlocks;
// And recalculate
numBigBlockDepotBlocks = (int) Math.ceil( (double) totalBlocks /
(double) (BIG_BLOCK_SIZE/4));
// Does this affect the total?
totalBlocks = startTotalBlocks + numBigBlockDepotBlocks;
// See if the excel bbd chain can fit into the header block.
// Remember to allow for the end of chain indicator
if (numBigBlockDepotBlocks > blockChainLength - 1 )
{
// Sod it - we need an extension block. We have to go through
// the whole tiresome calculation again
extensionBlock = 0;
// Compute the number of extension blocks
int bbdBlocksLeft = numBigBlockDepotBlocks - blockChainLength + 1;
numExtensionBlocks = (int) Math.ceil((double) bbdBlocksLeft /
(double) (BIG_BLOCK_SIZE/4 - 1));
// Modify the total number of blocks required and recalculate the
// the number of bbd blocks
totalBlocks = startTotalBlocks +
numExtensionBlocks +
numBigBlockDepotBlocks;
numBigBlockDepotBlocks = (int) Math.ceil( (double) totalBlocks /
(double) (BIG_BLOCK_SIZE/4));
// The final total
totalBlocks = startTotalBlocks +
numExtensionBlocks +
numBigBlockDepotBlocks;
}
else
{
extensionBlock = -2;
numExtensionBlocks = 0;
}
// Set the excel data start block to be after the header (and
// its extensions)
excelDataStartBlock = numExtensionBlocks;
logger.debug("excelDataStartBlock " + excelDataStartBlock);
// Set the bbd start block to be after all the excel data
bbdStartBlock = excelDataStartBlock +
excelDataBlocks +
additionalPropertyBlocks +
16;
logger.debug("bbdStartBlock " + bbdStartBlock);
// Set the root start block to be after all the big block depot blocks
rootStartBlock = bbdStartBlock +
numBigBlockDepotBlocks;
if (totalBlocks != rootStartBlock + numRootEntryBlocks)
{
logger.warn("Root start block and total blocks are inconsistent " +
" generated file may be corrupt");
logger.warn("RootStartBlock " + rootStartBlock + " totalBlocks " + totalBlocks);
}
}
/**
* Reads the additional property sets from the read in compound file
*
* @return the number of blocks needed to store these property sets
*/
private void readAdditionalPropertySets
(jxl.read.biff.CompoundFile readCompoundFile)
throws CopyAdditionalPropertySetsException, IOException
{
if (readCompoundFile == null)
{
return;
}
additionalPropertySets = new ArrayList();
readPropertySets = new HashMap();
String[] psnames = readCompoundFile.getPropertySetNames();
int blocksRequired = 0;
standardPropertySetMappings = new int[STANDARD_PROPERTY_SETS.length];
for (int i = 0 ; i < psnames.length ; i++)
{
// Add it to the hash map for later
PropertyStorage ps = readCompoundFile.getPropertySet(psnames[i]);
// If the name is non standard, then retrieve the property set
// information
boolean standard = false;
for (int j = 0 ; j < STANDARD_PROPERTY_SETS.length && !standard ; j++)
{
if (psnames[i].equalsIgnoreCase(STANDARD_PROPERTY_SETS[j]))
{
standard = true;
ReadPropertyStorage rps = new ReadPropertyStorage(ps, null, i);
readPropertySets.put(psnames[i], rps);
}
}
if (!standard)
{
try
{
byte[] data = null;
if (ps.size > 0 )
{
data = readCompoundFile.getStream(ps.name);
}
else
{
data = new byte[0];
}
ReadPropertyStorage rps = new ReadPropertyStorage(ps, data, i);
readPropertySets.put(psnames[i], rps);
additionalPropertySets.add(rps);
int blocks = data.length >= SMALL_BLOCK_THRESHOLD ?
getBigBlocksRequired(data.length) :
SMALL_BLOCK_THRESHOLD / BIG_BLOCK_SIZE;
blocksRequired += blocks;
}
catch (BiffException e)
{
logger.error(e);
throw new CopyAdditionalPropertySetsException();
}
}
}
additionalPropertyBlocks = blocksRequired;
}
/**
* Writes out the excel file in OLE compound file format
*
* @exception IOException
*/
public void write() throws IOException
{
writeHeader();
writeExcelData();
writeDocumentSummaryData();
writeSummaryData();
writeAdditionalPropertySets();
writeBigBlockDepot();
writePropertySets();
// Don't flush or close the stream - this is handled by the enclosing File
// object
}
/**
* Writes out any additional property sets
*/
private void writeAdditionalPropertySets() throws IOException
{
if (additionalPropertySets == null)
{
return;
}
logger.debug("Writing property set " + rootEntryPropertySet.propertyStorage.name);
int numBlocks2 = rootEntryPropertySet.data.length >= SMALL_BLOCK_THRESHOLD ?
getBigBlocksRequired(rootEntryPropertySet.data.length) :
SMALL_BLOCK_THRESHOLD / BIG_BLOCK_SIZE;
int requiredSize2 = numBlocks2 * BIG_BLOCK_SIZE;
out.write(rootEntryPropertySet.data, 0, rootEntryPropertySet.data.length);
byte[] padding2 = new byte[requiredSize2 - rootEntryPropertySet.data.length];
out.write(padding2, 0, padding2.length);
logger.debug("data length " + rootEntryPropertySet.data.length + " Padding " + padding2.length);
for (Iterator i = additionalPropertySets.iterator(); i.hasNext() ;)
{
ReadPropertyStorage rps = (ReadPropertyStorage) i.next();
byte[] data = rps.data;
logger.debug("Writing property set " + rps.propertyStorage.name);
int numBlocks = data.length >= SMALL_BLOCK_THRESHOLD ?
getBigBlocksRequired(data.length) :
SMALL_BLOCK_THRESHOLD / BIG_BLOCK_SIZE;
int requiredSize = numBlocks * BIG_BLOCK_SIZE;
out.write(data, 0, data.length);
byte[] padding = new byte[requiredSize - data.length];
out.write(padding, 0, padding.length);
}
}
/**
* Writes out the excel data, padding it out with empty bytes as
* necessary
* Also write out empty
*
* @exception IOException
*/
private void writeExcelData() throws IOException
{
logger.debug("num excel data blocks " + excelDataBlocks + " excelData size " + requiredSize);
out.write(excelData, 0, size);
byte[] padding = new byte[requiredSize - size];
out.write(padding);
}
/**
* Write out the document summary data. This is just blank
*
* @exception IOException
*/
private void writeDocumentSummaryData() throws IOException
{
byte[] padding = new byte[SMALL_BLOCK_THRESHOLD];
// Write out the summary information
out.write(padding);
}
/**
* Write out the summary data. This is just blank
*
* @exception IOException
*/
private void writeSummaryData() throws IOException
{
byte[] padding = new byte[SMALL_BLOCK_THRESHOLD];
// Write out the summary information
out.write(padding);
}
/**
* Writes the compound file header
*
* @exception IOException
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -