From 63b88fec6aea761981e0b21c674b4851bdacbd61 Mon Sep 17 00:00:00 2001 From: Andreas Beeker Date: Tue, 2 May 2017 23:24:50 +0000 Subject: [PATCH] HPSF: Use LittleEndianStream instead of offset calculation git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1793594 13f79535-47bb-0310-9956-ffa450edef68 --- src/java/org/apache/poi/hpsf/Array.java | 119 ++---- src/java/org/apache/poi/hpsf/Blob.java | 28 +- src/java/org/apache/poi/hpsf/ClassID.java | 112 ++--- .../org/apache/poi/hpsf/ClipboardData.java | 79 ++-- .../org/apache/poi/hpsf/CodePageString.java | 116 +++--- src/java/org/apache/poi/hpsf/Currency.java | 16 +- .../org/apache/poi/hpsf/CustomProperties.java | 67 ++- src/java/org/apache/poi/hpsf/Date.java | 16 +- src/java/org/apache/poi/hpsf/Decimal.java | 33 +- .../poi/hpsf/DocumentSummaryInformation.java | 46 ++- src/java/org/apache/poi/hpsf/Filetime.java | 42 +- src/java/org/apache/poi/hpsf/GUID.java | 20 +- .../apache/poi/hpsf/IndirectPropertyName.java | 15 +- .../org/apache/poi/hpsf/MutableProperty.java | 17 + src/java/org/apache/poi/hpsf/PropertySet.java | 58 +-- src/java/org/apache/poi/hpsf/TypeWriter.java | 28 +- .../apache/poi/hpsf/TypedPropertyValue.java | 262 ++++++------ .../org/apache/poi/hpsf/UnicodeString.java | 133 +++--- src/java/org/apache/poi/hpsf/Util.java | 32 -- src/java/org/apache/poi/hpsf/Variant.java | 391 +++++++----------- src/java/org/apache/poi/hpsf/VariantBool.java | 12 +- src/java/org/apache/poi/hpsf/Vector.java | 56 +-- .../org/apache/poi/hpsf/VersionedStream.java | 19 +- 23 files changed, 795 insertions(+), 922 deletions(-) diff --git a/src/java/org/apache/poi/hpsf/Array.java b/src/java/org/apache/poi/hpsf/Array.java index 8850ab9cd..f8c70fe12 100644 --- a/src/java/org/apache/poi/hpsf/Array.java +++ b/src/java/org/apache/poi/hpsf/Array.java @@ -17,23 +17,19 @@ 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; + void read( LittleEndianByteArrayInputStream lei ) { + _type = lei.readInt(); - _type = LittleEndian.getInt( data, offset ); - offset += LittleEndian.INT_SIZE; + long numDimensionsUnsigned = lei.readUInt(); - long numDimensionsUnsigned = LittleEndian.getUInt( data, offset ); - offset += LittleEndian.INT_SIZE; - - 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 ); + 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); } } - else - { - for ( int i = 0; i < numberOfScalars; i++ ) - { - TypedPropertyValue typedPropertyValue = new TypedPropertyValue( - type, null ); - offset += typedPropertyValue.readValuePadded( data, offset ); - } - } - - return offset - startOffset; + } + + TypedPropertyValue[] getValues(){ + return _values; } } diff --git a/src/java/org/apache/poi/hpsf/Blob.java b/src/java/org/apache/poi/hpsf/Blob.java index 547e2392b..af207feab 100644 --- a/src/java/org/apache/poi/hpsf/Blob.java +++ b/src/java/org/apache/poi/hpsf/Blob.java @@ -17,29 +17,19 @@ 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; } } diff --git a/src/java/org/apache/poi/hpsf/ClassID.java b/src/java/org/apache/poi/hpsf/ClassID.java index 0dacf8013..ec9c35555 100644 --- a/src/java/org/apache/poi/hpsf/ClassID.java +++ b/src/java/org/apache/poi/hpsf/ClassID.java @@ -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; /** - *

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.

+ * 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.

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

The number of bytes occupied by this object in the byte - * stream.

*/ + /** The number of bytes occupied by this object in the byte stream. */ public static final int LENGTH = 16; /** - *

The bytes making out the class ID in correct order, - * i.e. big-endian.

+ * The bytes making out the class ID in correct order, i.e. big-endian. */ private final byte[] bytes = new byte[LENGTH]; - - /** - *

Creates a {@link ClassID} and reads its value from a byte - * array.

+ * 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 /** - *

Creates a {@link ClassID} and initializes its value with - * 0x00 bytes.

+ * 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 /** - *

Creates a {@link ClassID} from a human-readable representation of the Class ID in standard - * format "{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}".

+ * 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 /** - *

Gets the bytes making out the class ID. They are returned in - * correct order, i.e. big-endian.

+ * 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 /** - *

Sets the bytes making out the class ID.

+ * 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 /** - *

Reads the class ID's value from a byte array by turning - * little-endian into big-endian.

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

Writes the class ID to a byte array in the - * little-endian format.

+ * 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 dst 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 offset 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 /** - *

Checks whether this ClassID is equal to another - * object.

+ * Checks whether this {@code ClassID} is equal to another object. * - * @param o the object to compare this PropertySet with - * @return true if the objects are equal, else - * false. + * @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(); } /** - *

Returns a human-readable representation of the Class ID in standard - * format "{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}".

+ * 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. */ diff --git a/src/java/org/apache/poi/hpsf/ClipboardData.java b/src/java/org/apache/poi/hpsf/ClipboardData.java index 42ce724a9..396a58277 100644 --- a/src/java/org/apache/poi/hpsf/ClipboardData.java +++ b/src/java/org/apache/poi/hpsf/ClipboardData.java @@ -16,69 +16,60 @@ ==================================================================== */ 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 ); + _format = lei.readInt(); + _value = new byte[size - LittleEndianConsts.INT_SIZE]; + lei.readFully(_value); } - int getSize() - { - return LittleEndian.INT_SIZE * 2 + _value.length; - } - - 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(); } } diff --git a/src/java/org/apache/poi/hpsf/CodePageString.java b/src/java/org/apache/poi/hpsf/CodePageString.java index 8eb8987b3..d0d138a40 100644 --- a/src/java/org/apache/poi/hpsf/CodePageString.java +++ b/src/java/org/apache/poi/hpsf/CodePageString.java @@ -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; - - _value = LittleEndian.getByteArray( data, offset, size ); - if ( size != 0 && _value[size - 1] != 0 ) { - // TODO Some files, such as TestVisioWithCodepage.vsd, are currently - // 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" ); + void read( LittleEndianByteArrayInputStream lei ) { + int offset = lei.getReadIndex(); + int size = lei.readInt(); + _value = new byte[size]; + if (size == 0) { + return; } + + // 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 + // See Bug #52258 for details + String msg = "CodePageString started at offset #" + offset + " is not NULL-terminated"; + LOG.log(POILogger.WARN, msg); + } + + TypedPropertyValue.skipPadding(lei); } - CodePageString( String string, int codepage ) - throws UnsupportedEncodingException - { - setJavaValue( string, codepage ); - } + String getJavaValue( int codepage ) throws UnsupportedEncodingException { + int cp = ( codepage == -1 ) ? Property.DEFAULT_CODEPAGE : codepage; + String result = CodePageUtil.getStringFromCodePage(_value, cp); - String getJavaValue( int codepage ) throws UnsupportedEncodingException - { - String result; - if ( codepage == -1 ) - result = new String( _value, StringUtil.UTF8 ); - else - result = CodePageUtil.getStringFromCodePage(_value, codepage); + 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; } } diff --git a/src/java/org/apache/poi/hpsf/Currency.java b/src/java/org/apache/poi/hpsf/Currency.java index 8928a56a0..34ae39a64 100644 --- a/src/java/org/apache/poi/hpsf/Currency.java +++ b/src/java/org/apache/poi/hpsf/Currency.java @@ -17,17 +17,17 @@ 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); } } diff --git a/src/java/org/apache/poi/hpsf/CustomProperties.java b/src/java/org/apache/poi/hpsf/CustomProperties.java index c420f1d3a..a279e2321 100644 --- a/src/java/org/apache/poi/hpsf/CustomProperties.java +++ b/src/java/org/apache/poi/hpsf/CustomProperties.java @@ -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 { + 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 { */ 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 { ") 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 { 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 { * {@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 { * {@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 { * {@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 { * {@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 { * {@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 { * {@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 { * @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; + } /** *

Gets the dictionary which contains IDs and names of the named custom @@ -317,16 +333,6 @@ public class CustomProperties extends HashMap { 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 { 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); + } } diff --git a/src/java/org/apache/poi/hpsf/Date.java b/src/java/org/apache/poi/hpsf/Date.java index 237420fcc..a116d21b8 100644 --- a/src/java/org/apache/poi/hpsf/Date.java +++ b/src/java/org/apache/poi/hpsf/Date.java @@ -17,17 +17,17 @@ 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); } } diff --git a/src/java/org/apache/poi/hpsf/Decimal.java b/src/java/org/apache/poi/hpsf/Decimal.java index d8c404c31..1ef50dabb 100644 --- a/src/java/org/apache/poi/hpsf/Decimal.java +++ b/src/java/org/apache/poi/hpsf/Decimal.java @@ -17,13 +17,10 @@ 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(); } } diff --git a/src/java/org/apache/poi/hpsf/DocumentSummaryInformation.java b/src/java/org/apache/poi/hpsf/DocumentSummaryInformation.java index f63ab8d92..7dd41a0cc 100644 --- a/src/java/org/apache/poi/hpsf/DocumentSummaryInformation.java +++ b/src/java/org/apache/poi/hpsf/DocumentSummaryInformation.java @@ -17,13 +17,15 @@ 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.

+ * + * 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 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 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); diff --git a/src/java/org/apache/poi/hpsf/Filetime.java b/src/java/org/apache/poi/hpsf/Filetime.java index 84a58bb1d..2ed36afcc 100644 --- a/src/java/org/apache/poi/hpsf/Filetime.java +++ b/src/java/org/apache/poi/hpsf/Filetime.java @@ -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; diff --git a/src/java/org/apache/poi/hpsf/GUID.java b/src/java/org/apache/poi/hpsf/GUID.java index 4bbd7f341..3483d5070 100644 --- a/src/java/org/apache/poi/hpsf/GUID.java +++ b/src/java/org/apache/poi/hpsf/GUID.java @@ -17,23 +17,21 @@ 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(); } } diff --git a/src/java/org/apache/poi/hpsf/IndirectPropertyName.java b/src/java/org/apache/poi/hpsf/IndirectPropertyName.java index fc09ca6e8..758f75569 100644 --- a/src/java/org/apache/poi/hpsf/IndirectPropertyName.java +++ b/src/java/org/apache/poi/hpsf/IndirectPropertyName.java @@ -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() {} } diff --git a/src/java/org/apache/poi/hpsf/MutableProperty.java b/src/java/org/apache/poi/hpsf/MutableProperty.java index 10d86be04..106ad6f24 100644 --- a/src/java/org/apache/poi/hpsf/MutableProperty.java +++ b/src/java/org/apache/poi/hpsf/MutableProperty.java @@ -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); + } } diff --git a/src/java/org/apache/poi/hpsf/PropertySet.java b/src/java/org/apache/poi/hpsf/PropertySet.java index 508ede25c..fb8344269 100644 --- a/src/java/org/apache/poi/hpsf/PropertySet.java +++ b/src/java/org/apache/poi/hpsf/PropertySet.java @@ -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

sections = new LinkedList
(); + private final List
sections = new ArrayList
(); /** @@ -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); + } } diff --git a/src/java/org/apache/poi/hpsf/TypeWriter.java b/src/java/org/apache/poi/hpsf/TypeWriter.java index 16270eb1d..ec9ba4c34 100644 --- a/src/java/org/apache/poi/hpsf/TypeWriter.java +++ b/src/java/org/apache/poi/hpsf/TypeWriter.java @@ -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; /** - *

Class for writing little-endian data and more.

+ * 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; } } diff --git a/src/java/org/apache/poi/hpsf/TypedPropertyValue.java b/src/java/org/apache/poi/hpsf/TypedPropertyValue.java index 3093a10af..7c0d726cb 100644 --- a/src/java/org/apache/poi/hpsf/TypedPropertyValue.java +++ b/src/java/org/apache/poi/hpsf/TypedPropertyValue.java @@ -18,168 +18,181 @@ */ 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; - - case Variant.VT_R4: - case Variant.VT_I2: - _value = Short.valueOf( LittleEndian.getShort( data, offset ) ); - return 4; - - case Variant.VT_INT: - case Variant.VT_I4: - _value = Integer.valueOf( LittleEndian.getInt( data, offset ) ); - return 4; - - case Variant.VT_R8: - _value = Double.valueOf( LittleEndian.getDouble( data, offset ) ); - return 8; - - case Variant.VT_CY: - _value = new Currency( data, offset ); - return Currency.SIZE; - - case Variant.VT_DATE: - _value = new Date( data, offset ); - return Date.SIZE; - - case Variant.VT_BSTR: - _value = new CodePageString( data, offset ); - return ( (CodePageString) _value ).getSize(); - - case Variant.VT_BOOL: - _value = new VariantBool( data, offset ); - return VariantBool.SIZE; - - case Variant.VT_DECIMAL: - _value = new Decimal( data, offset ); - return Decimal.SIZE; + break; case Variant.VT_I1: - _value = Byte.valueOf( data[offset] ); - return 1; + _value = lei.readByte(); + break; case Variant.VT_UI1: - _value = Short.valueOf( LittleEndian.getUByte( data, offset ) ); - return 2; + _value = lei.readUByte(); + break; + + case Variant.VT_I2: + _value = lei.readShort(); + break; case Variant.VT_UI2: - _value = Integer.valueOf( LittleEndian.getUShort( data, offset ) ); - return 4; + _value = lei.readUShort(); + break; + + case Variant.VT_INT: + case Variant.VT_I4: + _value = lei.readInt(); + break; case Variant.VT_UINT: case Variant.VT_UI4: case Variant.VT_ERROR: - _value = Long.valueOf( LittleEndian.getUInt( data, offset ) ); - return 4; + _value = lei.readUInt(); + break; case Variant.VT_I8: - _value = Long.valueOf( LittleEndian.getLong( data, offset ) ); - return 8; + _value = lei.readLong(); + break; - case Variant.VT_UI8: - _value = LittleEndian.getByteArray( data, offset, 8 ); - return 8; + 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 = lei.readDouble(); + break; + + case Variant.VT_CY: + Currency cur = new Currency(); + cur.read(lei); + _value = cur; + break; + + + case Variant.VT_DATE: + Date date = new Date(); + date.read(lei); + _value = date; + break; + + case Variant.VT_BSTR: case Variant.VT_LPSTR: - _value = new CodePageString( data, offset ); - return ( (CodePageString) _value ).getSize(); + CodePageString cps = new CodePageString(); + cps.read(lei); + _value = cps; + break; + + case Variant.VT_BOOL: + VariantBool vb = new VariantBool(); + vb.read(lei); + _value = vb; + break; + + case Variant.VT_DECIMAL: + 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> 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; + } } diff --git a/src/java/org/apache/poi/hpsf/Util.java b/src/java/org/apache/poi/hpsf/Util.java index d8ab7315d..d18fe811b 100644 --- a/src/java/org/apache/poi/hpsf/Util.java +++ b/src/java/org/apache/poi/hpsf/Util.java @@ -95,38 +95,6 @@ public class Util return ms_since_16010101 * (1000 * 10); } - - - /** - *

Compares to object arrays with regarding the objects' order. For - * example, [1, 2, 3] and [2, 1, 3] are equal.

- * - * @param c1 The first object array. - * @param c2 The second object array. - * @return true if the object arrays are equal, - * false 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; - } - /** *

Pads a byte array with 0x00 bytes so that its length is a multiple of * 4.

diff --git a/src/java/org/apache/poi/hpsf/Variant.java b/src/java/org/apache/poi/hpsf/Variant.java index 3f606043a..ba67252d6 100644 --- a/src/java/org/apache/poi/hpsf/Variant.java +++ b/src/java/org/apache/poi/hpsf/Variant.java @@ -22,277 +22,247 @@ import java.util.HashMap; import java.util.Map; /** - *

The Variant types as defined by Microsoft's COM. I - * found this information in - * http://www.marin.clara.net/COM/variant_type_definitions.htm.

+ * The {@code Variant} types as defined by Microsoft's COM. * - *

In the variant types descriptions the following shortcuts are - * used: [V] - may appear in a VARIANT, - * [T] - may appear in a TYPEDESC, - * [P] - may appear in an OLE property set, - * [S] - may appear in a Safe Array.

+ * In the variant types descriptions the following shortcuts are used: + *
    + *
  • [V] - may appear in a VARIANT + *
  • [T] - may appear in a TYPEDESC + *
  • [P] - may appear in an OLE property set + *
  • [S] - may appear in a Safe Array + *
*/ public class Variant { /** - *

[V][P] Nothing, i.e. not a single byte of data.

+ * [V][P] Nothing, i.e. not a single byte of data. */ public static final int VT_EMPTY = 0; /** - *

[V][P] SQL style Null.

+ * [V][P] SQL style Null. */ public static final int VT_NULL = 1; /** - *

[V][T][P][S] 2 byte signed int.

+ * [V][T][P][S] 2 byte signed int. */ public static final int VT_I2 = 2; /** - *

[V][T][P][S] 4 byte signed int.

+ * [V][T][P][S] 4 byte signed int. */ public static final int VT_I4 = 3; /** - *

[V][T][P][S] 4 byte real.

+ * [V][T][P][S] 4 byte real. */ public static final int VT_R4 = 4; /** - *

[V][T][P][S] 8 byte real.

+ * [V][T][P][S] 8 byte real. */ public static final int VT_R8 = 5; /** - *

[V][T][P][S] currency. How long is this? How is it to be - * interpreted?

+ * [V][T][P][S] currency. */ public static final int VT_CY = 6; /** - *

[V][T][P][S] date. How long is this? How is it to be - * interpreted?

+ * [V][T][P][S] date. */ public static final int VT_DATE = 7; /** - *

[V][T][P][S] OLE Automation string. How long is this? How is it - * to be interpreted?

+ * [V][T][P][S] OLE Automation string. */ public static final int VT_BSTR = 8; /** - *

[V][T][P][S] IDispatch *. How long is this? How is it to be - * interpreted?

+ * [V][T][P][S] IDispatch */ public static final int VT_DISPATCH = 9; /** - *

[V][T][S] SCODE. How - * long is this? How is it to be interpreted?

+ * [V][T][S] SCODE */ public static final int VT_ERROR = 10; /** - *

[V][T][P][S] True=-1, False=0.

+ * [V][T][P][S] True=-1, False=0. */ public static final int VT_BOOL = 11; /** - *

[V][T][P][S] VARIANT *. How long is this? How is it to be - * interpreted?

+ * [V][T][P][S] VARIANT */ public static final int VT_VARIANT = 12; /** - *

[V][T][S] IUnknown *. How long is this? How is it to be - * interpreted?

+ * [V][T][S] IUnknown */ public static final int VT_UNKNOWN = 13; /** - *

[V][T][S] 16 byte fixed point.

+ * [V][T][S] 16 byte fixed point. */ public static final int VT_DECIMAL = 14; /** - *

[T] signed char.

+ * [T] signed char. */ public static final int VT_I1 = 16; /** - *

[V][T][P][S] unsigned char.

+ * [V][T][P][S] unsigned char. */ public static final int VT_UI1 = 17; /** - *

[T][P] unsigned short.

+ * [T][P] unsigned short. */ public static final int VT_UI2 = 18; /** - *

[T][P] unsigned int.

+ * [T][P] unsigned int. */ public static final int VT_UI4 = 19; /** - *

[T][P] signed 64-bit int.

+ * [T][P] signed 64-bit int. */ public static final int VT_I8 = 20; /** - *

[T][P] unsigned 64-bit int.

+ * [T][P] unsigned 64-bit int. */ public static final int VT_UI8 = 21; /** - *

[T] signed machine int.

+ * [T] signed machine int. */ public static final int VT_INT = 22; /** - *

[T] unsigned machine int.

+ * [T] unsigned machine int. */ public static final int VT_UINT = 23; /** - *

[T] C style void.

+ * [T] C style void. */ public static final int VT_VOID = 24; /** - *

[T] Standard return type. How long is this? How is it to be - * interpreted?

+ * [T] Standard return type. */ public static final int VT_HRESULT = 25; /** - *

[T] pointer type. How long is this? How is it to be - * interpreted?

+ * [T] pointer type. */ public static final int VT_PTR = 26; /** - *

[T] (use VT_ARRAY in VARIANT).

+ * [T] (use VT_ARRAY in VARIANT). */ public static final int VT_SAFEARRAY = 27; /** - *

[T] C style array. How long is this? How is it to be - * interpreted?

+ * interpreted? */ public static final int VT_CARRAY = 28; /** - *

[T] user defined type. How long is this? How is it to be - * interpreted?

+ * [T] user defined type. */ public static final int VT_USERDEFINED = 29; /** - *

[T][P] null terminated string.

+ * [T][P] null terminated string. */ public static final int VT_LPSTR = 30; /** - *

[T][P] wide (Unicode) null terminated string.

+ * [T][P] wide (Unicode) null terminated string. */ public static final int VT_LPWSTR = 31; /** - *

[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.

+ * into the two dwords stored in the structure. */ public static final int VT_FILETIME = 64; /** - *

[P] Length prefixed bytes.

+ * [P] Length prefixed bytes. */ public static final int VT_BLOB = 65; /** - *

[P] Name of the stream follows.

+ * [P] Name of the stream follows. */ public static final int VT_STREAM = 66; /** - *

[P] Name of the storage follows.

+ * [P] Name of the storage follows. */ public static final int VT_STORAGE = 67; /** - *

[P] Stream contains an object. How long is this? How is it - * to be interpreted?

+ * [P] Stream contains an object. */ public static final int VT_STREAMED_OBJECT = 68; /** - *

[P] Storage contains an object. How long is this? How is it - * to be interpreted?

+ * [P] Storage contains an object. */ public static final int VT_STORED_OBJECT = 69; /** - *

[P] Blob contains an object. How long is this? How is it to be - * interpreted?

+ * [P] Blob contains an object. */ public static final int VT_BLOB_OBJECT = 70; /** - *

[P] Clipboard format. How long is this? How is it to be - * interpreted?

+ * [P] Clipboard format. */ public static final int VT_CF = 71; /** - *

[P] A Class ID.

+ * [P] A Class ID.

* - *

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.

+ * then a byte array containing the data.

* - *

The valid Clipboard Format Tags are:

+ * The valid Clipboard Format Tags are: * *
    - *
  • {@link Thumbnail#CFTAG_WINDOWS}
  • - *
  • {@link Thumbnail#CFTAG_MACINTOSH}
  • - *
  • {@link Thumbnail#CFTAG_NODATA}
  • - *
  • {@link Thumbnail#CFTAG_FMTID}
  • + *
  • {@link Thumbnail#CFTAG_WINDOWS} + *
  • {@link Thumbnail#CFTAG_MACINTOSH} + *
  • {@link Thumbnail#CFTAG_NODATA} + *
  • {@link Thumbnail#CFTAG_FMTID} *
* - *
typedef struct tagCLIPDATA {
+     * 
{@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;
+ * } CLIPDATA;}
* - *

See - * msdn.microsoft.com/library/en-us/com/stgrstrc_0uwk.asp.

+ * @see PROPVARIANT structure */ public static final int VT_CLSID = 72; @@ -305,199 +275,156 @@ public class Variant public static final int VT_VERSIONED_STREAM = 0x0049; /** - *

[P] simple counted array. How long is this? How is it to be - * interpreted?

+ * interpreted? */ public static final int VT_VECTOR = 0x1000; /** - *

[V] SAFEARRAY*. How - * long is this? How is it to be interpreted?

+ * [V] SAFEARRAY*. How + * long is this? How is it to be interpreted? */ public static final int VT_ARRAY = 0x2000; /** - *

[V] void* for local use. How long is this? How is it to be - * interpreted?

+ * interpreted? */ public static final int VT_BYREF = 0x4000; - /** - *

FIXME (3): Document this!

- */ public static final int VT_RESERVED = 0x8000; - /** - *

FIXME (3): Document this!

- */ public static final int VT_ILLEGAL = 0xFFFF; - /** - *

FIXME (3): Document this!

- */ public static final int VT_ILLEGALMASKED = 0xFFF; - /** - *

FIXME (3): Document this!

- */ public static final int VT_TYPEMASK = 0xFFF; /** - *

Maps the numbers denoting the variant types to their corresponding - * variant type names.

+ * Maps the numbers denoting the variant types to their corresponding + * variant type names. */ - private static Map numberToName; + private static final Map numberToName; - private static Map numberToLength; + private static final Map numberToLength; /** - *

Denotes a variant type with a length that is unknown to HPSF yet.

+ * Denotes a variant type with a length that is unknown to HPSF yet. */ - public static final Integer LENGTH_UNKNOWN = Integer.valueOf(-2); + public static final Integer LENGTH_UNKNOWN = -2; /** - *

Denotes a variant type with a variable length.

+ * Denotes a variant type with a variable length. */ - public static final Integer LENGTH_VARIABLE = Integer.valueOf(-1); + public static final Integer LENGTH_VARIABLE = -1; /** - *

Denotes a variant type with a length of 0 bytes.

+ * Denotes a variant type with a length of 0 bytes. */ - public static final Integer LENGTH_0 = Integer.valueOf(0); + public static final Integer LENGTH_0 = 0; /** - *

Denotes a variant type with a length of 2 bytes.

+ * Denotes a variant type with a length of 2 bytes. */ - public static final Integer LENGTH_2 = Integer.valueOf(2); + public static final Integer LENGTH_2 = 2; /** - *

Denotes a variant type with a length of 4 bytes.

+ * Denotes a variant type with a length of 4 bytes. */ - public static final Integer LENGTH_4 = Integer.valueOf(4); + public static final Integer LENGTH_4 = 4; /** - *

Denotes a variant type with a length of 8 bytes.

+ * Denotes a variant type with a length of 8 bytes. */ - public static final Integer LENGTH_8 = Integer.valueOf(8); + 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 number2Name = new HashMap(NUMBER_TO_NAME_LIST.length, 1.0F); + Map number2Len = new HashMap(NUMBER_TO_NAME_LIST.length, 1.0F); - static - { - /* Initialize the number-to-name map: */ - Map tm1 = new HashMap(); - 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 tm2 = new HashMap(tm1.size(), 1.0F); - tm2.putAll(tm1); - numberToName = Collections.unmodifiableMap(tm2); - - /* Initialize the number-to-length map: */ - Map tm3 = new HashMap(); - 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 tm4 = new HashMap(tm1.size(), 1.0F); - tm4.putAll(tm3); - numberToLength = Collections.unmodifiableMap(tm4); + 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); } - - /** - *

Returns the variant type name associated with a variant type - * number.

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

Returns a variant type's length.

+ * 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; } } diff --git a/src/java/org/apache/poi/hpsf/VariantBool.java b/src/java/org/apache/poi/hpsf/VariantBool.java index 962674de7..438312a6f 100644 --- a/src/java/org/apache/poi/hpsf/VariantBool.java +++ b/src/java/org/apache/poi/hpsf/VariantBool.java @@ -17,20 +17,22 @@ 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; } diff --git a/src/java/org/apache/poi/hpsf/Vector.java b/src/java/org/apache/poi/hpsf/Vector.java index a1ddb0812..31c1cba11 100644 --- a/src/java/org/apache/poi/hpsf/Vector.java +++ b/src/java/org/apache/poi/hpsf/Vector.java @@ -17,65 +17,41 @@ 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 ) - { - this._type = type; - read( data, startOffset ); - } - - Vector( short type ) - { + Vector( short type ) { this._type = type; } - int read( byte[] data, int startOffset ) - { - int offset = startOffset; + void read( LittleEndianByteArrayInputStream lei ) { + final long longLength = lei.readUInt(); - final long longLength = LittleEndian.getUInt( data, offset ); - offset += LittleEndian.INT_SIZE; - - 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; + 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; } - 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; - } - } - return offset - startOffset; } TypedPropertyValue[] getValues(){ diff --git a/src/java/org/apache/poi/hpsf/VersionedStream.java b/src/java/org/apache/poi/hpsf/VersionedStream.java index c3c066e4f..fe0828597 100644 --- a/src/java/org/apache/poi/hpsf/VersionedStream.java +++ b/src/java/org/apache/poi/hpsf/VersionedStream.java @@ -19,21 +19,18 @@ 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); } }