You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@poi.apache.org by kl...@apache.org on 2003/08/23 17:12:22 UTC

cvs commit: jakarta-poi/src/java/org/apache/poi/hpsf ReadingNotSupportedException.java TypeWriter.java UnsupportedVariantTypeException.java VariantSupport.java WritingNotSupportedException.java Property.java Section.java TypeReader.java Util.java Variant.java

klute       2003/08/23 08:12:22

  Modified:    src/java/org/apache/poi/hpsf Property.java Section.java
                        TypeReader.java Util.java Variant.java
  Added:       src/java/org/apache/poi/hpsf
                        ReadingNotSupportedException.java TypeWriter.java
                        UnsupportedVariantTypeException.java
                        VariantSupport.java
                        WritingNotSupportedException.java
  Log:
  - Fixed a bug that occured when reading a section with properties that are not stored with ascending offsets in the property list.
  - An error message is printed to standard error now when an unsupported variant type occurs. The data bytes are still available.
  
  Revision  Changes    Path
  1.13      +58 -8     jakarta-poi/src/java/org/apache/poi/hpsf/Property.java
  
  Index: Property.java
  ===================================================================
  RCS file: /home/cvs/jakarta-poi/src/java/org/apache/poi/hpsf/Property.java,v
  retrieving revision 1.12
  retrieving revision 1.13
  diff -u -r1.12 -r1.13
  --- Property.java	3 Aug 2003 20:16:46 -0000	1.12
  +++ Property.java	23 Aug 2003 15:12:22 -0000	1.13
  @@ -63,7 +63,10 @@
   package org.apache.poi.hpsf;
   
   import java.util.HashMap;
  +import java.util.LinkedList;
  +import java.util.List;
   import java.util.Map;
  +
   import org.apache.poi.util.LittleEndian;
   
   /**
  @@ -180,12 +183,12 @@
   
           try
           {
  -            value = TypeReader.read(src, o, length, (int) type);
  +            value = VariantSupport.read(src, o, length, (int) type);
           }
  -        catch (Throwable t)
  +        catch (UnsupportedVariantTypeException ex)
           {
  -            t.printStackTrace();
  -            value = "*** null ***";
  +            logUnsupported(ex);
  +            value = ex.getValue();
           }
       }
   
  @@ -196,7 +199,7 @@
        * be usable.</p>
        */
       protected Property()
  -    {}
  +    { }
   
   
   
  @@ -281,14 +284,61 @@
        */
       protected int getSize()
       {
  -        throw new UnsupportedOperationException("FIXME: Not yet implemented.");
  +        int length = LittleEndian.INT_SIZE;
  +        final int PADDING = 4; /* Pad to multiples of 4. */
  +        if (type > Integer.MAX_VALUE)
  +            throw new HPSFRuntimeException
  +                ("Variant type " + type + " is greater than " +
  +                Integer.MAX_VALUE + ".");
  +        switch ((int) type)
  +        {
  +            case Variant.VT_LPSTR:
  +            {
  +                int l = ((String) value).length() + 1;
  +                int r = l % PADDING;
  +                if (r > 0)
  +                    l += PADDING - r;
  +                length += l;
  +                break;
  +            }
  +            case Variant.VT_EMPTY:
  +                break;
  +            default:
  +                throw new HPSFRuntimeException
  +                    ("Writing is not yet implemented for variant type " +
  +                     type + ". Please report this problem to the POI team!");
  +        }
  +        return length;
       }
   
   
   
  -    public boolean equals(Object o)
  +    /**
  +     * @see Object#equals(java.lang.Object)
  +     */
  +    public boolean equals(final Object o)
       {
           throw new UnsupportedOperationException("FIXME: Not yet implemented.");
  +    }
  +
  +
  +
  +    /**
  +     * <p>Keeps a list of those variant types for those an "unsupported" message
  +     * has already been issued.</p>
  +     */
  +    protected static List unsupportedMessage;
  +
  +    private static void logUnsupported(final UnsupportedVariantTypeException ex)
  +    {
  +        if (unsupportedMessage == null)
  +            unsupportedMessage = new LinkedList();
  +        Long vt = new Long(ex.getVariantType());
  +        if (!unsupportedMessage.contains(vt))
  +        {
  +            System.err.println(ex.getMessage());
  +            unsupportedMessage.add(vt);
  +        }
       }
   
   }
  
  
  
  1.13      +107 -52   jakarta-poi/src/java/org/apache/poi/hpsf/Section.java
  
  Index: Section.java
  ===================================================================
  RCS file: /home/cvs/jakarta-poi/src/java/org/apache/poi/hpsf/Section.java,v
  retrieving revision 1.12
  retrieving revision 1.13
  diff -u -r1.12 -r1.13
  --- Section.java	3 Aug 2003 20:16:46 -0000	1.12
  +++ Section.java	23 Aug 2003 15:12:22 -0000	1.13
  @@ -54,10 +54,15 @@
    */
   package org.apache.poi.hpsf;
   
  +import java.util.ArrayList;
  +import java.util.Collections;
  +import java.util.Iterator;
  +import java.util.List;
   import java.util.Map;
  -import org.apache.poi.util.LittleEndian;
  +
   import org.apache.poi.hpsf.wellknown.PropertyIDMap;
   import org.apache.poi.hpsf.wellknown.SectionIDMap;
  +import org.apache.poi.util.LittleEndian;
   
   /**
    * <p>Represents a section in a {@link PropertySet}.</p>
  @@ -227,50 +232,81 @@
   
           /*
            * Read the properties. The offset is positioned at the first
  -         * entry of the property list. The problem is that we have to
  -         * read the property with ID 1 before we read other
  -         * properties, at least before other properties containing
  -         * strings. The reason is that property 1 specifies the
  -         * codepage. If it is 1200, all strings are in Unicode. In
  -         * other words: Before we can read any strings we have to know
  -         * whether they are in Unicode or not. Unfortunately property
  -         * 1 is not guaranteed to be the first in a section.
  +         * entry of the property list. There are two problems:
  +         * 
  +         * 1. For each property we have to find out its length. In the
  +         *    property list we find each property's ID and its offset relative
  +         *    to the section's beginning. Unfortunately the properties in the
  +         *    property list need not to be in ascending order, so it is not
  +         *    possible to calculate the length as
  +         *    (offset of property(i+1) - offset of property(i)). Before we can
  +         *    that we first have to sort the property list by ascending offsets.
  +         * 
  +         * 2. We have to read the property with ID 1 before we read other 
  +         *    properties, at least before other properties containing strings.
  +         *    The reason is that property 1 specifies the codepage. If it is
  +         *    1200, all strings are in Unicode. In other words: Before we can
  +         *    read any strings we have to know whether they are in Unicode or
  +         *    not. Unfortunately property 1 is not guaranteed to be the first in
  +         *    a section.
            *
  -         * The algorithm below reads the properties in two passes: The
  -         * first one looks for property ID 1 and extracts the codepage
  -         * number. The seconds pass reads the other properties.
  +         *    The algorithm below reads the properties in two passes: The first
  +         *    one looks for property ID 1 and extracts the codepage number. The
  +         *    seconds pass reads the other properties.
            */
           properties = new Property[propertyCount];
  -
  -        /* Pass 1: Look for the codepage. */
  -        int codepage = -1;
  +        
  +        /* Pass 1: Read the property list. */
           int pass1Offset = o1;
  +        List propertyList = new ArrayList(propertyCount);
  +        PropertyListEntry ple;
           for (int i = 0; i < properties.length; i++)
           {
  +            ple = new PropertyListEntry();
  +
               /* Read the property ID. */
  -            final int id = (int) LittleEndian.getUInt(src, pass1Offset);
  +            ple.id = (int) LittleEndian.getUInt(src, pass1Offset);
               pass1Offset += LittleEndian.INT_SIZE;
   
               /* Offset from the section's start. */
  -            final int sOffset = (int) LittleEndian.getUInt(src, pass1Offset);
  +            ple.offset = (int) LittleEndian.getUInt(src, pass1Offset);
               pass1Offset += LittleEndian.INT_SIZE;
   
  -            /* Calculate the length of the property. */
  -//            int length;
  -//            if (i == properties.length - 1)
  -//                length = (int) (src.length - this.offset - sOffset);
  -//            else
  -//                length = (int)
  -//                    LittleEndian.getUInt(src, pass1Offset +
  -//                                         LittleEndian.INT_SIZE) - sOffset;
  +            /* Add the entry to the property list. */
  +            propertyList.add(ple);
  +        }
   
  -            if (id == PropertyIDMap.PID_CODEPAGE)
  -            {
  -                /* Read the codepage if the property ID is 1. */
  +        /* Sort the property list by ascending offsets: */
  +        Collections.sort(propertyList);
   
  +        /* Calculate the properties' lengths. */
  +        for (int i = 0; i < propertyCount - 1; i++)
  +        {
  +            final PropertyListEntry ple1 =
  +                (PropertyListEntry) propertyList.get(i);
  +            final PropertyListEntry ple2 =
  +                (PropertyListEntry) propertyList.get(i + 1);
  +            ple1.length = ple2.offset - ple1.offset;
  +        }
  +        if (propertyCount > 0)
  +        {
  +            ple = (PropertyListEntry) propertyList.get(propertyCount - 1);
  +            ple.length = size - ple.offset;
  +        }
  +
  +        /* Look for the codepage. */
  +        int codepage = -1;
  +        for (final Iterator i = propertyList.iterator();
  +             codepage == -1 && i.hasNext();)
  +        {
  +            ple = (PropertyListEntry) i.next();
  +
  +            /* Read the codepage if the property ID is 1. */
  +            if (ple.id == PropertyIDMap.PID_CODEPAGE)
  +            {
                   /* Read the property's value type. It must be
                    * VT_I2. */
  -                int o = (int) (this.offset + sOffset);
  +                int o = (int) (this.offset + ple.offset);
                   final long type = LittleEndian.getUInt(src, o);
                   o += LittleEndian.INT_SIZE;
   
  @@ -284,35 +320,54 @@
               }
           }
   
  -        /* Pass 2: Read all properties, including 1. */
  -        for (int i = 0; i < properties.length; i++)
  +        /* Pass 2: Read all properties - including the codepage property,
  +         * if available. */
  +        int i1 = 0;
  +        for (final Iterator i = propertyList.iterator(); i.hasNext();)
           {
  -            /* Read the property ID. */
  -            final int id = (int) LittleEndian.getUInt(src, o1);
  -            o1 += LittleEndian.INT_SIZE;
  -
  -            /* Offset from the section. */
  -            final int sOffset = (int) LittleEndian.getUInt(src, o1);
  -            o1 += LittleEndian.INT_SIZE;
  -
  -            /* Calculate the length of the property. */
  -            int length;
  -            if (i == properties.length - 1)
  -                length = (int) (src.length - this.offset - sOffset);
  -            else
  -                length = (int)
  -                    LittleEndian.getUInt(src, o1 + LittleEndian.INT_SIZE) -
  -                    sOffset;
  -
  -            /* Create it. */
  -            properties[i] = new Property(id, src, this.offset + sOffset,
  -                                         length, codepage);
  +            ple = (PropertyListEntry) i.next();
  +            properties[i1++] = new Property(ple.id, src,
  +                                            this.offset + ple.offset,
  +                                            ple.length, codepage);
           }
   
           /*
            * Extract the dictionary (if available).
            */
           dictionary = (Map) getProperty(0);
  +    }
  +
  +
  +
  +    /**
  +     * <p>Represents an entry in the property list and holds a property's ID and
  +     * its offset from the section's beginning.</p>
  +     */
  +    class PropertyListEntry implements Comparable
  +    {
  +        int id;
  +        int offset;
  +        int length;
  +
  +        /**
  +         * <p>Compares this {@link PropertyListEntry} with another one by their
  +         * offsets. A {@link PropertyListEntry} is "smaller" than another one if
  +         * its offset from the section's begin is smaller.</p>
  +         *
  +         * @see Comparable#compareTo(java.lang.Object)
  +         */
  +        public int compareTo(final Object o)
  +        {
  +            if (!(o instanceof PropertyListEntry))
  +                throw new ClassCastException(o.toString());
  +            final int otherOffset = ((PropertyListEntry) o).offset;
  +            if (offset < otherOffset)
  +                return -1;
  +            else if (offset == otherOffset)
  +                return 0;
  +            else
  +                return 1;
  +        }
       }
   
   
  
  
  
  1.5       +1 -143    jakarta-poi/src/java/org/apache/poi/hpsf/TypeReader.java
  
  Index: TypeReader.java
  ===================================================================
  RCS file: /home/cvs/jakarta-poi/src/java/org/apache/poi/hpsf/TypeReader.java,v
  retrieving revision 1.4
  retrieving revision 1.5
  diff -u -r1.4 -r1.5
  --- TypeReader.java	26 Jul 2003 21:14:39 -0000	1.4
  +++ TypeReader.java	23 Aug 2003 15:12:22 -0000	1.5
  @@ -62,7 +62,6 @@
    */
   package org.apache.poi.hpsf;
   
  -import org.apache.poi.util.LittleEndian;
   
   /**
    * <p>Reader for specific data types.</p>
  @@ -75,145 +74,4 @@
    */
   public class TypeReader
   {
  -
  -    /**
  -     * <p>Reads a variant data type from a byte array.</p>
  -     *
  -     * @param src The byte array
  -     * @param offset The offset in the byte array where the variant
  -     * starts
  -     * @param length The length of the variant including the variant
  -     * type field
  -     * @param type The variant type to read
  -     * @return A Java object that corresponds best to the variant
  -     * field. For example, a VT_I4 is returned as a {@link Long}, a
  -     * VT_LPSTR as a {@link String}.
  -     *
  -     * @see Variant
  -     */
  -    public static Object read(final byte[] src, final int offset,
  -                              final int length, final int type)
  -    {
  -        /*
  -         * FIXME: Support reading more types and clean up this code!
  -         */
  -        Object value;
  -        int o1 = offset;
  -        int l1 = length - LittleEndian.INT_SIZE;
  -        switch (type)
  -        {
  -            case Variant.VT_EMPTY:
  -            {
  -                value = null;
  -                break;
  -            }
  -            case Variant.VT_I2:
  -            {
  -                /*
  -                 * Read a short. In Java it is represented as an
  -                 * Integer object.
  -                 */
  -                value = new Integer(LittleEndian.getUShort(src, o1));
  -                break;
  -            }
  -            case Variant.VT_I4:
  -            {
  -                /*
  -                 * Read a word. In Java it is represented as a
  -                 * Long object.
  -                 */
  -                value = new Long(LittleEndian.getUInt(src, o1));
  -                break;
  -            }
  -            case Variant.VT_FILETIME:
  -            {
  -                /*
  -                 * Read a FILETIME object. In Java it is represented
  -                 * as a Date object.
  -                 */
  -                final long low = LittleEndian.getUInt(src, o1);
  -                o1 += LittleEndian.INT_SIZE;
  -                final long high = LittleEndian.getUInt(src, o1);
  -                value = Util.filetimeToDate((int) high, (int) low);
  -                break;
  -            }
  -            case Variant.VT_LPSTR:
  -            {
  -                /*
  -                 * Read a byte string. In Java it is represented as a
  -                 * String object. The 0x00 bytes at the end must be
  -                 * stripped.
  -                 *
  -                 * FIXME: Reading an 8-bit string should pay attention
  -                 * to the codepage. Currently the byte making out the
  -                 * property's value are interpreted according to the
  -                 * platform's default character set.
  -                 */
  -                final int first = o1 + LittleEndian.INT_SIZE;
  -                long last = first + LittleEndian.getUInt(src, o1) - 1;
  -                o1 += LittleEndian.INT_SIZE;
  -                while (src[(int) last] == 0 && first <= last)
  -                    last--;
  -                value = new String(src, (int) first, (int) (last - first + 1));
  -                break;
  -            }
  -            case Variant.VT_LPWSTR:
  -            {
  -                /*
  -                 * Read a Unicode string. In Java it is represented as
  -                 * a String object. The 0x00 bytes at the end must be
  -                 * stripped.
  -                 */
  -                final int first = o1 + LittleEndian.INT_SIZE;
  -                long last = first + LittleEndian.getUInt(src, o1) - 1;
  -                long l = last - first;
  -                o1 += LittleEndian.INT_SIZE;
  -                StringBuffer b = new StringBuffer((int) (last - first));
  -                for (int i = 0; i <= l; i++)
  -                {
  -                    final int i1 = o1 + (i * 2);
  -                    final int i2 = i1 + 1;
  -                    b.append((char) ((src[i2] << 8) + src[i1]));
  -                }
  -                /* Strip 0x00 characters from the end of the string: */
  -                while (b.charAt(b.length() - 1) == 0x00)
  -                    b.setLength(b.length() - 1);
  -                value = b.toString();
  -                break;
  -            }
  -            case Variant.VT_CF:
  -            {
  -                final byte[] v = new byte[l1];
  -                for (int i = 0; i < l1; i++)
  -                    v[i] = src[(int) (o1 + i)];
  -                value = v;
  -                break;
  -            }
  -            case Variant.VT_BOOL:
  -            {
  -                /*
  -                 * The first four bytes in src, from src[offset] to
  -                 * src[offset + 3] contain the DWord for VT_BOOL, so
  -                 * skip it, we don't need it.
  -                 */
  -                // final int first = offset + LittleEndian.INT_SIZE;
  -                long bool = LittleEndian.getUInt(src, o1);
  -                if (bool != 0)
  -                    value = new Boolean(true);
  -                else
  -                    value = new Boolean(false);
  -                break;
  -            }
  -            default:
  -            {
  -                final byte[] v = new byte[l1];
  -                for (int i = 0; i < l1; i++)
  -                    v[i] = src[(int) (o1 + i)];
  -                value = v;
  -                break;
  -            }
  -        }
  -        return value;
  -    }
  -
   }
  
  
  
  1.10      +7 -1      jakarta-poi/src/java/org/apache/poi/hpsf/Util.java
  
  Index: Util.java
  ===================================================================
  RCS file: /home/cvs/jakarta-poi/src/java/org/apache/poi/hpsf/Util.java,v
  retrieving revision 1.9
  retrieving revision 1.10
  diff -u -r1.9 -r1.10
  --- Util.java	3 Aug 2003 20:16:46 -0000	1.9
  +++ Util.java	23 Aug 2003 15:12:22 -0000	1.10
  @@ -191,6 +191,12 @@
           return new Date(ms_since_19700101);
       }
   
  +    public static long dateToFileTime(final Date date)
  +    {
  +        long ms_since_19700101 = date.getTime();
  +        long ms_since_16010101 = ms_since_19700101 + EPOCH_DIFF;
  +        return ms_since_16010101 * (1000 * 10);
  +    }
   
   
       /**
  
  
  
  1.8       +60 -1     jakarta-poi/src/java/org/apache/poi/hpsf/Variant.java
  
  Index: Variant.java
  ===================================================================
  RCS file: /home/cvs/jakarta-poi/src/java/org/apache/poi/hpsf/Variant.java,v
  retrieving revision 1.7
  retrieving revision 1.8
  diff -u -r1.7 -r1.8
  --- Variant.java	26 Jul 2003 21:17:17 -0000	1.7
  +++ Variant.java	23 Aug 2003 15:12:22 -0000	1.8
  @@ -54,6 +54,9 @@
    */
   package org.apache.poi.hpsf;
   
  +import java.util.HashMap;
  +import java.util.Map;
  +
   /**
    * <p>The <em>Variant</em> types as defined by Microsoft's COM. I
    * found this information in <a
  @@ -372,5 +375,61 @@
        * <p>FIXME: Document this!</p>
        */
       public static final int VT_TYPEMASK = 0xFFF;
  +
  +
  +
  +    public static final Map m = new HashMap();
  +
  +    static
  +    {
  +        m.put(new Integer(0), "VT_EMPTY");
  +        m.put(new Integer(1), "VT_NULL");
  +        m.put(new Integer(2), "VT_I2");
  +        m.put(new Integer(3), "VT_I4");
  +        m.put(new Integer(4), "VT_R4");
  +        m.put(new Integer(5), "VT_R8");
  +        m.put(new Integer(6), "VT_CY");
  +        m.put(new Integer(7), "VT_DATE");
  +        m.put(new Integer(8), "VT_BSTR");
  +        m.put(new Integer(9), "VT_DISPATCH");
  +        m.put(new Integer(10), "VT_ERROR");
  +        m.put(new Integer(11), "VT_BOOL");
  +        m.put(new Integer(12), "VT_VARIANT");
  +        m.put(new Integer(13), "VT_UNKNOWN");
  +        m.put(new Integer(14), "VT_DECIMAL");
  +        m.put(new Integer(16), "VT_I1");
  +        m.put(new Integer(17), "VT_UI1");
  +        m.put(new Integer(18), "VT_UI2");
  +        m.put(new Integer(19), "VT_UI4");
  +        m.put(new Integer(20), "VT_I8");
  +        m.put(new Integer(21), "VT_UI8");
  +        m.put(new Integer(22), "VT_INT");
  +        m.put(new Integer(23), "VT_UINT");
  +        m.put(new Integer(24), "VT_VOID");
  +        m.put(new Integer(25), "VT_HRESULT");
  +        m.put(new Integer(26), "VT_PTR");
  +        m.put(new Integer(27), "VT_SAFEARRAY");
  +        m.put(new Integer(28), "VT_CARRAY");
  +        m.put(new Integer(29), "VT_USERDEFINED");
  +        m.put(new Integer(30), "VT_LPSTR");
  +        m.put(new Integer(31), "VT_LPWSTR");
  +        m.put(new Integer(64), "VT_FILETIME");
  +        m.put(new Integer(65), "VT_BLOB");
  +        m.put(new Integer(66), "VT_STREAM");
  +        m.put(new Integer(67), "VT_STORAGE");
  +        m.put(new Integer(68), "VT_STREAMED_OBJECT");
  +        m.put(new Integer(69), "VT_STORED_OBJECT");
  +        m.put(new Integer(70), "VT_BLOB_OBJECT");
  +        m.put(new Integer(71), "VT_CF");
  +        m.put(new Integer(72), "VT_CLSID");
  +    }
  +
  +
  +
  +    public static String getVariantName(final long variantType)
  +    {
  +        String name = (String) m.get(new Integer((int) variantType));
  +        return name != null ? name : "unknown variant type";
  +    }
   
   }
  
  
  
  1.1                  jakarta-poi/src/java/org/apache/poi/hpsf/ReadingNotSupportedException.java
  
  Index: ReadingNotSupportedException.java
  ===================================================================
  package org.apache.poi.hpsf;
  
  /**
   * <p>This exception is thrown when trying to read a (yet) unsupported variant
   * type.</p>
   *
   * @author Rainer Klute <a
   * href="mailto:klute@rainer-klute.de">&lt;klute@rainer-klute.de&gt;</a>
   * @since 2003-08-08
   * @version $Id: ReadingNotSupportedException.java,v 1.1 2003/08/23 15:12:22 klute Exp $
   */
  public class ReadingNotSupportedException
      extends UnsupportedVariantTypeException
  {
  
      /**
       * <p>Constructor</p>
       * 
       * @param variantType
       * @param value
       */
      public ReadingNotSupportedException(long variantType, Object value)
      {
          super(variantType, value);
      }
  
  }
  
  
  
  1.1                  jakarta-poi/src/java/org/apache/poi/hpsf/TypeWriter.java
  
  Index: TypeWriter.java
  ===================================================================
  /*
   *  ====================================================================
   *  The Apache Software License, Version 1.1
   *
   *  Copyright (c) 2003 The Apache Software Foundation.  All rights
   *  reserved.
   *
   *  Redistribution and use in source and binary forms, with or without
   *  modification, are permitted provided that the following conditions
   *  are met:
   *
   *  1. Redistributions of source code must retain the above copyright
   *  notice, this list of conditions and the following disclaimer.
   *
   *  2. Redistributions in binary form must reproduce the above copyright
   *  notice, this list of conditions and the following disclaimer in
   *  the documentation and/or other materials provided with the
   *  distribution.
   *
   *  3. The end-user documentation included with the redistribution,
   *  if any, must include the following acknowledgment:
   *  "This product includes software developed by the
   *  Apache Software Foundation (http://www.apache.org/)."
   *  Alternately, this acknowledgment may appear in the software itself,
   *  if and wherever such third-party acknowledgments normally appear.
   *
   *  4. The names "Apache" and "Apache Software Foundation" must
   *  not be used to endorse or promote products derived from this
   *  software without prior written permission. For written
   *  permission, please contact apache@apache.org.
   *
   *  5. Products derived from this software may not be called "Apache",
   *  nor may "Apache" appear in their name, without prior written
   *  permission of the Apache Software Foundation.
   *
   *  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   *  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   *  OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   *  DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   *  ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   *  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   *  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   *  USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   *  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   *  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   *  OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   *  SUCH DAMAGE.
   *  ====================================================================
   *
   *  This software consists of voluntary contributions made by many
   *  individuals on behalf of the Apache Software Foundation.  For more
   *  information on the Apache Software Foundation, please see
   *  <http://www.apache.org/>.
   *
   *  Portions of this software are based upon public domain software
   *  originally written at the National Center for Supercomputing Applications,
   *  University of Illinois, Urbana-Champaign.
   *
   *  Portions of this software are based upon public domain software
   *  originally written at the National Center for Supercomputing Applications,
   *  University of Illinois, Urbana-Champaign.
   */
  package org.apache.poi.hpsf;
  
  import java.io.IOException;
  import java.io.OutputStream;
  
  import org.apache.poi.util.LittleEndian;
  import org.apache.poi.util.LittleEndianConsts;
  
  /**
   * <p>Class for writing little-endian data and more.</p>
   *
   * @author Rainer Klute (klute@rainer-klute.de)
   * @version $Id: TypeWriter.java,v 1.1 2003/08/23 15:12:22 klute Exp $
   * @since 2003-02-20
   */
  public class TypeWriter
  {
  
      /**
       * <p>Writes a two-byte value (short) to an output stream.</p>
       *
       * @param out The stream to write to
       * @param n The value to write
       * @exception IOException if an I/O error occurs
       */
      public static int writeToStream(final OutputStream out, final short n)
          throws IOException
      {
          final int length = LittleEndian.SHORT_SIZE;
          byte[] buffer = new byte[length];
          LittleEndian.putUShort(buffer, 0, n);
          out.write(buffer, 0, length);
          return length;
      }
  
  
  
      /**
       * <p>Writes a four-byte value to an output stream.</p>
       *
       * @param out The stream to write to.
       * @param n The value to write.
       * @exception IOException if an I/O error occurs
       * @return The number of bytes written to the output stream. 
       */
      public static int writeToStream(final OutputStream out, final int n)
          throws IOException
      {
          final int l = LittleEndian.INT_SIZE;
          final byte[] buffer = new byte[l];
          LittleEndian.putInt(buffer, 0, n);
          out.write(buffer, 0, l);
          return l;
          
      }
  
  
  
      /**
       * <p>Writes an unsigned two-byte value to an output stream.</p>
       *
       * @param out The stream to write to
       * @param n The value to write
       * @exception IOException if an I/O error occurs
       */
      public static void writeUShortToStream(final OutputStream out, final int n)
          throws IOException
      {
          int high = n & 0xFFFF0000;
          if (high != 0)
              throw new IllegalPropertySetDataException
                  ("Value " + n + " cannot be represented by 2 bytes.");
          writeToStream(out, (short) n);
      }
  
  
  
      /**
       * <p>Writes an unsigned four-byte value to an output stream.</p>
       *
       * @param out The stream to write to.
       * @param n The value to write.
       * @return The number of bytes that have been written to the output stream.
       * @exception IOException if an I/O error occurs
       */
      public static int writeUIntToStream(final OutputStream out, final long n)
          throws IOException
      {
          long high = n & 0xFFFFFFFF00000000L;
          if (high != 0)
              throw new IllegalPropertySetDataException
                  ("Value " + n + " cannot be represented by 4 bytes.");
          return writeToStream(out, (int) n);
      }
  
  
  
      /**
       * <p>Writes a 16-byte {@link ClassID} to an output stream.</p>
       *
       * @param out The stream to write to
       * @param n The value to write
       * @exception IOException if an I/O error occurs
       */
      public static int writeToStream(final OutputStream out, final ClassID n)
          throws IOException
      {
          byte[] b = new byte[16];
          n.write(b, 0);
          out.write(b, 0, b.length);
          return b.length;
      }
  
  
  
      /**
       * <p>Writes an array of {@link Property} instances to an output stream
       * according to the Horrible Property Stream Format.</p>
       *
       * @param out The stream to write to
       * @param properties The array to write to the stream
       * @exception IOException if an I/O error occurs
       */
      public static void writeToStream(final OutputStream out,
                                       final Property[] properties)
          throws IOException, UnsupportedVariantTypeException
      {
          /* If there are no properties don't write anything. */
          if (properties == null)
              return;
  
          /* Write the property list. This is a list containing pairs of property
           * ID and offset into the stream. */
          for (int i = 0; i < properties.length; i++)
          {
              final Property p = (Property) properties[i];
              writeUIntToStream(out, p.getID());
              writeUIntToStream(out, p.getSize());
          }
  
          /* Write the properties themselves. */
          for (int i = 0; i < properties.length; i++)
          {
              final Property p = (Property) properties[i];
              long type = p.getType();
              writeUIntToStream(out, type);
              VariantSupport.write(out, (int) type, p.getValue());
          }
      }
  
  
  
  
  
  
  
  }
  
  
  
  1.1                  jakarta-poi/src/java/org/apache/poi/hpsf/UnsupportedVariantTypeException.java
  
  Index: UnsupportedVariantTypeException.java
  ===================================================================
  /*
   *  ====================================================================
   *  The Apache Software License, Version 1.1
   *
   *  Copyright (c) 2000 The Apache Software Foundation.  All rights
   *  reserved.
   *
   *  Redistribution and use in source and binary forms, with or without
   *  modification, are permitted provided that the following conditions
   *  are met:
   *
   *  1. Redistributions of source code must retain the above copyright
   *  notice, this list of conditions and the following disclaimer.
   *
   *  2. Redistributions in binary form must reproduce the above copyright
   *  notice, this list of conditions and the following disclaimer in
   *  the documentation and/or other materials provided with the
   *  distribution.
   *
   *  3. The end-user documentation included with the redistribution,
   *  if any, must include the following acknowledgment:
   *  "This product includes software developed by the
   *  Apache Software Foundation (http://www.apache.org/)."
   *  Alternately, this acknowledgment may appear in the software itself,
   *  if and wherever such third-party acknowledgments normally appear.
   *
   *  4. The names "Apache" and "Apache Software Foundation" must
   *  not be used to endorse or promote products derived from this
   *  software without prior written permission. For written
   *  permission, please contact apache@apache.org.
   *
   *  5. Products derived from this software may not be called "Apache",
   *  nor may "Apache" appear in their name, without prior written
   *  permission of the Apache Software Foundation.
   *
   *  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   *  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   *  OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   *  DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   *  ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   *  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   *  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   *  USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   *  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   *  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   *  OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   *  SUCH DAMAGE.
   *  ====================================================================
   *
   *  This software consists of voluntary contributions made by many
   *  individuals on behalf of the Apache Software Foundation.  For more
   *  information on the Apache Software Foundation, please see
   *  <http://www.apache.org/>.
   */
  package org.apache.poi.hpsf;
  
  import org.apache.poi.util.HexDump;
  
  /**
   * <p>This exception is thrown if HPSF encounters a variant type that isn't
   * supported yet. Although a variant type is unsupported the value can still be
   * retrieved using the {@link #getValue} method.</p>
   * 
   * <p>Obviously this class should disappear some day.</p>
   *
   * @author Rainer Klute <a
   * href="mailto:klute@rainer-klute.de">&lt;klute@rainer-klute.de&gt;</a>
   * @since 2003-08-05
   * @version $Id: UnsupportedVariantTypeException.java,v 1.1 2003/08/23 15:12:22 klute Exp $
   */
  public abstract class UnsupportedVariantTypeException extends HPSFException
  {
  
      private Object value;
  
      private long variantType;
  
  
  
      /**
       * <p>Constructor.</p>
       * 
       * @param variantType The unsupported variant type
       * @param value The value who's variant type is not yet supported
       */
      public UnsupportedVariantTypeException(final long variantType,
                                             final Object value)
      {
          super("HPSF does not yet support the variant type " + variantType + 
                " (" + Variant.getVariantName(variantType) + ", " +
                HexDump.toHex((int) variantType) + "). If you want support for " +
                "this variant type in one of the next POI releases please " +
                "submit a request for enhancement (RFE) to " +
                "<http://nagoya.apache.org/bugzilla/>! Thank you!");
          this.variantType = variantType;
          this.value = value;
      }
  
  
  
      /**
       * <p>Returns the offending variant type.</p>
       *
       * @return the offending variant type.
       */
      public long getVariantType()
      {
          return variantType;
      }
  
  
  
      /**
       * <p>Return the value who's variant type is not yet supported.</p>
       *
       * @return the value who's variant type is not yet supported
       */
      public Object getValue()
      {
          return value;
      }
  
  }
  
  
  
  1.1                  jakarta-poi/src/java/org/apache/poi/hpsf/VariantSupport.java
  
  Index: VariantSupport.java
  ===================================================================
  /*
   *  ====================================================================
   *  The Apache Software License, Version 1.1
   *
   *  Copyright (c) 2000 The Apache Software Foundation.  All rights
   *  reserved.
   *
   *  Redistribution and use in source and binary forms, with or without
   *  modification, are permitted provided that the following conditions
   *  are met:
   *
   *  1. Redistributions of source code must retain the above copyright
   *  notice, this list of conditions and the following disclaimer.
   *
   *  2. Redistributions in binary form must reproduce the above copyright
   *  notice, this list of conditions and the following disclaimer in
   *  the documentation and/or other materials provided with the
   *  distribution.
   *
   *  3. The end-user documentation included with the redistribution,
   *  if any, must include the following acknowledgment:
   *  "This product includes software developed by the
   *  Apache Software Foundation (http://www.apache.org/)."
   *  Alternately, this acknowledgment may appear in the software itself,
   *  if and wherever such third-party acknowledgments normally appear.
   *
   *  4. The names "Apache" and "Apache Software Foundation" must
   *  not be used to endorse or promote products derived from this
   *  software without prior written permission. For written
   *  permission, please contact apache@apache.org.
   *
   *  5. Products derived from this software may not be called "Apache",
   *  nor may "Apache" appear in their name, without prior written
   *  permission of the Apache Software Foundation.
   *
   *  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   *  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   *  OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   *  DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   *  ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   *  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   *  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   *  USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   *  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   *  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   *  OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   *  SUCH DAMAGE.
   *  ====================================================================
   *
   *  This software consists of voluntary contributions made by many
   *  individuals on behalf of the Apache Software Foundation.  For more
   *  information on the Apache Software Foundation, please see
   *  <http://www.apache.org/>.
   *
   *  Portions of this software are based upon public domain software
   *  originally written at the National Center for Supercomputing Applications,
   *  University of Illinois, Urbana-Champaign.
   *
   *  Portions of this software are based upon public domain software
   *  originally written at the National Center for Supercomputing Applications,
   *  University of Illinois, Urbana-Champaign.
   */
  package org.apache.poi.hpsf;
  
  import java.io.IOException;
  import java.io.OutputStream;
  import java.util.Date;
  
  import org.apache.poi.util.LittleEndian;
  import org.apache.poi.util.LittleEndianConsts;
  
  /**
   * <p>Supports reading and writing of variant data.</p>
   * 
   * <p><strong>FIXME:</strong> Reading and writing must be made more uniform than
   * it is now. The following items should be resolved:
   * 
   * <ul>
   *
   * <li><p>Reading requires a length parameter that is 4 byte greater than the
   * actual data, because the variant type field is included. </p></li>
   *
   * <li><p>Reading reads from a byte array while writing writes to an byte array
   * output stream.</p></li>
   *
   * <ul>
   *
   * @author Rainer Klute <a
   * href="mailto:klute@rainer-klute.de">&lt;klute@rainer-klute.de&gt;</a>
   * @since 08.08.2003
   * @version $Id: VariantSupport.java,v 1.1 2003/08/23 15:12:22 klute Exp $
   */
  public class VariantSupport extends Variant
  {
  
      /**
       * <p>Reads a variant data type from a byte array.</p>
       *
       * @param src The byte array
       * @param offset The offset in the byte array where the variant
       * starts
       * @param length The length of the variant including the variant
       * type field
       * @param type The variant type to read
       * @return A Java object that corresponds best to the variant
       * field. For example, a VT_I4 is returned as a {@link Long}, a
       * VT_LPSTR as a {@link String}.
       * @exception UnsupportedVariantTypeException if HPSF does not (yet)
       * support the variant type which is to be read 
       *
       * @see Variant
       */
      public static Object read(final byte[] src, final int offset,
                                final int length, final long type)
          throws ReadingNotSupportedException
      {
          Object value;
          int o1 = offset;
          int l1 = length - LittleEndian.INT_SIZE;
          switch ((int) type)
          {
              case Variant.VT_EMPTY:
              {
                  value = null;
                  break;
              }
              case Variant.VT_I2:
              {
                  /*
                   * Read a short. In Java it is represented as an
                   * Integer object.
                   */
                  value = new Integer(LittleEndian.getUShort(src, o1));
                  break;
              }
              case Variant.VT_I4:
              {
                  /*
                   * Read a word. In Java it is represented as a
                   * Long object.
                   */
                  value = new Long(LittleEndian.getUInt(src, o1));
                  break;
              }
              case Variant.VT_FILETIME:
              {
                  /*
                   * Read a FILETIME object. In Java it is represented
                   * as a Date object.
                   */
                  final long low = LittleEndian.getUInt(src, o1);
                  o1 += LittleEndian.INT_SIZE;
                  final long high = LittleEndian.getUInt(src, o1);
                  value = Util.filetimeToDate((int) high, (int) low);
                  break;
              }
              case Variant.VT_LPSTR:
              {
                  /*
                   * Read a byte string. In Java it is represented as a
                   * String object. The 0x00 bytes at the end must be
                   * stripped.
                   *
                   * FIXME: Reading an 8-bit string should pay attention
                   * to the codepage. Currently the byte making out the
                   * property's value are interpreted according to the
                   * platform's default character set.
                   */
                  final int first = o1 + LittleEndian.INT_SIZE;
                  long last = first + LittleEndian.getUInt(src, o1) - 1;
                  o1 += LittleEndian.INT_SIZE;
                  while (src[(int) last] == 0 && first <= last)
                      last--;
                  value = new String(src, (int) first, (int) (last - first + 1));
                  break;
              }
              case Variant.VT_LPWSTR:
              {
                  /*
                   * Read a Unicode string. In Java it is represented as
                   * a String object. The 0x00 bytes at the end must be
                   * stripped.
                   */
                  final int first = o1 + LittleEndian.INT_SIZE;
                  long last = first + LittleEndian.getUInt(src, o1) - 1;
                  long l = last - first;
                  o1 += LittleEndian.INT_SIZE;
                  StringBuffer b = new StringBuffer((int) (last - first));
                  for (int i = 0; i <= l; i++)
                  {
                      final int i1 = o1 + (i * 2);
                      final int i2 = i1 + 1;
                      final int high = src[i2] << 8;
                      final int low = src[i1] & 0xff;
                      final char c = (char) (high | low);
                      b.append(c);
                  }
                  /* Strip 0x00 characters from the end of the string: */
                  while (b.length() > 0 && b.charAt(b.length() - 1) == 0x00)
                      b.setLength(b.length() - 1);
                  value = b.toString();
                  break;
              }
              case Variant.VT_CF:
              {
                  final byte[] v = new byte[l1];
                  for (int i = 0; i < l1; i++)
                      v[i] = src[(int) (o1 + i)];
                  value = v;
                  break;
              }
              case Variant.VT_BOOL:
              {
                  /*
                   * The first four bytes in src, from src[offset] to
                   * src[offset + 3] contain the DWord for VT_BOOL, so
                   * skip it, we don't need it.
                   */
                  // final int first = offset + LittleEndian.INT_SIZE;
                  long bool = LittleEndian.getUInt(src, o1);
                  if (bool != 0)
                      value = new Boolean(true);
                  else
                      value = new Boolean(false);
                  break;
              }
              default:
              {
                  final byte[] v = new byte[l1];
                  for (int i = 0; i < l1; i++)
                      v[i] = src[(int) (o1 + i)];
                  throw new ReadingNotSupportedException(type, v);
              }
          }
          return value;
      }
  
  
  
      /**
       * <p>Writes a variant value to an output stream.</p>
       *
       * @param out The stream to write the value to.
       * @param type The variant's type.
       * @param value The variant's value.
       * @return The number of entities that have been written. In many cases an
       * "entity" is a byte but this is not always the case.
       */
      public static int write(final OutputStream out, final long type,
                                 final Object value)
          throws IOException, WritingNotSupportedException
      {
          switch ((int) type)
          {
              case Variant.VT_BOOL:
              {
                  int trueOrFalse;
                  int length = 0;
                  if (((Boolean) value).booleanValue())
                      trueOrFalse = 1;
                  else
                      trueOrFalse = 0;
                  length += TypeWriter.writeUIntToStream(out, trueOrFalse);
                  return length;
              }
              case Variant.VT_LPSTR:
              {
                  TypeWriter.writeUIntToStream
                      (out, ((String) value).length() + 1);
                  char[] s = toPaddedCharArray((String) value);
                  /* FIXME: The following line forces characters to bytes. This
                   * is generally wrong and should only be done according to a
                   * codepage. Alternatively Unicode could be written (see 
                   * Variant.VT_LPWSTR). */
                  byte[] b = new byte[s.length];
                  for (int i = 0; i < s.length; i++)
                      b[i] = (byte) s[i];
                  out.write(b);
                  return b.length;
              }
              case Variant.VT_LPWSTR:
              {
                  final int nrOfChars = ((String) value).length() + 1; 
                  TypeWriter.writeUIntToStream(out, nrOfChars);
                  char[] s = toPaddedCharArray((String) value);
                  for (int i = 0; i < s.length; i++)
                  {
                      final int high = (int) ((s[i] & 0xff00) >> 8);
                      final int low = (int) (s[i] & 0x00ff);
                      final byte highb = (byte) high;
                      final byte lowb = (byte) low;
                      out.write(lowb);
                      out.write(highb);
                  }
                  return nrOfChars * 2;
              }
              case Variant.VT_CF:
              {
                  final byte[] b = (byte[]) value; 
                  out.write(b);
                  return b.length;
              }
              case Variant.VT_EMPTY:
              {
                  TypeWriter.writeUIntToStream(out, Variant.VT_EMPTY);
                  return LittleEndianConsts.INT_SIZE;
              }
              case Variant.VT_I2:
              {
                  TypeWriter.writeToStream(out, ((Integer) value).shortValue());
                  return LittleEndianConsts.SHORT_SIZE;
              }
              case Variant.VT_I4:
              {
                  TypeWriter.writeToStream(out, ((Long) value).intValue());
                  return LittleEndianConsts.INT_SIZE;
              }
              case Variant.VT_FILETIME:
              {
                  int length = 0;
                  long filetime = Util.dateToFileTime((Date) value);
                  int high = (int) ((filetime >> 32) & 0xFFFFFFFFL);
                  int low = (int) (filetime & 0x00000000FFFFFFFFL);
                  length += TypeWriter.writeUIntToStream(out, 0x0000000FFFFFFFFL & low);
                  length += TypeWriter.writeUIntToStream(out, 0x0000000FFFFFFFFL & high);
                  return length;
              }
              default:
              {
                  throw new WritingNotSupportedException(type, value);
               }
          }
      }
  
  
  
      /**
       * <p>Converts a string into a 0x00-terminated character sequence padded 
       * with 0x00 bytes to a multiple of 4.</p>
       *
       * @param value The string to convert
       * @return The padded character array
       */
      private static char[] toPaddedCharArray(final String s)
      {
          final int PADDING = 4;
          int dl = s.length() + 1;
          final int r = dl % 4;
          if (r > 0)
              dl += PADDING - r;
          char[] buffer = new char[dl];
          s.getChars(0, s.length(), buffer, 0);
          for (int i = s.length(); i < dl; i++)
              buffer[i] = (char) 0;
          return buffer;
      }
  
  
  
      public static int getLength(final long variantType, final int lengthInBytes)
      {
          switch ((int) variantType)
          {
              case VT_LPWSTR:
                  return lengthInBytes / 2;
              default:
                  return lengthInBytes;
          }
      }
  }
  
  
  1.1                  jakarta-poi/src/java/org/apache/poi/hpsf/WritingNotSupportedException.java
  
  Index: WritingNotSupportedException.java
  ===================================================================
  package org.apache.poi.hpsf;
  
  /**
   * <p>This exception is thrown when trying to write a (yet) unsupported variant
   * type.</p>
   *
   * @author Rainer Klute <a
   * href="mailto:klute@rainer-klute.de">&lt;klute@rainer-klute.de&gt;</a>
   * @since 2003-08-08
   * @version $Id: WritingNotSupportedException.java,v 1.1 2003/08/23 15:12:22 klute Exp $
   */
  public class WritingNotSupportedException
      extends UnsupportedVariantTypeException
  {
  
      /**
       * <p>Constructor</p>
       * 
       * @param variantType
       * @param value
       */
      public WritingNotSupportedException(long variantType, Object value)
      {
          super(variantType, value);
      }
  
  }
  
  
  

---------------------------------------------------------------------
To unsubscribe, e-mail: poi-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: poi-dev-help@jakarta.apache.org