2004-04-09 09:05:39 -04:00
|
|
|
|
|
|
|
/* ====================================================================
|
|
|
|
Copyright 2002-2004 Apache Software Foundation
|
|
|
|
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
you may not use this file except in compliance with the License.
|
|
|
|
You may obtain a copy of the License at
|
|
|
|
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
See the License for the specific language governing permissions and
|
|
|
|
limitations under the License.
|
|
|
|
==================================================================== */
|
|
|
|
|
2002-02-13 23:00:59 -05:00
|
|
|
package org.apache.poi.hpsf;
|
|
|
|
|
2003-12-02 12:46:01 -05:00
|
|
|
import java.io.UnsupportedEncodingException;
|
2003-08-02 15:02:28 -04:00
|
|
|
import java.util.HashMap;
|
|
|
|
import java.util.Map;
|
2003-08-23 11:12:22 -04:00
|
|
|
|
2003-12-02 12:46:01 -05:00
|
|
|
import org.apache.poi.util.HexDump;
|
2002-05-26 18:18:40 -04:00
|
|
|
import org.apache.poi.util.LittleEndian;
|
2002-02-13 23:00:59 -05:00
|
|
|
|
|
|
|
/**
|
2002-07-18 11:51:39 -04:00
|
|
|
* <p>A property in a {@link Section} of a {@link PropertySet}.</p>
|
2002-02-13 23:00:59 -05:00
|
|
|
*
|
2002-07-18 11:51:39 -04:00
|
|
|
* <p>The property's <strong>ID</strong> gives the property a meaning
|
|
|
|
* in the context of its {@link Section}. Each {@link Section} spans
|
|
|
|
* its own name space of property IDs.</p>
|
2002-02-13 23:00:59 -05:00
|
|
|
*
|
2002-07-18 11:51:39 -04:00
|
|
|
* <p>The property's <strong>type</strong> determines how its
|
|
|
|
* <strong>value </strong> is interpreted. For example, if the type is
|
|
|
|
* {@link Variant#VT_LPSTR} (byte string), the value consists of a
|
2002-07-22 04:25:19 -04:00
|
|
|
* DWord telling how many bytes the string contains. The bytes follow
|
|
|
|
* immediately, including any null bytes that terminate the
|
2002-07-18 11:51:39 -04:00
|
|
|
* string. The type {@link Variant#VT_I4} denotes a four-byte integer
|
|
|
|
* value, {@link Variant#VT_FILETIME} some date and time (of a
|
|
|
|
* file).</p>
|
2002-02-13 23:00:59 -05:00
|
|
|
*
|
2003-08-30 05:13:53 -04:00
|
|
|
* <p>Please note that not all {@link Variant} types yet. This might change
|
|
|
|
* over time but largely depends on your feedback so that the POI team knows
|
|
|
|
* which variant types are really needed. So please feel free to submit error
|
|
|
|
* reports or patches for the types you need.</p>
|
2002-02-13 23:00:59 -05:00
|
|
|
*
|
2003-08-30 05:13:53 -04:00
|
|
|
* @author Rainer Klute <a
|
|
|
|
* href="mailto:klute@rainer-klute.de"><klute@rainer-klute.de></a>
|
2002-07-18 11:51:39 -04:00
|
|
|
* @author Drew Varner (Drew.Varner InAndAround sc.edu)
|
|
|
|
* @see Section
|
|
|
|
* @see Variant
|
|
|
|
* @version $Id$
|
|
|
|
* @since 2002-02-09
|
2002-02-13 23:00:59 -05:00
|
|
|
*/
|
2002-07-18 11:51:39 -04:00
|
|
|
public class Property
|
|
|
|
{
|
2002-02-13 23:00:59 -05:00
|
|
|
|
2003-08-02 15:02:28 -04:00
|
|
|
/** <p>Codepage 1200 denotes Unicode.</p> */
|
2003-09-18 14:56:35 -04:00
|
|
|
public static final int CP_UNICODE = 1200;
|
2002-12-10 01:15:20 -05:00
|
|
|
|
2003-08-02 15:02:28 -04:00
|
|
|
/** <p>The property's ID.</p> */
|
2003-08-30 05:13:53 -04:00
|
|
|
protected long id;
|
2002-02-13 23:00:59 -05:00
|
|
|
|
2002-05-11 10:47:24 -04:00
|
|
|
|
2002-02-13 23:00:59 -05:00
|
|
|
/**
|
2002-07-18 11:51:39 -04:00
|
|
|
* <p>Returns the property's ID.</p>
|
2002-05-11 10:47:24 -04:00
|
|
|
*
|
2002-07-18 11:51:39 -04:00
|
|
|
* @return The ID value
|
2002-02-13 23:00:59 -05:00
|
|
|
*/
|
2003-08-30 05:13:53 -04:00
|
|
|
public long getID()
|
2002-07-18 11:51:39 -04:00
|
|
|
{
|
2002-02-13 23:00:59 -05:00
|
|
|
return id;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2003-08-02 15:02:28 -04:00
|
|
|
/** <p>The property's type.</p> */
|
2003-08-03 16:16:46 -04:00
|
|
|
protected long type;
|
2002-05-11 10:47:24 -04:00
|
|
|
|
2002-02-13 23:00:59 -05:00
|
|
|
|
|
|
|
/**
|
2002-07-18 11:51:39 -04:00
|
|
|
* <p>Returns the property's type.</p>
|
2002-05-11 10:47:24 -04:00
|
|
|
*
|
2002-07-18 11:51:39 -04:00
|
|
|
* @return The type value
|
2002-02-13 23:00:59 -05:00
|
|
|
*/
|
2002-07-18 11:51:39 -04:00
|
|
|
public long getType()
|
|
|
|
{
|
2002-02-13 23:00:59 -05:00
|
|
|
return type;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2003-08-02 15:02:28 -04:00
|
|
|
/** <p>The property's value.</p> */
|
2003-08-03 16:16:46 -04:00
|
|
|
protected Object value;
|
2002-02-13 23:00:59 -05:00
|
|
|
|
2002-05-11 10:47:24 -04:00
|
|
|
|
2002-02-13 23:00:59 -05:00
|
|
|
/**
|
2002-07-18 11:51:39 -04:00
|
|
|
* <p>Returns the property's value.</p>
|
2002-05-11 10:47:24 -04:00
|
|
|
*
|
2002-07-18 11:51:39 -04:00
|
|
|
* @return The property's value
|
2002-02-13 23:00:59 -05:00
|
|
|
*/
|
2002-07-18 11:51:39 -04:00
|
|
|
public Object getValue()
|
|
|
|
{
|
2002-02-13 23:00:59 -05:00
|
|
|
return value;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2002-07-18 11:51:39 -04:00
|
|
|
* <p>Creates a {@link Property} instance by reading its bytes
|
|
|
|
* from the property set stream.</p>
|
2002-02-13 23:00:59 -05:00
|
|
|
*
|
2002-07-18 11:51:39 -04:00
|
|
|
* @param id The property's ID.
|
|
|
|
* @param src The bytes the property set stream consists of.
|
|
|
|
* @param offset The property's type/value pair's offset in the
|
|
|
|
* section.
|
|
|
|
* @param length The property's type/value pair's length in bytes.
|
2002-12-10 01:15:20 -05:00
|
|
|
* @param codepage The section's and thus the property's
|
|
|
|
* codepage. It is needed only when reading string values.
|
2003-12-02 12:46:01 -05:00
|
|
|
*
|
|
|
|
* @exception UnsupportedEncodingException if the specified codepage is not
|
|
|
|
* supported
|
2002-02-13 23:00:59 -05:00
|
|
|
*/
|
2003-08-30 05:13:53 -04:00
|
|
|
public Property(final long id, final byte[] src, final long offset,
|
2003-08-02 15:02:28 -04:00
|
|
|
final int length, final int codepage)
|
2003-12-02 12:46:01 -05:00
|
|
|
throws UnsupportedEncodingException
|
2002-07-18 11:51:39 -04:00
|
|
|
{
|
2002-02-13 23:00:59 -05:00
|
|
|
this.id = id;
|
|
|
|
|
2002-05-11 10:47:24 -04:00
|
|
|
/*
|
2002-12-10 01:15:20 -05:00
|
|
|
* ID 0 is a special case since it specifies a dictionary of
|
|
|
|
* property IDs and property names.
|
2002-05-11 10:47:24 -04:00
|
|
|
*/
|
2002-07-18 11:51:39 -04:00
|
|
|
if (id == 0)
|
2003-08-02 15:02:28 -04:00
|
|
|
{
|
2002-12-10 01:15:20 -05:00
|
|
|
value = readDictionary(src, offset, length, codepage);
|
2002-02-13 23:00:59 -05:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2002-05-11 10:47:24 -04:00
|
|
|
int o = (int) offset;
|
|
|
|
type = LittleEndian.getUInt(src, o);
|
|
|
|
o += LittleEndian.INT_SIZE;
|
2002-02-13 23:00:59 -05:00
|
|
|
|
2003-08-02 15:02:28 -04:00
|
|
|
try
|
|
|
|
{
|
2003-12-02 12:46:01 -05:00
|
|
|
value = VariantSupport.read(src, o, length, (int) type, codepage);
|
2003-08-02 15:02:28 -04:00
|
|
|
}
|
2003-08-23 11:12:22 -04:00
|
|
|
catch (UnsupportedVariantTypeException ex)
|
2003-08-02 15:02:28 -04:00
|
|
|
{
|
2003-08-30 05:13:53 -04:00
|
|
|
VariantSupport.writeUnsupportedTypeMessage(ex);
|
2003-08-23 11:12:22 -04:00
|
|
|
value = ex.getValue();
|
2003-08-02 15:02:28 -04:00
|
|
|
}
|
2002-02-13 23:00:59 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2003-08-03 16:16:46 -04:00
|
|
|
/**
|
|
|
|
* <p>Creates an empty property. It must be filled using the set method to
|
|
|
|
* be usable.</p>
|
|
|
|
*/
|
|
|
|
protected Property()
|
2003-08-23 11:12:22 -04:00
|
|
|
{ }
|
2003-08-03 16:16:46 -04:00
|
|
|
|
|
|
|
|
|
|
|
|
2002-02-13 23:00:59 -05:00
|
|
|
/**
|
2002-07-18 11:51:39 -04:00
|
|
|
* <p>Reads a dictionary.</p>
|
2002-02-13 23:00:59 -05:00
|
|
|
*
|
2002-07-18 11:51:39 -04:00
|
|
|
* @param src The byte array containing the bytes making out the
|
|
|
|
* dictionary.
|
|
|
|
* @param offset At this offset within <var>src</var> the
|
|
|
|
* dictionary starts.
|
|
|
|
* @param length The dictionary contains at most this many bytes.
|
2002-12-10 01:15:20 -05:00
|
|
|
* @param codepage The codepage of the string values.
|
2002-07-18 11:51:39 -04:00
|
|
|
* @return The dictonary
|
2002-02-13 23:00:59 -05:00
|
|
|
*/
|
2002-05-11 10:47:24 -04:00
|
|
|
protected Map readDictionary(final byte[] src, final long offset,
|
2003-08-02 15:02:28 -04:00
|
|
|
final int length, final int codepage)
|
2002-07-18 11:51:39 -04:00
|
|
|
{
|
2003-08-02 15:02:28 -04:00
|
|
|
/* Check whether "offset" points into the "src" array". */
|
|
|
|
if (offset < 0 || offset > src.length)
|
|
|
|
throw new HPSFRuntimeException
|
|
|
|
("Illegal offset " + offset + " while HPSF stream contains " +
|
|
|
|
length + " bytes.");
|
2002-12-10 01:15:20 -05:00
|
|
|
int o = (int) offset;
|
2002-05-11 10:47:24 -04:00
|
|
|
|
|
|
|
/*
|
2002-12-10 01:15:20 -05:00
|
|
|
* Read the number of dictionary entries.
|
2002-05-11 10:47:24 -04:00
|
|
|
*/
|
|
|
|
final long nrEntries = LittleEndian.getUInt(src, o);
|
|
|
|
o += LittleEndian.INT_SIZE;
|
|
|
|
|
2002-12-10 01:15:20 -05:00
|
|
|
final Map m = new HashMap((int) nrEntries, (float) 1.0);
|
2002-07-18 11:51:39 -04:00
|
|
|
for (int i = 0; i < nrEntries; i++)
|
2003-08-02 15:02:28 -04:00
|
|
|
{
|
2002-12-10 01:15:20 -05:00
|
|
|
/* The key. */
|
2002-05-11 10:47:24 -04:00
|
|
|
final Long id = new Long(LittleEndian.getUInt(src, o));
|
|
|
|
o += LittleEndian.INT_SIZE;
|
|
|
|
|
2002-12-10 01:15:20 -05:00
|
|
|
/* The value (a string). The length is the either the
|
|
|
|
* number of characters if the character set is Unicode or
|
|
|
|
* else the number of bytes. The length includes
|
|
|
|
* terminating 0x00 bytes which we have to strip off to
|
|
|
|
* create a Java string. */
|
|
|
|
long sLength = LittleEndian.getUInt(src, o);
|
2002-05-11 10:47:24 -04:00
|
|
|
o += LittleEndian.INT_SIZE;
|
2002-07-18 11:51:39 -04:00
|
|
|
|
2002-12-10 01:15:20 -05:00
|
|
|
/* Read the bytes or characters depending on whether the
|
|
|
|
* character set is Unicode or not. */
|
2003-08-02 15:02:28 -04:00
|
|
|
StringBuffer b = new StringBuffer((int) sLength);
|
|
|
|
for (int j = 0; j < sLength; j++)
|
|
|
|
if (codepage == CP_UNICODE)
|
|
|
|
{
|
|
|
|
final int i1 = o + (j * 2);
|
|
|
|
final int i2 = i1 + 1;
|
|
|
|
b.append((char) ((src[i2] << 8) + src[i1]));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
b.append((char) src[o + j]);
|
|
|
|
|
|
|
|
/* Strip 0x00 characters from the end of the string: */
|
2004-01-08 09:25:35 -05:00
|
|
|
while (b.length() > 0 && b.charAt(b.length() - 1) == 0x00)
|
2003-08-02 15:02:28 -04:00
|
|
|
b.setLength(b.length() - 1);
|
|
|
|
if (codepage == CP_UNICODE)
|
|
|
|
{
|
|
|
|
if (sLength % 2 == 1)
|
|
|
|
sLength++;
|
|
|
|
o += (sLength + sLength);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
o += sLength;
|
2002-12-10 01:15:20 -05:00
|
|
|
m.put(id, b.toString());
|
2002-02-13 23:00:59 -05:00
|
|
|
}
|
|
|
|
return m;
|
|
|
|
}
|
|
|
|
|
2003-08-02 15:02:28 -04:00
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* <p>Returns the property's size in bytes. This is always a multiple of
|
|
|
|
* 4.</p>
|
|
|
|
*
|
|
|
|
* @return the property's size in bytes
|
2003-08-30 05:13:53 -04:00
|
|
|
*
|
|
|
|
* @exception WritingNotSupportedException if HPSF does not yet support the
|
|
|
|
* property's variant type.
|
2003-08-02 15:02:28 -04:00
|
|
|
*/
|
2003-08-30 05:13:53 -04:00
|
|
|
protected int getSize() throws WritingNotSupportedException
|
2003-08-02 15:02:28 -04:00
|
|
|
{
|
2003-08-30 05:13:53 -04:00
|
|
|
int length = VariantSupport.getVariantLength(type);
|
|
|
|
if (length >= 0)
|
|
|
|
return length; /* Fixed length */
|
|
|
|
if (length == -2)
|
|
|
|
/* Unknown length */
|
|
|
|
throw new WritingNotSupportedException(type, null);
|
|
|
|
|
|
|
|
/* Variable length: */
|
2003-08-23 11:12:22 -04:00
|
|
|
final int PADDING = 4; /* Pad to multiples of 4. */
|
|
|
|
switch ((int) type)
|
|
|
|
{
|
|
|
|
case Variant.VT_LPSTR:
|
|
|
|
{
|
|
|
|
int l = ((String) value).length() + 1;
|
|
|
|
int r = l % PADDING;
|
|
|
|
if (r > 0)
|
|
|
|
l += PADDING - r;
|
|
|
|
length += l;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case Variant.VT_EMPTY:
|
|
|
|
break;
|
|
|
|
default:
|
2003-08-30 05:13:53 -04:00
|
|
|
throw new WritingNotSupportedException(type, value);
|
2003-08-23 11:12:22 -04:00
|
|
|
}
|
|
|
|
return length;
|
2003-08-02 15:02:28 -04:00
|
|
|
}
|
|
|
|
|
2003-08-03 16:16:46 -04:00
|
|
|
|
|
|
|
|
2003-08-23 11:12:22 -04:00
|
|
|
/**
|
2003-09-18 14:56:35 -04:00
|
|
|
* <p>Compares two properties. Please beware that a property with ID == 0 is
|
|
|
|
* a special case: It does not have a type, and its value is the section's
|
|
|
|
* dictionary.</p>
|
|
|
|
*
|
2003-08-23 11:12:22 -04:00
|
|
|
* @see Object#equals(java.lang.Object)
|
|
|
|
*/
|
|
|
|
public boolean equals(final Object o)
|
2003-08-03 16:16:46 -04:00
|
|
|
{
|
2003-08-30 05:13:53 -04:00
|
|
|
if (!(o instanceof Property))
|
|
|
|
return false;
|
|
|
|
final Property p = (Property) o;
|
|
|
|
final Object pValue = p.getValue();
|
2003-09-18 14:56:35 -04:00
|
|
|
final long pId = p.getID();
|
|
|
|
if (id != pId || (id != 0 && type != p.getType()))
|
2003-08-30 05:13:53 -04:00
|
|
|
return false;
|
|
|
|
if (value == null && pValue == null)
|
|
|
|
return true;
|
|
|
|
if (value == null || pValue == null)
|
|
|
|
return false;
|
2003-09-18 14:56:35 -04:00
|
|
|
|
2003-08-30 05:13:53 -04:00
|
|
|
/* It's clear now that both values are non-null. */
|
|
|
|
final Class valueClass = value.getClass();
|
|
|
|
final Class pValueClass = pValue.getClass();
|
|
|
|
if (!(valueClass.isAssignableFrom(pValueClass)) &&
|
|
|
|
!(pValueClass.isAssignableFrom(valueClass)))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (value instanceof byte[])
|
|
|
|
return Util.equal((byte[]) value, (byte[]) pValue);
|
|
|
|
|
|
|
|
return value.equals(pValue);
|
2003-08-03 16:16:46 -04:00
|
|
|
}
|
|
|
|
|
2003-08-23 11:12:22 -04:00
|
|
|
|
|
|
|
|
|
|
|
/**
|
2003-08-30 05:13:53 -04:00
|
|
|
* @see Object#hashCode()
|
2003-08-23 11:12:22 -04:00
|
|
|
*/
|
2003-08-30 05:13:53 -04:00
|
|
|
public int hashCode()
|
|
|
|
{
|
|
|
|
long hashCode = 0;
|
|
|
|
hashCode += id;
|
|
|
|
hashCode += type;
|
|
|
|
if (value != null)
|
|
|
|
hashCode += value.hashCode();
|
|
|
|
final int returnHashCode = (int) (hashCode & 0x0ffffffffL );
|
|
|
|
return returnHashCode;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2003-08-23 11:12:22 -04:00
|
|
|
|
2003-08-30 05:13:53 -04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @see Object#toString()
|
|
|
|
*/
|
|
|
|
public String toString()
|
2003-08-23 11:12:22 -04:00
|
|
|
{
|
2003-08-30 05:13:53 -04:00
|
|
|
final StringBuffer b = new StringBuffer();
|
|
|
|
b.append(getClass().getName());
|
|
|
|
b.append('[');
|
|
|
|
b.append("id: ");
|
|
|
|
b.append(getID());
|
|
|
|
b.append(", type: ");
|
|
|
|
b.append(getType());
|
2003-12-02 12:46:01 -05:00
|
|
|
final Object value = getValue();
|
2003-08-30 05:13:53 -04:00
|
|
|
b.append(", value: ");
|
2003-12-02 12:46:01 -05:00
|
|
|
b.append(value.toString());
|
|
|
|
if (value instanceof String)
|
|
|
|
{
|
|
|
|
final String s = (String) value;
|
|
|
|
final int l = s.length();
|
|
|
|
final byte[] bytes = new byte[l * 2];
|
|
|
|
for (int i = 0; i < l; i++)
|
|
|
|
{
|
|
|
|
final char c = s.charAt(i);
|
|
|
|
final byte high = (byte) ((c & 0x00ff00) >> 8);
|
|
|
|
final byte low = (byte) ((c & 0x0000ff) >> 0);
|
|
|
|
bytes[i * 2] = high;
|
|
|
|
bytes[i * 2 + 1] = low;
|
|
|
|
}
|
|
|
|
final String hex = HexDump.dump(bytes, 0L, 0);
|
|
|
|
b.append(" [");
|
|
|
|
b.append(hex);
|
|
|
|
b.append("]");
|
|
|
|
}
|
2003-08-30 05:13:53 -04:00
|
|
|
b.append(']');
|
|
|
|
return b.toString();
|
2003-08-23 11:12:22 -04:00
|
|
|
}
|
|
|
|
|
2002-02-13 23:00:59 -05:00
|
|
|
}
|