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;
/**
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();
}
}
* be usable.</p>
*/
protected Property()
- {}
+ { }
*/
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);
+ }
+ }
+
}
--- /dev/null
+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"><klute@rainer-klute.de></a>
+ * @since 2003-08-08
+ * @version $Id$
+ */
+public class ReadingNotSupportedException
+ extends UnsupportedVariantTypeException
+{
+
+ /**
+ * <p>Constructor</p>
+ *
+ * @param variantType
+ * @param value
+ */
+ public ReadingNotSupportedException(long variantType, Object value)
+ {
+ super(variantType, value);
+ }
+
+}
*/
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>
/*
* 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);
+ }
+
+ /* Sort the property list by ascending offsets: */
+ Collections.sort(propertyList);
- if (id == PropertyIDMap.PID_CODEPAGE)
- {
- /* Read the codepage if the property ID is 1. */
+ /* 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;
}
}
- /* 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);
}
/*
+ /**
+ * <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;
+ }
+ }
+
+
+
/**
* <p>Returns the value of the property with the specified ID. If
* the property is not available, <code>null</code> is returned
*/
package org.apache.poi.hpsf;
-import org.apache.poi.util.LittleEndian;
/**
* <p>Reader for specific data types.</p>
*/
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;
- }
-
}
--- /dev/null
+/*
+ * ====================================================================
+ * 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$
+ * @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());
+ }
+ }
+
+
+
+
+
+
+
+}
--- /dev/null
+/*
+ * ====================================================================
+ * 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"><klute@rainer-klute.de></a>
+ * @since 2003-08-05
+ * @version $Id$
+ */
+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;
+ }
+
+}
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);
+ }
/**
*/
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
*/
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";
+ }
+
}
\ No newline at end of file
--- /dev/null
+/*
+ * ====================================================================
+ * 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"><klute@rainer-klute.de></a>
+ * @since 08.08.2003
+ * @version $Id$
+ */
+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;
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+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"><klute@rainer-klute.de></a>
+ * @since 2003-08-08
+ * @version $Id$
+ */
+public class WritingNotSupportedException
+ extends UnsupportedVariantTypeException
+{
+
+ /**
+ * <p>Constructor</p>
+ *
+ * @param variantType
+ * @param value
+ */
+ public WritingNotSupportedException(long variantType, Object value)
+ {
+ super(variantType, value);
+ }
+
+}