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