/*
* file: FixedMeta.java
* author: Jon Iles
* copyright: (c) Packwood Software 2002-2003
* date: 03/01/2003
*/
/*
* 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 net.sf.mpxj.mpp;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.io.StringWriter;
/**
* This class is used to represent the "FixedMeta" file entries that are
* found in a Microsoft Project MPP file. These file entries describe the
* structure of the "FixedData" blocks with which they are associated.
* The structure of the Fixed Meta block is not currently fully understood.
*
* Note that this class has package level access only, and is not intended
* for use outside of this context.
*/
final class FixedMeta extends MPPComponent
{
/**
* Constructor. Reads the meta data from an input stream. Note that
* this version of the constructor copes with more MSP inconsistencies.
* We already know the block size, so we ignore the item count in the
* block and work it out for ourselves.
*
* @param is input stream from which the meta data is read
* @param itemSize size of each item in the block
* @throws IOException on file read failure
*/
FixedMeta(InputStream is, final int itemSize)
throws IOException
{
this(is, new FixedMetaItemSizeProvider()
{
@Override public int getItemSize(int fileSize, int itemCount)
{
return itemSize;
}
});
}
/**
* Constructor. Supply an item size provider to allow different strategies to be
* used to determine the correct item size.
*
* @param is input stream from which the meta data is read
* @param itemSizeProvider item size provider used to calculate the item size
* @throws IOException
*/
FixedMeta(InputStream is, FixedMetaItemSizeProvider itemSizeProvider)
throws IOException
{
//
// The POI file system guarantees that this is accurate
//
int fileSize = is.available();
//
// First 4 bytes
//
if (readInt(is) != MAGIC)
{
throw new IOException("Bad magic number");
}
readInt(is);
m_itemCount = readInt(is);
readInt(is);
int itemSize = itemSizeProvider.getItemSize(fileSize, m_itemCount);
m_adjustedItemCount = (fileSize - HEADER_SIZE) / itemSize;
m_array = new Object[m_adjustedItemCount];
for (int loop = 0; loop < m_adjustedItemCount; loop++)
{
m_array[loop] = readByteArray(is, itemSize);
}
}
/**
* Constructor, allowing a selection of possible block sizes to be supplied.
*
* @param is input stream
* @param itemSizes list of potential block sizes
*/
FixedMeta(InputStream is, final int... itemSizes)
throws IOException
{
this(is, new FixedMetaItemSizeProvider()
{
@Override public int getItemSize(int fileSize, int itemCount)
{
int itemSize = itemSizes[0];
int available = fileSize - HEADER_SIZE;
int distance = Integer.MIN_VALUE;
for (int index = 0; index < itemSizes.length; index++)
{
int testItemSize = itemSizes[index];
if (available % testItemSize == 0)
{
int testDistance = (itemCount * testItemSize) - available;
if (testDistance <= 0 && testDistance > distance)
{
itemSize = testItemSize;
distance = testDistance;
}
}
}
return itemSize;
}
});
}
/**
* This method retrieves the number of items in the FixedData block, as reported in the block header.
*
* @return number of items in the fixed data block
*/
public int getItemCount()
{
return (m_itemCount);
}
/**
* This method retrieves the number of items in the FixedData block.
* Where we don't trust the number of items reported by the block header
* this value is adjusted based on what we know about the block size
* and the size of the individual items.
*
* @return number of items in the fixed data block
*/
public int getAdjustedItemCount()
{
return (m_adjustedItemCount);
}
/**
* This method retrieves a byte array containing the data at the
* given index in the block. If no data is found at the given index
* this method returns null.
*
* @param index index of the data item to be retrieved
* @return byte array containing the requested data
*/
public byte[] getByteArrayValue(int index)
{
byte[] result = null;
if (index >= 0 && index < m_array.length && m_array[index] != null)
{
result = (byte[]) m_array[index];
}
return (result);
}
/**
* This method dumps the contents of this FixedMeta block as a String.
* Note that this facility is provided as a debugging aid.
*
* @return formatted contents of this block
*/
@Override public String toString()
{
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
pw.println("BEGIN: FixedMeta");
pw.println(" Adjusted Item count: " + m_adjustedItemCount);
for (int loop = 0; loop < m_adjustedItemCount; loop++)
{
pw.println(" Data at index: " + loop);
pw.println(" " + MPPUtility.hexdump((byte[]) m_array[loop], true));
}
pw.println("END: FixedMeta");
pw.println();
pw.close();
return (sw.toString());
}
/**
* Number of items in the data block, as reported in the block header.
*/
private int m_itemCount;
/**
* Number of items in the data block, adjusted based on block size and item size.
*/
private int m_adjustedItemCount;
/**
* Unknown data items relating to each entry in the fixed data block.
*/
private Object[] m_array;
/**
* Constant representing the magic number appearing
* at the start of the block.
*/
private static final int MAGIC = 0xFADFADBA;
/**
* Header size.
*/
private static final int HEADER_SIZE = 16;
}