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