]> source.dussan.org Git - poi.git/commitdiff
HPSF: Use LittleEndianStream instead of offset calculation
authorAndreas Beeker <kiwiwings@apache.org>
Tue, 2 May 2017 23:24:50 +0000 (23:24 +0000)
committerAndreas Beeker <kiwiwings@apache.org>
Tue, 2 May 2017 23:24:50 +0000 (23:24 +0000)
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1793594 13f79535-47bb-0310-9956-ffa450edef68

23 files changed:
src/java/org/apache/poi/hpsf/Array.java
src/java/org/apache/poi/hpsf/Blob.java
src/java/org/apache/poi/hpsf/ClassID.java
src/java/org/apache/poi/hpsf/ClipboardData.java
src/java/org/apache/poi/hpsf/CodePageString.java
src/java/org/apache/poi/hpsf/Currency.java
src/java/org/apache/poi/hpsf/CustomProperties.java
src/java/org/apache/poi/hpsf/Date.java
src/java/org/apache/poi/hpsf/Decimal.java
src/java/org/apache/poi/hpsf/DocumentSummaryInformation.java
src/java/org/apache/poi/hpsf/Filetime.java
src/java/org/apache/poi/hpsf/GUID.java
src/java/org/apache/poi/hpsf/IndirectPropertyName.java
src/java/org/apache/poi/hpsf/MutableProperty.java
src/java/org/apache/poi/hpsf/PropertySet.java
src/java/org/apache/poi/hpsf/TypeWriter.java
src/java/org/apache/poi/hpsf/TypedPropertyValue.java
src/java/org/apache/poi/hpsf/UnicodeString.java
src/java/org/apache/poi/hpsf/Util.java
src/java/org/apache/poi/hpsf/Variant.java
src/java/org/apache/poi/hpsf/VariantBool.java
src/java/org/apache/poi/hpsf/Vector.java
src/java/org/apache/poi/hpsf/VersionedStream.java

index 8850ab9cd639f7ab2d0e58de7be9c7cddf70facd..f8c70fe1270803f8273ee91281ddb084b472c1c2 100644 (file)
 package org.apache.poi.hpsf;
 
 import org.apache.poi.util.Internal;
-import org.apache.poi.util.LittleEndian;
+import org.apache.poi.util.LittleEndianByteArrayInputStream;
 
 @Internal
 class Array
 {
-    static class ArrayDimension
-    {
-        static final int SIZE = 8;
-
-        private int _indexOffset;
+    static class ArrayDimension {
         private long _size;
+        @SuppressWarnings("unused")
+        private int _indexOffset;
 
-        ArrayDimension( byte[] data, int offset )
-        {
-            _size = LittleEndian.getUInt( data, offset );
-            _indexOffset = LittleEndian.getInt( data, offset
-                    + LittleEndian.INT_SIZE );
+        void read( LittleEndianByteArrayInputStream lei ) {
+            _size = lei.readUInt();
+            _indexOffset = lei.readInt();
         }
     }
 
@@ -42,96 +38,69 @@ class Array
         private ArrayDimension[] _dimensions;
         private int _type;
 
-        ArrayHeader( byte[] data, int startOffset )
-        {
-            int offset = startOffset;
-
-            _type = LittleEndian.getInt( data, offset );
-            offset += LittleEndian.INT_SIZE;
+        void read( LittleEndianByteArrayInputStream lei ) {
+            _type = lei.readInt();
 
-            long numDimensionsUnsigned = LittleEndian.getUInt( data, offset );
-            offset += LittleEndian.INT_SIZE;
+            long numDimensionsUnsigned = lei.readUInt();
 
-            if ( !( 1 <= numDimensionsUnsigned && numDimensionsUnsigned <= 31 ) )
-                throw new IllegalPropertySetDataException(
-                        "Array dimension number " + numDimensionsUnsigned
-                                + " is not in [1; 31] range" );
+            if ( !( 1 <= numDimensionsUnsigned && numDimensionsUnsigned <= 31 ) ) {
+                String msg = "Array dimension number "+numDimensionsUnsigned+" is not in [1; 31] range";
+                throw new IllegalPropertySetDataException(msg);
+            }
+                
             int numDimensions = (int) numDimensionsUnsigned;
 
             _dimensions = new ArrayDimension[numDimensions];
-            for ( int i = 0; i < numDimensions; i++ )
-            {
-                _dimensions[i] = new ArrayDimension( data, offset );
-                offset += ArrayDimension.SIZE;
+            for ( int i = 0; i < numDimensions; i++ ) {
+                ArrayDimension ad = new ArrayDimension();
+                ad.read(lei);
+                _dimensions[i] = ad;
             }
         }
 
-        long getNumberOfScalarValues()
-        {
+        long getNumberOfScalarValues() {
             long result = 1;
-            for ( ArrayDimension dimension : _dimensions )
+            for ( ArrayDimension dimension : _dimensions ) {
                 result *= dimension._size;
+            }
             return result;
         }
 
-        int getSize()
-        {
-            return LittleEndian.INT_SIZE * 2 + _dimensions.length
-                    * ArrayDimension.SIZE;
-        }
-
-        int getType()
-        {
+        int getType() {
             return _type;
         }
     }
 
-    private ArrayHeader _header;
+    private final ArrayHeader _header = new ArrayHeader();
     private TypedPropertyValue[] _values;
 
-    Array()
-    {
-    }
+    Array() {}
 
-    Array( final byte[] data, final int offset )
-    {
-        read( data, offset );
-    }
-
-    int read( final byte[] data, final int startOffset )
-    {
-        int offset = startOffset;
-
-        _header = new ArrayHeader( data, offset );
-        offset += _header.getSize();
+    void read( LittleEndianByteArrayInputStream lei ) {
+        _header.read(lei);
 
         long numberOfScalarsLong = _header.getNumberOfScalarValues();
-        if ( numberOfScalarsLong > Integer.MAX_VALUE )
-            throw new UnsupportedOperationException(
-                    "Sorry, but POI can't store array of properties with size of "
-                            + numberOfScalarsLong + " in memory" );
+        if ( numberOfScalarsLong > Integer.MAX_VALUE ) {
+            String msg =
+                "Sorry, but POI can't store array of properties with size of " +
+                numberOfScalarsLong + " in memory";
+            throw new UnsupportedOperationException(msg);
+        }
         int numberOfScalars = (int) numberOfScalarsLong;
 
         _values = new TypedPropertyValue[numberOfScalars];
-        final int type = _header._type;
-        if ( type == Variant.VT_VARIANT )
-        {
-            for ( int i = 0; i < numberOfScalars; i++ )
-            {
-                TypedPropertyValue typedPropertyValue = new TypedPropertyValue();
-                offset += typedPropertyValue.read( data, offset );
-            }
-        }
-        else
-        {
-            for ( int i = 0; i < numberOfScalars; i++ )
-            {
-                TypedPropertyValue typedPropertyValue = new TypedPropertyValue(
-                        type, null );
-                offset += typedPropertyValue.readValuePadded( data, offset );
+        int paddedType = (_header._type == Variant.VT_VARIANT) ? 0 : _header._type;
+        for ( int i = 0; i < numberOfScalars; i++ ) {
+            TypedPropertyValue typedPropertyValue = new TypedPropertyValue(paddedType, null);
+            typedPropertyValue.read(lei);
+            _values[i] = typedPropertyValue;
+            if (paddedType != 0) {
+                TypedPropertyValue.skipPadding(lei);
             }
         }
-
-        return offset - startOffset;
+    }
+    
+    TypedPropertyValue[] getValues(){
+        return _values;
     }
 }
index 547e2392ba35afd95864161e0f2bdbc981aae82d..af207feabb232f1078e6b0343e44f64e3c38111a 100644 (file)
 package org.apache.poi.hpsf;
 
 import org.apache.poi.util.Internal;
-import org.apache.poi.util.LittleEndian;
+import org.apache.poi.util.LittleEndianInput;
 
 @Internal
-class Blob
-{
+class Blob {
     private byte[] _value;
 
-    Blob( byte[] data, int offset )
-    {
-        int size = LittleEndian.getInt( data, offset );
-
-        if ( size == 0 )
-        {
-            _value = new byte[0];
-            return;
+    Blob() {}
+    
+    void read( LittleEndianInput lei ) {
+        int size = lei.readInt();
+        _value = new byte[size];
+        if ( size > 0 ) {
+            lei.readFully(_value);
         }
-
-        _value = LittleEndian.getByteArray( data, offset
-                + LittleEndian.INT_SIZE, size );
-    }
-
-    int getSize()
-    {
-        return LittleEndian.INT_SIZE + _value.length;
     }
 }
index 0dacf801392342058c86100b707f02dc4f5a7f97..ec9c35555e637361839a0a45359270ede0aab261 100644 (file)
@@ -20,16 +20,16 @@ package org.apache.poi.hpsf;
 import java.util.Arrays;
 
 import org.apache.poi.util.HexDump;
-import org.apache.poi.util.StringUtil;
 
 /**
- *  <p>Represents a class ID (16 bytes). Unlike other little-endian
- *  type the {@link ClassID} is not just 16 bytes stored in the wrong
- *  order. Instead, it is a double word (4 bytes) followed by two
- *  words (2 bytes each) followed by 8 bytes.</p>
+ * Represents a class ID (16 bytes). Unlike other little-endian
+ * type the {@link ClassID} is not just 16 bytes stored in the wrong
+ * order. Instead, it is a double word (4 bytes) followed by two
+ * words (2 bytes each) followed by 8 bytes.<p>
+ *  
+ * The ClassID (or CLSID) is a UUID - see RFC 4122 
  */
-public class ClassID
-{
+public class ClassID {
     public static final ClassID OLE10_PACKAGE  = new ClassID("{0003000C-0000-0000-C000-000000000046}");
     public static final ClassID PPT_SHOW       = new ClassID("{64818D10-4F9B-11CF-86EA-00AA00B929E8}");
     public static final ClassID XLS_WORKBOOK   = new ClassID("{00020841-0000-0000-C000-000000000046}");
@@ -68,21 +68,16 @@ public class ClassID
     
     public static final ClassID EQUATION30     = new ClassID("{0002CE02-0000-0000-C000-000000000046}");
        
-    /** <p>The number of bytes occupied by this object in the byte
-     * stream.</p> */
+    /** The number of bytes occupied by this object in the byte stream. */
     public static final int LENGTH = 16;
        
     /**
-     * <p>The bytes making out the class ID in correct order,
-     * i.e. big-endian.</p>
+     * The bytes making out the class ID in correct order, i.e. big-endian.
      */
     private final byte[] bytes = new byte[LENGTH];
 
-
-
     /**
-     *  <p>Creates a {@link ClassID} and reads its value from a byte
-     *  array.</p>
+     * Creates a {@link ClassID} and reads its value from a byte array.
      *
      * @param src The byte array to read from.
      * @param offset The offset of the first byte to read.
@@ -93,8 +88,7 @@ public class ClassID
 
 
     /**
-     *  <p>Creates a {@link ClassID} and initializes its value with
-     *  0x00 bytes.</p>
+     * Creates a {@link ClassID} and initializes its value with 0x00 bytes.
      */
     public ClassID() {
         Arrays.fill(bytes, (byte)0);
@@ -102,8 +96,8 @@ public class ClassID
 
 
     /**
-     * <p>Creates a {@link ClassID} from a human-readable representation of the Class ID in standard 
-     * format <code>"{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}"</code>.</p>
+     * Creates a {@link ClassID} from a human-readable representation of the Class ID in standard 
+     * format {@code "{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}"}.
      * 
      * @param externalForm representation of the Class ID represented by this object.
      */
@@ -116,8 +110,7 @@ public class ClassID
     
 
     /**
-     * @return The number of bytes occupied by this object in the byte
-     * stream.
+     * @return The number of bytes occupied by this object in the byte stream.
      */
     public int length() {
         return LENGTH;
@@ -126,8 +119,7 @@ public class ClassID
 
 
     /**
-     * <p>Gets the bytes making out the class ID. They are returned in
-     * correct order, i.e. big-endian.</p>
+     * Gets the bytes making out the class ID. They are returned in correct order, i.e. big-endian.
      *
      * @return the bytes making out the class ID.
      */
@@ -138,7 +130,7 @@ public class ClassID
 
 
     /**
-     * <p>Sets the bytes making out the class ID.</p>
+     * Sets the bytes making out the class ID.
      *
      * @param bytes The bytes making out the class ID in big-endian format. They
      * are copied without their order being changed.
@@ -150,13 +142,10 @@ public class ClassID
 
 
     /**
-     * <p>Reads the class ID's value from a byte array by turning
-     * little-endian into big-endian.</p>
+     * Reads the class ID's value from a byte array by turning little-endian into big-endian.
      *
      * @param src The byte array to read from
-     *
-     * @param offset The offset within the <var>src</var> byte array
-     *
+     * @param offset The offset within the {@code src} byte array
      * @return A byte array containing the class ID.
      */
     public byte[] read(final byte[] src, final int offset) {
@@ -180,18 +169,15 @@ public class ClassID
         return bytes;
     }
 
-
-
     /**
-     * <p>Writes the class ID to a byte array in the
-     * little-endian format.</p>
+     * Writes the class ID to a byte array in the little-endian format.
      *
      * @param dst The byte array to write to.
      *
-     * @param offset The offset within the <var>dst</var> byte array.
+     * @param offset The offset within the {@code dst} byte array.
      *
      * @exception ArrayStoreException if there is not enough room for the class
-     * ID 16 bytes in the byte array after the <var>offset</var> position.
+     * ID 16 bytes in the byte array after the {@code offset} position.
      */
     public void write(final byte[] dst, final int offset)
     throws ArrayStoreException {
@@ -223,30 +209,44 @@ public class ClassID
 
 
     /**
-     * <p>Checks whether this <code>ClassID</code> is equal to another
-     * object.</p>
+     * Checks whether this {@code ClassID} is equal to another object.
      *
-     * @param o the object to compare this <code>PropertySet</code> with
-     * @return <code>true</code> if the objects are equal, else
-     * <code>false</code>.
+     * @param o the object to compare this {@code ClassID} with
+     * @return {@code true} if the objects are equal, else {@code false}.
      */
     @Override
     public boolean equals(final Object o) {
-        if (o == null || !(o instanceof ClassID)) {
-            return false;
-        }
-        final ClassID cid = (ClassID) o;
-        if (bytes.length != cid.bytes.length) {
-            return false;
-        }
-        for (int i = 0; i < bytes.length; i++) {
-            if (bytes[i] != cid.bytes[i]) {
-                return false;
-            }
-        }
-        return true;
+        return (o instanceof ClassID) && Arrays.equals(bytes, ((ClassID)o).bytes);
     }
 
+    /**
+     * Checks whether this {@code ClassID} is equal to another ClassID with inverted endianess,
+     * because there are apparently not only version 1 GUIDs (aka "network" with big-endian encoding),
+     * but also version 2 GUIDs (aka "native" with little-endian encoding) out there.
+     *
+     * @param o the object to compare this {@code ClassID} with
+     * @return {@code true} if the objects are equal, else {@code false}.
+     */
+    public boolean equalsInverted(ClassID o) {
+        return
+            o.bytes[0] == bytes[3] &&
+            o.bytes[1] == bytes[2] &&
+            o.bytes[2] == bytes[1] &&
+            o.bytes[3] == bytes[0] &&
+            o.bytes[4] == bytes[5] &&
+            o.bytes[5] == bytes[4] &&
+            o.bytes[6] == bytes[7] &&
+            o.bytes[7] == bytes[6] &&
+            o.bytes[8] == bytes[8] &&
+            o.bytes[9] == bytes[9] &&
+            o.bytes[10] == bytes[10] &&
+            o.bytes[11] == bytes[11] &&
+            o.bytes[12] == bytes[12] &&
+            o.bytes[13] == bytes[13] &&
+            o.bytes[14] == bytes[14] &&
+            o.bytes[15] == bytes[15]
+        ;
+    }
 
 
     /**
@@ -254,12 +254,12 @@ public class ClassID
      */
     @Override
     public int hashCode() {
-        return new String(bytes, StringUtil.UTF8).hashCode();
+        return toString().hashCode();
     }
 
     /**
-     * <p>Returns a human-readable representation of the Class ID in standard 
-     * format <code>"{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}"</code>.</p>
+     * Returns a human-readable representation of the Class ID in standard 
+     * format {@code "{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}"}.
      * 
      * @return String representation of the Class ID represented by this object.
      */
index 42ce724a94591bd5169da5b3aba5dca9c621cc02..396a58277278a09d1218e5e7b0a76fa06e0cb74b 100644 (file)
 ==================================================================== */
 package org.apache.poi.hpsf;
 
-import java.io.IOException;
-import java.io.OutputStream;
-
+import org.apache.poi.util.IOUtils;
 import org.apache.poi.util.Internal;
-import org.apache.poi.util.LittleEndian;
+import org.apache.poi.util.LittleEndianByteArrayInputStream;
+import org.apache.poi.util.LittleEndianByteArrayOutputStream;
+import org.apache.poi.util.LittleEndianConsts;
 import org.apache.poi.util.POILogFactory;
 import org.apache.poi.util.POILogger;
 
 @Internal
-class ClipboardData
-{
-    private static final POILogger logger = POILogFactory
-            .getLogger( ClipboardData.class );
+class ClipboardData {
+    private static final POILogger LOG = POILogFactory.getLogger( ClipboardData.class );
 
-    private int _format;
+    private int _format = 0;
     private byte[] _value;
+    
+    ClipboardData() {}
 
-    ClipboardData( byte[] data, int offset )
-    {
-        int size = LittleEndian.getInt( data, offset );
+    void read( LittleEndianByteArrayInputStream lei ) {
+        int offset = lei.getReadIndex();
+        int size = lei.readInt();
 
-        if ( size < 4 )
-        {
-            logger.log( POILogger.WARN, "ClipboardData at offset ",
-                    Integer.valueOf( offset ), " size less than 4 bytes "
-                            + "(doesn't even have format field!). "
-                            + "Setting to format == 0 and hope for the best" );
+        if ( size < 4 ) {
+            String msg = 
+                "ClipboardData at offset "+offset+" size less than 4 bytes "+
+                "(doesn't even have format field!). Setting to format == 0 and hope for the best";
+            LOG.log( POILogger.WARN, msg);
             _format = 0;
             _value = new byte[0];
             return;
         }
 
-        _format = LittleEndian.getInt( data, offset + LittleEndian.INT_SIZE );
-        _value = LittleEndian.getByteArray( data, offset
-                + LittleEndian.INT_SIZE * 2, size - LittleEndian.INT_SIZE );
-    }
-
-    int getSize()
-    {
-        return LittleEndian.INT_SIZE * 2 + _value.length;
+        _format = lei.readInt();
+        _value = new byte[size - LittleEndianConsts.INT_SIZE];
+        lei.readFully(_value);
     }
 
-    byte[] getValue()
-    {
+    byte[] getValue() {
         return _value;
     }
 
-    byte[] toByteArray()
-    {
-        byte[] result = new byte[getSize()];
-        LittleEndian.putInt( result, 0 * LittleEndian.INT_SIZE,
-                LittleEndian.INT_SIZE + _value.length );
-        LittleEndian.putInt( result, 1 * LittleEndian.INT_SIZE, _format );
-        System.arraycopy( _value, 0, result, LittleEndian.INT_SIZE
-                + LittleEndian.INT_SIZE, _value.length );
-        return result;
+    byte[] toByteArray() {
+        byte[] result = new byte[LittleEndianConsts.INT_SIZE*2+_value.length];
+        LittleEndianByteArrayOutputStream bos = new LittleEndianByteArrayOutputStream(result,0);
+        try {
+            bos.writeInt(LittleEndianConsts.INT_SIZE + _value.length);
+            bos.writeInt(_format);
+            bos.write(_value);
+            return result;
+        } finally {
+            IOUtils.closeQuietly(bos);
+        }
     }
-
-    int write( OutputStream out ) throws IOException
-    {
-        LittleEndian.putInt( LittleEndian.INT_SIZE + _value.length, out );
-        LittleEndian.putInt( _format, out );
-        out.write( _value );
-        return 2 * LittleEndian.INT_SIZE + _value.length;
+    
+    void setValue( byte[] value ) {
+        _value = value.clone();
     }
 }
index 8eb8987b3439ed4b8b7b91de6f13340cfe146cb3..d0d138a40baeb9563445f2f4d5ba756b0368ed3f 100644 (file)
@@ -23,89 +23,83 @@ import java.io.UnsupportedEncodingException;
 import org.apache.poi.util.CodePageUtil;
 import org.apache.poi.util.Internal;
 import org.apache.poi.util.LittleEndian;
+import org.apache.poi.util.LittleEndianByteArrayInputStream;
+import org.apache.poi.util.LittleEndianConsts;
 import org.apache.poi.util.POILogFactory;
 import org.apache.poi.util.POILogger;
-import org.apache.poi.util.StringUtil;
 
 @Internal
-class CodePageString
-{
-    private final static POILogger logger = POILogFactory
-            .getLogger( CodePageString.class );
+class CodePageString {
+    private final static POILogger LOG = POILogFactory.getLogger( CodePageString.class );
 
     private byte[] _value;
+    
+    
+    CodePageString() {}
 
-    CodePageString( final byte[] data, final int startOffset )
-    {
-        int offset = startOffset;
-
-        int size = LittleEndian.getInt( data, offset );
-        offset += LittleEndian.INT_SIZE;
+    void read( LittleEndianByteArrayInputStream lei ) {
+        int offset = lei.getReadIndex();
+        int size = lei.readInt();
+        _value = new byte[size];
+        if (size == 0) {
+            return;
+        }
 
-        _value = LittleEndian.getByteArray( data, offset, size );
-        if ( size != 0 && _value[size - 1] != 0 ) {
+        // If Size is zero, this field MUST be zero bytes in length. If Size is
+        // nonzero and the CodePage property set's CodePage property has the value CP_WINUNICODE
+        // (0x04B0), then the value MUST be a null-terminated array of 16-bit Unicode characters,
+        // followed by zero padding to a multiple of 4 bytes. If Size is nonzero and the property set's
+        // CodePage property has any other value, it MUST be a null-terminated array of 8-bit characters
+        // from the code page identified by the CodePage property, followed by zero padding to a
+        // multiple of 4 bytes. The string represented by this field MAY contain embedded or additional
+        // trailing null characters and an OLEPS implementation MUST be able to handle such strings.        
+        
+        lei.readFully(_value);
+        if (_value[size - 1] != 0 ) {
             // TODO Some files, such as TestVisioWithCodepage.vsd, are currently
-            //  triggering this for values that don't look like codepages
+            // triggering this for values that don't look like codepages
             // See Bug #52258 for details
-            logger.log(POILogger.WARN, "CodePageString started at offset #" + offset
-                        + " is not NULL-terminated" );
-//            throw new IllegalPropertySetDataException(
-//                    "CodePageString started at offset #" + offset
-//                            + " is not NULL-terminated" );
+            String msg = "CodePageString started at offset #" + offset + " is not NULL-terminated";
+            LOG.log(POILogger.WARN, msg);
         }
-    }
 
-    CodePageString( String string, int codepage )
-            throws UnsupportedEncodingException
-    {
-        setJavaValue( string, codepage );
+        TypedPropertyValue.skipPadding(lei);
     }
 
-    String getJavaValue( int codepage ) throws UnsupportedEncodingException
-    {
-        String result;
-        if ( codepage == -1 )
-            result = new String( _value, StringUtil.UTF8 );
-        else
-            result = CodePageUtil.getStringFromCodePage(_value, codepage);
+    String getJavaValue( int codepage ) throws UnsupportedEncodingException {
+        int cp = ( codepage == -1 ) ? Property.DEFAULT_CODEPAGE : codepage;
+        String result = CodePageUtil.getStringFromCodePage(_value, cp);
+
+        
         final int terminator = result.indexOf( '\0' );
-        if ( terminator == -1 )
-        {
-            logger.log(
-                    POILogger.WARN,
-                    "String terminator (\\0) for CodePageString property value not found."
-                            + "Continue without trimming and hope for the best." );
+        if ( terminator == -1 ) {
+            String msg = 
+                "String terminator (\\0) for CodePageString property value not found." +
+                "Continue without trimming and hope for the best.";
+            LOG.log(POILogger.WARN, msg);
             return result;
         }
-        if ( terminator != result.length() - 1 )
-        {
-            logger.log(
-                    POILogger.WARN,
-                    "String terminator (\\0) for CodePageString property value occured before the end of string. "
-                            + "Trimming and hope for the best." );
+        if ( terminator != result.length() - 1 ) {
+            String msg = 
+                "String terminator (\\0) for CodePageString property value occured before the end of string. "+
+                "Trimming and hope for the best.";
+            LOG.log(POILogger.WARN, msg );
         }
         return result.substring( 0, terminator );
     }
 
-    int getSize()
-    {
-        return LittleEndian.INT_SIZE + _value.length;
+    int getSize() {
+        return LittleEndianConsts.INT_SIZE + _value.length;
     }
 
-    void setJavaValue( String string, int codepage )
-            throws UnsupportedEncodingException
-    {
-        String stringNT = string + "\0";
-        if ( codepage == -1 )
-            _value = stringNT.getBytes(StringUtil.UTF8);
-        else
-            _value = CodePageUtil.getBytesInCodePage(stringNT, codepage);
+    void setJavaValue( String string, int codepage ) throws UnsupportedEncodingException {
+        int cp = ( codepage == -1 ) ? Property.DEFAULT_CODEPAGE : codepage;
+        _value = CodePageUtil.getBytesInCodePage(string + "\0", cp);
     }
 
-    int write( OutputStream out ) throws IOException
-    {
-        LittleEndian.putInt( _value.length, out );
+    int write( OutputStream out ) throws IOException {
+        LittleEndian.putUInt( _value.length, out );
         out.write( _value );
-        return LittleEndian.INT_SIZE + _value.length;
+        return LittleEndianConsts.INT_SIZE + _value.length;
     }
 }
index 8928a56a036ae52621608c3fd6abc6b40475d5db..34ae39a645a33dff8673e08bf50d1933b8cb3676 100644 (file)
 package org.apache.poi.hpsf;
 
 import org.apache.poi.util.Internal;
-import org.apache.poi.util.LittleEndian;
+import org.apache.poi.util.LittleEndianByteArrayInputStream;
 
 @Internal
-class Currency
-{
-    static final int SIZE = 8;
+class Currency {
+    private static final int SIZE = 8;
 
-    private byte[] _value;
+    private final byte[] _value = new byte[SIZE];
+    
+    Currency() {}
 
-    Currency( byte[] data, int offset )
-    {
-        _value = LittleEndian.getByteArray( data, offset, SIZE );
+    void read( LittleEndianByteArrayInputStream lei ) {
+        lei.readFully(_value);
     }
 }
index c420f1d3a0fca089365d8ee22d7b666426688ae3..a279e2321ed21c1b7aa51edc60c2587dae0e734f 100644 (file)
@@ -17,6 +17,8 @@
 
 package org.apache.poi.hpsf;
 
+import java.io.UnsupportedEncodingException;
+import java.nio.charset.Charset;
 import java.util.Date;
 import java.util.HashMap;
 import java.util.Map;
@@ -24,6 +26,9 @@ import java.util.Set;
 
 import org.apache.commons.collections4.bidimap.TreeBidiMap;
 import org.apache.poi.hpsf.wellknown.PropertyIDMap;
+import org.apache.poi.util.CodePageUtil;
+import org.apache.poi.util.POILogFactory;
+import org.apache.poi.util.POILogger;
 
 /**
  * Maintains the instances of {@link CustomProperty} that belong to a
@@ -55,6 +60,7 @@ import org.apache.poi.hpsf.wellknown.PropertyIDMap;
  */
 @SuppressWarnings("serial")
 public class CustomProperties extends HashMap<Long,CustomProperty> {
+    private static final POILogger LOG = POILogFactory.getLogger(CustomProperties.class);
 
     /**
      * Maps property IDs to property names and vice versa.
@@ -66,6 +72,7 @@ public class CustomProperties extends HashMap<Long,CustomProperty> {
      */
     private boolean isPure = true;
 
+    private int codepage = -1;
 
     /**
      * Puts a {@link CustomProperty} into this map. It is assumed that the
@@ -90,6 +97,8 @@ public class CustomProperties extends HashMap<Long,CustomProperty> {
                     ") do not match.");
         }
 
+        checkCodePage(name);
+        
         /* Register name and ID in the dictionary. Mapping in both directions is possible. If there is already a  */
         super.remove(dictionary.getKey(name));
         dictionary.put(cp.getID(), name);
@@ -124,7 +133,8 @@ public class CustomProperties extends HashMap<Long,CustomProperty> {
             customProperty.setID(oldId);
         } else {
             long lastKey = (dictionary.isEmpty()) ? 0 : dictionary.lastKey();
-            customProperty.setID(Math.max(lastKey,PropertyIDMap.PID_MAX) + 1);
+            long nextKey = Math.max(lastKey,PropertyIDMap.PID_MAX)+1;
+            customProperty.setID(nextKey);
         }
         return this.put(name, customProperty);
     }
@@ -152,7 +162,7 @@ public class CustomProperties extends HashMap<Long,CustomProperty> {
      *         {@code null} if there was no such property before.
      */
     public Object put(final String name, final String value) {
-        final Property p = new Property(-1, Variant.VT_LPWSTR, value);
+        final Property p = new MutableProperty(-1, Variant.VT_LPSTR, value);
         return put(new CustomProperty(p, name));
     }
 
@@ -165,7 +175,7 @@ public class CustomProperties extends HashMap<Long,CustomProperty> {
      *         {@code null} if there was no such property before.
      */
     public Object put(final String name, final Long value) {
-        final Property p = new Property(-1, Variant.VT_I8, value);
+        final Property p = new MutableProperty(-1, Variant.VT_I8, value);
         return put(new CustomProperty(p, name));
     }
 
@@ -178,7 +188,7 @@ public class CustomProperties extends HashMap<Long,CustomProperty> {
      *         {@code null} if there was no such property before.
      */
     public Object put(final String name, final Double value) {
-        final Property p = new Property(-1, Variant.VT_R8, value);
+        final Property p = new MutableProperty(-1, Variant.VT_R8, value);
         return put(new CustomProperty(p, name));
     }
 
@@ -191,7 +201,7 @@ public class CustomProperties extends HashMap<Long,CustomProperty> {
      *         {@code null} if there was no such property before.
      */
     public Object put(final String name, final Integer value) {
-        final Property p = new Property(-1, Variant.VT_I4, value);
+        final Property p = new MutableProperty(-1, Variant.VT_I4, value);
         return put(new CustomProperty(p, name));
     }
 
@@ -204,7 +214,7 @@ public class CustomProperties extends HashMap<Long,CustomProperty> {
      *         {@code null} if there was no such property before.
      */
     public Object put(final String name, final Boolean value) {
-        final Property p = new Property(-1, Variant.VT_BOOL, value);
+        final Property p = new MutableProperty(-1, Variant.VT_BOOL, value);
         return put(new CustomProperty(p, name));
     }
 
@@ -233,7 +243,7 @@ public class CustomProperties extends HashMap<Long,CustomProperty> {
      *         {@code null} if there was no such property before.
      */
     public Object put(final String name, final Date value) {
-        final Property p = new Property(-1, Variant.VT_FILETIME, value);
+        final Property p = new MutableProperty(-1, Variant.VT_FILETIME, value);
         return put(new CustomProperty(p, name));
     }
 
@@ -274,11 +284,17 @@ public class CustomProperties extends HashMap<Long,CustomProperty> {
      * @param codepage the codepage
      */
     public void setCodepage(final int codepage) {
-        Property p = new Property(PropertyIDMap.PID_CODEPAGE, Variant.VT_I2, codepage);
-        put(new CustomProperty(p));
+        this.codepage = codepage;
     }
 
-
+    /**
+     * Gets the codepage.
+     *
+     * @return the codepage or -1 if the codepage is undefined.
+     */
+    public int getCodepage() {
+        return codepage;
+    }
 
     /**
      * <p>Gets the dictionary which contains IDs and names of the named custom
@@ -317,16 +333,6 @@ public class CustomProperties extends HashMap<Long,CustomProperty> {
         return false;
     }
 
-    /**
-     * Gets the codepage.
-     *
-     * @return the codepage or -1 if the codepage is undefined.
-     */
-    public int getCodepage() {
-        CustomProperty cp = get(PropertyIDMap.PID_CODEPAGE);
-        return (cp == null) ? -1 : (Integer)cp.getValue();
-    }
-
     /**
      * Tells whether this {@link CustomProperties} instance is pure or one or
      * more properties of the underlying low-level property set has been
@@ -347,4 +353,25 @@ public class CustomProperties extends HashMap<Long,CustomProperty> {
     public void setPure(final boolean isPure) {
         this.isPure = isPure;
     }
+    
+    private void checkCodePage(String value) {
+        int cp = getCodepage();
+        if (cp == -1) {
+            cp = Property.DEFAULT_CODEPAGE;
+        }
+        if (cp == CodePageUtil.CP_UNICODE) {
+            return;
+        }
+        String cps = "";
+        try {
+            cps = CodePageUtil.codepageToEncoding(cp, false);
+        } catch (UnsupportedEncodingException e) {
+            LOG.log(POILogger.ERROR, "Codepage '"+cp+"' can't be found.");
+        }
+        if (!cps.isEmpty() && Charset.forName(cps).newEncoder().canEncode(value)) {
+            return;
+        }
+        LOG.log(POILogger.DEBUG, "Charset '"+cps+"' can't encode '"+value+"' - switching to unicode.");
+        setCodepage(CodePageUtil.CP_UNICODE);
+    }
 }
index 237420fcc06b6d530575366718acc2df932c2327..a116d21b8bd279b4a7de3285a195454018c5b45e 100644 (file)
 package org.apache.poi.hpsf;
 
 import org.apache.poi.util.Internal;
-import org.apache.poi.util.LittleEndian;
+import org.apache.poi.util.LittleEndianByteArrayInputStream;
 
 @Internal
-class Date
-{
-    static final int SIZE = 8;
+class Date {
+    private static final int SIZE = 8;
 
-    private byte[] _value;
+    private final byte[] _value = new byte[SIZE];
+    
+    Date() {}
 
-    Date( byte[] data, int offset )
-    {
-        _value = LittleEndian.getByteArray( data, offset, SIZE );
+    void read( LittleEndianByteArrayInputStream lei ) {
+        lei.readFully(_value);
     }
 }
index d8c404c31cea05a501482ad5c5f9521adf2c5446..1ef50dabb04ad60eddbb9d78f9a373d82265941d 100644 (file)
 package org.apache.poi.hpsf;
 
 import org.apache.poi.util.Internal;
-import org.apache.poi.util.LittleEndian;
+import org.apache.poi.util.LittleEndianByteArrayInputStream;
 
 @Internal
-class Decimal
-{
-    static final int SIZE = 16;
-
+class Decimal {
     /**
      * Findbugs: UNR_UNREAD_FIELD
      */
@@ -33,23 +30,13 @@ class Decimal
     private int field_4_hi32;
     private long field_5_lo64;
 
-    Decimal( final byte[] data, final int startOffset )
-    {
-        int offset = startOffset;
-
-        field_1_wReserved = LittleEndian.getShort( data, offset );
-        offset += LittleEndian.SHORT_SIZE;
-
-        field_2_scale = data[offset];
-        offset += LittleEndian.BYTE_SIZE;
-
-        field_3_sign = data[offset];
-        offset += LittleEndian.BYTE_SIZE;
-
-        field_4_hi32 = LittleEndian.getInt( data, offset );
-        offset += LittleEndian.INT_SIZE;
-
-        field_5_lo64 = LittleEndian.getLong( data, offset );
-        offset += LittleEndian.LONG_SIZE;
+    Decimal() {}
+    
+    void read( LittleEndianByteArrayInputStream lei ) {
+        field_1_wReserved = lei.readShort();
+        field_2_scale = lei.readByte();
+        field_3_sign = lei.readByte();
+        field_4_hi32 = lei.readInt();
+        field_5_lo64 = lei.readLong();
     }
 }
index f63ab8d92d321f46df665f11642bb1c9f4ca6226..7dd41a0cc3017f39a361a45074ece8ec24beb59d 100644 (file)
 
 package org.apache.poi.hpsf;
 
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 
 import org.apache.poi.hpsf.wellknown.PropertyIDMap;
 import org.apache.poi.hpsf.wellknown.SectionIDMap;
-import org.apache.poi.util.CodePageUtil;
 
 /**
  * Convenience class representing a DocumentSummary Information stream in a
@@ -70,6 +72,32 @@ public class DocumentSummaryInformation extends SpecialPropertySet {
         }
     }
 
+    /**
+     * Creates a {@link DocumentSummaryInformation} instance from an {@link
+     * InputStream} in the Horrible Property Set Format.<p>
+     *
+     * The constructor reads the first few bytes from the stream
+     * and determines whether it is really a property set stream. If
+     * it is, it parses the rest of the stream. If it is not, it
+     * resets the stream to its beginning in order to let other
+     * components mess around with the data and throws an
+     * exception.
+     *
+     * @param stream Holds the data making out the property set
+     * stream.
+     * @throws MarkUnsupportedException
+     *    if the stream does not support the {@link InputStream#markSupported} method.
+     * @throws IOException
+     *    if the {@link InputStream} cannot be accessed as needed.
+     * @exception NoPropertySetStreamException
+     *    if the input stream does not contain a property set.
+     * @exception UnsupportedEncodingException
+     *    if a character encoding is not supported.
+     */
+    public DocumentSummaryInformation(final InputStream stream)
+    throws NoPropertySetStreamException, MarkUnsupportedException, IOException, UnsupportedEncodingException {
+        super(stream);
+    }
     
     /**
      * Returns the category (or {@code null}).
@@ -732,13 +760,13 @@ public class DocumentSummaryInformation extends SpecialPropertySet {
             final Map<Long,String> dictionary = section.getDictionary();
             final Property[] properties = section.getProperties();
             int propertyCount = 0;
-            for (int i = 0; i < properties.length; i++) {
-                final Property p = properties[i];
+            for (Property p : properties) {
                 final long id = p.getID();
-                if (id != 0 && id != 1) {
+                if (id == PropertyIDMap.PID_CODEPAGE) {
+                    cps.setCodepage((Integer)p.getValue());
+                } else if (id > PropertyIDMap.PID_CODEPAGE) {
                     propertyCount++;
-                    final CustomProperty cp = new CustomProperty(p,
-                            dictionary.get(Long.valueOf(id)));
+                    final CustomProperty cp = new CustomProperty(p, dictionary.get(id));
                     cps.put(cp.getName(), cp);
                 }
             }
@@ -758,17 +786,17 @@ public class DocumentSummaryInformation extends SpecialPropertySet {
         ensureSection2();
         final Section section = getSections().get(1);
         final Map<Long,String> dictionary = customProperties.getDictionary();
-        section.clear();
+        // section.clear();
 
         /* Set the codepage. If both custom properties and section have a
          * codepage, the codepage from the custom properties wins, else take the
-         * one that is defined. If none is defined, take Unicode. */
+         * one that is defined. If none is defined, take ISO-8859-1. */
         int cpCodepage = customProperties.getCodepage();
         if (cpCodepage < 0) {
             cpCodepage = section.getCodepage();
         }
         if (cpCodepage < 0) {
-            cpCodepage = CodePageUtil.CP_UNICODE;
+            cpCodepage = Property.DEFAULT_CODEPAGE;
         }
         customProperties.setCodepage(cpCodepage);
         section.setCodepage(cpCodepage);
index 84a58bb1dbaf41d88ea107eb93afd15cf8c32c72..2ed36afccc64454f29d272a14ac6128316365624 100644 (file)
@@ -20,49 +20,45 @@ import java.io.IOException;
 import java.io.OutputStream;
 
 import org.apache.poi.util.LittleEndian;
+import org.apache.poi.util.LittleEndianByteArrayInputStream;
+import org.apache.poi.util.LittleEndianConsts;
 
-class Filetime
-{
-    static final int SIZE = LittleEndian.INT_SIZE * 2;
+class Filetime {
+    private static final int SIZE = LittleEndian.INT_SIZE * 2;
 
     private int _dwHighDateTime;
     private int _dwLowDateTime;
+    
+    Filetime() {}
 
-    Filetime( byte[] data, int offset )
-    {
-        _dwLowDateTime = LittleEndian.getInt( data, offset + 0
-                * LittleEndian.INT_SIZE );
-        _dwHighDateTime = LittleEndian.getInt( data, offset + 1
-                * LittleEndian.INT_SIZE );
-    }
-
-    Filetime( int low, int high )
-    {
+    Filetime( int low, int high ) {
         _dwLowDateTime = low;
         _dwHighDateTime = high;
     }
 
-    long getHigh()
-    {
+
+    void read( LittleEndianByteArrayInputStream lei ) {
+        _dwLowDateTime = lei.readInt();
+        _dwHighDateTime = lei.readInt();
+    }
+
+    long getHigh() {
         return _dwHighDateTime;
     }
 
-    long getLow()
-    {
+    long getLow() {
         return _dwLowDateTime;
     }
 
-    byte[] toByteArray()
-    {
+    byte[] toByteArray() {
         byte[] result = new byte[SIZE];
-        LittleEndian.putInt( result, 0 * LittleEndian.INT_SIZE, _dwLowDateTime );
+        LittleEndian.putInt( result, 0 * LittleEndianConsts.INT_SIZE, _dwLowDateTime );
         LittleEndian
-                .putInt( result, 1 * LittleEndian.INT_SIZE, _dwHighDateTime );
+                .putInt( result, 1 * LittleEndianConsts.INT_SIZE, _dwHighDateTime );
         return result;
     }
 
-    int write( OutputStream out ) throws IOException
-    {
+    int write( OutputStream out ) throws IOException {
         LittleEndian.putInt( _dwLowDateTime, out );
         LittleEndian.putInt( _dwHighDateTime, out );
         return SIZE;
index 4bbd7f34148e2987e81be5269c14dc3c7dc2b956..3483d50709d230701fabdaab65db63df2ec8be44 100644 (file)
 package org.apache.poi.hpsf;
 
 import org.apache.poi.util.Internal;
-import org.apache.poi.util.LittleEndian;
+import org.apache.poi.util.LittleEndianByteArrayInputStream;
 
 @Internal
-class GUID
-{
-    static final int SIZE = 16;
-
+class GUID {
     private int _data1;
     private short _data2;
     private short _data3;
     private long _data4;
 
-    GUID( byte[] data, int offset )
-    {
-        _data1 = LittleEndian.getInt( data, offset + 0 );
-        _data2 = LittleEndian.getShort( data, offset + 4 );
-        _data3 = LittleEndian.getShort( data, offset + 6 );
-        _data4 = LittleEndian.getLong( data, offset + 8 );
+    GUID() {}
+    
+    void read( LittleEndianByteArrayInputStream lei ) {
+        _data1 = lei.readInt();
+        _data2 = lei.readShort();
+        _data3 = lei.readShort();
+        _data4 = lei.readLong();
     }
 }
index fc09ca6e8a2e88e0135786acb3ccdb7ca639adc9..758f755691d5ec8f61c07d3aea13a95f990bf132 100644 (file)
@@ -19,17 +19,6 @@ package org.apache.poi.hpsf;
 import org.apache.poi.util.Internal;
 
 @Internal
-class IndirectPropertyName
-{
-    private CodePageString _value;
-
-    IndirectPropertyName( byte[] data, int offset ) //NOSONAR
-    {
-        _value = new CodePageString( data, offset );
-    }
-
-    int getSize()
-    {
-        return _value.getSize();
-    }
+class IndirectPropertyName extends CodePageString {
+    IndirectPropertyName() {}
 }
index 10d86be0403ab643278d12ef3513686ddb090f76..106ad6f2403db4bef9624f22033fdfd203c78aa5 100644 (file)
@@ -17,6 +17,9 @@
 
 package org.apache.poi.hpsf;
 
+import java.io.UnsupportedEncodingException;
+
+import org.apache.poi.util.LittleEndianByteArrayInputStream;
 import org.apache.poi.util.Removal;
 
 /**
@@ -27,6 +30,7 @@ import org.apache.poi.util.Removal;
  * 
  * @deprecated POI 3.16 - use Property as base class instead
  */
+@Deprecated
 @Removal(version="3.18")
 public class MutableProperty extends Property {
     public MutableProperty() {}
@@ -35,4 +39,17 @@ public class MutableProperty extends Property {
        super(p);
     }
 
+    public MutableProperty(final long id, final long type, final Object value) {
+        super(id, type, value);
+    }
+    
+    public MutableProperty(final long id, final byte[] src, final long offset, final int length, final int codepage)
+    throws UnsupportedEncodingException {
+        super(id, src, offset, length, codepage);
+    }
+    
+    public MutableProperty(final long id, LittleEndianByteArrayInputStream leis, final int length, final int codepage)
+    throws UnsupportedEncodingException {
+        super(id, leis, length, codepage);
+    }
 }
index 508ede25cfeeca69ff39b591043f330f1f6a7963..fb83442693af333ae90a8ccb3466fa8b7673d044 100644 (file)
@@ -23,10 +23,8 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.io.UnsupportedEncodingException;
-import java.nio.charset.Charset;
-import java.util.Arrays;
+import java.util.ArrayList;
 import java.util.Collections;
-import java.util.LinkedList;
 import java.util.List;
 
 import org.apache.poi.EmptyFileException;
@@ -34,6 +32,7 @@ import org.apache.poi.hpsf.wellknown.PropertyIDMap;
 import org.apache.poi.hpsf.wellknown.SectionIDMap;
 import org.apache.poi.poifs.filesystem.DirectoryEntry;
 import org.apache.poi.poifs.filesystem.Entry;
+import org.apache.poi.util.CodePageUtil;
 import org.apache.poi.util.IOUtils;
 import org.apache.poi.util.LittleEndian;
 import org.apache.poi.util.LittleEndianConsts;
@@ -90,17 +89,17 @@ public class PropertySet {
     /**
      * The "byteOrder" field must equal this value.
      */
-    private static final int BYTE_ORDER_ASSERTION = 0xFFFE;
+    /* package */ static final int BYTE_ORDER_ASSERTION = 0xFFFE;
 
     /**
      * The "format" field must equal this value.
      */
-    private static final int FORMAT_ASSERTION = 0x0000;
+    /* package */ static final int FORMAT_ASSERTION = 0x0000;
 
     /**
      * The length of the property set stream header.
      */
-    private static final int OFFSET_HEADER =
+    /* package */ static final int OFFSET_HEADER =
         LittleEndianConsts.SHORT_SIZE + /* Byte order    */
         LittleEndianConsts.SHORT_SIZE + /* Format        */
         LittleEndianConsts.INT_SIZE +   /* OS version    */
@@ -135,7 +134,7 @@ public class PropertySet {
     /**
      * The sections in this {@link PropertySet}.
      */
-    private final List<Section> sections = new LinkedList<Section>();
+    private final List<Section> sections = new ArrayList<Section>();
 
     
     /**
@@ -513,11 +512,11 @@ public class PropertySet {
         final int nrSections = getSectionCount();
 
         /* Write the property set's header. */
-        TypeWriter.writeToStream(out, (short) getByteOrder());
-        TypeWriter.writeToStream(out, (short) getFormat());
-        TypeWriter.writeToStream(out, getOSVersion());
-        TypeWriter.writeToStream(out, getClassID());
-        TypeWriter.writeToStream(out, nrSections);
+        LittleEndian.putShort(out, (short) getByteOrder());
+        LittleEndian.putShort(out, (short) getFormat());
+        LittleEndian.putInt(getOSVersion(), out);
+        putClassId(out, getClassID());
+        LittleEndian.putInt(nrSections, out);
         int offset = OFFSET_HEADER;
 
         /* Write the section list, i.e. the references to the sections. Each
@@ -530,8 +529,8 @@ public class PropertySet {
             if (formatID == null) {
                 throw new NoFormatIDException();
             }
-            TypeWriter.writeToStream(out, section.getFormatID());
-            TypeWriter.writeUIntToStream(out, offset);
+            putClassId(out, formatID);
+            LittleEndian.putUInt(offset, out);
             try {
                 offset += section.getSize();
             } catch (HPSFRuntimeException ex) {
@@ -643,7 +642,12 @@ public class PropertySet {
                     return Long.toString( LittleEndian.getUInt(b) );
                 default:
                     // Maybe it's a string? who knows!
-                    return new String(b, Charset.forName("ASCII"));
+                    try {
+                        return CodePageUtil.getStringFromCodePage(b, Property.DEFAULT_CODEPAGE);
+                    } catch (UnsupportedEncodingException e) {
+                        // doesn't happen ...
+                        return "";
+                    }
             }
         }
         return propertyValue.toString();
@@ -656,7 +660,7 @@ public class PropertySet {
      * represents a Summary Information, else {@code false}.
      */
     public boolean isSummaryInformation() {
-        return matchesSummary(SectionIDMap.SUMMARY_INFORMATION_ID); 
+        return !sections.isEmpty() && matchesSummary(getFirstSection().getFormatID(), SectionIDMap.SUMMARY_INFORMATION_ID); 
     }
 
     /**
@@ -666,15 +670,17 @@ public class PropertySet {
      * represents a Document Summary Information, else {@code false}.
      */
     public boolean isDocumentSummaryInformation() {
-        return matchesSummary(SectionIDMap.DOCUMENT_SUMMARY_INFORMATION_ID[0]); 
-    }
-
-    private boolean matchesSummary(byte[] summaryBytes) {
-        return !sections.isEmpty() &&
-            Arrays.equals(getFirstSection().getFormatID().getBytes(), summaryBytes); 
+        return !sections.isEmpty() && matchesSummary(getFirstSection().getFormatID(), SectionIDMap.DOCUMENT_SUMMARY_INFORMATION_ID); 
     }
     
-    
+    /* package */ static boolean matchesSummary(ClassID actual, ClassID... expected) {
+        for (ClassID sum : expected) {
+            if (sum.equals(actual) || sum.equalsInverted(actual)) {
+                return true;
+            }
+        }
+        return false;
+    }
 
     /**
      * Convenience method returning the {@link Property} array contained in this
@@ -892,4 +898,10 @@ public class PropertySet {
     protected void set1stProperty(long id, byte[] value) {
         getFirstSection().setProperty((int)id, value);
     }
+    
+    private static void putClassId(final OutputStream out, final ClassID n) throws IOException {
+        byte[] b = new byte[16];
+        n.write(b, 0);
+        out.write(b, 0, b.length);
+    }
 }
index 16270eb1d56696b0f4315115a8adbe1d1d0fd8e7..ec9ba4c34f23aa751606555264070b5e78e6b86d 100644 (file)
@@ -21,10 +21,15 @@ import java.io.IOException;
 import java.io.OutputStream;
 
 import org.apache.poi.util.LittleEndian;
+import org.apache.poi.util.LittleEndianConsts;
+import org.apache.poi.util.Removal;
 
 /**
- * <p>Class for writing little-endian data and more.</p>
+ * Class for writing little-endian data and more.
+ * @deprecated POI 3.16 beta 2 - use LittleEndian instead
  */
+@Deprecated
+@Removal(version="3.18")
 public class TypeWriter
 {
 
@@ -40,7 +45,7 @@ public class TypeWriter
             throws IOException
     {
         LittleEndian.putShort( out, n ); // FIXME: unsigned
-        return LittleEndian.SHORT_SIZE;
+        return LittleEndianConsts.SHORT_SIZE;
     }
 
     /**
@@ -55,7 +60,7 @@ public class TypeWriter
             throws IOException
     {
         LittleEndian.putInt( n, out );
-        return LittleEndian.INT_SIZE;
+        return LittleEndianConsts.INT_SIZE;
     }
 
     /**
@@ -70,7 +75,7 @@ public class TypeWriter
             throws IOException
     {
         LittleEndian.putLong( n, out );
-        return LittleEndian.LONG_SIZE;
+        return LittleEndianConsts.LONG_SIZE;
     }
 
     /**
@@ -84,9 +89,10 @@ public class TypeWriter
             throws IOException
     {
         int high = n & 0xFFFF0000;
-        if ( high != 0 )
+        if ( high != 0 ) {
             throw new IllegalPropertySetDataException( "Value " + n
                     + " cannot be represented by 2 bytes." );
+        }
         LittleEndian.putUShort( n, out );
     }
 
@@ -102,11 +108,12 @@ public class TypeWriter
             throws IOException
     {
         long high = n & 0xFFFFFFFF00000000L;
-        if ( high != 0 && high != 0xFFFFFFFF00000000L )
+        if ( high != 0 && high != 0xFFFFFFFF00000000L ) {
             throw new IllegalPropertySetDataException( "Value " + n
                     + " cannot be represented by 4 bytes." );
+        }
         LittleEndian.putUInt( n, out );
-        return LittleEndian.INT_SIZE;
+        return LittleEndianConsts.INT_SIZE;
     }
 
     /**
@@ -145,8 +152,9 @@ public class TypeWriter
         throws IOException, UnsupportedVariantTypeException
     {
         /* If there are no properties don't write anything. */
-        if (properties == null)
+        if (properties == null) {
             return;
+        }
 
         /* Write the property list. This is a list containing pairs of property
          * ID and offset into the stream. */
@@ -154,7 +162,7 @@ public class TypeWriter
         {
             final Property p = properties[i];
             writeUIntToStream(out, p.getID());
-            writeUIntToStream(out, p.getSize());
+            writeUIntToStream(out, p.getSize(codepage));
         }
 
         /* Write the properties themselves. */
@@ -181,7 +189,7 @@ public class TypeWriter
             throws IOException
     {
         LittleEndian.putDouble( n, out );
-        return LittleEndian.DOUBLE_SIZE;
+        return LittleEndianConsts.DOUBLE_SIZE;
     }
 
 }
index 3093a10af4af8743b08a53e50dc35501c96c6ab3..7c0d726cb8cbdc7ce57df953cb6ceded578e3700 100644 (file)
  */
 package org.apache.poi.hpsf;
 
+import java.math.BigInteger;
+
 import org.apache.poi.util.Internal;
-import org.apache.poi.util.LittleEndian;
+import org.apache.poi.util.LittleEndianByteArrayInputStream;
+import org.apache.poi.util.LittleEndianConsts;
 import org.apache.poi.util.POILogFactory;
 import org.apache.poi.util.POILogger;
 
 @Internal
-class TypedPropertyValue
-{
-    private static final POILogger logger = POILogFactory
-            .getLogger( TypedPropertyValue.class );
+class TypedPropertyValue {
+    private static final POILogger LOG = POILogFactory.getLogger( TypedPropertyValue.class );
 
     private int _type;
-
     private Object _value;
 
-    TypedPropertyValue()
-    {
-    }
-
-    TypedPropertyValue( byte[] data, int startOffset )
-    {
-        read( data, startOffset );
-    }
-
-    TypedPropertyValue( int type, Object value )
-    {
+    TypedPropertyValue( int type, Object value ) {
         _type = type;
         _value = value;
     }
 
-    Object getValue()
-    {
+    Object getValue() {
         return _value;
     }
 
-    int read( byte[] data, int startOffset )
-    {
-        int offset = startOffset;
-
-        _type = LittleEndian.getShort( data, offset );
-        offset += LittleEndian.SHORT_SIZE;
-
-        short padding = LittleEndian.getShort( data, offset );
-        offset += LittleEndian.SHORT_SIZE;
-        if ( padding != 0 )
-        {
-            logger.log( POILogger.WARN, "TypedPropertyValue padding at offset "
-                    + offset + " MUST be 0, but it's value is " + padding );
+    void read( LittleEndianByteArrayInputStream lei ) {
+        _type = lei.readShort();
+        short padding = lei.readShort();
+        if ( padding != 0 ) {
+            LOG.log( POILogger.WARN, "TypedPropertyValue padding at offset "
+                    + lei.getReadIndex() + " MUST be 0, but it's value is " + padding );
         }
-
-        offset += readValue( data, offset );
-
-        return offset - startOffset;
+        readValue( lei );
     }
 
-    int readValue( byte[] data, int offset ) // NOSONAR
-    {
-        switch ( _type )
-        {
+    void readValue( LittleEndianByteArrayInputStream lei ) {
+        switch ( _type ) {
         case Variant.VT_EMPTY:
         case Variant.VT_NULL:
             _value = null;
-            return 0;
+            break;
+
+        case Variant.VT_I1:
+            _value = lei.readByte();
+            break;
+
+        case Variant.VT_UI1:
+            _value =  lei.readUByte();
+            break;
 
-        case Variant.VT_R4:
         case Variant.VT_I2:
-            _value = Short.valueOf( LittleEndian.getShort( data, offset ) );
-            return 4;
+            _value = lei.readShort();
+            break;
 
+        case Variant.VT_UI2:
+            _value = lei.readUShort();
+            break;
+            
         case Variant.VT_INT:
         case Variant.VT_I4:
-            _value = Integer.valueOf( LittleEndian.getInt( data, offset ) );
-            return 4;
+            _value = lei.readInt();
+            break;
+
+        case Variant.VT_UINT:
+        case Variant.VT_UI4:
+        case Variant.VT_ERROR:
+            _value = lei.readUInt();
+            break;
+
+        case Variant.VT_I8:
+            _value = lei.readLong();
+            break;
+
+        case Variant.VT_UI8: {
+            byte[] biBytesLE = new byte[LittleEndianConsts.LONG_SIZE];
+            lei.readFully(biBytesLE);
+
+            // first byte needs to be 0 for unsigned BigInteger
+            byte[] biBytesBE = new byte[9];
+            int i=biBytesLE.length;
+            for (byte b : biBytesLE) {
+                if (i<=8) {
+                    biBytesBE[i] = b;
+                }
+                i--;
+            }
+            _value = new BigInteger(biBytesBE);
+            break;
+        }
 
+            
+        case Variant.VT_R4:
+            _value = Float.intBitsToFloat(lei.readInt());
+            break;
+            
         case Variant.VT_R8:
-            _value = Double.valueOf( LittleEndian.getDouble( data, offset ) );
-            return 8;
+            _value = lei.readDouble();
+            break;
 
         case Variant.VT_CY:
-            _value = new Currency( data, offset );
-            return Currency.SIZE;
+            Currency cur = new Currency();
+            cur.read(lei);
+            _value = cur;
+            break;
+            
 
         case Variant.VT_DATE:
-            _value = new Date( data, offset );
-            return Date.SIZE;
+            Date date = new Date();
+            date.read(lei);
+            _value = date;
+            break;
 
         case Variant.VT_BSTR:
-            _value = new CodePageString( data, offset );
-            return ( (CodePageString) _value ).getSize();
+        case Variant.VT_LPSTR:
+            CodePageString cps = new CodePageString();
+            cps.read(lei);
+            _value = cps;
+            break;
 
         case Variant.VT_BOOL:
-            _value = new VariantBool( data, offset );
-            return VariantBool.SIZE;
+            VariantBool vb = new VariantBool();
+            vb.read(lei);
+            _value = vb;
+            break;
 
         case Variant.VT_DECIMAL:
-            _value = new Decimal( data, offset );
-            return Decimal.SIZE;
-
-        case Variant.VT_I1:
-            _value = Byte.valueOf( data[offset] );
-            return 1;
-
-        case Variant.VT_UI1:
-            _value = Short.valueOf( LittleEndian.getUByte( data, offset ) );
-            return 2;
-
-        case Variant.VT_UI2:
-            _value = Integer.valueOf( LittleEndian.getUShort( data, offset ) );
-            return 4;
-
-        case Variant.VT_UINT:
-        case Variant.VT_UI4:
-        case Variant.VT_ERROR:
-            _value = Long.valueOf( LittleEndian.getUInt( data, offset ) );
-            return 4;
-
-        case Variant.VT_I8:
-            _value = Long.valueOf( LittleEndian.getLong( data, offset ) );
-            return 8;
-
-        case Variant.VT_UI8:
-            _value = LittleEndian.getByteArray( data, offset, 8 );
-            return 8;
-
-        case Variant.VT_LPSTR:
-            _value = new CodePageString( data, offset );
-            return ( (CodePageString) _value ).getSize();
+            Decimal dec = new Decimal();
+            dec.read(lei);
+            _value = dec;
+            break;
 
         case Variant.VT_LPWSTR:
-            _value = new UnicodeString( data, offset );
-            return ( (UnicodeString) _value ).getSize();
+            UnicodeString us = new UnicodeString();
+            us.read(lei);
+            _value = us;
+            break;
 
         case Variant.VT_FILETIME:
-            _value = new Filetime( data, offset );
-            return Filetime.SIZE;
+            Filetime ft = new Filetime();
+            ft.read(lei);
+            _value = ft;
+            break;
 
         case Variant.VT_BLOB:
-            _value = new Blob( data, offset );
-            return ( (Blob) _value ).getSize();
+        case Variant.VT_BLOB_OBJECT:
+            Blob blob = new Blob();
+            blob.read(lei);
+            _value = blob;
+            break;
 
         case Variant.VT_STREAM:
         case Variant.VT_STORAGE:
         case Variant.VT_STREAMED_OBJECT:
         case Variant.VT_STORED_OBJECT:
-            _value = new IndirectPropertyName( data, offset );
-            return ( (IndirectPropertyName) _value ).getSize();
-
-        case Variant.VT_BLOB_OBJECT:
-            _value = new Blob( data, offset );
-            return ( (Blob) _value ).getSize();
+            IndirectPropertyName ipn = new IndirectPropertyName();
+            ipn.read(lei);
+            _value = ipn;
+            break;
 
         case Variant.VT_CF:
-            _value = new ClipboardData( data, offset );
-            return ( (ClipboardData) _value ).getSize();
+            ClipboardData cd = new ClipboardData();
+            cd.read(lei);
+            _value = cd;
+            break;
 
         case Variant.VT_CLSID:
-            _value = new GUID( data, offset );
-            return GUID.SIZE;
+            GUID guid = new GUID();
+            guid.read(lei);
+            _value = lei;
+            break;
 
         case Variant.VT_VERSIONED_STREAM:
-            _value = new VersionedStream( data, offset );
-            return ( (VersionedStream) _value ).getSize();
+            VersionedStream vs = new VersionedStream();
+            vs.read(lei);
+            _value = vs;
+            break;
 
         case Variant.VT_VECTOR | Variant.VT_I2:
         case Variant.VT_VECTOR | Variant.VT_I4:
@@ -202,8 +215,10 @@ class TypedPropertyValue
         case Variant.VT_VECTOR | Variant.VT_FILETIME:
         case Variant.VT_VECTOR | Variant.VT_CF:
         case Variant.VT_VECTOR | Variant.VT_CLSID:
-            _value = new Vector( (short) ( _type & 0x0FFF ) );
-            return ( (Vector) _value ).read( data, offset );
+            Vector vec = new Vector( (short) ( _type & 0x0FFF ) );
+            vec.read(lei);
+            _value = vec;
+            break;
 
         case Variant.VT_ARRAY | Variant.VT_I2:
         case Variant.VT_ARRAY | Variant.VT_I4:
@@ -222,20 +237,27 @@ class TypedPropertyValue
         case Variant.VT_ARRAY | Variant.VT_UI4:
         case Variant.VT_ARRAY | Variant.VT_INT:
         case Variant.VT_ARRAY | Variant.VT_UINT:
-            _value = new Array();
-            return ( (Array) _value ).read( data, offset );
+            Array arr = new Array();
+            arr.read(lei);
+            _value = arr;
+            break;
 
         default:
-            throw new UnsupportedOperationException(
-                    "Unknown (possibly, incorrect) TypedPropertyValue type: "
-                            + _type );
+            String msg = "Unknown (possibly, incorrect) TypedPropertyValue type: " + _type;
+            throw new UnsupportedOperationException(msg);
         }
     }
 
-    int readValuePadded( byte[] data, int offset )
-    {
-        int nonPadded = readValue( data, offset );
-        return ( nonPadded & 0x03 ) == 0 ? nonPadded : nonPadded
-                + ( 4 - ( nonPadded & 0x03 ) );
+    static void skipPadding( LittleEndianByteArrayInputStream lei ) {
+        final int offset = lei.getReadIndex();
+        int skipBytes = (4 - (offset & 3)) & 3;
+        for (int i=0; i<skipBytes; i++) {
+            lei.mark(1);
+            int b = lei.read();
+            if (b == -1 || b != 0) {
+                lei.reset();
+                break;
+            }
+        }
     }
 }
index 0d3cdbf292c991aeb24abbbf2332183911a1847f..0826ced2d72eb8b81d9f8f2e3325e2eb97e70500 100644 (file)
 ==================================================================== */
 package org.apache.poi.hpsf;
 
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+
+import org.apache.poi.util.CodePageUtil;
 import org.apache.poi.util.Internal;
 import org.apache.poi.util.LittleEndian;
+import org.apache.poi.util.LittleEndianByteArrayInputStream;
+import org.apache.poi.util.LittleEndianConsts;
 import org.apache.poi.util.POILogFactory;
 import org.apache.poi.util.POILogger;
 import org.apache.poi.util.StringUtil;
 
 @Internal
 class UnicodeString {
-    private final static POILogger logger = 
-            POILogFactory.getLogger( UnicodeString.class );
+    private static final POILogger LOG = POILogFactory.getLogger( UnicodeString.class );
 
     private byte[] _value;
+    
+    UnicodeString() {}
 
-    UnicodeString(byte[] data, int offset) {
-        int length = LittleEndian.getInt( data, offset );
-        int dataOffset = offset + LittleEndian.INT_SIZE;
+    void read(LittleEndianByteArrayInputStream lei) {
+        final int length = lei.readInt();
+        final int unicodeBytes = length*2;
+        _value = new byte[unicodeBytes];
         
-        if (! validLength(length, data, dataOffset)) {
-            // If the length looks wrong, this might be because the offset is sometimes expected 
-            // to be on a 4 byte boundary. Try checking with that if so, rather than blowing up with
-            // and  ArrayIndexOutOfBoundsException below
-            boolean valid = false;
-            int past4byte = offset % 4;
-            if (past4byte != 0) {
-                offset = offset + past4byte;
-                length = LittleEndian.getInt( data, offset );
-                dataOffset = offset + LittleEndian.INT_SIZE;
-                
-                valid = validLength(length, data, dataOffset);
-            }
-            
-            if (!valid) {
-                throw new IllegalPropertySetDataException(
-                        "UnicodeString started at offset #" + offset +
-                        " is not NULL-terminated" );
-            }
-        }
-
-        if ( length == 0 )
-        {
-            _value = new byte[0];
+        // If Length is zero, this field MUST be zero bytes in length. If Length is
+        // nonzero, this field MUST be a null-terminated array of 16-bit Unicode characters, followed by
+        // zero padding to a multiple of 4 bytes. The string represented by this field SHOULD NOT
+        // contain embedded or additional trailing null characters.
+        
+        if (length == 0) {
             return;
         }
 
-        _value = LittleEndian.getByteArray( data, dataOffset, length * 2 );
-    }
-    
-    /**
-     * Checks to see if the specified length seems valid,
-     *  given the amount of data available still to read,
-     *  and the requirement that the string be NULL-terminated
-     */
-    boolean validLength(int length, byte[] data, int offset) {
-        if (length == 0) {
-            return true;
-        }
+        final int offset = lei.getReadIndex();
+        
+        lei.readFully(_value);
 
-        int endOffset = offset + (length * 2);
-        if (endOffset <= data.length) {
-            // Data Length is OK, ensure it's null terminated too
-            if (data[endOffset-1] == 0 && data[endOffset-2] == 0) {
-                // Length looks plausible
-                return true;
-            }
+        if (_value[unicodeBytes-2] != 0 || _value[unicodeBytes-1] != 0) {
+            String msg = "UnicodeString started at offset #" + offset + " is not NULL-terminated";
+            throw new IllegalPropertySetDataException(msg);
         }
-
-        // Something's up/invalid with that length for the given data+offset
-        return false;
-    }
-
-    int getSize()
-    {
-        return LittleEndian.INT_SIZE + _value.length;
+        
+        TypedPropertyValue.skipPadding(lei);
     }
-
-    byte[] getValue()
-    {
+    
+    byte[] getValue() {
         return _value;
     }
 
-    String toJavaString()
-    {
-        if ( _value.length == 0 )
+    String toJavaString() {
+        if ( _value.length == 0 ) {
             return null;
+        }
 
-        String result = StringUtil.getFromUnicodeLE( _value, 0,
-                _value.length >> 1 );
+        String result = StringUtil.getFromUnicodeLE( _value, 0, _value.length >> 1 );
 
         final int terminator = result.indexOf( '\0' );
-        if ( terminator == -1 )
-        {
-            logger.log(
-                    POILogger.WARN,
-                    "String terminator (\\0) for UnicodeString property value not found."
-                            + "Continue without trimming and hope for the best." );
+        if ( terminator == -1 ) {
+            String msg =
+                "String terminator (\\0) for UnicodeString property value not found."+
+                "Continue without trimming and hope for the best.";
+            LOG.log(POILogger.WARN, msg);
             return result;
         }
-        if ( terminator != result.length() - 1 )
-        {
-            logger.log(
-                    POILogger.WARN,
-                    "String terminator (\\0) for UnicodeString property value occured before the end of string. "
-                            + "Trimming and hope for the best." );
+        
+        if ( terminator != result.length() - 1 ) {
+            String msg =
+                "String terminator (\\0) for UnicodeString property value occured before the end of string. "+
+                "Trimming and hope for the best.";
+            LOG.log(POILogger.WARN, msg);
         }
         return result.substring( 0, terminator );
     }
+
+    void setJavaValue( String string ) throws UnsupportedEncodingException {
+        _value = CodePageUtil.getBytesInCodePage(string + "\0", CodePageUtil.CP_UNICODE);
+    }
+
+    int write( OutputStream out ) throws IOException {
+        LittleEndian.putUInt( _value.length / 2, out );
+        out.write( _value );
+        return LittleEndianConsts.INT_SIZE + _value.length;
+    }
 }
index d8ab7315db6c574eac154c3afd886c5498b30942..d18fe811be77f3b05887a8e3fa1db0097e39b4d9 100644 (file)
@@ -95,38 +95,6 @@ public class Util
         return ms_since_16010101 * (1000 * 10);
     }
 
-
-
-    /**
-     * <p>Compares to object arrays with regarding the objects' order. For
-     * example, [1, 2, 3] and [2, 1, 3] are equal.</p>
-     *
-     * @param c1 The first object array.
-     * @param c2 The second object array.
-     * @return <code>true</code> if the object arrays are equal,
-     * <code>false</code> if they are not.
-     */
-    public static boolean equals(Object[] c1, Object[] c2)
-    {
-        for (int i1 = 0; i1 < c1.length; i1++)
-        {
-            final Object obj1 = c1[i1];
-            boolean matchFound = false;
-            for (int i2 = 0; !matchFound && i2 < c1.length; i2++)
-            {
-                final Object obj2 = c2[i2];
-                if (obj1.equals(obj2))
-                {
-                    matchFound = true;
-                    c2[i2] = null;
-                }
-            }
-            if (!matchFound)
-                return false;
-        }
-        return true;
-    }
-
     /**
      * <p>Pads a byte array with 0x00 bytes so that its length is a multiple of
      * 4.</p>
index 3f606043aac87a1065dc177e2b2db179b5f4ad43..ba67252d63b08f632670ddbe0e6e807244a32bbd 100644 (file)
@@ -22,277 +22,247 @@ 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
- * href="http://www.marin.clara.net/COM/variant_type_definitions.htm">
- * http://www.marin.clara.net/COM/variant_type_definitions.htm</a>.</p>
+ * The {@code Variant} types as defined by Microsoft's COM.
  *
- * <p>In the variant types descriptions the following shortcuts are
- * used: <strong> [V]</strong> - may appear in a VARIANT,
- * <strong>[T]</strong> - may appear in a TYPEDESC,
- * <strong>[P]</strong> - may appear in an OLE property set,
- * <strong>[S]</strong> - may appear in a Safe Array.</p>
+ * In the variant types descriptions the following shortcuts are used:
+ * <ul>
+ * <li>[V] - may appear in a VARIANT
+ * <li>[T] - may appear in a TYPEDESC
+ * <li>[P] - may appear in an OLE property set
+ * <li>[S] - may appear in a Safe Array
+ * </ul>
  */
 public class Variant
 {
 
     /**
-     * <p>[V][P] Nothing, i.e. not a single byte of data.</p>
+     * [V][P] Nothing, i.e. not a single byte of data.
      */
     public static final int VT_EMPTY = 0;
 
     /**
-     * <p>[V][P] SQL style Null.</p>
+     * [V][P] SQL style Null.
      */
     public static final int VT_NULL = 1;
 
     /**
-     * <p>[V][T][P][S] 2 byte signed int.</p>
+     * [V][T][P][S] 2 byte signed int.
      */
     public static final int VT_I2 = 2;
 
     /**
-     * <p>[V][T][P][S] 4 byte signed int.</p>
+     * [V][T][P][S] 4 byte signed int.
      */
     public static final int VT_I4 = 3;
 
     /**
-     * <p>[V][T][P][S] 4 byte real.</p>
+     * [V][T][P][S] 4 byte real.
      */
     public static final int VT_R4 = 4;
 
     /**
-     * <p>[V][T][P][S] 8 byte real.</p>
+     * [V][T][P][S] 8 byte real.
      */
     public static final int VT_R8 = 5;
 
     /**
-     * <p>[V][T][P][S] currency. <span style="background-color:
-     * #ffff00">How long is this? How is it to be
-     * interpreted?</span></p>
+     * [V][T][P][S] currency.
      */
     public static final int VT_CY = 6;
 
     /**
-     * <p>[V][T][P][S] date. <span style="background-color:
-     * #ffff00">How long is this? How is it to be
-     * interpreted?</span></p>
+     * [V][T][P][S] date.
      */
     public static final int VT_DATE = 7;
 
     /**
-     * <p>[V][T][P][S] OLE Automation string. <span
-     * style="background-color: #ffff00">How long is this? How is it
-     * to be interpreted?</span></p>
+     * [V][T][P][S] OLE Automation string.
      */
     public static final int VT_BSTR = 8;
 
     /**
-     * <p>[V][T][P][S] IDispatch *. <span style="background-color:
-     * #ffff00">How long is this? How is it to be
-     * interpreted?</span></p>
+     * [V][T][P][S] IDispatch
      */
     public static final int VT_DISPATCH = 9;
 
     /**
-     * <p>[V][T][S] SCODE. <span style="background-color: #ffff00">How
-     * long is this? How is it to be interpreted?</span></p>
+     * [V][T][S] SCODE
      */
     public static final int VT_ERROR = 10;
 
     /**
-     * <p>[V][T][P][S] True=-1, False=0.</p>
+     * [V][T][P][S] True=-1, False=0.
      */
     public static final int VT_BOOL = 11;
 
     /**
-     * <p>[V][T][P][S] VARIANT *. <span style="background-color:
-     * #ffff00">How long is this? How is it to be
-     * interpreted?</span></p>
+     * [V][T][P][S] VARIANT
      */
     public static final int VT_VARIANT = 12;
 
     /**
-     * <p>[V][T][S] IUnknown *. <span style="background-color:
-     * #ffff00">How long is this? How is it to be
-     * interpreted?</span></p>
+     * [V][T][S] IUnknown
      */
     public static final int VT_UNKNOWN = 13;
 
     /**
-     * <p>[V][T][S] 16 byte fixed point.</p>
+     * [V][T][S] 16 byte fixed point.
      */
     public static final int VT_DECIMAL = 14;
 
     /**
-     * <p>[T] signed char.</p>
+     * [T] signed char.
      */
     public static final int VT_I1 = 16;
 
     /**
-     * <p>[V][T][P][S] unsigned char.</p>
+     * [V][T][P][S] unsigned char.
      */
     public static final int VT_UI1 = 17;
 
     /**
-     * <p>[T][P] unsigned short.</p>
+     * [T][P] unsigned short.
      */
     public static final int VT_UI2 = 18;
 
     /**
-     * <p>[T][P] unsigned int.</p>
+     * [T][P] unsigned int.
      */
     public static final int VT_UI4 = 19;
 
     /**
-     * <p>[T][P] signed 64-bit int.</p>
+     * [T][P] signed 64-bit int.
      */
     public static final int VT_I8 = 20;
 
     /**
-     * <p>[T][P] unsigned 64-bit int.</p>
+     * [T][P] unsigned 64-bit int.
      */
     public static final int VT_UI8 = 21;
 
     /**
-     * <p>[T] signed machine int.</p>
+     * [T] signed machine int.
      */
     public static final int VT_INT = 22;
 
     /**
-     * <p>[T] unsigned machine int.</p>
+     * [T] unsigned machine int.
      */
     public static final int VT_UINT = 23;
 
     /**
-     * <p>[T] C style void.</p>
+     * [T] C style void.
      */
     public static final int VT_VOID = 24;
 
     /**
-     * <p>[T] Standard return type. <span style="background-color:
-     * #ffff00">How long is this? How is it to be
-     * interpreted?</span></p>
+     * [T] Standard return type.
      */
     public static final int VT_HRESULT = 25;
 
     /**
-     * <p>[T] pointer type. <span style="background-color:
-     * #ffff00">How long is this? How is it to be
-     * interpreted?</span></p>
+     * [T] pointer type.
      */
     public static final int VT_PTR = 26;
 
     /**
-     * <p>[T] (use VT_ARRAY in VARIANT).</p>
+     * [T] (use VT_ARRAY in VARIANT).
      */
     public static final int VT_SAFEARRAY = 27;
 
     /**
-     * <p>[T] C style array. <span style="background-color:
+     * [T] C style array. <span style="background-color:
      * #ffff00">How long is this? How is it to be
-     * interpreted?</span></p>
+     * interpreted?</span>
      */
     public static final int VT_CARRAY = 28;
 
     /**
-     * <p>[T] user defined type. <span style="background-color:
-     * #ffff00">How long is this? How is it to be
-     * interpreted?</span></p>
+     * [T] user defined type.
      */
     public static final int VT_USERDEFINED = 29;
 
     /**
-     * <p>[T][P] null terminated string.</p>
+     * [T][P] null terminated string.
      */
     public static final int VT_LPSTR = 30;
 
     /**
-     * <p>[T][P] wide (Unicode) null terminated string.</p>
+     * [T][P] wide (Unicode) null terminated string.
      */
     public static final int VT_LPWSTR = 31;
 
     /**
-     * <p>[P] FILETIME. The FILETIME structure holds a date and time
+     * [P] FILETIME. The FILETIME structure holds a date and time
      * associated with a file. The structure identifies a 64-bit
      * integer specifying the number of 100-nanosecond intervals which
      * have passed since January 1, 1601. This 64-bit value is split
-     * into the two dwords stored in the structure.</p>
+     * into the two dwords stored in the structure.
      */
     public static final int VT_FILETIME = 64;
 
     /**
-     * <p>[P] Length prefixed bytes.</p>
+     * [P] Length prefixed bytes.
      */
     public static final int VT_BLOB = 65;
 
     /**
-     * <p>[P] Name of the stream follows.</p>
+     * [P] Name of the stream follows.
      */
     public static final int VT_STREAM = 66;
 
     /**
-     * <p>[P] Name of the storage follows.</p>
+     * [P] Name of the storage follows.
      */
     public static final int VT_STORAGE = 67;
 
     /**
-     * <p>[P] Stream contains an object. <span
-     * style="background-color: #ffff00"> How long is this? How is it
-     * to be interpreted?</span></p>
+     * [P] Stream contains an object.
      */
     public static final int VT_STREAMED_OBJECT = 68;
 
     /**
-     * <p>[P] Storage contains an object. <span
-     * style="background-color: #ffff00"> How long is this? How is it
-     * to be interpreted?</span></p>
+     * [P] Storage contains an object.
      */
     public static final int VT_STORED_OBJECT = 69;
 
     /**
-     * <p>[P] Blob contains an object. <span style="background-color:
-     * #ffff00">How long is this? How is it to be
-     * interpreted?</span></p>
+     * [P] Blob contains an object.
      */
     public static final int VT_BLOB_OBJECT = 70;
 
     /**
-     * <p>[P] Clipboard format. <span style="background-color:
-     * #ffff00">How long is this? How is it to be
-     * interpreted?</span></p>
+     * [P] Clipboard format.
      */
     public static final int VT_CF = 71;
 
     /**
-     * <p>[P] A Class ID.</p>
+     * [P] A Class ID.<p>
      *
-     * <p>It consists of a 32 bit unsigned integer indicating the size
+     * It consists of a 32 bit unsigned integer indicating the size
      * of the structure, a 32 bit signed integer indicating (Clipboard
      * Format Tag) indicating the type of data that it contains, and
-     * then a byte array containing the data.</p>
+     * then a byte array containing the data.<p>
      *
-     * <p>The valid Clipboard Format Tags are:</p>
+     * The valid Clipboard Format Tags are:
      *
      * <ul>
-     *  <li>{@link Thumbnail#CFTAG_WINDOWS}</li>
-     *  <li>{@link Thumbnail#CFTAG_MACINTOSH}</li>
-     *  <li>{@link Thumbnail#CFTAG_NODATA}</li>
-     *  <li>{@link Thumbnail#CFTAG_FMTID}</li>
+     *  <li>{@link Thumbnail#CFTAG_WINDOWS}
+     *  <li>{@link Thumbnail#CFTAG_MACINTOSH}
+     *  <li>{@link Thumbnail#CFTAG_NODATA}
+     *  <li>{@link Thumbnail#CFTAG_FMTID}
      * </ul>
      *
-     * <pre>typedef struct tagCLIPDATA {
+     * <pre>{@code
+     * typedef struct tagCLIPDATA {
      * // cbSize is the size of the buffer pointed to
      * // by pClipData, plus sizeof(ulClipFmt)
      * ULONG              cbSize;
      * long               ulClipFmt;
      * BYTE*              pClipData;
-     * } CLIPDATA;</pre>
+     * } CLIPDATA;}</pre>
      *
-     * <p>See <a
-     * href="msdn.microsoft.com/library/en-us/com/stgrstrc_0uwk.asp"
-     * target="_blank">
-     * msdn.microsoft.com/library/en-us/com/stgrstrc_0uwk.asp</a>.</p>
+     * @see <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/aa380072(v=vs.85).aspx">PROPVARIANT structure</a>
      */
     public static final int VT_CLSID = 72;
 
@@ -305,199 +275,156 @@ public class Variant
     public static final int VT_VERSIONED_STREAM = 0x0049;
 
     /**
-     * <p>[P] simple counted array. <span style="background-color:
+     * [P] simple counted array. <span style="background-color:
      * #ffff00">How long is this? How is it to be
-     * interpreted?</span></p>
+     * interpreted?</span>
      */
     public static final int VT_VECTOR = 0x1000;
 
     /**
-     * <p>[V] SAFEARRAY*. <span style="background-color: #ffff00">How
-     * long is this? How is it to be interpreted?</span></p>
+     * [V] SAFEARRAY*. <span style="background-color: #ffff00">How
+     * long is this? How is it to be interpreted?</span>
      */
     public static final int VT_ARRAY = 0x2000;
 
     /**
-     * <p>[V] void* for local use. <span style="background-color:
+     * [V] void* for local use. <span style="background-color:
      * #ffff00">How long is this? How is it to be
-     * interpreted?</span></p>
+     * interpreted?</span>
      */
     public static final int VT_BYREF = 0x4000;
 
-    /**
-     * <p>FIXME (3): Document this!</p>
-     */
     public static final int VT_RESERVED = 0x8000;
 
-    /**
-     * <p>FIXME (3): Document this!</p>
-     */
     public static final int VT_ILLEGAL = 0xFFFF;
 
+    public static final int VT_ILLEGALMASKED = 0xFFF;
+
+    public static final int VT_TYPEMASK = 0xFFF;
+
+
+
     /**
-     * <p>FIXME (3): Document this!</p>
+     * Maps the numbers denoting the variant types to their corresponding
+     * variant type names.
      */
-    public static final int VT_ILLEGALMASKED = 0xFFF;
+    private static final Map<Long,String> numberToName;
+
+    private static final Map<Long,Integer> numberToLength;
 
     /**
-     * <p>FIXME (3): Document this!</p>
+     * Denotes a variant type with a length that is unknown to HPSF yet.
      */
-    public static final int VT_TYPEMASK = 0xFFF;
+    public static final Integer LENGTH_UNKNOWN = -2;
 
+    /**
+     * Denotes a variant type with a variable length.
+     */
+    public static final Integer LENGTH_VARIABLE = -1;
 
+    /**
+     * Denotes a variant type with a length of 0 bytes.
+     */
+    public static final Integer LENGTH_0 = 0;
 
     /**
-     * <p>Maps the numbers denoting the variant types to their corresponding
-     * variant type names.</p>
+     * Denotes a variant type with a length of 2 bytes.
      */
-    private static Map<Long,String> numberToName;
+    public static final Integer LENGTH_2 = 2;
 
-    private static Map<Long,Integer> numberToLength;
+    /**
+     * Denotes a variant type with a length of 4 bytes.
+     */
+    public static final Integer LENGTH_4 = 4;
 
     /**
-     * <p>Denotes a variant type with a length that is unknown to HPSF yet.</p>
+     * Denotes a variant type with a length of 8 bytes.
      */
-    public static final Integer LENGTH_UNKNOWN = Integer.valueOf(-2);
-
-    /**
-     * <p>Denotes a variant type with a variable length.</p>
-     */
-    public static final Integer LENGTH_VARIABLE = Integer.valueOf(-1);
-
-    /**
-     * <p>Denotes a variant type with a length of 0 bytes.</p>
-     */
-    public static final Integer LENGTH_0 = Integer.valueOf(0);
-
-    /**
-     * <p>Denotes a variant type with a length of 2 bytes.</p>
-     */
-    public static final Integer LENGTH_2 = Integer.valueOf(2);
-
-    /**
-     * <p>Denotes a variant type with a length of 4 bytes.</p>
-     */
-    public static final Integer LENGTH_4 = Integer.valueOf(4);
-
-    /**
-     * <p>Denotes a variant type with a length of 8 bytes.</p>
-     */
-    public static final Integer LENGTH_8 = Integer.valueOf(8);
-
-
-
-    static
-    {
-        /* Initialize the number-to-name map: */
-        Map<Long,String> tm1 = new HashMap<Long,String>();
-        tm1.put(Long.valueOf(0), "VT_EMPTY");
-        tm1.put(Long.valueOf(1), "VT_NULL");
-        tm1.put(Long.valueOf(2), "VT_I2");
-        tm1.put(Long.valueOf(3), "VT_I4");
-        tm1.put(Long.valueOf(4), "VT_R4");
-        tm1.put(Long.valueOf(5), "VT_R8");
-        tm1.put(Long.valueOf(6), "VT_CY");
-        tm1.put(Long.valueOf(7), "VT_DATE");
-        tm1.put(Long.valueOf(8), "VT_BSTR");
-        tm1.put(Long.valueOf(9), "VT_DISPATCH");
-        tm1.put(Long.valueOf(10), "VT_ERROR");
-        tm1.put(Long.valueOf(11), "VT_BOOL");
-        tm1.put(Long.valueOf(12), "VT_VARIANT");
-        tm1.put(Long.valueOf(13), "VT_UNKNOWN");
-        tm1.put(Long.valueOf(14), "VT_DECIMAL");
-        tm1.put(Long.valueOf(16), "VT_I1");
-        tm1.put(Long.valueOf(17), "VT_UI1");
-        tm1.put(Long.valueOf(18), "VT_UI2");
-        tm1.put(Long.valueOf(19), "VT_UI4");
-        tm1.put(Long.valueOf(20), "VT_I8");
-        tm1.put(Long.valueOf(21), "VT_UI8");
-        tm1.put(Long.valueOf(22), "VT_INT");
-        tm1.put(Long.valueOf(23), "VT_UINT");
-        tm1.put(Long.valueOf(24), "VT_VOID");
-        tm1.put(Long.valueOf(25), "VT_HRESULT");
-        tm1.put(Long.valueOf(26), "VT_PTR");
-        tm1.put(Long.valueOf(27), "VT_SAFEARRAY");
-        tm1.put(Long.valueOf(28), "VT_CARRAY");
-        tm1.put(Long.valueOf(29), "VT_USERDEFINED");
-        tm1.put(Long.valueOf(30), "VT_LPSTR");
-        tm1.put(Long.valueOf(31), "VT_LPWSTR");
-        tm1.put(Long.valueOf(64), "VT_FILETIME");
-        tm1.put(Long.valueOf(65), "VT_BLOB");
-        tm1.put(Long.valueOf(66), "VT_STREAM");
-        tm1.put(Long.valueOf(67), "VT_STORAGE");
-        tm1.put(Long.valueOf(68), "VT_STREAMED_OBJECT");
-        tm1.put(Long.valueOf(69), "VT_STORED_OBJECT");
-        tm1.put(Long.valueOf(70), "VT_BLOB_OBJECT");
-        tm1.put(Long.valueOf(71), "VT_CF");
-        tm1.put(Long.valueOf(72), "VT_CLSID");
-        Map<Long,String> tm2 = new HashMap<Long,String>(tm1.size(), 1.0F);
-        tm2.putAll(tm1);
-        numberToName = Collections.unmodifiableMap(tm2);
-
-        /* Initialize the number-to-length map: */
-        Map<Long,Integer> tm3 = new HashMap<Long,Integer>();
-        tm3.put(Long.valueOf(0), LENGTH_0);
-        tm3.put(Long.valueOf(1), LENGTH_UNKNOWN);
-        tm3.put(Long.valueOf(2), LENGTH_2);
-        tm3.put(Long.valueOf(3), LENGTH_4);
-        tm3.put(Long.valueOf(4), LENGTH_4);
-        tm3.put(Long.valueOf(5), LENGTH_8);
-        tm3.put(Long.valueOf(6), LENGTH_UNKNOWN);
-        tm3.put(Long.valueOf(7), LENGTH_UNKNOWN);
-        tm3.put(Long.valueOf(8), LENGTH_UNKNOWN);
-        tm3.put(Long.valueOf(9), LENGTH_UNKNOWN);
-        tm3.put(Long.valueOf(10), LENGTH_UNKNOWN);
-        tm3.put(Long.valueOf(11), LENGTH_UNKNOWN);
-        tm3.put(Long.valueOf(12), LENGTH_UNKNOWN);
-        tm3.put(Long.valueOf(13), LENGTH_UNKNOWN);
-        tm3.put(Long.valueOf(14), LENGTH_UNKNOWN);
-        tm3.put(Long.valueOf(16), LENGTH_UNKNOWN);
-        tm3.put(Long.valueOf(17), LENGTH_UNKNOWN);
-        tm3.put(Long.valueOf(18), LENGTH_UNKNOWN);
-        tm3.put(Long.valueOf(19), LENGTH_UNKNOWN);
-        tm3.put(Long.valueOf(20), LENGTH_UNKNOWN);
-        tm3.put(Long.valueOf(21), LENGTH_UNKNOWN);
-        tm3.put(Long.valueOf(22), LENGTH_UNKNOWN);
-        tm3.put(Long.valueOf(23), LENGTH_UNKNOWN);
-        tm3.put(Long.valueOf(24), LENGTH_UNKNOWN);
-        tm3.put(Long.valueOf(25), LENGTH_UNKNOWN);
-        tm3.put(Long.valueOf(26), LENGTH_UNKNOWN);
-        tm3.put(Long.valueOf(27), LENGTH_UNKNOWN);
-        tm3.put(Long.valueOf(28), LENGTH_UNKNOWN);
-        tm3.put(Long.valueOf(29), LENGTH_UNKNOWN);
-        tm3.put(Long.valueOf(30), LENGTH_VARIABLE);
-        tm3.put(Long.valueOf(31), LENGTH_UNKNOWN);
-        tm3.put(Long.valueOf(64), LENGTH_8);
-        tm3.put(Long.valueOf(65), LENGTH_UNKNOWN);
-        tm3.put(Long.valueOf(66), LENGTH_UNKNOWN);
-        tm3.put(Long.valueOf(67), LENGTH_UNKNOWN);
-        tm3.put(Long.valueOf(68), LENGTH_UNKNOWN);
-        tm3.put(Long.valueOf(69), LENGTH_UNKNOWN);
-        tm3.put(Long.valueOf(70), LENGTH_UNKNOWN);
-        tm3.put(Long.valueOf(71), LENGTH_UNKNOWN);
-        tm3.put(Long.valueOf(72), LENGTH_UNKNOWN);
-        Map<Long,Integer> tm4 = new HashMap<Long,Integer>(tm1.size(), 1.0F);
-        tm4.putAll(tm3);
-        numberToLength = Collections.unmodifiableMap(tm4);
-    }
+    public static final Integer LENGTH_8 = 8;
 
+    private static final Object NUMBER_TO_NAME_LIST[][] = {
+        {  0L, "VT_EMPTY",           LENGTH_0 },
+        {  1L, "VT_NULL",            LENGTH_UNKNOWN },
+        {  2L, "VT_I2",              LENGTH_2 },
+        {  3L, "VT_I4",              LENGTH_4 },
+        {  4L, "VT_R4",              LENGTH_4 },
+        {  5L, "VT_R8",              LENGTH_8 },
+        {  6L, "VT_CY",              LENGTH_UNKNOWN },
+        {  7L, "VT_DATE",            LENGTH_UNKNOWN },
+        {  8L, "VT_BSTR",            LENGTH_UNKNOWN },
+        {  9L, "VT_DISPATCH",        LENGTH_UNKNOWN },
+        { 10L, "VT_ERROR",           LENGTH_UNKNOWN },
+        { 11L, "VT_BOOL",            LENGTH_UNKNOWN },
+        { 12L, "VT_VARIANT",         LENGTH_UNKNOWN },
+        { 13L, "VT_UNKNOWN",         LENGTH_UNKNOWN },
+        { 14L, "VT_DECIMAL",         LENGTH_UNKNOWN },
+        { 16L, "VT_I1",              LENGTH_UNKNOWN },
+        { 17L, "VT_UI1",             LENGTH_UNKNOWN },
+        { 18L, "VT_UI2",             LENGTH_UNKNOWN },
+        { 19L, "VT_UI4",             LENGTH_UNKNOWN },
+        { 20L, "VT_I8",              LENGTH_UNKNOWN },
+        { 21L, "VT_UI8",             LENGTH_UNKNOWN },
+        { 22L, "VT_INT",             LENGTH_UNKNOWN },
+        { 23L, "VT_UINT",            LENGTH_UNKNOWN },
+        { 24L, "VT_VOID",            LENGTH_UNKNOWN },
+        { 25L, "VT_HRESULT",         LENGTH_UNKNOWN },
+        { 26L, "VT_PTR",             LENGTH_UNKNOWN },
+        { 27L, "VT_SAFEARRAY",       LENGTH_UNKNOWN },
+        { 28L, "VT_CARRAY",          LENGTH_UNKNOWN },
+        { 29L, "VT_USERDEFINED",     LENGTH_UNKNOWN },
+        { 30L, "VT_LPSTR",           LENGTH_VARIABLE },
+        { 31L, "VT_LPWSTR",          LENGTH_UNKNOWN },
+        { 64L, "VT_FILETIME",        LENGTH_8 },
+        { 65L, "VT_BLOB",            LENGTH_UNKNOWN },
+        { 66L, "VT_STREAM",          LENGTH_UNKNOWN },
+        { 67L, "VT_STORAGE",         LENGTH_UNKNOWN },
+        { 68L, "VT_STREAMED_OBJECT", LENGTH_UNKNOWN },
+        { 69L, "VT_STORED_OBJECT",   LENGTH_UNKNOWN },
+        { 70L, "VT_BLOB_OBJECT",     LENGTH_UNKNOWN },
+        { 71L, "VT_CF",              LENGTH_UNKNOWN },
+        { 72L, "VT_CLSID",           LENGTH_UNKNOWN }
+    };
 
+    /* Initialize the number-to-name and number-to-length map: */
+    static {
+        Map<Long,String> number2Name = new HashMap<Long,String>(NUMBER_TO_NAME_LIST.length, 1.0F);
+        Map<Long,Integer> number2Len = new HashMap<Long,Integer>(NUMBER_TO_NAME_LIST.length, 1.0F);
+
+        for (Object[] nn : NUMBER_TO_NAME_LIST) {
+            number2Name.put((Long)nn[0], (String)nn[1]);
+            number2Len.put((Long)nn[0], (Integer)nn[2]);
+        }
+        numberToName = Collections.unmodifiableMap(number2Name);
+        numberToLength = Collections.unmodifiableMap(number2Len);
+    }
 
     /**
-     * <p>Returns the variant type name associated with a variant type
-     * number.</p>
+     * Returns the variant type name associated with a variant type
+     * number.
      *
      * @param variantType The variant type number
      * @return The variant type name or the string "unknown variant type"
      */
-    public static String getVariantName(final long variantType)
-    {
-        final String name = numberToName.get(Long.valueOf(variantType));
-        return name != null ? name : "unknown variant type";
+    public static String getVariantName(final long variantType) {
+        long vt = variantType;
+        String name = "";
+        if ((vt & VT_VECTOR) != 0) {
+            name = "Vector of ";
+            vt -= VT_VECTOR;
+        } else if ((vt & VT_ARRAY) != 0) {
+            name = "Array of ";
+            vt -= VT_ARRAY;
+        } else if ((vt & VT_BYREF) != 0) {
+            name = "ByRef of ";
+            vt -= VT_BYREF;
+        }
+        
+        name += numberToName.get(vt);
+        return (name != null && !"".equals(name)) ? name : "unknown variant type";
     }
 
     /**
-     * <p>Returns a variant type's length.</p>
+     * Returns a variant type's length.
      *
      * @param variantType The variant type number
      * @return The length of the variant type's data in bytes. If the length is
@@ -505,13 +432,9 @@ public class Variant
      * know the length, -2 is returned. The latter usually indicates an
      * unsupported variant type.
      */
-    public static int getVariantLength(final long variantType)
-    {
-        final Long key = Long.valueOf((int) variantType);
-        final Integer length = numberToLength.get(key);
-        if (length == null)
-            return -2;
-        return length.intValue();
+    public static int getVariantLength(final long variantType) {
+        final Integer length = numberToLength.get(variantType);
+        return (length != null) ? length : LENGTH_UNKNOWN;
     }
 
 }
index 962674de71673a8c61f5fcfa4ac35bd612a49cfc..438312a6f66e54874b99798f9c215122d714dbdd 100644 (file)
 package org.apache.poi.hpsf;
 
 import org.apache.poi.util.Internal;
-import org.apache.poi.util.LittleEndian;
+import org.apache.poi.util.LittleEndianByteArrayInputStream;
 import org.apache.poi.util.POILogFactory;
 import org.apache.poi.util.POILogger;
 
 @Internal
 class VariantBool {
-    private final static POILogger logger = POILogFactory.getLogger( VariantBool.class );
+    private final static POILogger LOG = POILogFactory.getLogger( VariantBool.class );
 
     static final int SIZE = 2;
 
     private boolean _value;
 
-    VariantBool( byte[] data, int offset ) {
-        short value = LittleEndian.getShort( data, offset );
+    VariantBool() {}
+    
+    void read( LittleEndianByteArrayInputStream lei ) {
+        short value = lei.readShort();
         switch (value) {
             case 0:
                 _value = false;
@@ -39,7 +41,7 @@ class VariantBool {
                 _value = true;
                 break;
             default:
-                logger.log( POILogger.WARN, "VARIANT_BOOL value '"+value+"' is incorrect" );
+                LOG.log( POILogger.WARN, "VARIANT_BOOL value '"+value+"' is incorrect" );
                 _value = true;
                 break;
         }
index a1ddb0812ebe4f0177770f1f1f45546d9e9a267d..31c1cba1123c6cbe9586d248d8ca55de10730396 100644 (file)
 package org.apache.poi.hpsf;
 
 import org.apache.poi.util.Internal;
-import org.apache.poi.util.LittleEndian;
+import org.apache.poi.util.LittleEndianByteArrayInputStream;
 
 /**
  * Holder for vector-type properties
- * 
- * @author Sergey Vladimirov (vlsergey {at} gmail {dot} com)
  */
 @Internal
-class Vector
-{
+class Vector {
     private final short _type;
 
     private TypedPropertyValue[] _values;
 
-    Vector( byte[] data, int startOffset, short type )
-    {
+    Vector( short type ) {
         this._type = type;
-        read( data, startOffset );
     }
 
-    Vector( short type )
-    {
-        this._type = type;
-    }
-
-    int read( byte[] data, int startOffset )
-    {
-        int offset = startOffset;
-
-        final long longLength = LittleEndian.getUInt( data, offset );
-        offset += LittleEndian.INT_SIZE;
+    void read( LittleEndianByteArrayInputStream lei ) {
+        final long longLength = lei.readUInt();
 
-        if ( longLength > Integer.MAX_VALUE )
-            throw new UnsupportedOperationException( "Vector is too long -- "
-                    + longLength );
+        if ( longLength > Integer.MAX_VALUE ) {
+            throw new UnsupportedOperationException( "Vector is too long -- " + longLength );
+        }
         final int length = (int) longLength;
 
         _values = new TypedPropertyValue[length];
 
-        if ( _type == Variant.VT_VARIANT )
-        {
-            for ( int i = 0; i < length; i++ )
-            {
-                TypedPropertyValue value = new TypedPropertyValue();
-                offset += value.read( data, offset );
-                _values[i] = value;
-            }
-        }
-        else
-        {
-            for ( int i = 0; i < length; i++ )
-            {
-                TypedPropertyValue value = new TypedPropertyValue( _type, null );
-                // be aware: not padded here
-                offset += value.readValue( data, offset );
-                _values[i] = value;
+        int paddedType = (_type == Variant.VT_VARIANT) ? 0 : _type;
+        for ( int i = 0; i < length; i++ ) {
+            TypedPropertyValue value = new TypedPropertyValue(paddedType, null);
+            if (paddedType == 0) {
+                value.read(lei);
+            } else {
+                value.readValue(lei);
             }
+            _values[i] = value;
         }
-        return offset - startOffset;
     }
 
     TypedPropertyValue[] getValues(){
index c3c066e4fabbd361f047b29627faf9d0e551cf00..fe082859739d097ebb58765c78efed7c6bc82c18 100644 (file)
 package org.apache.poi.hpsf;
 
 import org.apache.poi.util.Internal;
+import org.apache.poi.util.LittleEndianByteArrayInputStream;
 
 @Internal
 class VersionedStream
 {
-    private GUID _versionGuid;
-    private IndirectPropertyName _streamName;
+    private final GUID _versionGuid = new GUID();
+    private final IndirectPropertyName _streamName = new IndirectPropertyName();
+    
+    VersionedStream() {}
 
-    VersionedStream( byte[] data, int offset )
-    {
-        _versionGuid = new GUID( data, offset );
-        _streamName = new IndirectPropertyName( data, offset + GUID.SIZE );
-    }
-
-    int getSize()
-    {
-        return GUID.SIZE + _streamName.getSize();
+    void read( LittleEndianByteArrayInputStream lei ) {
+        _versionGuid.read(lei);
+        _streamName.read(lei);
     }
 }