You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@poi.apache.org by ki...@apache.org on 2016/11/27 20:19:19 UTC
svn commit: r1771640 [2/4] - in /poi: site/src/documentation/content/xdocs/
trunk/src/java/org/apache/poi/hpsf/
trunk/src/java/org/apache/poi/hpsf/extractor/
trunk/src/java/org/apache/poi/util/
trunk/src/testcases/org/apache/poi/hpsf/basic/
Modified: poi/trunk/src/java/org/apache/poi/hpsf/MutableSection.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/hpsf/MutableSection.java?rev=1771640&r1=1771639&r2=1771640&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/hpsf/MutableSection.java (original)
+++ poi/trunk/src/java/org/apache/poi/hpsf/MutableSection.java Sun Nov 27 20:19:18 2016
@@ -17,683 +17,27 @@
package org.apache.poi.hpsf;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.Date;
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.ListIterator;
-import java.util.Map;
+import java.io.UnsupportedEncodingException;
-import org.apache.poi.hpsf.wellknown.PropertyIDMap;
-import org.apache.poi.util.CodePageUtil;
-import org.apache.poi.util.LittleEndian;
+import org.apache.poi.util.Removal;
/**
* <p>Adds writing capability to the {@link Section} class.</p>
*
* <p>Please be aware that this class' functionality will be merged into the
* {@link Section} class at a later time, so the API will change.</p>
+ *
+ * @deprecated POI 3.16 - use Section as base class instead
*/
-public class MutableSection extends Section
-{
- /**
- * <p>If the "dirty" flag is true, the section's size must be
- * (re-)calculated before the section is written.</p>
- */
- private boolean dirty = true;
-
-
-
- /**
- * <p>List to assemble the properties. Unfortunately a wrong
- * decision has been taken when specifying the "properties" field
- * as an Property[]. It should have been a {@link java.util.List}.</p>
- */
- private List<Property> preprops;
-
-
-
- /**
- * <p>Contains the bytes making out the section. This byte array is
- * established when the section's size is calculated and can be reused
- * later. It is valid only if the "dirty" flag is false.</p>
- */
- private byte[] sectionBytes;
-
-
-
- /**
- * <p>Creates an empty mutable section.</p>
- */
- public MutableSection()
- {
- dirty = true;
- formatID = null;
- offset = -1;
- preprops = new LinkedList<Property>();
- }
-
-
-
- /**
- * <p>Constructs a <code>MutableSection</code> by doing a deep copy of an
- * existing <code>Section</code>. All nested <code>Property</code>
- * instances, will be their mutable counterparts in the new
- * <code>MutableSection</code>.</p>
- *
- * @param s The section set to copy
- */
- public MutableSection(final Section s)
- {
- setFormatID(s.getFormatID());
- final Property[] pa = s.getProperties();
- final MutableProperty[] mpa = new MutableProperty[pa.length];
- for (int i = 0; i < pa.length; i++)
- mpa[i] = new MutableProperty(pa[i]);
- setProperties(mpa);
- setDictionary(s.getDictionary());
- }
-
-
-
- /**
- * <p>Sets the section's format ID.</p>
- *
- * @param formatID The section's format ID
- *
- * @see #setFormatID(byte[])
- * @see Section#getFormatID
- */
- public void setFormatID(final ClassID formatID)
- {
- this.formatID = formatID;
- }
-
-
-
- /**
- * <p>Sets the section's format ID.</p>
- *
- * @param formatID The section's format ID as a byte array. It components
- * are in big-endian format.
- *
- * @see #setFormatID(ClassID)
- * @see Section#getFormatID
- */
- public void setFormatID(final byte[] formatID)
- {
- ClassID fid = getFormatID();
- if (fid == null)
- {
- fid = new ClassID();
- setFormatID(fid);
- }
- fid.setBytes(formatID);
- }
-
-
-
- /**
- * <p>Sets this section's properties. Any former values are overwritten.</p>
- *
- * @param properties This section's new properties.
- */
- public void setProperties(final Property[] properties)
- {
- this.properties = properties;
- preprops = new LinkedList<Property>();
- for (int i = 0; i < properties.length; i++)
- preprops.add(properties[i]);
- dirty = true;
- }
-
-
-
- /**
- * <p>Sets the string value of the property with the specified ID.</p>
- *
- * @param id The property's ID
- * @param value The property's value. It will be written as a Unicode
- * string.
- *
- * @see #setProperty(int, long, Object)
- * @see #getProperty
- */
- public void setProperty(final int id, final String value)
- {
- setProperty(id, Variant.VT_LPWSTR, value);
- dirty = true;
- }
-
-
-
- /**
- * <p>Sets the int value of the property with the specified ID.</p>
- *
- * @param id The property's ID
- * @param value The property's value.
- *
- * @see #setProperty(int, long, Object)
- * @see #getProperty
- */
- public void setProperty(final int id, final int value)
- {
- setProperty(id, Variant.VT_I4, Integer.valueOf(value));
- dirty = true;
- }
-
-
-
- /**
- * <p>Sets the long value of the property with the specified ID.</p>
- *
- * @param id The property's ID
- * @param value The property's value.
- *
- * @see #setProperty(int, long, Object)
- * @see #getProperty
- */
- public void setProperty(final int id, final long value)
- {
- setProperty(id, Variant.VT_I8, Long.valueOf(value));
- dirty = true;
- }
-
-
-
- /**
- * <p>Sets the boolean value of the property with the specified ID.</p>
- *
- * @param id The property's ID
- * @param value The property's value.
- *
- * @see #setProperty(int, long, Object)
- * @see #getProperty
- */
- public void setProperty(final int id, final boolean value)
- {
- setProperty(id, Variant.VT_BOOL, Boolean.valueOf(value));
- dirty = true;
- }
-
-
-
- /**
- * <p>Sets the value and the variant type of the property with the
- * specified ID. If a property with this ID is not yet present in
- * the section, it will be added. An already present property with
- * the specified ID will be overwritten. A default mapping will be
- * used to choose the property's type.</p>
- *
- * @param id The property's ID.
- * @param variantType The property's variant type.
- * @param value The property's value.
- *
- * @see #setProperty(int, String)
- * @see #getProperty
- * @see Variant
- */
- public void setProperty(final int id, final long variantType,
- final Object value)
- {
- final MutableProperty p = new MutableProperty();
- p.setID(id);
- p.setType(variantType);
- p.setValue(value);
- setProperty(p);
- dirty = true;
- }
-
-
-
- /**
- * <p>Sets a property.</p>
- *
- * @param p The property to be set.
- *
- * @see #setProperty(int, long, Object)
- * @see #getProperty
- * @see Variant
- */
- public void setProperty(final Property p)
- {
- final long id = p.getID();
- removeProperty(id);
- preprops.add(p);
- dirty = true;
- }
-
-
-
- /**
- * <p>Removes a property.</p>
- *
- * @param id The ID of the property to be removed
- */
- public void removeProperty(final long id)
- {
- for (final Iterator<Property> i = preprops.iterator(); i.hasNext();)
- if (i.next().getID() == id)
- {
- i.remove();
- break;
- }
- dirty = true;
- }
-
-
-
- /**
- * <p>Sets the value of the boolean property with the specified
- * ID.</p>
- *
- * @param id The property's ID
- * @param value The property's value
- *
- * @see #setProperty(int, long, Object)
- * @see #getProperty
- * @see Variant
- */
- protected void setPropertyBooleanValue(final int id, final boolean value)
- {
- setProperty(id, Variant.VT_BOOL, Boolean.valueOf(value));
- }
-
-
-
- /**
- * <p>Returns the section's size.</p>
- *
- * @return the section's size.
- */
- public int getSize()
- {
- if (dirty)
- {
- try
- {
- size = calcSize();
- dirty = false;
- }
- catch (HPSFRuntimeException ex)
- {
- throw ex;
- }
- catch (Exception ex)
- {
- throw new HPSFRuntimeException(ex);
- }
- }
- return size;
- }
-
-
-
- /**
- * <p>Calculates the section's size. It is the sum of the lengths of the
- * section's header (8), the properties list (16 times the number of
- * properties) and the properties themselves.</p>
- *
- * @return the section's length in bytes.
- * @throws WritingNotSupportedException
- * @throws IOException
- */
- private int calcSize() throws WritingNotSupportedException, IOException
- {
- final ByteArrayOutputStream out = new ByteArrayOutputStream();
- write(out);
- out.close();
- /* Pad to multiple of 4 bytes so that even the Windows shell (explorer)
- * shows custom properties. */
- sectionBytes = Util.pad4(out.toByteArray());
- return sectionBytes.length;
- }
-
-
-
- /**
- * <p>Writes this section into an output stream.</p>
- *
- * <p>Internally this is done by writing into three byte array output
- * streams: one for the properties, one for the property list and one for
- * the section as such. The two former are appended to the latter when they
- * have received all their data.</p>
- *
- * @param out The stream to write into.
- *
- * @return The number of bytes written, i.e. the section's size.
- * @exception IOException if an I/O error occurs
- * @exception WritingNotSupportedException if HPSF does not yet support
- * writing a property's variant type.
- */
- public int write(final OutputStream out)
- throws WritingNotSupportedException, IOException
- {
- /* Check whether we have already generated the bytes making out the
- * section. */
- if (!dirty && sectionBytes != null)
- {
- out.write(sectionBytes);
- return sectionBytes.length;
- }
-
- /* The properties are written to this stream. */
- final ByteArrayOutputStream propertyStream =
- new ByteArrayOutputStream();
-
- /* The property list is established here. After each property that has
- * been written to "propertyStream", a property list entry is written to
- * "propertyListStream". */
- final ByteArrayOutputStream propertyListStream =
- new ByteArrayOutputStream();
-
- /* Maintain the current position in the list. */
- int position = 0;
-
- /* Increase the position variable by the size of the property list so
- * that it points behind the property list and to the beginning of the
- * properties themselves. */
- position += 2 * LittleEndian.INT_SIZE +
- getPropertyCount() * 2 * LittleEndian.INT_SIZE;
-
- /* Writing the section's dictionary it tricky. If there is a dictionary
- * (property 0) the codepage property (property 1) must be set, too. */
- int codepage = -1;
- if (getProperty(PropertyIDMap.PID_DICTIONARY) != null)
- {
- final Object p1 = getProperty(PropertyIDMap.PID_CODEPAGE);
- if (p1 != null)
- {
- if (!(p1 instanceof Integer))
- throw new IllegalPropertySetDataException
- ("The codepage property (ID = 1) must be an " +
- "Integer object.");
- }
- else
- /* Warning: The codepage property is not set although a
- * dictionary is present. In order to cope with this problem we
- * add the codepage property and set it to Unicode. */
- setProperty(PropertyIDMap.PID_CODEPAGE, Variant.VT_I2,
- Integer.valueOf(CodePageUtil.CP_UNICODE));
- codepage = getCodepage();
- }
-
- /* Sort the property list by their property IDs: */
- Collections.sort(preprops, new Comparator<Property>()
- {
- public int compare(final Property p1, final Property p2)
- {
- if (p1.getID() < p2.getID())
- return -1;
- else if (p1.getID() == p2.getID())
- return 0;
- else
- return 1;
- }
- });
-
- /* Write the properties and the property list into their respective
- * streams: */
- for (final ListIterator<Property> i = preprops.listIterator(); i.hasNext();)
- {
- final MutableProperty p = (MutableProperty) i.next();
- final long id = p.getID();
-
- /* Write the property list entry. */
- TypeWriter.writeUIntToStream(propertyListStream, p.getID());
- TypeWriter.writeUIntToStream(propertyListStream, position);
-
- /* If the property ID is not equal 0 we write the property and all
- * is fine. However, if it equals 0 we have to write the section's
- * dictionary which has an implicit type only and an explicit
- * value. */
- if (id != 0)
- /* Write the property and update the position to the next
- * property. */
- position += p.write(propertyStream, getCodepage());
- else
- {
- if (codepage == -1)
- throw new IllegalPropertySetDataException
- ("Codepage (property 1) is undefined.");
- position += writeDictionary(propertyStream, dictionary,
- codepage);
- }
- }
- propertyStream.close();
- propertyListStream.close();
-
- /* Write the section: */
- byte[] pb1 = propertyListStream.toByteArray();
- byte[] pb2 = propertyStream.toByteArray();
-
- /* Write the section's length: */
- TypeWriter.writeToStream(out, LittleEndian.INT_SIZE * 2 +
- pb1.length + pb2.length);
-
- /* Write the section's number of properties: */
- TypeWriter.writeToStream(out, getPropertyCount());
-
- /* Write the property list: */
- out.write(pb1);
-
- /* Write the properties: */
- out.write(pb2);
-
- int streamLength = LittleEndian.INT_SIZE * 2 + pb1.length + pb2.length;
- return streamLength;
- }
-
-
-
- /**
- * <p>Writes the section's dictionary.</p>
- *
- * @param out The output stream to write to.
- * @param dictionary The dictionary.
- * @param codepage The codepage to be used to write the dictionary items.
- * @return The number of bytes written
- * @exception IOException if an I/O exception occurs.
- */
- private static int writeDictionary(final OutputStream out,
- final Map<Long,String> dictionary, final int codepage)
- throws IOException
- {
- int length = TypeWriter.writeUIntToStream(out, dictionary.size());
- for (Map.Entry<Long,String> ls : dictionary.entrySet()) {
- final Long key = ls.getKey();
- final String value = ls.getValue();
-
- if (codepage == CodePageUtil.CP_UNICODE)
- {
- /* Write the dictionary item in Unicode. */
- int sLength = value.length() + 1;
- if ((sLength & 1) == 1) {
- sLength++;
- }
- length += TypeWriter.writeUIntToStream(out, key.longValue());
- length += TypeWriter.writeUIntToStream(out, sLength);
- final byte[] ca = CodePageUtil.getBytesInCodePage(value, codepage);
- for (int j = 2; j < ca.length; j += 2)
- {
- out.write(ca[j+1]);
- out.write(ca[j]);
- length += 2;
- }
- sLength -= value.length();
- while (sLength > 0)
- {
- out.write(0x00);
- out.write(0x00);
- length += 2;
- sLength--;
- }
- }
- else
- {
- /* Write the dictionary item in another codepage than
- * Unicode. */
- length += TypeWriter.writeUIntToStream(out, key.longValue());
- length += TypeWriter.writeUIntToStream(out, value.length() + 1);
- final byte[] ba = CodePageUtil.getBytesInCodePage(value, codepage);
- for (int j = 0; j < ba.length; j++)
- {
- out.write(ba[j]);
- length++;
- }
- out.write(0x00);
- length++;
- }
- }
- return length;
- }
-
-
-
- /**
- * <p>Overwrites the super class' method to cope with a redundancy:
- * the property count is maintained in a separate member variable, but
- * shouldn't.</p>
- *
- * @return The number of properties in this section
- */
- public int getPropertyCount()
- {
- return preprops.size();
- }
-
-
-
- /**
- * <p>Gets this section's properties.</p>
- *
- * @return this section's properties.
- */
- public Property[] getProperties()
- {
- properties = preprops.toArray(new Property[0]);
- return properties;
- }
-
-
-
- /**
- * <p>Gets a property.</p>
- *
- * @param id The ID of the property to get
- * @return The property or <code>null</code> if there is no such property
- */
- public Object getProperty(final long id)
- {
- /* Calling getProperties() ensures that properties and preprops are in
- * sync.</p> */
- getProperties();
- return super.getProperty(id);
- }
-
-
-
- /**
- * <p>Sets the section's dictionary. All keys in the dictionary must be
- * {@link java.lang.Long} instances, all values must be
- * {@link java.lang.String}s. This method overwrites the properties with IDs
- * 0 and 1 since they are reserved for the dictionary and the dictionary's
- * codepage. Setting these properties explicitly might have surprising
- * effects. An application should never do this but always use this
- * method.</p>
- *
- * @param dictionary The dictionary
- *
- * @exception IllegalPropertySetDataException if the dictionary's key and
- * value types are not correct.
- *
- * @see Section#getDictionary()
- */
- public void setDictionary(final Map<Long,String> dictionary)
- throws IllegalPropertySetDataException
- {
- if (dictionary != null)
- {
- this.dictionary = dictionary;
-
- /* Set the dictionary property (ID 0). Please note that the second
- * parameter in the method call below is unused because dictionaries
- * don't have a type. */
- setProperty(PropertyIDMap.PID_DICTIONARY, -1, dictionary);
-
- /* If the codepage property (ID 1) for the strings (keys and
- * values) used in the dictionary is not yet defined, set it to
- * Unicode. */
- final Integer codepage =
- (Integer) getProperty(PropertyIDMap.PID_CODEPAGE);
- if (codepage == null)
- setProperty(PropertyIDMap.PID_CODEPAGE, Variant.VT_I2,
- Integer.valueOf(CodePageUtil.CP_UNICODE));
- }
- else
- /* Setting the dictionary to null means to remove property 0.
- * However, it does not mean to remove property 1 (codepage). */
- removeProperty(PropertyIDMap.PID_DICTIONARY);
- }
-
-
-
- /**
- * <p>Sets a property.</p>
- *
- * @param id The property ID.
- * @param value The property's value. The value's class must be one of those
- * supported by HPSF.
- */
- public void setProperty(final int id, final Object value)
- {
- if (value instanceof String)
- setProperty(id, (String) value);
- else if (value instanceof Long)
- setProperty(id, ((Long) value).longValue());
- else if (value instanceof Integer)
- setProperty(id, ((Integer) value).intValue());
- else if (value instanceof Short)
- setProperty(id, ((Short) value).intValue());
- else if (value instanceof Boolean)
- setProperty(id, ((Boolean) value).booleanValue());
- else if (value instanceof Date)
- setProperty(id, Variant.VT_FILETIME, value);
- else
- throw new HPSFRuntimeException(
- "HPSF does not support properties of type " +
- value.getClass().getName() + ".");
- }
-
-
-
- /**
- * <p>Removes all properties from the section including 0 (dictionary) and
- * 1 (codepage).</p>
- */
- public void clear()
- {
- final Property[] properties = getProperties();
- for (int i = 0; i < properties.length; i++)
- {
- final Property p = properties[i];
- removeProperty(p.getID());
- }
- }
-
- /**
- * <p>Sets the codepage.</p>
- *
- * @param codepage the codepage
- */
- public void setCodepage(final int codepage)
- {
- setProperty(PropertyIDMap.PID_CODEPAGE, Variant.VT_I2,
- Integer.valueOf(codepage));
+@Removal(version="3.18")
+public class MutableSection extends Section {
+ public MutableSection() {}
+
+ public MutableSection(final Section s) {
+ super(s);
+ }
+
+ public MutableSection(final byte[] src, final int offset) throws UnsupportedEncodingException {
+ super(src,offset);
}
}
Modified: poi/trunk/src/java/org/apache/poi/hpsf/NoFormatIDException.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/hpsf/NoFormatIDException.java?rev=1771640&r1=1771639&r2=1771640&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/hpsf/NoFormatIDException.java (original)
+++ poi/trunk/src/java/org/apache/poi/hpsf/NoFormatIDException.java Sun Nov 27 20:19:18 2016
@@ -18,53 +18,47 @@
package org.apache.poi.hpsf;
/**
- * <p>This exception is thrown if a {@link MutablePropertySet} is to be written
- * but does not have a formatID set (see {@link
- * MutableSection#setFormatID(ClassID)} or
- * {@link org.apache.poi.hpsf.MutableSection#setFormatID(byte[])}.
+ * This exception is thrown if a {@link PropertySet} is to be written
+ * but does not have a formatID set (see {@link Section#setFormatID(ClassID)} or
+ * {@link org.apache.poi.hpsf.Section#setFormatID(byte[])}.
*/
-public class NoFormatIDException extends HPSFRuntimeException
-{
+public class NoFormatIDException extends HPSFRuntimeException {
/**
- * <p>Constructor</p>
+ * Constructor
*/
- public NoFormatIDException()
- {
+ public NoFormatIDException() {
super();
}
/**
- * <p>Constructor</p>
+ * Constructor
*
* @param msg The exception's message string
*/
- public NoFormatIDException(final String msg)
- {
+ public NoFormatIDException(final String msg) {
super(msg);
}
/**
- * <p>Constructor</p>
+ * Constructor
*
* @param reason This exception's underlying reason
*/
- public NoFormatIDException(final Throwable reason)
- {
+ public NoFormatIDException(final Throwable reason) {
super(reason);
}
/**
- * <p>Constructor</p>
+ * Constructor
*
* @param msg The exception's message string
* @param reason This exception's underlying reason
*/
- public NoFormatIDException(final String msg, final Throwable reason)
- {
+ public NoFormatIDException(final String msg, final Throwable reason) {
super(msg, reason);
}
Modified: poi/trunk/src/java/org/apache/poi/hpsf/Property.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/hpsf/Property.java?rev=1771640&r1=1771639&r2=1771640&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/hpsf/Property.java (original)
+++ poi/trunk/src/java/org/apache/poi/hpsf/Property.java Sun Nov 27 20:19:18 2016
@@ -17,8 +17,11 @@
package org.apache.poi.hpsf;
+import java.io.IOException;
+import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
+import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.Map;
@@ -29,104 +32,74 @@ import org.apache.poi.util.POILogFactory
import org.apache.poi.util.POILogger;
/**
- * <p>A property in a {@link Section} of a {@link PropertySet}.</p>
+ * A property in a {@link Section} of a {@link PropertySet}.<p>
*
- * <p>The property's <strong>ID</strong> gives the property a meaning
+ * The property's {@code ID} gives the property a meaning
* in the context of its {@link Section}. Each {@link Section} spans
- * its own name space of property IDs.</p>
+ * its own name space of property IDs.<p>
*
- * <p>The property's <strong>type</strong> determines how its
- * <strong>value </strong> is interpreted. For example, if the type is
+ * The property's {@code type} determines how its
+ * {@code value} is interpreted. For example, if the type is
* {@link Variant#VT_LPSTR} (byte string), the value consists of a
* DWord telling how many bytes the string contains. The bytes follow
* immediately, including any null bytes that terminate the
* 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>
+ * value, {@link Variant#VT_FILETIME} some date and time (of a file).<p>
*
- * <p>Please note that not all {@link Variant} types yet. This might change
+ * 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>
- *
- * <p>Microsoft documentation: <a
- * href="http://msdn.microsoft.com/library/en-us/stg/stg/property_set_display_name_dictionary.asp?frame=true">
- * Property Set Display Name Dictionary</a>.
+ * reports or patches for the types you need.
*
* @see Section
* @see Variant
+ * @see <a href="https://msdn.microsoft.com/en-us/library/dd942421.aspx">
+ * [MS-OLEPS]: Object Linking and Embedding (OLE) Property Set Data Structures</a>
*/
-public class Property
-{
+public class Property {
- /** <p>The property's ID.</p> */
- protected long id;
+ /** The property's ID. */
+ private long id;
+ /** The property's type. */
+ private long type;
- /**
- * <p>Returns the property's ID.</p>
- *
- * @return The ID value
- */
- public long getID()
- {
- return id;
- }
-
-
-
- /** <p>The property's type.</p> */
- protected long type;
+ /** The property's value. */
+ protected Object value;
/**
- * <p>Returns the property's type.</p>
- *
- * @return The type value
+ * Creates an empty property. It must be filled using the set method to be usable.
*/
- public long getType()
- {
- return type;
+ public Property() {
}
-
-
- /** <p>The property's value.</p> */
- protected Object value;
-
-
/**
- * <p>Returns the property's value.</p>
+ * Creates a {@code Property} as a copy of an existing {@code Property}.
*
- * @return The property's value
+ * @param p The property to copy.
*/
- public Object getValue()
- {
- return value;
+ public Property(Property p) {
+ this(p.id, p.type, p.value);
}
-
-
-
+
/**
- * <p>Creates a property.</p>
+ * Creates a property.
*
* @param id the property's ID.
* @param type the property's type, see {@link Variant}.
* @param value the property's value. Only certain types are allowed, see
* {@link Variant}.
*/
- public Property(final long id, final long type, final Object value)
- {
+ public Property(final long id, final long type, final Object value) {
this.id = id;
this.type = type;
this.value = value;
}
-
-
/**
- * <p>Creates a {@link Property} instance by reading its bytes
- * from the property set stream.</p>
+ * Creates a {@link Property} instance by reading its bytes
+ * from the property set stream.
*
* @param id The property's ID.
* @param src The bytes the property set stream consists of.
@@ -138,18 +111,15 @@ public class Property
* @exception UnsupportedEncodingException if the specified codepage is not
* supported.
*/
- public Property(final long id, final byte[] src, final long offset,
- final int length, final int codepage)
- throws UnsupportedEncodingException
- {
+ public Property(final long id, final byte[] src, final long offset, final int length, final int codepage)
+ throws UnsupportedEncodingException {
this.id = id;
/*
* ID 0 is a special case since it specifies a dictionary of
* property IDs and property names.
*/
- if (id == 0)
- {
+ if (id == 0) {
value = readDictionary(src, offset, length, codepage);
return;
}
@@ -158,12 +128,9 @@ public class Property
type = LittleEndian.getUInt(src, o);
o += LittleEndian.INT_SIZE;
- try
- {
+ try {
value = VariantSupport.read(src, o, length, (int) type, codepage);
- }
- catch (UnsupportedVariantTypeException ex)
- {
+ } catch (UnsupportedVariantTypeException ex) {
VariantSupport.writeUnsupportedTypeMessage(ex);
value = ex.getValue();
}
@@ -172,19 +139,68 @@ public class Property
/**
- * <p>Creates an empty property. It must be filled using the set method to
- * be usable.</p>
+ * Returns the property's ID.
+ *
+ * @return The ID value
*/
- protected Property()
- { }
+ public long getID() {
+ return id;
+ }
+ /**
+ * Sets the property's ID.
+ *
+ * @param id the ID
+ */
+ public void setID(final long id) {
+ this.id = id;
+ }
+ /**
+ * Returns the property's type.
+ *
+ * @return The type value
+ */
+ public long getType() {
+ return type;
+ }
/**
- * <p>Reads a dictionary.</p>
+ * Sets the property's type.
+ *
+ * @param type the property's type
+ */
+ public void setType(final long type) {
+ this.type = type;
+ }
+
+ /**
+ * Returns the property's value.
+ *
+ * @return The property's value
+ */
+ public Object getValue() {
+ return value;
+ }
+
+ /**
+ * Sets the property's value.
+ *
+ * @param value the property's value
+ */
+ public void setValue(final Object value) {
+ this.value = value;
+ }
+
+
+
+
+
+ /**
+ * Reads a dictionary.
*
* @param src The byte array containing the bytes making out the dictionary.
- * @param offset At this offset within <var>src </var> the dictionary
+ * @param offset At this offset within {@code src} the dictionary
* starts.
* @param length The dictionary contains at most this many bytes.
* @param codepage The codepage of the string values.
@@ -192,15 +208,14 @@ public class Property
* @throws UnsupportedEncodingException if the dictionary's codepage is not
* (yet) supported.
*/
- protected Map<?, ?> readDictionary(final byte[] src, final long offset,
- final int length, final int codepage)
- throws UnsupportedEncodingException
- {
+ protected Map<?, ?> readDictionary(final byte[] src, final long offset, final int length, final int codepage)
+ throws UnsupportedEncodingException {
/* Check whether "offset" points into the "src" array". */
- if (offset < 0 || offset > src.length)
+ if (offset < 0 || offset > src.length) {
throw new HPSFRuntimeException
("Illegal offset " + offset + " while HPSF stream contains " +
length + " bytes.");
+ }
int o = (int) offset;
/*
@@ -209,13 +224,10 @@ public class Property
final long nrEntries = LittleEndian.getUInt(src, o);
o += LittleEndian.INT_SIZE;
- final Map<Object, Object> m = new LinkedHashMap<Object, Object>(
- (int) nrEntries, (float) 1.0 );
+ final Map<Object, Object> m = new LinkedHashMap<Object, Object>((int) nrEntries, (float) 1.0 );
- try
- {
- for (int i = 0; i < nrEntries; i++)
- {
+ try {
+ for (int i = 0; i < nrEntries; i++) {
/* The key. */
final Long id = Long.valueOf(LittleEndian.getUInt(src, o));
o += LittleEndian.INT_SIZE;
@@ -230,17 +242,13 @@ public class Property
/* Read the string. */
final StringBuffer b = new StringBuffer();
- switch (codepage)
- {
+ switch (codepage) {
case -1:
- {
/* Without a codepage the length is equal to the number of
* bytes. */
b.append(new String(src, o, (int) sLength, Charset.forName("ASCII")));
break;
- }
case CodePageUtil.CP_UNICODE:
- {
/* The length is the number of characters, i.e. the number
* of bytes is twice the number of the characters. */
final int nrBytes = (int) (sLength * 2);
@@ -250,36 +258,30 @@ public class Property
h[i2] = src[o + i2 + 1];
h[i2 + 1] = src[o + i2];
}
- b.append(new String(h, 0, nrBytes,
- CodePageUtil.codepageToEncoding(codepage)));
+ b.append(new String(h, 0, nrBytes, CodePageUtil.codepageToEncoding(codepage)));
break;
- }
default:
- {
/* For encodings other than Unicode the length is the number
* of bytes. */
- b.append(new String(src, o, (int) sLength,
- VariantSupport.codepageToEncoding(codepage)));
+ b.append(new String(src, o, (int) sLength, CodePageUtil.codepageToEncoding(codepage)));
break;
- }
}
/* Strip 0x00 characters from the end of the string: */
- while (b.length() > 0 && b.charAt(b.length() - 1) == 0x00)
+ while (b.length() > 0 && b.charAt(b.length() - 1) == 0x00) {
b.setLength(b.length() - 1);
- if (codepage == CodePageUtil.CP_UNICODE)
- {
- if (sLength % 2 == 1)
+ }
+ if (codepage == CodePageUtil.CP_UNICODE) {
+ if (sLength % 2 == 1) {
sLength++;
+ }
o += (sLength + sLength);
- }
- else
+ } else {
o += sLength;
+ }
m.put(id, b.toString());
}
- }
- catch (RuntimeException ex)
- {
+ } catch (RuntimeException ex) {
final POILogger l = POILogFactory.getLogger(getClass());
l.log(POILogger.WARN,
"The property set's dictionary contains bogus data. "
@@ -292,8 +294,7 @@ public class Property
/**
- * <p>Returns the property's size in bytes. This is always a multiple of
- * 4.</p>
+ * Returns the property's size in bytes. This is always a multiple of 4.
*
* @return the property's size in bytes
*
@@ -303,18 +304,18 @@ public class Property
protected int getSize() throws WritingNotSupportedException
{
int length = VariantSupport.getVariantLength(type);
- if (length >= 0)
+ if (length >= 0) {
return length; /* Fixed length */
- if (length == -2)
+ }
+ if (length == -2) {
/* Unknown length */
throw new WritingNotSupportedException(type, null);
+ }
/* Variable length: */
final int PADDING = 4; /* Pad to multiples of 4. */
- switch ((int) type)
- {
- case Variant.VT_LPSTR:
- {
+ switch ((int) type) {
+ case Variant.VT_LPSTR: {
int l = ((String) value).length() + 1;
int r = l % PADDING;
if (r > 0)
@@ -333,51 +334,53 @@ public class Property
/**
- * <p>Compares two properties.</p> <p>Please beware that a property with
+ * Compares two properties.<p>
+ *
+ * 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. Another special case are strings: Two properties
- * may have the different types Variant.VT_LPSTR and Variant.VT_LPWSTR;</p>
+ * may have the different types Variant.VT_LPSTR and Variant.VT_LPWSTR;
*
* @see Object#equals(java.lang.Object)
*/
- public boolean equals(final Object o)
- {
+ public boolean equals(final Object o) {
if (!(o instanceof Property)) {
return false;
}
final Property p = (Property) o;
final Object pValue = p.getValue();
final long pId = p.getID();
- if (id != pId || (id != 0 && !typesAreEqual(type, p.getType())))
+ if (id != pId || (id != 0 && !typesAreEqual(type, p.getType()))) {
return false;
- if (value == null && pValue == null)
+ }
+ if (value == null && pValue == null) {
return true;
- if (value == null || pValue == null)
+ }
+ if (value == null || pValue == null) {
return false;
+ }
/* 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)))
+ !(pValueClass.isAssignableFrom(valueClass))) {
return false;
+ }
- if (value instanceof byte[])
- return Util.equal((byte[]) value, (byte[]) pValue);
+ if (value instanceof byte[]) {
+ return Arrays.equals((byte[]) value, (byte[]) pValue);
+ }
return value.equals(pValue);
}
- private boolean typesAreEqual(final long t1, final long t2)
- {
- if (t1 == t2 ||
+ private boolean typesAreEqual(final long t1, final long t2) {
+ return (t1 == t2 ||
(t1 == Variant.VT_LPSTR && t2 == Variant.VT_LPWSTR) ||
- (t2 == Variant.VT_LPSTR && t1 == Variant.VT_LPWSTR)) {
- return true;
- }
- return false;
+ (t2 == Variant.VT_LPSTR && t1 == Variant.VT_LPWSTR));
}
@@ -385,15 +388,14 @@ public class Property
/**
* @see Object#hashCode()
*/
- public int hashCode()
- {
+ public int hashCode() {
long hashCode = 0;
hashCode += id;
hashCode += type;
- if (value != null)
+ if (value != null) {
hashCode += value.hashCode();
- final int returnHashCode = (int) (hashCode & 0x0ffffffffL );
- return returnHashCode;
+ }
+ return (int) (hashCode & 0x0ffffffffL );
}
@@ -402,8 +404,7 @@ public class Property
/**
* @see Object#toString()
*/
- public String toString()
- {
+ public String toString() {
final StringBuffer b = new StringBuffer();
b.append(getClass().getName());
b.append('[');
@@ -413,14 +414,12 @@ public class Property
b.append(getType());
final Object value = getValue();
b.append(", value: ");
- if (value instanceof String)
- {
+ if (value instanceof String) {
b.append(value.toString());
final String s = (String) value;
final int l = s.length();
final byte[] bytes = new byte[l * 2];
- for (int i = 0; i < l; i++)
- {
+ 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);
@@ -433,21 +432,43 @@ public class Property
b.append(hex);
}
b.append("]");
- }
- else if (value instanceof byte[])
- {
+ } else if (value instanceof byte[]) {
byte[] bytes = (byte[])value;
if(bytes.length > 0) {
String hex = HexDump.dump(bytes, 0L, 0);
b.append(hex);
}
- }
- else
- {
+ } else {
b.append(value.toString());
}
b.append(']');
return b.toString();
}
+ /**
+ * Writes the property to an output stream.
+ *
+ * @param out The output stream to write to.
+ * @param codepage The codepage to use for writing non-wide strings
+ * @return the number of bytes written to the stream
+ *
+ * @exception IOException if an I/O error occurs
+ * @exception WritingNotSupportedException if a variant type is to be
+ * written that is not yet supported
+ */
+ public int write(final OutputStream out, final int codepage)
+ throws IOException, WritingNotSupportedException {
+ int length = 0;
+ long variantType = getType();
+
+ /* Ensure that wide strings are written if the codepage is Unicode. */
+ if (codepage == CodePageUtil.CP_UNICODE && variantType == Variant.VT_LPSTR) {
+ variantType = Variant.VT_LPWSTR;
+ }
+
+ length += TypeWriter.writeUIntToStream(out, variantType);
+ length += VariantSupport.write(out, variantType, getValue(), codepage);
+ return length;
+ }
+
}
Modified: poi/trunk/src/java/org/apache/poi/hpsf/PropertySet.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/hpsf/PropertySet.java?rev=1771640&r1=1771639&r2=1771640&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/hpsf/PropertySet.java (original)
+++ poi/trunk/src/java/org/apache/poi/hpsf/PropertySet.java Sun Nov 27 20:19:18 2016
@@ -17,246 +17,195 @@
package org.apache.poi.hpsf;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
-import java.util.ArrayList;
+import java.nio.charset.Charset;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.LinkedList;
import java.util.List;
+import org.apache.poi.EmptyFileException;
+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.IOUtils;
import org.apache.poi.util.LittleEndian;
+import org.apache.poi.util.LittleEndianConsts;
/**
- * <p>Represents a property set in the Horrible Property Set Format
+ * Represents a property set in the Horrible Property Set Format
* (HPSF). These are usually metadata of a Microsoft Office
- * document.</p>
+ * document.<p>
*
- * <p>An application that wants to access these metadata should create
+ * An application that wants to access these metadata should create
* an instance of this class or one of its subclasses by calling the
* factory method {@link PropertySetFactory#create} and then retrieve
- * the information its needs by calling appropriate methods.</p>
+ * the information its needs by calling appropriate methods.<p>
*
- * <p>{@link PropertySetFactory#create} does its work by calling one
+ * {@link PropertySetFactory#create} does its work by calling one
* of the constructors {@link PropertySet#PropertySet(InputStream)} or
* {@link PropertySet#PropertySet(byte[])}. If the constructor's
* argument is not in the Horrible Property Set Format, i.e. not a
* property set stream, or if any other error occurs, an appropriate
- * exception is thrown.</p>
+ * exception is thrown.<p>
*
- * <p>A {@link PropertySet} has a list of {@link Section}s, and each
+ * A {@link PropertySet} has a list of {@link Section}s, and each
* {@link Section} has a {@link Property} array. Use {@link
* #getSections} to retrieve the {@link Section}s, then call {@link
* Section#getProperties} for each {@link Section} to get hold of the
- * {@link Property} arrays.</p> Since the vast majority of {@link
- * PropertySet}s contains only a single {@link Section}, the
- * convenience method {@link #getProperties} returns the properties of
- * a {@link PropertySet}'s {@link Section} (throwing a {@link
- * NoSingleSectionException} if the {@link PropertySet} contains more
- * (or less) than exactly one {@link Section}).
+ * {@link Property} arrays.<p>
+ *
+ * Since the vast majority of {@link PropertySet}s contains only a single
+ * {@link Section}, the convenience method {@link #getProperties} returns
+ * the properties of a {@link PropertySet}'s {@link Section} (throwing a
+ * {@link NoSingleSectionException} if the {@link PropertySet} contains
+ * more (or less) than exactly one {@link Section}).
*/
-public class PropertySet
-{
-
+public class PropertySet {
/**
- * <p>The "byteOrder" field must equal this value.</p>
+ * If the OS version field holds this value the property set stream was
+ * created on a 16-bit Windows system.
*/
- static final byte[] BYTE_ORDER_ASSERTION =
- {(byte) 0xFE, (byte) 0xFF};
+ public static final int OS_WIN16 = 0x0000;
/**
- * <p>Specifies this {@link PropertySet}'s byte order. See the
- * HPFS documentation for details!</p>
+ * If the OS version field holds this value the property set stream was
+ * created on a Macintosh system.
*/
- protected int byteOrder;
+ public static final int OS_MACINTOSH = 0x0001;
/**
- * <p>Returns the property set stream's low-level "byte order"
- * field. It is always <tt>0xFFFE</tt> .</p>
- *
- * @return The property set stream's low-level "byte order" field.
+ * If the OS version field holds this value the property set stream was
+ * created on a 32-bit Windows system.
*/
- public int getByteOrder()
- {
- return byteOrder;
- }
-
-
+ public static final int OS_WIN32 = 0x0002;
/**
- * <p>The "format" field must equal this value.</p>
+ * The "byteOrder" field must equal this value.
*/
- static final byte[] FORMAT_ASSERTION =
- {(byte) 0x00, (byte) 0x00};
+ private static final int BYTE_ORDER_ASSERTION = 0xFFFE;
/**
- * <p>Specifies this {@link PropertySet}'s format. See the HPFS
- * documentation for details!</p>
+ * The "format" field must equal this value.
*/
- protected int format;
+ private static final int FORMAT_ASSERTION = 0x0000;
/**
- * <p>Returns the property set stream's low-level "format"
- * field. It is always <tt>0x0000</tt> .</p>
- *
- * @return The property set stream's low-level "format" field.
+ * The length of the property set stream header.
*/
- public int getFormat()
- {
- return format;
- }
+ private static final int OFFSET_HEADER =
+ LittleEndianConsts.SHORT_SIZE + /* Byte order */
+ LittleEndianConsts.SHORT_SIZE + /* Format */
+ LittleEndianConsts.INT_SIZE + /* OS version */
+ ClassID.LENGTH + /* Class ID */
+ LittleEndianConsts.INT_SIZE; /* Section count */
-
-
+
/**
- * <p>Specifies the version of the operating system that created
- * this {@link PropertySet}. See the HPFS documentation for
- * details!</p>
+ * Specifies this {@link PropertySet}'s byte order. See the
+ * HPFS documentation for details!
*/
- protected int osVersion;
-
+ private int byteOrder;
/**
- * <p>If the OS version field holds this value the property set stream was
- * created on a 16-bit Windows system.</p>
+ * Specifies this {@link PropertySet}'s format. See the HPFS
+ * documentation for details!
*/
- public static final int OS_WIN16 = 0x0000;
-
+ private int format;
+
/**
- * <p>If the OS version field holds this value the property set stream was
- * created on a Macintosh system.</p>
+ * Specifies the version of the operating system that created this
+ * {@link PropertySet}. See the HPFS documentation for details!
*/
- public static final int OS_MACINTOSH = 0x0001;
+ private int osVersion;
/**
- * <p>If the OS version field holds this value the property set stream was
- * created on a 32-bit Windows system.</p>
+ * Specifies this {@link PropertySet}'s "classID" field. See
+ * the HPFS documentation for details!
*/
- public static final int OS_WIN32 = 0x0002;
+ private ClassID classID;
/**
- * <p>Returns the property set stream's low-level "OS version"
- * field.</p>
- *
- * @return The property set stream's low-level "OS version" field.
+ * The sections in this {@link PropertySet}.
*/
- public int getOSVersion()
- {
- return osVersion;
- }
-
-
-
- /**
- * <p>Specifies this {@link PropertySet}'s "classID" field. See
- * the HPFS documentation for details!</p>
- */
- protected ClassID classID;
-
- /**
- * <p>Returns the property set stream's low-level "class ID"
- * field.</p>
- *
- * @return The property set stream's low-level "class ID" field.
- */
- public ClassID getClassID()
- {
- return classID;
- }
-
-
+ private final List<Section> sections = new LinkedList<Section>();
+
/**
- * <p>Returns the number of {@link Section}s in the property
- * set.</p>
- *
- * @return The number of {@link Section}s in the property set.
+ * Constructs a {@code PropertySet} instance. Its
+ * primary task is to initialize the field with their proper values.
+ * It also sets fields that might change to reasonable defaults.
*/
- public int getSectionCount()
- {
- return sections.size();
- }
+ public PropertySet() {
+ /* Initialize the "byteOrder" field. */
+ byteOrder = BYTE_ORDER_ASSERTION;
+ /* Initialize the "format" field. */
+ format = FORMAT_ASSERTION;
+ /* Initialize "osVersion" field as if the property has been created on
+ * a Win32 platform, whether this is the case or not. */
+ osVersion = (OS_WIN32 << 16) | 0x0A04;
- /**
- * <p>The sections in this {@link PropertySet}.</p>
- */
- protected List<Section> sections;
+ /* Initialize the "classID" field. */
+ classID = new ClassID();
- /**
- * <p>Returns the {@link Section}s in the property set.</p>
- *
- * @return The {@link Section}s in the property set.
- */
- public List<Section> getSections()
- {
- return sections;
+ /* Initialize the sections. Since property set must have at least
+ * one section it is added right here. */
+ addSection(new MutableSection());
}
/**
- * <p>Creates an empty (uninitialized) {@link PropertySet}.</p>
+ * Creates a {@link PropertySet} instance from an {@link
+ * InputStream} in the Horrible Property Set Format.<p>
*
- * <p><strong>Please note:</strong> For the time being this
- * constructor is protected since it is used for internal purposes
- * only, but expect it to become public once the property set's
- * writing functionality is implemented.</p>
- */
- protected PropertySet()
- { }
-
-
-
- /**
- * <p>Creates a {@link PropertySet} instance from an {@link
- * InputStream} in the Horrible Property Set Format.</p>
- *
- * <p>The constructor reads the first few bytes from the stream
+ * 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.</p>
+ * 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.
+ * @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 PropertySet(final InputStream stream)
- throws NoPropertySetStreamException, MarkUnsupportedException,
- IOException, UnsupportedEncodingException
- {
- if (isPropertySetStream(stream))
- {
- final int avail = stream.available();
- final byte[] buffer = new byte[avail];
- IOUtils.readFully(stream, buffer);
- init(buffer, 0, buffer.length);
- }
- else
+ throws NoPropertySetStreamException, MarkUnsupportedException,
+ IOException, UnsupportedEncodingException {
+ if (!isPropertySetStream(stream)) {
throw new NoPropertySetStreamException();
+ }
+
+ final byte[] buffer = IOUtils.toByteArray(stream);
+ init(buffer, 0, buffer.length);
}
/**
- * <p>Creates a {@link PropertySet} instance from a byte array
- * that represents a stream in the Horrible Property Set
- * Format.</p>
+ * Creates a {@link PropertySet} instance from a byte array that
+ * represents a stream in the Horrible Property Set Format.
*
* @param stream The byte array holding the stream data.
- * @param offset The offset in <var>stream</var> where the stream
+ * @param offset The offset in {@code stream} where the stream
* data begin. If the stream data begin with the first byte in the
- * array, the <var>offset</var> is 0.
+ * array, the {@code offset} is 0.
* @param length The length of the stream data.
* @throws NoPropertySetStreamException if the byte array is not a
* property set stream.
@@ -264,20 +213,16 @@ public class PropertySet
* @exception UnsupportedEncodingException if the codepage is not supported.
*/
public PropertySet(final byte[] stream, final int offset, final int length)
- throws NoPropertySetStreamException, UnsupportedEncodingException
- {
- if (isPropertySetStream(stream, offset, length))
- init(stream, offset, length);
- else
+ throws NoPropertySetStreamException, UnsupportedEncodingException {
+ if (!isPropertySetStream(stream, offset, length)) {
throw new NoPropertySetStreamException();
+ }
+ init(stream, offset, length);
}
-
-
/**
- * <p>Creates a {@link PropertySet} instance from a byte array
- * that represents a stream in the Horrible Property Set
- * Format.</p>
+ * Creates a {@link PropertySet} instance from a byte array
+ * that represents a stream in the Horrible Property Set Format.
*
* @param stream The byte array holding the stream data. The
* complete byte array contents is the stream data.
@@ -287,75 +232,185 @@ public class PropertySet
* @exception UnsupportedEncodingException if the codepage is not supported.
*/
public PropertySet(final byte[] stream)
- throws NoPropertySetStreamException, UnsupportedEncodingException
- {
+ throws NoPropertySetStreamException, UnsupportedEncodingException {
this(stream, 0, stream.length);
}
+
+ /**
+ * Constructs a {@code PropertySet} by doing a deep copy of
+ * an existing {@code PropertySet}. All nested elements, i.e.
+ * {@code Section}s and {@code Property} instances, will be their
+ * counterparts in the new {@code PropertySet}.
+ *
+ * @param ps The property set to copy
+ */
+ public PropertySet(PropertySet ps) {
+ setByteOrder(ps.getByteOrder());
+ setFormat(ps.getFormat());
+ setOSVersion(ps.getOSVersion());
+ setClassID(ps.getClassID());
+ for (final Section section : ps.getSections()) {
+ sections.add(new MutableSection(section));
+ }
+ }
+
+ /**
+ * @return The property set stream's low-level "byte order" field. It is always {@code 0xFFFE}.
+ */
+ public int getByteOrder() {
+ return byteOrder;
+ }
+ /**
+ * Returns the property set stream's low-level "byte order" field.
+ *
+ * @param byteOrder The property set stream's low-level "byte order" field.
+ */
+ public void setByteOrder(int byteOrder) {
+ this.byteOrder = byteOrder;
+ }
/**
- * <p>Checks whether an {@link InputStream} is in the Horrible
- * Property Set Format.</p>
+ * @return The property set stream's low-level "format" field. It is always {@code 0x0000}.
+ */
+ public int getFormat() {
+ return format;
+ }
+
+ /**
+ * Sets the property set stream's low-level "format" field.
+ *
+ * @param format The property set stream's low-level "format" field.
+ */
+ public void setFormat(int format) {
+ this.format = format;
+ }
+
+ /**
+ * @return The property set stream's low-level "OS version" field.
+ */
+ public int getOSVersion() {
+ return osVersion;
+ }
+
+ /**
+ * Sets the property set stream's low-level "OS version" field.
+ *
+ * @param osVersion The property set stream's low-level "OS version" field.
+ */
+ public void setOSVersion(int osVersion) {
+ this.osVersion = osVersion;
+ }
+
+
+ /**
+ * @return The property set stream's low-level "class ID" field.
+ */
+ public ClassID getClassID() {
+ return classID;
+ }
+
+ /**
+ * Sets the property set stream's low-level "class ID" field.
+ *
+ * @param classID The property set stream's low-level "class ID" field.
+ */
+ public void setClassID(ClassID classID) {
+ this.classID = classID;
+ }
+
+ /**
+ * @return The number of {@link Section}s in the property set.
+ */
+ public int getSectionCount() {
+ return sections.size();
+ }
+
+ /**
+ * @return The unmodifiable list of {@link Section}s in the property set.
+ */
+ public List<Section> getSections() {
+ return Collections.unmodifiableList(sections);
+ }
+
+
+
+ /**
+ * Adds a section to this property set.
+ *
+ * @param section The {@link Section} to add. It will be appended
+ * after any sections that are already present in the property set
+ * and thus become the last section.
+ */
+ public void addSection(final Section section) {
+ sections.add(section);
+ }
+
+ /**
+ * Removes all sections from this property set.
+ */
+ public void clearSections() {
+ sections.clear();
+ }
+
+ /**
+ * The id to name mapping of the properties in this set.
+ *
+ * @return the id to name mapping of the properties in this set or {@code null} if not applicable
+ */
+ public PropertyIDMap getPropertySetIDMap() {
+ return null;
+ }
+
+
+ /**
+ * Checks whether an {@link InputStream} is in the Horrible
+ * Property Set Format.
*
* @param stream The {@link InputStream} to check. In order to
* perform the check, the method reads the first bytes from the
* stream. After reading, the stream is reset to the position it
* had before reading. The {@link InputStream} must support the
* {@link InputStream#mark} method.
- * @return <code>true</code> if the stream is a property set
- * stream, else <code>false</code>.
+ * @return {@code true} if the stream is a property set
+ * stream, else {@code false}.
* @throws MarkUnsupportedException if the {@link InputStream}
* does not support the {@link InputStream#mark} method.
* @exception IOException if an I/O error occurs
*/
public static boolean isPropertySetStream(final InputStream stream)
- throws MarkUnsupportedException, IOException
- {
+ throws MarkUnsupportedException, IOException {
/*
* Read at most this many bytes.
*/
final int BUFFER_SIZE = 50;
/*
- * Mark the current position in the stream so that we can
- * reset to this position if the stream does not contain a
- * property set.
- */
- if (!stream.markSupported())
- throw new MarkUnsupportedException(stream.getClass().getName());
- stream.mark(BUFFER_SIZE);
-
- /*
* Read a couple of bytes from the stream.
*/
- final byte[] buffer = new byte[BUFFER_SIZE];
- final int bytes =
- stream.read(buffer, 0,
- Math.min(buffer.length, stream.available()));
- final boolean isPropertySetStream =
- isPropertySetStream(buffer, 0, bytes);
- stream.reset();
- return isPropertySetStream;
+ try {
+ final byte[] buffer = IOUtils.peekFirstNBytes(stream, BUFFER_SIZE);
+ final boolean isPropertySetStream = isPropertySetStream(buffer, 0, buffer.length);
+ return isPropertySetStream;
+ } catch (EmptyFileException e) {
+ return false;
+ }
}
/**
- * <p>Checks whether a byte array is in the Horrible Property Set
- * Format.</p>
+ * Checks whether a byte array is in the Horrible Property Set Format.
*
* @param src The byte array to check.
* @param offset The offset in the byte array.
* @param length The significant number of bytes in the byte
* array. Only this number of bytes will be checked.
- * @return <code>true</code> if the byte array is a property set
- * stream, <code>false</code> if not.
+ * @return {@code true} if the byte array is a property set
+ * stream, {@code false} if not.
*/
- public static boolean isPropertySetStream(final byte[] src,
- final int offset,
- final int length)
- {
+ public static boolean isPropertySetStream(final byte[] src, final int offset, final int length) {
/* FIXME (3): Ensure that at most "length" bytes are read. */
/*
@@ -365,45 +420,39 @@ public class PropertySet
int o = offset;
final int byteOrder = LittleEndian.getUShort(src, o);
o += LittleEndian.SHORT_SIZE;
- byte[] temp = new byte[LittleEndian.SHORT_SIZE];
- LittleEndian.putShort(temp, 0, (short) byteOrder);
- if (!Util.equal(temp, BYTE_ORDER_ASSERTION))
+ if (byteOrder != BYTE_ORDER_ASSERTION) {
return false;
+ }
final int format = LittleEndian.getUShort(src, o);
o += LittleEndian.SHORT_SIZE;
- temp = new byte[LittleEndian.SHORT_SIZE];
- LittleEndian.putShort(temp, 0, (short) format);
- if (!Util.equal(temp, FORMAT_ASSERTION))
+ if (format != FORMAT_ASSERTION) {
return false;
+ }
// final long osVersion = LittleEndian.getUInt(src, offset);
o += LittleEndian.INT_SIZE;
// final ClassID classID = new ClassID(src, offset);
o += ClassID.LENGTH;
final long sectionCount = LittleEndian.getUInt(src, o);
- o += LittleEndian.INT_SIZE;
- if (sectionCount < 0)
- return false;
- return true;
+ return (sectionCount >= 0);
}
/**
- * <p>Initializes this {@link PropertySet} instance from a byte
+ * Initializes this {@link PropertySet} instance from a byte
* array. The method assumes that it has been checked already that
* the byte array indeed represents a property set stream. It does
- * no more checks on its own.</p>
+ * no more checks on its own.
*
* @param src Byte array containing the property set stream
* @param offset The property set stream starts at this offset
- * from the beginning of <var>src</var>
+ * from the beginning of {@code src}
* @param length Length of the property set stream.
* @throws UnsupportedEncodingException if HPSF does not (yet) support the
* property set's character encoding.
*/
private void init(final byte[] src, final int offset, final int length)
- throws UnsupportedEncodingException
- {
+ throws UnsupportedEncodingException {
/* FIXME (3): Ensure that at most "length" bytes are read. */
/*
@@ -420,9 +469,9 @@ public class PropertySet
o += ClassID.LENGTH;
final int sectionCount = LittleEndian.getInt(src, o);
o += LittleEndian.INT_SIZE;
- if (sectionCount < 0)
- throw new HPSFRuntimeException("Section count " + sectionCount +
- " is negative.");
+ if (sectionCount < 0) {
+ throw new HPSFRuntimeException("Section count " + sectionCount + " is negative.");
+ }
/*
* Read the sections, which are following the header. They
@@ -430,209 +479,334 @@ public class PropertySet
* consists of a format ID telling what the section contains
* and an offset telling how many bytes from the start of the
* stream the section begins.
- */
- /*
+ *
* Most property sets have only one section. The Document
* Summary Information stream has 2. Everything else is a rare
* exception and is no longer fostered by Microsoft.
*/
- sections = new ArrayList<Section>( sectionCount );
/*
* Loop over the section descriptor array. Each descriptor
* consists of a ClassID and a DWord, and we have to increment
* "offset" accordingly.
*/
- for (int i = 0; i < sectionCount; i++)
- {
- final Section s = new Section(src, o);
+ for (int i = 0; i < sectionCount; i++) {
+ final Section s = new MutableSection(src, o);
o += ClassID.LENGTH + LittleEndian.INT_SIZE;
sections.add(s);
}
}
+ /**
+ * Writes the property set to an output stream.
+ *
+ * @param out the output stream to write the section to
+ * @exception IOException if an error when writing to the output stream
+ * occurs
+ * @exception WritingNotSupportedException if HPSF does not yet support
+ * writing a property's variant type.
+ */
+ public void write(final OutputStream out)
+ throws WritingNotSupportedException, IOException {
+ /* Write the number of sections in this property set stream. */
+ 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);
+ int offset = OFFSET_HEADER;
+
+ /* Write the section list, i.e. the references to the sections. Each
+ * entry in the section list consist of the section's class ID and the
+ * section's offset relative to the beginning of the stream. */
+ offset += nrSections * (ClassID.LENGTH + LittleEndianConsts.INT_SIZE);
+ final int sectionsBegin = offset;
+ for (final Section section : getSections()) {
+ final ClassID formatID = section.getFormatID();
+ if (formatID == null) {
+ throw new NoFormatIDException();
+ }
+ TypeWriter.writeToStream(out, section.getFormatID());
+ TypeWriter.writeUIntToStream(out, offset);
+ try {
+ offset += section.getSize();
+ } catch (HPSFRuntimeException ex) {
+ final Throwable cause = ex.getReason();
+ if (cause instanceof UnsupportedEncodingException) {
+ throw new IllegalPropertySetDataException(cause);
+ }
+ throw ex;
+ }
+ }
+ /* Write the sections themselves. */
+ offset = sectionsBegin;
+ for (final Section section : getSections()) {
+ offset += section.write(out);
+ }
+
+ /* Indicate that we're done */
+ out.close();
+ }
/**
- * <p>Checks whether this {@link PropertySet} represents a Summary
- * Information.</p>
+ * Writes a property set to a document in a POI filesystem directory.
+ *
+ * @param dir The directory in the POI filesystem to write the document to.
+ * @param name The document's name. If there is already a document with the
+ * same name in the directory the latter will be overwritten.
*
- * @return <code>true</code> if this {@link PropertySet}
- * represents a Summary Information, else <code>false</code>.
+ * @throws WritingNotSupportedException if the filesystem doesn't support writing
+ * @throws IOException if the old entry can't be deleted or the new entry be written
*/
- public boolean isSummaryInformation()
- {
- if (sections.size() <= 0)
- return false;
- return Util.equal(sections.get(0).getFormatID().getBytes(),
- SectionIDMap.SUMMARY_INFORMATION_ID);
+ public void write(final DirectoryEntry dir, final String name)
+ throws WritingNotSupportedException, IOException {
+ /* If there is already an entry with the same name, remove it. */
+ if (dir.hasEntry(name)) {
+ final Entry e = dir.getEntry(name);
+ e.delete();
+ }
+
+ /* Create the new entry. */
+ dir.createDocument(name, toInputStream());
}
+ /**
+ * Returns the contents of this property set stream as an input stream.
+ * The latter can be used for example to write the property set into a POIFS
+ * document. The input stream represents a snapshot of the property set.
+ * If the latter is modified while the input stream is still being
+ * read, the modifications will not be reflected in the input stream but in
+ * the {@link MutablePropertySet} only.
+ *
+ * @return the contents of this property set stream
+ *
+ * @throws WritingNotSupportedException if HPSF does not yet support writing
+ * of a property's variant type.
+ * @throws IOException if an I/O exception occurs.
+ */
+ public InputStream toInputStream() throws IOException, WritingNotSupportedException {
+ final ByteArrayOutputStream psStream = new ByteArrayOutputStream();
+ try {
+ write(psStream);
+ } finally {
+ psStream.close();
+ }
+ final byte[] streamData = psStream.toByteArray();
+ return new ByteArrayInputStream(streamData);
+ }
+ /**
+ * Fetches the property with the given ID, then does its
+ * best to return it as a String
+ *
+ * @param propertyId the property id
+ *
+ * @return The property as a String, or null if unavailable
+ */
+ protected String getPropertyStringValue(final int propertyId) {
+ Object propertyValue = getProperty(propertyId);
+ return getPropertyStringValue(propertyValue);
+ }
+
+ /**
+ * Return the string representation of a property value
+ *
+ * @param propertyValue the property value
+ *
+ * @return The property value as a String, or null if unavailable
+ */
+ public static String getPropertyStringValue(final Object propertyValue) {
+ // Normal cases
+ if (propertyValue == null) {
+ return null;
+ }
+ if (propertyValue instanceof String) {
+ return (String)propertyValue;
+ }
+
+ // Do our best with some edge cases
+ if (propertyValue instanceof byte[]) {
+ byte[] b = (byte[])propertyValue;
+ switch (b.length) {
+ case 0:
+ return "";
+ case 1:
+ return Byte.toString(b[0]);
+ case 2:
+ return Integer.toString( LittleEndian.getUShort(b) );
+ case 4:
+ return Long.toString( LittleEndian.getUInt(b) );
+ default:
+ // Maybe it's a string? who knows!
+ return new String(b, Charset.forName("ASCII"));
+ }
+ }
+ return propertyValue.toString();
+ }
/**
- * <p>Checks whether this {@link PropertySet} is a Document
- * Summary Information.</p>
+ * Checks whether this {@link PropertySet} represents a Summary Information.
*
- * @return <code>true</code> if this {@link PropertySet}
- * represents a Document Summary Information, else <code>false</code>.
+ * @return {@code true} if this {@link PropertySet}
+ * represents a Summary Information, else {@code false}.
*/
- public boolean isDocumentSummaryInformation()
- {
- if (sections.size() <= 0)
- return false;
- return Util.equal(sections.get(0).getFormatID().getBytes(),
- SectionIDMap.DOCUMENT_SUMMARY_INFORMATION_ID[0]);
+ public boolean isSummaryInformation() {
+ return matchesSummary(SectionIDMap.SUMMARY_INFORMATION_ID);
}
+ /**
+ * Checks whether this {@link PropertySet} is a Document Summary Information.
+ *
+ * @return {@code true} if this {@link 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);
+ }
+
+
/**
- * <p>Convenience method returning the {@link Property} array
- * contained in this property set. It is a shortcut for getting
- * the {@link PropertySet}'s {@link Section}s list and then
- * getting the {@link Property} array from the first {@link
- * Section}.</p>
+ * Convenience method returning the {@link Property} array contained in this
+ * property set. It is a shortcut for getting he {@link PropertySet}'s
+ * {@link Section}s list and then getting the {@link Property} array from the
+ * first {@link Section}.
*
* @return The properties of the only {@link Section} of this
* {@link PropertySet}.
* @throws NoSingleSectionException if the {@link PropertySet} has
* more or less than one {@link Section}.
*/
- public Property[] getProperties()
- throws NoSingleSectionException
- {
+ public Property[] getProperties() throws NoSingleSectionException {
return getFirstSection().getProperties();
}
/**
- * <p>Convenience method returning the value of the property with
- * the specified ID. If the property is not available,
- * <code>null</code> is returned and a subsequent call to {@link
- * #wasNull} will return <code>true</code> .</p>
+ * Convenience method returning the value of the property with the specified ID.
+ * If the property is not available, {@code null} is returned and a subsequent
+ * call to {@link #wasNull} will return {@code true}.
*
* @param id The property ID
* @return The property value
* @throws NoSingleSectionException if the {@link PropertySet} has
* more or less than one {@link Section}.
*/
- protected Object getProperty(final int id) throws NoSingleSectionException
- {
+ protected Object getProperty(final int id) throws NoSingleSectionException {
return getFirstSection().getProperty(id);
}
/**
- * <p>Convenience method returning the value of a boolean property
- * with the specified ID. If the property is not available,
- * <code>false</code> is returned. A subsequent call to {@link
- * #wasNull} will return <code>true</code> to let the caller
- * distinguish that case from a real property value of
- * <code>false</code>.</p>
+ * Convenience method returning the value of a boolean property with the
+ * specified ID. If the property is not available, {@code false} is returned.
+ * A subsequent call to {@link #wasNull} will return {@code true} to let the
+ * caller distinguish that case from a real property value of {@code false}.
*
* @param id The property ID
* @return The property value
* @throws NoSingleSectionException if the {@link PropertySet} has
* more or less than one {@link Section}.
*/
- protected boolean getPropertyBooleanValue(final int id)
- throws NoSingleSectionException
- {
+ protected boolean getPropertyBooleanValue(final int id) throws NoSingleSectionException {
return getFirstSection().getPropertyBooleanValue(id);
}
/**
- * <p>Convenience method returning the value of the numeric
+ * Convenience method returning the value of the numeric
* property with the specified ID. If the property is not
* available, 0 is returned. A subsequent call to {@link #wasNull}
- * will return <code>true</code> to let the caller distinguish
- * that case from a real property value of 0.</p>
+ * will return {@code true} to let the caller distinguish
+ * that case from a real property value of 0.
*
* @param id The property ID
* @return The propertyIntValue value
* @throws NoSingleSectionException if the {@link PropertySet} has
* more or less than one {@link Section}.
*/
- protected int getPropertyIntValue(final int id)
- throws NoSingleSectionException
- {
+ protected int getPropertyIntValue(final int id) throws NoSingleSectionException {
return getFirstSection().getPropertyIntValue(id);
}
/**
- * <p>Checks whether the property which the last call to {@link
+ * Checks whether the property which the last call to {@link
* #getPropertyIntValue} or {@link #getProperty} tried to access
* was available or not. This information might be important for
* callers of {@link #getPropertyIntValue} since the latter
* returns 0 if the property does not exist. Using {@link
* #wasNull}, the caller can distiguish this case from a
- * property's real value of 0.</p>
+ * property's real value of 0.
*
- * @return <code>true</code> if the last call to {@link
+ * @return {@code true} if the last call to {@link
* #getPropertyIntValue} or {@link #getProperty} tried to access a
- * property that was not available, else <code>false</code>.
+ * property that was not available, else {@code false}.
* @throws NoSingleSectionException if the {@link PropertySet} has
* more than one {@link Section}.
*/
- public boolean wasNull() throws NoSingleSectionException
- {
+ public boolean wasNull() throws NoSingleSectionException {
return getFirstSection().wasNull();
}
/**
- * <p>Gets the {@link PropertySet}'s first section.</p>
+ * Gets the {@link PropertySet}'s first section.
*
* @return The {@link PropertySet}'s first section.
*/
- public Section getFirstSection()
- {
- if (getSectionCount() < 1)
+ public Section getFirstSection() {
+ if (sections.isEmpty()) {
throw new MissingSectionException("Property set does not contain any sections.");
+ }
return sections.get(0);
}
/**
- * <p>If the {@link PropertySet} has only a single section this
- * method returns it.</p>
+ * If the {@link PropertySet} has only a single section this method returns it.
*
* @return The singleSection value
*/
- public Section getSingleSection()
- {
+ public Section getSingleSection() {
final int sectionCount = getSectionCount();
- if (sectionCount != 1)
- throw new NoSingleSectionException
- ("Property set contains " + sectionCount + " sections.");
+ if (sectionCount != 1) {
+ throw new NoSingleSectionException("Property set contains " + sectionCount + " sections.");
+ }
return sections.get(0);
}
/**
- * <p>Returns <code>true</code> if the <code>PropertySet</code> is equal
- * to the specified parameter, else <code>false</code>.</p>
+ * Returns {@code true} if the {@code PropertySet} is equal
+ * to the specified parameter, else {@code false}.
*
- * @param o the object to compare this <code>PropertySet</code> with
+ * @param o the object to compare this {@code PropertySet} with
*
- * @return <code>true</code> if the objects are equal, <code>false</code>
+ * @return {@code true} if the objects are equal, {@code false}
* if not
*/
@Override
- public boolean equals(final Object o)
- {
- if (o == null || !(o instanceof PropertySet))
+ public boolean equals(final Object o) {
+ if (o == null || !(o instanceof PropertySet)) {
return false;
+ }
final PropertySet ps = (PropertySet) o;
int byteOrder1 = ps.getByteOrder();
int byteOrder2 = getByteOrder();
@@ -648,11 +822,12 @@ public class PropertySet
!classID1.equals(classID2) ||
format1 != format2 ||
osVersion1 != osVersion2 ||
- sectionCount1 != sectionCount2)
+ sectionCount1 != sectionCount2) {
return false;
+ }
/* Compare the sections: */
- return Util.equals(getSections(), ps.getSections());
+ return getSections().containsAll(ps.getSections());
}
@@ -660,8 +835,7 @@ public class PropertySet
/**
* @see Object#hashCode()
*/
- public int hashCode()
- {
+ public int hashCode() {
throw new UnsupportedOperationException("FIXME: Not yet implemented.");
}
@@ -670,8 +844,7 @@ public class PropertySet
/**
* @see Object#toString()
*/
- public String toString()
- {
+ public String toString() {
final StringBuilder b = new StringBuilder();
final int sectionCount = getSectionCount();
b.append(getClass().getName());
@@ -687,10 +860,32 @@ public class PropertySet
b.append(", sectionCount: ");
b.append(sectionCount);
b.append(", sections: [\n");
- for (Section section: getSections())
+ for (Section section: getSections()) {
b.append(section);
+ }
b.append(']');
b.append(']');
return b.toString();
}
+
+
+ protected void remove1stProperty(long id) {
+ getFirstSection().removeProperty(id);
+ }
+
+ protected void set1stProperty(long id, String value) {
+ getFirstSection().setProperty((int)id, value);
+ }
+
+ protected void set1stProperty(long id, int value) {
+ getFirstSection().setProperty((int)id, value);
+ }
+
+ protected void set1stProperty(long id, boolean value) {
+ getFirstSection().setProperty((int)id, value);
+ }
+
+ protected void set1stProperty(long id, byte[] value) {
+ getFirstSection().setProperty((int)id, value);
+ }
}
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@poi.apache.org
For additional commands, e-mail: commits-help@poi.apache.org