add classes that will replace VariantSupport class just a bit later

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1187595 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Sergey Vladimirov 2011-10-21 23:45:01 +00:00
parent aaadb09934
commit 0df5442322
17 changed files with 769 additions and 0 deletions

View File

@ -0,0 +1,121 @@
package org.apache.poi.hpsf;
import org.apache.poi.util.Internal;
import org.apache.poi.util.LittleEndian;
@Internal
class Array
{
static class ArrayDimension
{
static final int SIZE = 8;
private int _indexOffset;
private long _size;
ArrayDimension( byte[] data, int offset )
{
_size = LittleEndian.getUInt( data, offset );
_indexOffset = LittleEndian.getInt( data, offset
+ LittleEndian.INT_SIZE );
}
}
static class ArrayHeader
{
private ArrayDimension[] _dimensions;
private int _type;
ArrayHeader( byte[] data, int startOffset )
{
int offset = startOffset;
_type = LittleEndian.getInt( data, offset );
offset += LittleEndian.INT_SIZE;
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" );
int numDimensions = (int) numDimensionsUnsigned;
_dimensions = new ArrayDimension[numDimensions];
for ( int i = 0; i < numDimensions; i++ )
{
_dimensions[i] = new ArrayDimension( data, offset );
offset += ArrayDimension.SIZE;
}
}
long getNumberOfScalarValues()
{
long result = 1;
for ( ArrayDimension dimension : _dimensions )
result *= dimension._size;
return result;
}
int getSize()
{
return LittleEndian.INT_SIZE * 2 + _dimensions.length
* ArrayDimension.SIZE;
}
int getType()
{
return _type;
}
}
private ArrayHeader _header;
private TypedPropertyValue[] _values;
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();
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" );
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 );
}
}
return offset - startOffset;
}
}

View File

@ -0,0 +1,30 @@
package org.apache.poi.hpsf;
import org.apache.poi.util.LittleEndian;
import org.apache.poi.util.Internal;
@Internal
class Blob
{
private byte[] _value;
Blob( byte[] data, int offset )
{
int size = LittleEndian.getInt( data, offset );
if ( size == 0 )
{
_value = new byte[0];
return;
}
_value = LittleEndian.getByteArray( _value, offset
+ LittleEndian.INT_SIZE, size );
}
int getSize()
{
return LittleEndian.INT_SIZE + _value.length;
}
}

View File

@ -0,0 +1,27 @@
package org.apache.poi.hpsf;
import org.apache.poi.util.LittleEndian;
class ClipboardData
{
private int _format;
private byte[] _value;
ClipboardData( byte[] data, int offset )
{
int size = LittleEndian.getInt( data, offset );
if ( size < 4 )
throw new IllegalPropertySetDataException(
"ClipboardData size less than 4 bytes "
+ "(doesn't even have format field!)" );
_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;
}
}

View File

@ -0,0 +1,31 @@
package org.apache.poi.hpsf;
import org.apache.poi.util.LittleEndian;
import org.apache.poi.util.Internal;
@Internal
class CodePageString
{
byte[] _value;
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 ( _value[size - 1] != 0 )
throw new IllegalPropertySetDataException(
"CodePageString started at offset #" + offset
+ " is not NULL-terminated" );
}
int getSize()
{
return LittleEndian.INT_SIZE + _value.length;
}
}

View File

@ -0,0 +1,18 @@
package org.apache.poi.hpsf;
import org.apache.poi.util.LittleEndian;
import org.apache.poi.util.Internal;
@Internal
class Currency
{
static final int SIZE = 8;
private byte[] _value;
Currency( byte[] data, int offset )
{
_value = LittleEndian.getByteArray( data, offset, SIZE );
}
}

View File

@ -0,0 +1,18 @@
package org.apache.poi.hpsf;
import org.apache.poi.util.LittleEndian;
import org.apache.poi.util.Internal;
@Internal
class Date
{
static final int SIZE = 8;
private byte[] _value;
Date( byte[] data, int offset )
{
_value = LittleEndian.getByteArray( data, offset, SIZE );
}
}

View File

@ -0,0 +1,37 @@
package org.apache.poi.hpsf;
import org.apache.poi.util.Internal;
import org.apache.poi.util.LittleEndian;
@Internal
class Decimal
{
static final int SIZE = 16;
private short field_1_wReserved;
private byte field_2_scale;
private byte field_3_sign;
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;
}
}

View File

@ -0,0 +1,17 @@
package org.apache.poi.hpsf;
import org.apache.poi.util.LittleEndian;
class Filetime
{
static final int SIZE = LittleEndian.LONG_SIZE * 2;
private long _dwLowDateTime;
private long _dwHighDateTime;
Filetime( byte[] data, int offset )
{
_dwLowDateTime = LittleEndian.getLong( data, offset + 0 );
_dwHighDateTime = LittleEndian.getLong( data, offset + 4 );
}
}

View File

@ -0,0 +1,24 @@
package org.apache.poi.hpsf;
import org.apache.poi.util.Internal;
import org.apache.poi.util.LittleEndian;
@Internal
class GUID
{
static final int SIZE = 16;
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 );
}
}

View File

@ -0,0 +1,19 @@
package org.apache.poi.hpsf;
import org.apache.poi.util.Internal;
@Internal
class IndirectPropertyName
{
private CodePageString _value;
IndirectPropertyName( byte[] data, int offset )
{
_value = new CodePageString( data, offset );
}
int getSize()
{
return _value.getSize();
}
}

View File

@ -0,0 +1,224 @@
package org.apache.poi.hpsf;
import org.apache.poi.util.LittleEndian;
import org.apache.poi.util.Internal;
@Internal
class TypedPropertyValue
{
private int _type;
private Object _value;
TypedPropertyValue()
{
}
TypedPropertyValue( byte[] data, int startOffset )
{
read( data, startOffset );
}
TypedPropertyValue( int type, Object value )
{
_type = type;
_value = 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 );
if ( padding != 0 )
throw new IllegalPropertySetDataException(
"Property padding at offset " + offset
+ " MUST be 0, but it's value is " + padding );
offset += LittleEndian.SHORT_SIZE;
offset += readValuePadded( data, offset );
return offset - startOffset;
}
int readValue( byte[] data, int offset )
{
switch ( _type )
{
case Variant.VT_EMPTY:
case Variant.VT_NULL:
_value = null;
return 0;
case Variant.VT_I2:
_value = Short.valueOf( LittleEndian.getShort( data, offset ) );
return 4;
case Variant.VT_I4:
_value = Integer.valueOf( LittleEndian.getInt( data, offset ) );
return 4;
case Variant.VT_R4:
_value = Short.valueOf( LittleEndian.getShort( 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_ERROR:
_value = Long.valueOf( LittleEndian.getUInt( data, offset ) );
return 4;
case Variant.VT_BOOL:
_value = new VariantBool( data, offset );
return VariantBool.SIZE;
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_UI4:
_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_INT:
_value = Integer.valueOf( LittleEndian.getInt( data, offset ) );
return 4;
case Variant.VT_UINT:
_value = Long.valueOf( LittleEndian.getUInt( data, offset ) );
return 4;
case Variant.VT_LPSTR:
_value = new CodePageString( data, offset );
return ( (CodePageString) _value ).getSize();
case Variant.VT_LPWSTR:
_value = new UnicodeString( data, offset );
return ( (UnicodeString) _value ).getSize();
case Variant.VT_FILETIME:
_value = new Filetime( data, offset );
return Filetime.SIZE;
case Variant.VT_BLOB:
_value = new Blob( data, offset );
return ( (Blob) _value ).getSize();
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();
case Variant.VT_CF:
_value = new ClipboardData( data, offset );
return ( (ClipboardData) _value ).getSize();
case Variant.VT_CLSID:
_value = new GUID( data, offset );
return GUID.SIZE;
case Variant.VT_VERSIONED_STREAM:
_value = new VersionedStream( data, offset );
return ( (VersionedStream) _value ).getSize();
case Variant.VT_VECTOR | Variant.VT_I2:
case Variant.VT_VECTOR | Variant.VT_I4:
case Variant.VT_VECTOR | Variant.VT_R4:
case Variant.VT_VECTOR | Variant.VT_R8:
case Variant.VT_VECTOR | Variant.VT_CY:
case Variant.VT_VECTOR | Variant.VT_DATE:
case Variant.VT_VECTOR | Variant.VT_BSTR:
case Variant.VT_VECTOR | Variant.VT_ERROR:
case Variant.VT_VECTOR | Variant.VT_BOOL:
case Variant.VT_VECTOR | Variant.VT_VARIANT:
case Variant.VT_VECTOR | Variant.VT_I1:
case Variant.VT_VECTOR | Variant.VT_UI1:
case Variant.VT_VECTOR | Variant.VT_UI2:
case Variant.VT_VECTOR | Variant.VT_UI4:
case Variant.VT_VECTOR | Variant.VT_I8:
case Variant.VT_VECTOR | Variant.VT_UI8:
case Variant.VT_VECTOR | Variant.VT_LPSTR:
case Variant.VT_VECTOR | Variant.VT_LPWSTR:
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 );
case Variant.VT_ARRAY | Variant.VT_I2:
case Variant.VT_ARRAY | Variant.VT_I4:
case Variant.VT_ARRAY | Variant.VT_R4:
case Variant.VT_ARRAY | Variant.VT_R8:
case Variant.VT_ARRAY | Variant.VT_CY:
case Variant.VT_ARRAY | Variant.VT_DATE:
case Variant.VT_ARRAY | Variant.VT_BSTR:
case Variant.VT_ARRAY | Variant.VT_ERROR:
case Variant.VT_ARRAY | Variant.VT_BOOL:
case Variant.VT_ARRAY | Variant.VT_VARIANT:
case Variant.VT_ARRAY | Variant.VT_DECIMAL:
case Variant.VT_ARRAY | Variant.VT_I1:
case Variant.VT_ARRAY | Variant.VT_UI1:
case Variant.VT_ARRAY | Variant.VT_UI2:
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 );
default:
throw new UnsupportedOperationException(
"Unknown (possibly, incorrect) TypedPropertyValue type: "
+ _type );
}
}
int readValuePadded( byte[] data, int offset )
{
int nonPadded = readValue( data, offset );
return ( nonPadded & 0x03 ) == 0 ? nonPadded : nonPadded
+ ( 4 - ( nonPadded & 0x03 ) );
}
}

View File

@ -0,0 +1,35 @@
package org.apache.poi.hpsf;
import org.apache.poi.util.Internal;
import org.apache.poi.util.LittleEndian;
@Internal
class UnicodeString
{
private byte[] _value;
UnicodeString( byte[] data, int offset )
{
int length = LittleEndian.getInt( data, offset );
if ( length == 0 )
{
_value = new byte[0];
return;
}
_value = new byte[length * 2];
LittleEndian.getByteArray( data, offset + LittleEndian.INT_SIZE,
length * 2 );
if ( _value[length * 2 - 1] != 0 || _value[length * 2 - 2] != 0 )
throw new IllegalPropertySetDataException(
"UnicodeString started at offset #" + offset
+ " is not NULL-terminated" );
}
int getSize()
{
return LittleEndian.INT_SIZE + _value.length;
}
}

View File

@ -298,6 +298,14 @@ public class Variant
*/ */
public static final int VT_CLSID = 72; public static final int VT_CLSID = 72;
/**
* "MUST be a VersionedStream. The storage representing the (non-simple)
* property set MUST have a stream element with the name in the StreamName
* field." -- [MS-OLEPS] -- v20110920; Object Linking and Embedding (OLE)
* Property Set Data Structures; page 24 / 63
*/
public static final int VT_VERSIONED_STREAM = 0x0049;
/** /**
* <p>[P] simple counted array. <span style="background-color: * <p>[P] simple counted array. <span style="background-color:
* #ffff00">How long is this? How is it to be * #ffff00">How long is this? How is it to be

View File

@ -0,0 +1,32 @@
package org.apache.poi.hpsf;
import org.apache.poi.util.LittleEndian;
import org.apache.poi.util.Internal;
@Internal
class VariantBool
{
static final int SIZE = 2;
private boolean _value;
VariantBool( byte[] data, int offset )
{
short value = LittleEndian.getShort( data, offset );
if ( value == 0x0000 )
{
_value = false;
return;
}
if ( value == 0xffff )
{
_value = true;
return;
}
throw new IllegalPropertySetDataException( "VARIANT_BOOL value '"
+ value + "' is incorrect" );
}
}

View File

@ -0,0 +1,43 @@
package org.apache.poi.hpsf;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;
import org.apache.poi.util.LittleEndian;
/**
* Holder for vector-type properties
*
* @author Sergey Vladimirov (vlsergey {at} gmail {dot} com)
*/
public class VariantVector
{
private final List<Property> values;
public VariantVector( int codepage, long id, long type, byte[] data, int startOffset )
throws UnsupportedEncodingException, ReadingNotSupportedException
{
if ( ( type & 0x1000 ) != 0x1000 )
throw new IllegalArgumentException( "Specified type is not vector" );
final long elementType = type ^ 0x1000;
int offset = startOffset;
final long longLength = LittleEndian.getUInt( data, offset );
offset += LittleEndian.INT_SIZE;
if ( longLength > Integer.MAX_VALUE )
throw new UnsupportedOperationException( "Vector is too long -- "
+ longLength );
final int length = (int) longLength;
this.values = new ArrayList<Property>();
for ( int i = 0; i < length; i++ )
{
Property property = new Property( id, elementType, null );
VariantSupport.read( data, offset, length, elementType, codepage );
}
}
}

View File

@ -0,0 +1,64 @@
package org.apache.poi.hpsf;
import org.apache.poi.util.Internal;
import org.apache.poi.util.LittleEndian;
/**
* Holder for vector-type properties
*
* @author Sergey Vladimirov (vlsergey {at} gmail {dot} com)
*/
@Internal
class Vector
{
private final short _type;
private TypedPropertyValue[] _values;
Vector( short type )
{
this._type = type;
}
Vector( byte[] data, int startOffset, short type )
{
this._type = type;
read( data, startOffset );
}
int read( byte[] data, int startOffset )
{
int offset = startOffset;
final long longLength = LittleEndian.getUInt( data, offset );
offset += LittleEndian.INT_SIZE;
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 );
offset += value.readValuePadded( data, offset );
_values[i] = value;
}
}
return offset - startOffset;
}
}

View File

@ -0,0 +1,21 @@
package org.apache.poi.hpsf;
import org.apache.poi.util.Internal;
@Internal
class VersionedStream
{
private GUID _versionGuid;
private IndirectPropertyName _streamName;
VersionedStream( byte[] data, int offset )
{
_versionGuid = new GUID( data, offset );
_streamName = new IndirectPropertyName( data, offset + GUID.SIZE );
}
int getSize()
{
return GUID.SIZE + _streamName.getSize();
}
}