All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.sf.mpxj.mpp.FixedData Maven / Gradle / Ivy

Go to download

Library that provides facilities to allow project information to be manipulated in Java and .Net. Supports a range of data formats: Microsoft Project Exchange (MPX), Microsoft Project (MPP,MPT), Microsoft Project Data Interchange (MSPDI XML), Microsoft Project Database (MPD), Planner (XML), Primavera (PM XML, XER, and database), Asta Powerproject (PP, MDB), Asta Easyplan (PP), Phoenix Project Manager (PPX), FastTrack Schedule (FTS), and the Standard Data Exchange Format (SDEF).

There is a newer version: 13.8.0
Show newest version
/*
 * file:       FixedData.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 "FixedData" file entries that are
 * found in a Microsoft Project MPP file. The name "Fixed Data" appears to
 * refer to the fact that the items held in these blocks have a known maximum
 * size, rather than all of the items being identically sized records.
 *
 * Note that this class has package level access only, and is not intended
 * for use outside of this context.
 */
final class FixedData extends MPPComponent
{
   /**
    * This constructor retrieves the data from the input stream. It
    * makes use of the meta data regarding this data block that has
    * already been read in from the MPP file.
    *
    * Note that we actually read in the entire data block in one go.
    * This is due to the fact that MS Project sometimes describes data
    * using offsets that are out of sequence, and items that may overlap.
    * Ideally this data would be read directly from the input stream, but
    * this was problematic, so this less than ideal solution has been
    * adopted.
    *
    * @param meta meta data about the contents of this fixed data block
    * @param is input stream from which the data is read
    * @throws IOException on file read failure
    */
   FixedData(FixedMeta meta, InputStream is)
      throws IOException
   {
      this(meta, is, 0);
   }

   /**
    * This version of the above constructor allows us to limit the
    * size of blocks we copy where we have an idea of the maximum expected
    * block size. This prevents us from reading ridiculously large amounts
    * of unnecessary data, causing OutOfMemory exceptions.
    *
    * @param meta meta data about the contents of this fixed data block
    * @param is input stream from which the data is read
    * @param maxExpectedSize maximum expected block size
    * @throws IOException on file read failure
    */
   FixedData(FixedMeta meta, InputStream is, int maxExpectedSize)
      throws IOException
   {
      this(meta, is, maxExpectedSize, 0);
   }

   /**
    * This version of the above constructor allows us to limit the
    * size of blocks we copy where we have an idea of the maximum expected
    * block size. This prevents us from reading ridiculously large amounts
    * of unnecessary data, causing OutOfMemory exceptions.
    *
    * This constructor will also use the given minimum size in the case that the
    * meta data block reports a size of 0
    *
    * @param meta meta data about the contents of this fixed data block
    * @param maxExpectedSize maximum expected block size
    * @param minSize minimum size that will be read if size of block is reported as 0.
    * @param is input stream from which the data is read
    * @throws IOException on file read failure
    */
   FixedData(FixedMeta meta, InputStream is, int maxExpectedSize, int minSize)
      throws IOException
   {
      byte[] buffer = new byte[is.available()];
      is.read(buffer);

      int itemCount = meta.getAdjustedItemCount();
      m_array = new Object[itemCount];
      m_offset = new int[itemCount];

      int available;

      for (int loop = 0; loop < itemCount; loop++)
      {
         byte[] metaData = meta.getByteArrayValue(loop);
         int itemOffset = MPPUtility.getInt(metaData, 4);

         if (itemOffset < 0 || itemOffset > buffer.length)
         {
            continue;
         }

         int itemSize;
         if (loop + 1 == itemCount)
         {
            itemSize = buffer.length - itemOffset;
         }
         else
         {
            byte[] nextMetaData = meta.getByteArrayValue(loop + 1);
            int nextItemOffset = MPPUtility.getInt(nextMetaData, 4);
            itemSize = nextItemOffset - itemOffset;
         }

         if (itemSize == 0)
         {
            itemSize = minSize;
         }

         available = buffer.length - itemOffset;

         if (itemSize < 0 || itemSize > available)
         {
            if (maxExpectedSize == 0)
            {
               itemSize = available;
            }
            else
            {
               if (maxExpectedSize < available)
               {
                  itemSize = maxExpectedSize;
               }
               else
               {
                  itemSize = available;
               }
            }
         }

         if (maxExpectedSize != 0 && itemSize > maxExpectedSize)
         {
            itemSize = maxExpectedSize;
         }

         if (itemSize > 0)
         {
            m_array[loop] = MPPUtility.cloneSubArray(buffer, itemOffset, itemSize);
            m_offset[loop] = itemOffset;
         }
      }
   }

   /**
    * This constructor does the same job as the one above, but assumes that
    * the item size reported in the meta information is wrong, and
    * instead uses the supplied item size.
    *
    * @param meta meta data about the contents of this fixed data block
    * @param itemSize expected item size
    * @param is input stream from which the data is read
    * @throws IOException
    */
   FixedData(FixedMeta meta, int itemSize, InputStream is)
      throws IOException
   {
      byte[] buffer = new byte[is.available()];
      is.read(buffer);

      int itemCount = meta.getAdjustedItemCount();
      m_array = new Object[itemCount];
      m_offset = new int[itemCount];

      byte[] metaData;
      int itemOffset;
      int available;

      for (int loop = 0; loop < itemCount; loop++)
      {
         metaData = meta.getByteArrayValue(loop);
         itemOffset = MPPUtility.getInt(metaData, 4);

         if (itemOffset > buffer.length)
         {
            continue;
         }

         available = buffer.length - itemOffset;

         if (itemSize < 0)
         {
            itemSize = available;
         }
         else
         {
            if (itemSize > available)
            {
               itemSize = available;
            }
         }

         m_array[loop] = MPPUtility.cloneSubArray(buffer, itemOffset, itemSize);
         m_offset[loop] = itemOffset;
      }
   }

   /**
    * This constructor is provided to allow the contents of a fixed data
    * block to be read when the size of the items in the data block is
    * fixed and known in advance. This is used in one particular instance
    * where the contents of the meta data block do not appear to be
    * consistent.
    *
    * @param itemSize the size of the data items in the block
    * @param is input stream from which the data is read
    * @throws IOException on file read failure
    */
   FixedData(int itemSize, InputStream is)
      throws IOException
   {
      this(itemSize, is, false);
   }

   /**
    * This constructor is provided to allow the contents of a fixed data
    * block to be read when the size of the items in the data block is
    * fixed and known in advance. This is used in one particular instance
    * where the contents of the meta data block do not appear to be
    * consistent.
    *
    * @param itemSize the size of the data items in the block
    * @param is input stream from which the data is read
    * @param readRemainderBlock read the final block even if it is not full size
    * @throws IOException on file read failure
    */
   FixedData(int itemSize, InputStream is, boolean readRemainderBlock)
      throws IOException
   {
      int offset = 0;
      int itemCount = is.available() / itemSize;
      if (readRemainderBlock == true && is.available() % itemSize != 0)
      {
         ++itemCount;
      }

      m_array = new Object[itemCount];
      m_offset = new int[itemCount];

      for (int loop = 0; loop < itemCount; loop++)
      {
         m_offset[loop] = offset;

         int currentItemSize = itemSize;
         if (readRemainderBlock == true && is.available() < itemSize)
         {
            currentItemSize = is.available();
         }
         m_array[loop] = readByteArray(is, currentItemSize);
         offset += itemSize;
      }
   }

   /**
    * 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);
   }

   /**
    * Accessor method used to retrieve the number of items held in
    * this fixed data block. Note that this item count is made without
    * reference to the meta data associated with this block.
    *
    * @return number of items in the block
    */
   public int getItemCount()
   {
      return (m_array.length);
   }

   /**
    * Returns a flag indicating if the supplied offset is valid for
    * the data in this fixed data block.
    *
    * @param offset offset value
    * @return boolean flag
    */
   public boolean isValidOffset(Integer offset)
   {
      return (offset == null ? false : isValidOffset(offset.intValue()));
   }

   /**
    * Returns a flag indicating if the supplied offset is valid for
    * the data in this fixed data block.
    *
    * @param offset offset value
    * @return boolean flag
    */
   public boolean isValidOffset(int offset)
   {
      return (offset >= 0 && offset < m_array.length);
   }

   /**
    * This method converts an offset value into an array index, which in
    * turn allows the data present in the fixed block to be retrieved. Note
    * that if the requested offset is not found, then this method returns -1.
    *
    * @param offset Offset of the data in the fixed block
    * @return Index of data item within the fixed data block
    */
   public int getIndexFromOffset(int offset)
   {
      int result = -1;

      for (int loop = 0; loop < m_offset.length; loop++)
      {
         if (m_offset[loop] == offset)
         {
            result = loop;
            break;
         }
      }

      return (result);
   }

   /**
    * This method dumps the contents of this FixedData 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 FixedData");
      for (int loop = 0; loop < m_array.length; loop++)
      {
         pw.println("   Data at index: " + loop + " offset: " + m_offset[loop]);
         pw.println("  " + MPPUtility.hexdump((byte[]) m_array[loop], true));
      }
      pw.println("END FixedData");

      pw.println();
      pw.close();
      return (sw.toString());
   }

   /**
    * An array containing all of the items of data held in this block.
    */
   private Object[] m_array;

   /**
    * Array containing offset values for each item in the array.
    */
   private int[] m_offset;

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy