- 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. git-svn-id: https://svn.apache.org/repos/asf/jakarta/poi/trunk@353307 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
f3bb5efecf
commit
4e855eadb1
@ -63,7 +63,10 @@
|
|||||||
package org.apache.poi.hpsf;
|
package org.apache.poi.hpsf;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import org.apache.poi.util.LittleEndian;
|
import org.apache.poi.util.LittleEndian;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -180,12 +183,12 @@ public class Property
|
|||||||
|
|
||||||
try
|
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();
|
logUnsupported(ex);
|
||||||
value = "*** null ***";
|
value = ex.getValue();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -281,14 +284,61 @@ public class Property
|
|||||||
*/
|
*/
|
||||||
protected int getSize()
|
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.");
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,27 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -54,10 +54,15 @@
|
|||||||
*/
|
*/
|
||||||
package org.apache.poi.hpsf;
|
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 java.util.Map;
|
||||||
import org.apache.poi.util.LittleEndian;
|
|
||||||
import org.apache.poi.hpsf.wellknown.PropertyIDMap;
|
import org.apache.poi.hpsf.wellknown.PropertyIDMap;
|
||||||
import org.apache.poi.hpsf.wellknown.SectionIDMap;
|
import org.apache.poi.hpsf.wellknown.SectionIDMap;
|
||||||
|
import org.apache.poi.util.LittleEndian;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Represents a section in a {@link PropertySet}.</p>
|
* <p>Represents a section in a {@link PropertySet}.</p>
|
||||||
@ -227,50 +232,81 @@ public class Section
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Read the properties. The offset is positioned at the first
|
* Read the properties. The offset is positioned at the first
|
||||||
* entry of the property list. The problem is that we have to
|
* entry of the property list. There are two problems:
|
||||||
* 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
|
* 1. For each property we have to find out its length. In the
|
||||||
* first one looks for property ID 1 and extracts the codepage
|
* property list we find each property's ID and its offset relative
|
||||||
* number. The seconds pass reads the other properties.
|
* 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.
|
||||||
*/
|
*/
|
||||||
properties = new Property[propertyCount];
|
properties = new Property[propertyCount];
|
||||||
|
|
||||||
/* Pass 1: Look for the codepage. */
|
/* Pass 1: Read the property list. */
|
||||||
int codepage = -1;
|
|
||||||
int pass1Offset = o1;
|
int pass1Offset = o1;
|
||||||
|
List propertyList = new ArrayList(propertyCount);
|
||||||
|
PropertyListEntry ple;
|
||||||
for (int i = 0; i < properties.length; i++)
|
for (int i = 0; i < properties.length; i++)
|
||||||
{
|
{
|
||||||
|
ple = new PropertyListEntry();
|
||||||
|
|
||||||
/* Read the property ID. */
|
/* Read the property ID. */
|
||||||
final int id = (int) LittleEndian.getUInt(src, pass1Offset);
|
ple.id = (int) LittleEndian.getUInt(src, pass1Offset);
|
||||||
pass1Offset += LittleEndian.INT_SIZE;
|
pass1Offset += LittleEndian.INT_SIZE;
|
||||||
|
|
||||||
/* Offset from the section's start. */
|
/* 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;
|
pass1Offset += LittleEndian.INT_SIZE;
|
||||||
|
|
||||||
/* Calculate the length of the property. */
|
/* Add the entry to the property list. */
|
||||||
// int length;
|
propertyList.add(ple);
|
||||||
// if (i == properties.length - 1)
|
}
|
||||||
// length = (int) (src.length - this.offset - sOffset);
|
|
||||||
// else
|
|
||||||
// length = (int)
|
|
||||||
// LittleEndian.getUInt(src, pass1Offset +
|
|
||||||
// LittleEndian.INT_SIZE) - sOffset;
|
|
||||||
|
|
||||||
if (id == PropertyIDMap.PID_CODEPAGE)
|
/* Sort the property list by ascending offsets: */
|
||||||
|
Collections.sort(propertyList);
|
||||||
|
|
||||||
|
/* Calculate the properties' lengths. */
|
||||||
|
for (int i = 0; i < propertyCount - 1; i++)
|
||||||
{
|
{
|
||||||
/* Read the codepage if the property ID is 1. */
|
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
|
/* Read the property's value type. It must be
|
||||||
* VT_I2. */
|
* VT_I2. */
|
||||||
int o = (int) (this.offset + sOffset);
|
int o = (int) (this.offset + ple.offset);
|
||||||
final long type = LittleEndian.getUInt(src, o);
|
final long type = LittleEndian.getUInt(src, o);
|
||||||
o += LittleEndian.INT_SIZE;
|
o += LittleEndian.INT_SIZE;
|
||||||
|
|
||||||
@ -284,29 +320,15 @@ public class Section
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Pass 2: Read all properties, including 1. */
|
/* Pass 2: Read all properties - including the codepage property,
|
||||||
for (int i = 0; i < properties.length; i++)
|
* if available. */
|
||||||
|
int i1 = 0;
|
||||||
|
for (final Iterator i = propertyList.iterator(); i.hasNext();)
|
||||||
{
|
{
|
||||||
/* Read the property ID. */
|
ple = (PropertyListEntry) i.next();
|
||||||
final int id = (int) LittleEndian.getUInt(src, o1);
|
properties[i1++] = new Property(ple.id, src,
|
||||||
o1 += LittleEndian.INT_SIZE;
|
this.offset + ple.offset,
|
||||||
|
ple.length, codepage);
|
||||||
/* 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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -317,6 +339,39 @@ public class Section
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <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
|
* <p>Returns the value of the property with the specified ID. If
|
||||||
* the property is not available, <code>null</code> is returned
|
* the property is not available, <code>null</code> is returned
|
||||||
|
@ -62,7 +62,6 @@
|
|||||||
*/
|
*/
|
||||||
package org.apache.poi.hpsf;
|
package org.apache.poi.hpsf;
|
||||||
|
|
||||||
import org.apache.poi.util.LittleEndian;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Reader for specific data types.</p>
|
* <p>Reader for specific data types.</p>
|
||||||
@ -75,145 +74,4 @@ import org.apache.poi.util.LittleEndian;
|
|||||||
*/
|
*/
|
||||||
public class TypeReader
|
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
219
src/java/org/apache/poi/hpsf/TypeWriter.java
Normal file
219
src/java/org/apache/poi/hpsf/TypeWriter.java
Normal file
@ -0,0 +1,219 @@
|
|||||||
|
/*
|
||||||
|
* ====================================================================
|
||||||
|
* 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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,123 @@
|
|||||||
|
/*
|
||||||
|
* ====================================================================
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -191,6 +191,12 @@ public class Util
|
|||||||
return new Date(ms_since_19700101);
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -54,6 +54,9 @@
|
|||||||
*/
|
*/
|
||||||
package org.apache.poi.hpsf;
|
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
|
* <p>The <em>Variant</em> types as defined by Microsoft's COM. I
|
||||||
* found this information in <a
|
* found this information in <a
|
||||||
@ -373,4 +376,60 @@ public class Variant
|
|||||||
*/
|
*/
|
||||||
public static final int VT_TYPEMASK = 0xFFF;
|
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";
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
370
src/java/org/apache/poi/hpsf/VariantSupport.java
Normal file
370
src/java/org/apache/poi/hpsf/VariantSupport.java
Normal file
@ -0,0 +1,370 @@
|
|||||||
|
/*
|
||||||
|
* ====================================================================
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user