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 [3/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/PropertySetFactory.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/hpsf/PropertySetFactory.java?rev=1771640&r1=1771639&r2=1771640&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/hpsf/PropertySetFactory.java (original)
+++ poi/trunk/src/java/org/apache/poi/hpsf/PropertySetFactory.java Sun Nov 27 20:19:18 2016
@@ -22,24 +22,22 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.io.UnsupportedEncodingException;
 
-import org.apache.poi.hpsf.wellknown.SectionIDMap;
 import org.apache.poi.poifs.filesystem.DirectoryEntry;
 import org.apache.poi.poifs.filesystem.DocumentEntry;
 import org.apache.poi.poifs.filesystem.DocumentInputStream;
 
 /**
- * <p>Factory class to create instances of {@link SummaryInformation},
- * {@link DocumentSummaryInformation} and {@link PropertySet}.</p>
+ * Factory class to create instances of {@link SummaryInformation},
+ * {@link DocumentSummaryInformation} and {@link PropertySet}.
  */
-public class PropertySetFactory
-{
+public class PropertySetFactory {
     /**
-     * <p>Creates the most specific {@link PropertySet} from an entry
+     * Creates the most specific {@link PropertySet} from an entry
      *  in the specified POIFS Directory. This is preferrably a {@link
      * DocumentSummaryInformation} or a {@link SummaryInformation}. If
      * the specified entry does not contain a property set stream, an 
      * exception is thrown. If no entry is found with the given name,
-     * an exception is thrown.</p>
+     * an exception is thrown.
      *
      * @param dir The directory to find the PropertySet in
      * @param name The name of the entry containing the PropertySet
@@ -52,55 +50,53 @@ public class PropertySetFactory
      * supported.
      */
     public static PropertySet create(final DirectoryEntry dir, final String name)
-        throws FileNotFoundException, NoPropertySetStreamException,
-               IOException, UnsupportedEncodingException
-    {
+    throws FileNotFoundException, NoPropertySetStreamException, IOException, UnsupportedEncodingException {
         InputStream inp = null;
         try {
             DocumentEntry entry = (DocumentEntry)dir.getEntry(name);
             inp = new DocumentInputStream(entry);
             try {
                 return create(inp);
-            } catch (MarkUnsupportedException e) { return null; }
+            } catch (MarkUnsupportedException e) {
+                return null;
+            }
         } finally {
-            if (inp != null) inp.close();
+            if (inp != null) {
+                inp.close();
+            }
         }
     }
 
     /**
-     * <p>Creates the most specific {@link PropertySet} from an {@link
+     * Creates the most specific {@link PropertySet} from an {@link
      * InputStream}. This is preferrably a {@link
      * DocumentSummaryInformation} or a {@link SummaryInformation}. If
      * the specified {@link InputStream} does not contain a property
      * set stream, an exception is thrown and the {@link InputStream}
-     * is repositioned at its beginning.</p>
+     * is repositioned at its beginning.
      *
      * @param stream Contains the property set stream's data.
      * @return The created {@link PropertySet}.
      * @throws NoPropertySetStreamException if the stream does not
      * contain a property set.
      * @throws MarkUnsupportedException if the stream does not support
-     * the <code>mark</code> operation.
+     * the {@code mark} operation.
      * @throws IOException if some I/O problem occurs.
      * @exception UnsupportedEncodingException if the specified codepage is not
      * supported.
      */
     public static PropertySet create(final InputStream stream)
-        throws NoPropertySetStreamException, MarkUnsupportedException,
-               UnsupportedEncodingException, IOException
-    {
+    throws NoPropertySetStreamException, MarkUnsupportedException, UnsupportedEncodingException, IOException {
         final PropertySet ps = new PropertySet(stream);
-        try
-        {
-            if (ps.isSummaryInformation())
+        try {
+            if (ps.isSummaryInformation()) {
                 return new SummaryInformation(ps);
-            else if (ps.isDocumentSummaryInformation())
+            } else if (ps.isDocumentSummaryInformation()) {
                 return new DocumentSummaryInformation(ps);
-            else
+            } else {
                 return ps;
-        }
-        catch (UnexpectedPropertySetTypeException ex)
-        {
+            }
+        } catch (UnexpectedPropertySetTypeException ex) {
             /* This exception will never be throws because we already checked
              * explicitly for this case above. */
             throw new IllegalStateException(ex);
@@ -108,44 +104,20 @@ public class PropertySetFactory
     }
 
     /**
-     * <p>Creates a new summary information.</p>
+     * Creates a new summary information.
      *
      * @return the new summary information.
      */
-    public static SummaryInformation newSummaryInformation()
-    {
-        final MutablePropertySet ps = new MutablePropertySet();
-        final MutableSection s = (MutableSection) ps.getFirstSection();
-        s.setFormatID(SectionIDMap.SUMMARY_INFORMATION_ID);
-        try
-        {
-            return new SummaryInformation(ps);
-        }
-        catch (UnexpectedPropertySetTypeException ex)
-        {
-            /* This should never happen. */
-            throw new HPSFRuntimeException(ex);
-        }
+    public static SummaryInformation newSummaryInformation() {
+        return new SummaryInformation();
     }
 
     /**
-     * <p>Creates a new document summary information.</p>
+     * Creates a new document summary information.
      *
      * @return the new document summary information.
      */
-    public static DocumentSummaryInformation newDocumentSummaryInformation()
-    {
-        final MutablePropertySet ps = new MutablePropertySet();
-        final MutableSection s = (MutableSection) ps.getFirstSection();
-        s.setFormatID(SectionIDMap.DOCUMENT_SUMMARY_INFORMATION_ID[0]);
-        try
-        {
-            return new DocumentSummaryInformation(ps);
-        }
-        catch (UnexpectedPropertySetTypeException ex)
-        {
-            /* This should never happen. */
-            throw new HPSFRuntimeException(ex);
-        }
+    public static DocumentSummaryInformation newDocumentSummaryInformation() {
+        return new DocumentSummaryInformation();
     }
 }
\ No newline at end of file

Modified: poi/trunk/src/java/org/apache/poi/hpsf/Section.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/hpsf/Section.java?rev=1771640&r1=1771639&r2=1771640&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/hpsf/Section.java (original)
+++ poi/trunk/src/java/org/apache/poi/hpsf/Section.java Sun Nov 27 20:19:18 2016
@@ -17,127 +17,96 @@
 
 package org.apache.poi.hpsf;
 
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
 import java.io.UnsupportedEncodingException;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.List;
+import java.util.Date;
 import java.util.Map;
+import java.util.TreeMap;
 
+import org.apache.commons.collections4.bidimap.TreeBidiMap;
 import org.apache.poi.hpsf.wellknown.PropertyIDMap;
 import org.apache.poi.hpsf.wellknown.SectionIDMap;
+import org.apache.poi.util.CodePageUtil;
 import org.apache.poi.util.LittleEndian;
 
 /**
- * <p>Represents a section in a {@link PropertySet}.</p>
+ * Represents a section in a {@link PropertySet}.
  */
-public class Section
-{
+public class Section {
 
     /**
-     * <p>Maps property IDs to section-private PID strings. These
-     * strings can be found in the property with ID 0.</p>
+     * Maps property IDs to section-private PID strings. These
+     * strings can be found in the property with ID 0.
      */
-    protected Map<Long,String> dictionary;
+    private Map<Long,String> dictionary;
 
     /**
-     * <p>The section's format ID, {@link #getFormatID}.</p>
+     * The section's format ID, {@link #getFormatID}.
      */
-    protected ClassID formatID;
-
-
+    private ClassID formatID;
     /**
-     * <p>Returns the format ID. The format ID is the "type" of the
-     * section. For example, if the format ID of the first {@link
-     * Section} contains the bytes specified by
-     * <code>org.apache.poi.hpsf.wellknown.SectionIDMap.SUMMARY_INFORMATION_ID</code>
-     * the section (and thus the property set) is a SummaryInformation.</p>
-     *
-     * @return The format ID
+     * If the "dirty" flag is true, the section's size must be
+     * (re-)calculated before the section is written.
      */
-    public ClassID getFormatID()
-    {
-        return formatID;
-    }
-
-
+    private boolean dirty = true;
 
     /**
-     * @see #getOffset
+     * 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.
      */
-    protected long offset;
-
+    private byte[] sectionBytes;
 
     /**
-     * <p>Returns the offset of the section in the stream.</p>
-     *
-     * @return The offset of the section in the stream.
+     * The offset of the section in the stream.
      */
-    public long getOffset()
-    {
-        return offset;
-    }
-
-
+    private long offset = -1;
 
     /**
-     * @see #getSize
+     * The section's size in bytes.
      */
-    protected int size;
-
+    private int size;
 
     /**
-     * <p>Returns the section's size in bytes.</p>
-     *
-     * @return The section's size in bytes.
+     * This section's properties.
      */
-    public int getSize()
-    {
-        return size;
-    }
-
-
+    private final Map<Long,Property> properties = new TreeMap<Long,Property>();
 
     /**
-     * <p>Returns the number of properties in this section.</p>
-     *
-     * @return The number of properties in this section.
+     * This member is {@code true} if the last call to {@link
+     * #getPropertyIntValue} or {@link #getProperty} tried to access a
+     * property that was not available, else {@code false}.
      */
-    public int getPropertyCount()
-    {
-        return properties.length;
-    }
-
-
+    private boolean wasNull;
 
     /**
-     * @see #getProperties
+     * Creates an empty {@link Section}.
      */
-    protected Property[] properties;
-
+    public Section() {
+    }
 
     /**
-     * <p>Returns this section's properties.</p>
+     * Constructs a {@code Section} by doing a deep copy of an
+     * existing {@code Section}. All nested {@code Property}
+     * instances, will be their mutable counterparts in the new
+     * {@code MutableSection}.
      *
-     * @return This section's properties.
+     * @param s The section set to copy
      */
-    public Property[] getProperties()
-    {
-        return properties;
+    public Section(final Section s) {
+        setFormatID(s.getFormatID());
+        for (Property p : s.properties.values()) {
+            properties.put(p.getID(), new MutableProperty(p));
+        }
+        setDictionary(s.getDictionary());
     }
 
 
 
     /**
-     * <p>Creates an empty and uninitialized {@link Section}.
-     */
-    protected Section()
-    { }
-
-
-
-    /**
-     * <p>Creates a {@link Section} instance from a byte array.</p>
+     * Creates a {@link Section} instance from a byte array.
      *
      * @param src Contains the complete property set stream.
      * @param offset The position in the stream that points to the
@@ -147,9 +116,7 @@ public class Section
      * supported.
      */
     @SuppressWarnings("unchecked")
-    public Section(final byte[] src, final int offset)
-    throws UnsupportedEncodingException
-    {
+    public Section(final byte[] src, final int offset) throws UnsupportedEncodingException {
         int o1 = offset;
 
         /*
@@ -201,82 +168,59 @@ public class Section
          *    one looks for property ID 1 and extracts the codepage number. The
          *    seconds pass reads the other properties.
          */
-        properties = new Property[propertyCount];
-
         /* Pass 1: Read the property list. */
         int pass1Offset = o1;
-        final List<PropertyListEntry> propertyList = new ArrayList<PropertyListEntry>(propertyCount);
-        PropertyListEntry ple;
-        for (int i = 0; i < properties.length; i++)
-        {
-            ple = new PropertyListEntry();
-
+        long cpOffset = -1;
+        final TreeBidiMap<Long,Long> offset2Id = new TreeBidiMap<Long,Long>();
+        for (int i = 0; i < propertyCount; i++) {
             /* Read the property ID. */
-            ple.id = (int) LittleEndian.getUInt(src, pass1Offset);
+            long id = LittleEndian.getUInt(src, pass1Offset);
             pass1Offset += LittleEndian.INT_SIZE;
 
             /* Offset from the section's start. */
-            ple.offset = (int) LittleEndian.getUInt(src, pass1Offset);
+            long off = LittleEndian.getUInt(src, pass1Offset);
             pass1Offset += LittleEndian.INT_SIZE;
 
-            /* Add the entry to the property list. */
-            propertyList.add(ple);
-        }
-
-        /* Sort the property list by ascending offsets: */
-        Collections.sort(propertyList);
-
-        /* Calculate the properties' lengths. */
-        for (int i = 0; i < propertyCount - 1; i++)
-        {
-            PropertyListEntry ple1 = propertyList.get(i);
-            PropertyListEntry ple2 = propertyList.get(i + 1);
-            ple1.length = ple2.offset - ple1.offset;
-        }
-        if (propertyCount > 0)
-        {
-            ple = propertyList.get(propertyCount - 1);
-            ple.length = size - ple.offset;
+            offset2Id.put(off, id);
+            
+            if (id == PropertyIDMap.PID_CODEPAGE) {
+                cpOffset = off;
+            }
         }
 
         /* Look for the codepage. */
         int codepage = -1;
-        for (final Iterator<PropertyListEntry> i = propertyList.iterator();
-             codepage == -1 && i.hasNext();)
-        {
-            ple = i.next();
-
-            /* Read the codepage if the property ID is 1. */
-            if (ple.id == PropertyIDMap.PID_CODEPAGE)
-            {
-                /* Read the property's value type. It must be
-                 * VT_I2. */
-                int o = (int) (this.offset + ple.offset);
-                final long type = LittleEndian.getUInt(src, o);
-                o += LittleEndian.INT_SIZE;
-
-                if (type != Variant.VT_I2)
-                    throw new HPSFRuntimeException
-                        ("Value type of property ID 1 is not VT_I2 but " +
-                         type + ".");
-
-                /* Read the codepage number. */
-                codepage = LittleEndian.getUShort(src, o);
+        if (cpOffset != -1) {
+            /* Read the property's value type. It must be VT_I2. */
+            long o = this.offset + cpOffset;
+            final long type = LittleEndian.getUInt(src, (int)o);
+            o += LittleEndian.INT_SIZE;
+
+            if (type != Variant.VT_I2) {
+                throw new HPSFRuntimeException
+                    ("Value type of property ID 1 is not VT_I2 but " +
+                     type + ".");
             }
+
+            /* Read the codepage number. */
+            codepage = LittleEndian.getUShort(src, (int)o);
         }
+        
 
         /* Pass 2: Read all properties - including the codepage property,
          * if available. */
-        int i1 = 0;
-        for (final Iterator<PropertyListEntry> i = propertyList.iterator(); i.hasNext();)
-        {
-            ple = i.next();
-            Property p = new Property(ple.id, src,
-                    this.offset + ple.offset,
-                    ple.length, codepage);
-            if (p.getID() == PropertyIDMap.PID_CODEPAGE)
-                p = new Property(p.getID(), p.getType(), Integer.valueOf(codepage));
-            properties[i1++] = p;
+        for (Map.Entry<Long,Long> me : offset2Id.entrySet()) {
+            long off = me.getKey();
+            long id = me.getValue();
+            Property p;
+            if (id == PropertyIDMap.PID_CODEPAGE) {
+                p = new Property(PropertyIDMap.PID_CODEPAGE, Variant.VT_I2, codepage);
+            } else {
+                int pLen = propLen(offset2Id, off, size);
+                long o = this.offset + off;
+                p = new Property(id, src, o, pLen, codepage);
+            }
+            properties.put(id, p);
         }
 
         /*
@@ -285,202 +229,367 @@ public class Section
         dictionary = (Map<Long,String>) getProperty(0);
     }
 
+    /**
+     * Retrieves the length of the given property (by key)
+     *
+     * @param offset2Id the offset to id map
+     * @param entryOffset the current entry key
+     * @param maxSize the maximum offset/size of the section stream
+     * @return the length of the current property
+     */
+    private static int propLen(
+        TreeBidiMap<Long,Long> offset2Id,
+        Long entryOffset,
+        long maxSize) {
+        Long nextKey = offset2Id.nextKey(entryOffset);
+        long begin = entryOffset;
+        long end = (nextKey != null) ? nextKey : maxSize;
+        return (int)(end - begin);
+    }
 
 
     /**
-     * <p>Represents an entry in the property list and holds a property's ID and
-     * its offset from the section's beginning.</p>
+     * Returns the format ID. The format ID is the "type" of the
+     * section. For example, if the format ID of the first {@link
+     * Section} contains the bytes specified by
+     * {@code org.apache.poi.hpsf.wellknown.SectionIDMap.SUMMARY_INFORMATION_ID}
+     * the section (and thus the property set) is a SummaryInformation.
+     *
+     * @return The format ID
      */
-    static class PropertyListEntry implements Comparable<PropertyListEntry>
-    {
-        int id;
-        int offset;
-        int length;
-
-        /**
-         * <p>Compares this {@link PropertyListEntry} with another one by their
-         * offsets. A {@link PropertyListEntry} is "smaller" than another one if
-         * its offset from the section's begin is smaller.</p>
-         *
-         * @see Comparable#compareTo(java.lang.Object)
-         */
-        public int compareTo(final PropertyListEntry o)
-        {
-            final int otherOffset = o.offset;
-            if (offset < otherOffset)
-                return -1;
-            else if (offset == otherOffset)
-                return 0;
-            else
-                return 1;
-        }
+    public ClassID getFormatID() {
+        return formatID;
+    }
 
-        
-        
-        public int hashCode() {
-            final int prime = 31;
-            int result = 1;
-            result = prime * result + id;
-            result = prime * result + length;
-            result = prime * result + offset;
-            return result;
+    /**
+     * Sets the section's format ID.
+     *
+     * @param formatID The section's format ID
+     */
+    public void setFormatID(final ClassID formatID) {
+        this.formatID = formatID;
+    }
+
+    /**
+     * Sets the section's format ID.
+     *
+     * @param formatID The section's format ID as a byte array. It components
+     * are in big-endian format.
+     */
+    public void setFormatID(final byte[] formatID) {
+        ClassID fid = getFormatID();
+        if (fid == null) {
+            fid = new ClassID();
+            setFormatID(fid);
         }
+        fid.setBytes(formatID);
+    }
 
+    /**
+     * Returns the offset of the section in the stream.
+     *
+     * @return The offset of the section in the stream.
+     */
+    public long getOffset() {
+        return offset;
+    }
 
+    /**
+     * Returns the number of properties in this section.
+     *
+     * @return The number of properties in this section.
+     */
+    public int getPropertyCount() {
+        return properties.size();
+    }
 
-        public boolean equals(Object obj) {
-            if (this == obj) {
-                return true;
-            }
-            if (obj == null) {
-                return false;
-            }
-            if (getClass() != obj.getClass()) {
-                return false;
-            }
-            PropertyListEntry other = (PropertyListEntry) obj;
-            if (id != other.id) {
-                return false;
-            }
-            if (length != other.length) {
-                return false;
-            }
-            if (offset != other.offset) {
-                return false;
-            }
-            return true;
+    /**
+     * Returns this section's properties.
+     *
+     * @return This section's properties.
+     */
+    public Property[] getProperties() {
+        return properties.values().toArray(new Property[properties.size()]);
+    }
+
+    /**
+     * Sets this section's properties. Any former values are overwritten.
+     *
+     * @param properties This section's new properties.
+     */
+    public void setProperties(final Property[] properties) {
+        this.properties.clear();
+        for (Property p : properties) {
+            this.properties.put(p.getID(), p);
         }
+        dirty = true;
+    }
 
+    /**
+     * Returns 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's ID
+     *
+     * @return The property's value
+     */
+    public Object getProperty(final long id) {
+        wasNull = !properties.containsKey(id);
+        return (wasNull) ? null : properties.get(id).getValue();
+    }
 
+    /**
+     * Sets the string value of the property with the specified ID.
+     *
+     * @param id The property's ID
+     * @param value The property's value. It will be written as a Unicode
+     * string.
+     */
+    public void setProperty(final int id, final String value) {
+        setProperty(id, Variant.VT_LPWSTR, value);
+    }
 
-        public String toString()
-        {
-            final StringBuffer b = new StringBuffer();
-            b.append(getClass().getName());
-            b.append("[id=");
-            b.append(id);
-            b.append(", offset=");
-            b.append(offset);
-            b.append(", length=");
-            b.append(length);
-            b.append(']');
-            return b.toString();
-        }
+    /**
+     * Sets the int value of the property with the specified ID.
+     *
+     * @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));
     }
 
 
 
     /**
-     * <p>Returns 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>
+     * Sets the long value of the property with the specified ID.
      *
      * @param id The property's ID
+     * @param value The property's value.
      *
-     * @return The property's value
+     * @see #setProperty(int, long, Object)
+     * @see #getProperty
      */
-    public Object getProperty(final long id)
-    {
-        wasNull = false;
-        for (int i = 0; i < properties.length; i++)
-            if (id == properties[i].getID())
-                return properties[i].getValue();
-        wasNull = true;
-        return null;
+    public void setProperty(final int id, final long value) {
+        setProperty(id, Variant.VT_I8, Long.valueOf(value));
     }
 
 
 
     /**
-     * <p>Returns the value of the numeric property with the specified
+     * Sets the boolean value of the property with the specified ID.
+     *
+     * @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));
+    }
+
+
+
+    /**
+     * 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.
+     *
+     * @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) {
+        setProperty(new Property(id, variantType, value));
+    }
+
+
+
+    /**
+     * Sets a property.
+     *
+     * @param p The property to be set.
+     *
+     * @see #setProperty(int, long, Object)
+     * @see #getProperty
+     * @see Variant
+     */
+    public void setProperty(final Property p) {
+        Property old = properties.get(p.getID());
+        if (old == null || !old.equals(p)) {
+            properties.put(p.getID(), p);
+            dirty = true;
+        }
+    }
+
+    /**
+     * Sets a property.
+     *
+     * @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() + ".");
+        }
+    }
+
+    /**
+     * Returns 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>
+     * {@code true} to let the caller distinguish that case from
+     * a real property value of 0.
      *
      * @param id The property's ID
      *
      * @return The property's value
      */
-    protected int getPropertyIntValue(final long id)
-    {
+    protected int getPropertyIntValue(final long id) {
         final Number i;
         final Object o = getProperty(id);
-        if (o == null)
+        if (o == null) {
             return 0;
-        if (!(o instanceof Long || o instanceof Integer))
+        }
+        if (!(o instanceof Long || o instanceof Integer)) {
             throw new HPSFRuntimeException
                 ("This property is not an integer type, but " +
                  o.getClass().getName() + ".");
+        }
         i = (Number) o;
         return i.intValue();
     }
 
-
-
     /**
-     * <p>Returns the value of the boolean property with the specified
-     * ID. If the property is not available, <code>false</code> is
+     * Returns the value of the 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</code> to let the caller distinguish that case from
-     * a real property value of <code>false</code>.</p>
+     * {@code true} to let the caller distinguish that case from
+     * a real property value of {@code false}.
      *
      * @param id The property's ID
      *
      * @return The property's value
      */
-    protected boolean getPropertyBooleanValue(final int id)
-    {
+    protected boolean getPropertyBooleanValue(final int id) {
         final Boolean b = (Boolean) getProperty(id);
         if (b == null) {
             return false;
         }
         return b.booleanValue();
-        }
+    }
 
+    /**
+     * Sets the value of the boolean property with the specified
+     * ID.
+     *
+     * @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));
+    }
 
+    /**
+     * @return the section's size in bytes.
+     */
+    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>This member is <code>true</code> if the last call to {@link
-     * #getPropertyIntValue} or {@link #getProperty} tried to access a
-     * property that was not available, else <code>false</code>.</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.
+     *
+     * @return the section's length in bytes.
+     * @throws WritingNotSupportedException
+     * @throws IOException
      */
-    private boolean wasNull;
+    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>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>
+     * 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}.
      */
-    public boolean wasNull()
-    {
+    public boolean wasNull() {
         return wasNull;
     }
 
 
 
     /**
-     * <p>Returns the PID string associated with a property ID. The ID
+     * Returns the PID string associated with a property ID. The ID
      * is first looked up in the {@link Section}'s private
      * dictionary. If it is not found there, the method calls {@link
-     * SectionIDMap#getPIDString}.</p>
+     * SectionIDMap#getPIDString}.
      *
      * @param pid The property ID
      *
      * @return The property ID's string value
      */
-    public String getPIDString(final long pid)
-    {
+    public String getPIDString(final long pid) {
         String s = null;
         if (dictionary != null) {
             s = dictionary.get(Long.valueOf(pid));
@@ -491,39 +600,65 @@ public class Section
         return s;
     }
 
+    /**
+     * Removes all properties from the section including 0 (dictionary) and
+     * 1 (codepage).
+     */
+    public void clear()
+    {
+        final Property[] properties = getProperties();
+        for (int i = 0; i < properties.length; i++)
+        {
+            final Property p = properties[i];
+            removeProperty(p.getID());
+        }
+    }
+
+    /**
+     * Sets the codepage.
+     *
+     * @param codepage the codepage
+     */
+    public void setCodepage(final int codepage)
+    {
+        setProperty(PropertyIDMap.PID_CODEPAGE, Variant.VT_I2,
+                Integer.valueOf(codepage));
+    }
+
 
 
     /**
-     * <p>Checks whether this section is equal to another object. The result is
-     * <code>false</code> if one of the the following conditions holds:</p>
+     * Checks whether this section is equal to another object. The result is
+     * {@code false} if one of the the following conditions holds:
      *
      * <ul>
      *
-     * <li><p>The other object is not a {@link Section}.</p></li>
+     * <li>The other object is not a {@link Section}.</li>
      *
-     * <li><p>The format IDs of the two sections are not equal.</p></li>
+     * <li>The format IDs of the two sections are not equal.</li>
      *
-     * <li><p>The sections have a different number of properties. However,
-     * properties with ID 1 (codepage) are not counted.</p></li>
+     * <li>The sections have a different number of properties. However,
+     * properties with ID 1 (codepage) are not counted.</li>
      *
-     * <li><p>The other object is not a {@link Section}.</p></li>
+     * <li>The other object is not a {@link Section}.</li>
      *
-     * <li><p>The properties have different values. The order of the properties
-     * is irrelevant.</p></li>
+     * <li>The properties have different values. The order of the properties
+     * is irrelevant.</li>
      *
      * </ul>
      *
      * @param o The object to compare this section with
-     * @return <code>true</code> if the objects are equal, <code>false</code> if
+     * @return {@code true} if the objects are equal, {@code false} if
      * not
      */
-    public boolean equals(final Object o)
-    {
-        if (o == null || !(o instanceof Section))
+    public boolean equals(final Object o) {
+        if (o == null || !(o instanceof Section)) {
             return false;
+        }
         final Section s = (Section) o;
-        if (!s.getFormatID().equals(getFormatID()))
+        if (!s.getFormatID().equals(getFormatID())) {
             return false;
+        }
 
         /* Compare all properties except 0 and 1 as they must be handled
          * specially. */
@@ -536,34 +671,26 @@ public class Section
          * arrays. */
         Property p10 = null;
         Property p20 = null;
-        for (int i = 0; i < pa1.length; i++)
-        {
+        for (int i = 0; i < pa1.length; i++) {
             final long id = pa1[i].getID();
-            if (id == 0)
-            {
+            if (id == 0) {
                 p10 = pa1[i];
                 pa1 = remove(pa1, i);
                 i--;
             }
-            if (id == 1)
-            {
-                // p11 = pa1[i];
+            if (id == 1) {
                 pa1 = remove(pa1, i);
                 i--;
             }
         }
-        for (int i = 0; i < pa2.length; i++)
-        {
+        for (int i = 0; i < pa2.length; i++) {
             final long id = pa2[i].getID();
-            if (id == 0)
-            {
+            if (id == 0) {
                 p20 = pa2[i];
                 pa2 = remove(pa2, i);
                 i--;
             }
-            if (id == 1)
-            {
-                // p21 = pa2[i];
+            if (id == 1) {
                 pa2 = remove(pa2, i);
                 i--;
             }
@@ -571,52 +698,266 @@ public class Section
 
         /* If the number of properties (not counting property 1) is unequal the
          * sections are unequal. */
-        if (pa1.length != pa2.length)
+        if (pa1.length != pa2.length) {
             return false;
+        }
 
         /* If the dictionaries are unequal the sections are unequal. */
         boolean dictionaryEqual = true;
-        if (p10 != null && p20 != null)
+        if (p10 != null && p20 != null) {
             dictionaryEqual = p10.getValue().equals(p20.getValue());
-        else if (p10 != null || p20 != null)
+        } else if (p10 != null || p20 != null) {
             dictionaryEqual = false;
+        }
         if (dictionaryEqual) {
             return Util.equals(pa1, pa2);
         }
         return false;
     }
 
-
+    /**
+     * Removes a property.
+     *
+     * @param id The ID of the property to be removed
+     */
+    public void removeProperty(final long id) {
+        dirty |= (properties.remove(id) != null);
+    }
 
     /**
-     * <p>Removes a field from a property array. The resulting array is
-     * compactified and returned.</p>
+     * Removes a field from a property array. The resulting array is
+     * compactified and returned.
      *
      * @param pa The property array.
      * @param i The index of the field to be removed.
      * @return the compactified array.
      */
-    private Property[] remove(final Property[] pa, final int i)
-    {
+    private Property[] remove(final Property[] pa, final int i) {
         final Property[] h = new Property[pa.length - 1];
-        if (i > 0)
+        if (i > 0) {
             System.arraycopy(pa, 0, h, 0, i);
+        }
         System.arraycopy(pa, i + 1, h, i, h.length - i);
         return h;
     }
+    /**
+     * Writes this section into an output stream.<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.
+     *
+     * @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();
+        }
+        
+        /* Write the properties and the property list into their respective
+         * streams: */
+        for (Property p : properties.values()) {
+            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;
+    }
+
+
+
+    /**
+     * Writes the section's dictionary.
+     *
+     * @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;
+    }
+
+    /**
+     * 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.
+     *
+     * @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);
+        }
+    }
 
 
 
     /**
      * @see Object#hashCode()
      */
-    public int hashCode()
-    {
+    public int hashCode() {
         long hashCode = 0;
         hashCode += getFormatID().hashCode();
         final Property[] pa = getProperties();
-        for (int i = 0; i < pa.length; i++)
+        for (int i = 0; i < pa.length; i++) {
             hashCode += pa[i].hashCode();
+        }
         final int returnHashCode = (int) (hashCode & 0x0ffffffffL);
         return returnHashCode;
     }
@@ -626,8 +967,7 @@ public class Section
     /**
      * @see Object#toString()
      */
-    public String toString()
-    {
+    public String toString() {
         final StringBuffer b = new StringBuffer();
         final Property[] pa = getProperties();
         b.append(getClass().getName());
@@ -641,8 +981,7 @@ public class Section
         b.append(", size: ");
         b.append(getSize());
         b.append(", properties: [\n");
-        for (int i = 0; i < pa.length; i++)
-        {
+        for (int i = 0; i < pa.length; i++) {
             b.append(pa[i].toString());
             b.append(",\n");
         }
@@ -654,35 +993,33 @@ public class Section
 
 
     /**
-     * <p>Gets the section's dictionary. A dictionary allows an application to
+     * Gets the section's dictionary. A dictionary allows an application to
      * use human-readable property names instead of numeric property IDs. It
      * contains mappings from property IDs to their associated string
      * values. The dictionary is stored as the property with ID 0. The codepage
-     * for the strings in the dictionary is defined by property with ID 1.</p>
+     * for the strings in the dictionary is defined by property with ID 1.
      *
-     * @return the dictionary or <code>null</code> if the section does not have
+     * @return the dictionary or {@code null} if the section does not have
      * a dictionary.
      */
-    public Map<Long,String> getDictionary()
-    {
+    public Map<Long,String> getDictionary() {
         return dictionary;
     }
 
 
 
     /**
-     * <p>Gets the section's codepage, if any.</p>
+     * Gets the section's codepage, if any.
      *
      * @return The section's codepage if one is defined, else -1.
      */
     public int getCodepage()
     {
-        final Integer codepage =
-            (Integer) getProperty(PropertyIDMap.PID_CODEPAGE);
-        if (codepage == null)
+        final Integer codepage = (Integer) getProperty(PropertyIDMap.PID_CODEPAGE);
+        if (codepage == null) {
             return -1;
+        }
         int cp = codepage.intValue();
         return cp;
     }
-
 }

Modified: poi/trunk/src/java/org/apache/poi/hpsf/SpecialPropertySet.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/hpsf/SpecialPropertySet.java?rev=1771640&r1=1771639&r2=1771640&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/hpsf/SpecialPropertySet.java (original)
+++ poi/trunk/src/java/org/apache/poi/hpsf/SpecialPropertySet.java Sun Nov 27 20:19:18 2016
@@ -17,397 +17,24 @@
 
 package org.apache.poi.hpsf;
 
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.nio.charset.Charset;
-import java.util.List;
-
-import org.apache.poi.hpsf.wellknown.PropertyIDMap;
-import org.apache.poi.poifs.filesystem.DirectoryEntry;
-import org.apache.poi.util.LittleEndian;
+import org.apache.poi.util.Removal;
 
 /**
- * <p>Abstract superclass for the convenience classes {@link
- * SummaryInformation} and {@link DocumentSummaryInformation}.</p>
+ * Interface for the convenience classes {@link SummaryInformation}
+ * and {@link DocumentSummaryInformation}.<p>
  *
- * <p>The motivation behind this class is quite nasty if you look
- * behind the scenes, but it serves the application programmer well by
- * providing him with the easy-to-use {@link SummaryInformation} and
- * {@link DocumentSummaryInformation} classes. When parsing the data a
- * property set stream consists of (possibly coming from an {@link
- * java.io.InputStream}) we want to read and process each byte only
- * once. Since we don't know in advance which kind of property set we
- * have, we can expect only the most general {@link
- * PropertySet}. Creating a special subclass should be as easy as
- * calling the special subclass' constructor and pass the general
- * {@link PropertySet} in. To make things easy internally, the special
- * class just holds a reference to the general {@link PropertySet} and
- * delegates all method calls to it.</p>
+ * This used to be an abstract class to support late loading
+ * of the SummaryInformation classes, as their concrete instance can
+ * only be determined after the PropertySet has been loaded.
  *
- * <p>A cleaner implementation would have been like this: The {@link
- * PropertySetFactory} parses the stream data into some internal
- * object first.  Then it finds out whether the stream is a {@link
- * SummaryInformation}, a {@link DocumentSummaryInformation} or a
- * general {@link PropertySet}.  However, the current implementation
- * went the other way round historically: the convenience classes came
- * only late to my mind.</p>
+ * @deprecated POI 3.16 - use PropertySet as base class instead
  */
-public abstract class SpecialPropertySet extends MutablePropertySet
-{
-    /**
-     * The id to name mapping of the properties in this set.
-     * 
-     * @return the id to name mapping of the properties in this set
-     */
-    public abstract PropertyIDMap getPropertySetIDMap();
-
-    /**
-     * <p>The "real" property set <code>SpecialPropertySet</code>
-     * delegates to.</p>
-     */
-    private final MutablePropertySet delegate;
-
-
-
-    /**
-     * <p>Creates a <code>SpecialPropertySet</code>.
-     *
-     * @param ps The property set to be encapsulated by the
-     * <code>SpecialPropertySet</code>
-     */
-    public SpecialPropertySet(final PropertySet ps)
-    {
-        delegate = new MutablePropertySet(ps);
-    }
-
-
-
-    /**
-     * <p>Creates a <code>SpecialPropertySet</code>.
-     *
-     * @param ps The mutable property set to be encapsulated by the
-     * <code>SpecialPropertySet</code>
-     */
-    public SpecialPropertySet(final MutablePropertySet ps)
-    {
-        delegate = ps;
-    }
-
-
-
-    /**
-     * @see PropertySet#getByteOrder
-     */
-    @Override
-    public int getByteOrder()
-    {
-        return delegate.getByteOrder();
-    }
-
-
-
-    /**
-     * @see PropertySet#getFormat
-     */
-    @Override
-    public int getFormat()
-    {
-        return delegate.getFormat();
-    }
-
-
-
-    /**
-     * @see PropertySet#getOSVersion
-     */
-    @Override
-    public int getOSVersion()
-    {
-        return delegate.getOSVersion();
-    }
-
-
-
-    /**
-     * @see PropertySet#getClassID
-     */
-    @Override
-    public ClassID getClassID()
-    {
-        return delegate.getClassID();
-    }
-
-
-
-    /**
-     * @see PropertySet#getSectionCount
-     */
-    @Override
-    public int getSectionCount()
-    {
-        return delegate.getSectionCount();
-    }
-
-
-
-    /**
-     * @see PropertySet#getSections
-     */
-    @Override
-    public List<Section> getSections()
-    {
-        return delegate.getSections();
-    }
-
-
-
-    /**
-     * @see PropertySet#isSummaryInformation
-     */
-    @Override
-    public boolean isSummaryInformation()
-    {
-        return delegate.isSummaryInformation();
-    }
-
-
-
-    /**
-     * @see PropertySet#isDocumentSummaryInformation
-     */
-    @Override
-    public boolean isDocumentSummaryInformation()
-    {
-        return delegate.isDocumentSummaryInformation();
-    }
-
-
-
-    /**
-     * @see PropertySet#getSingleSection
-     */
-    @Override
-    public Section getFirstSection()
-    {
-        return delegate.getFirstSection();
-    }
-
-
-    /**
-     * @see org.apache.poi.hpsf.MutablePropertySet#addSection(org.apache.poi.hpsf.Section)
-     */
-    @Override
-    public void addSection(final Section section)
-    {
-        delegate.addSection(section);
-    }
-
-
-
-    /**
-     * @see org.apache.poi.hpsf.MutablePropertySet#clearSections()
-     */
-    @Override
-    public void clearSections()
-    {
-        delegate.clearSections();
-    }
-
-
-
-    /**
-     * @see org.apache.poi.hpsf.MutablePropertySet#setByteOrder(int)
-     */
-    @Override
-    public void setByteOrder(final int byteOrder)
-    {
-        delegate.setByteOrder(byteOrder);
-    }
-
-
-
-    /**
-     * @see org.apache.poi.hpsf.MutablePropertySet#setClassID(org.apache.poi.hpsf.ClassID)
-     */
-    @Override
-    public void setClassID(final ClassID classID)
-    {
-        delegate.setClassID(classID);
-    }
-
-
-
-    /**
-     * @see org.apache.poi.hpsf.MutablePropertySet#setFormat(int)
-     */
-    @Override
-    public void setFormat(final int format)
-    {
-        delegate.setFormat(format);
-    }
-
-
-
-    /**
-     * @see org.apache.poi.hpsf.MutablePropertySet#setOSVersion(int)
-     */
-    @Override
-    public void setOSVersion(final int osVersion)
-    {
-        delegate.setOSVersion(osVersion);
-    }
-
-
-
-    /**
-     * @see org.apache.poi.hpsf.MutablePropertySet#toInputStream()
-     */
-    @Override
-    public InputStream toInputStream() throws IOException, WritingNotSupportedException
-    {
-        return delegate.toInputStream();
-    }
-
-
-
-    /**
-     * @see org.apache.poi.hpsf.MutablePropertySet#write(org.apache.poi.poifs.filesystem.DirectoryEntry, java.lang.String)
-     */
-    @Override
-    public void write(final DirectoryEntry dir, final String name) throws WritingNotSupportedException, IOException
-    {
-        delegate.write(dir, name);
-    }
-
-    /**
-     * @see org.apache.poi.hpsf.MutablePropertySet#write(java.io.OutputStream)
-     */
-    @Override
-    public void write(final OutputStream out) throws WritingNotSupportedException, IOException
-    {
-        delegate.write(out);
-    }
-
-    /**
-     * @see org.apache.poi.hpsf.PropertySet#equals(java.lang.Object)
-     */
-    @Override
-    public boolean equals(final Object o)
-    {
-        return delegate.equals(o);
+@Removal(version="3.18")
+public class SpecialPropertySet extends MutablePropertySet {
+    public SpecialPropertySet() {
     }
 
-    /**
-     * @see org.apache.poi.hpsf.PropertySet#getProperties()
-     */
-    @Override
-    public Property[] getProperties() throws NoSingleSectionException
-    {
-        return delegate.getProperties();
+    public SpecialPropertySet(final PropertySet ps) throws UnexpectedPropertySetTypeException {
+        super(ps);
     }
-
-    /**
-     * @see org.apache.poi.hpsf.PropertySet#getProperty(int)
-     */
-    @Override
-    protected Object getProperty(final int id) throws NoSingleSectionException
-    {
-        return delegate.getProperty(id);
-    }
-
-
-
-    /**
-     * @see org.apache.poi.hpsf.PropertySet#getPropertyBooleanValue(int)
-     */
-    @Override
-    protected boolean getPropertyBooleanValue(final int id) throws NoSingleSectionException
-    {
-        return delegate.getPropertyBooleanValue(id);
-    }
-
-
-
-    /**
-     * @see org.apache.poi.hpsf.PropertySet#getPropertyIntValue(int)
-     */
-    @Override
-    protected int getPropertyIntValue(final int id) throws NoSingleSectionException
-    {
-        return delegate.getPropertyIntValue(id);
-    }
-
-
-    
-    /**
-     * 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);
-    }
-    protected 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;
-            if (b.length == 0) {
-                return "";
-            }
-            if (b.length == 1) {
-                return Byte.toString(b[0]);
-            }
-            if (b.length == 2) {
-                return Integer.toString( LittleEndian.getUShort(b) );
-            }
-            if (b.length == 4) {
-                return Long.toString( LittleEndian.getUInt(b) );
-            }
-            // Maybe it's a string? who knows!
-            return new String(b, Charset.forName("ASCII"));
-        }
-        return propertyValue.toString();
-    }
-
-
-    /**
-     * @see org.apache.poi.hpsf.PropertySet#hashCode()
-     */
-    @Override
-    public int hashCode()
-    {
-        return delegate.hashCode();
-    }
-
-
-
-    /**
-     * @see org.apache.poi.hpsf.PropertySet#toString()
-     */
-    @Override
-    public String toString()
-    {
-        return delegate.toString();
-    }
-
-
-
-    /**
-     * @see org.apache.poi.hpsf.PropertySet#wasNull()
-     */
-    @Override
-    public boolean wasNull() throws NoSingleSectionException
-    {
-        return delegate.wasNull();
-    }
-
 }

Modified: poi/trunk/src/java/org/apache/poi/hpsf/SummaryInformation.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/hpsf/SummaryInformation.java?rev=1771640&r1=1771639&r2=1771640&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/hpsf/SummaryInformation.java (original)
+++ poi/trunk/src/java/org/apache/poi/hpsf/SummaryInformation.java Sun Nov 27 20:19:18 2016
@@ -20,18 +20,18 @@ package org.apache.poi.hpsf;
 import java.util.Date;
 
 import org.apache.poi.hpsf.wellknown.PropertyIDMap;
+import org.apache.poi.hpsf.wellknown.SectionIDMap;
 
 /**
- * <p>Convenience class representing a Summary Information stream in a
- * Microsoft Office document.</p>
+ * Convenience class representing a Summary Information stream in a
+ * Microsoft Office document.
  *
  * @see DocumentSummaryInformation
  */
 public final class SummaryInformation extends SpecialPropertySet {
 
     /**
-     * <p>The document name a summary information stream usually has in a POIFS
-     * filesystem.</p>
+     * The document name a summary information stream usually has in a POIFS filesystem.
      */
     public static final String DEFAULT_STREAM_NAME = "\005SummaryInformation";
 
@@ -39,324 +39,291 @@ public final class SummaryInformation ex
     	return PropertyIDMap.getSummaryInformationProperties();
     }
 
-
     /**
-     * <p>Creates a {@link SummaryInformation} from a given {@link
-     * PropertySet}.</p>
+     * Creates a {@link SummaryInformation} from a given {@link
+     * PropertySet}.
+     *
+     * @param ps A property set which should be created from a summary
+     *        information stream.
+     * @throws UnexpectedPropertySetTypeException if {@code ps} does not
+     *         contain a summary information stream.
+     */
+    public SummaryInformation() {
+        getFirstSection().setFormatID(SectionIDMap.SUMMARY_INFORMATION_ID);
+    }
+        
+    /**
+     * Creates a {@link SummaryInformation} from a given {@link
+     * PropertySet}.
      *
      * @param ps A property set which should be created from a summary
      *        information stream.
-     * @throws UnexpectedPropertySetTypeException if <var>ps</var> does not
+     * @throws UnexpectedPropertySetTypeException if {@code ps} does not
      *         contain a summary information stream.
      */
-    public SummaryInformation(final PropertySet ps)
-            throws UnexpectedPropertySetTypeException
-    {
+    public SummaryInformation(final PropertySet ps) throws UnexpectedPropertySetTypeException {
         super(ps);
-        if (!isSummaryInformation())
-            throw new UnexpectedPropertySetTypeException("Not a "
-                    + getClass().getName());
+        if (!isSummaryInformation()) {
+            throw new UnexpectedPropertySetTypeException("Not a " + getClass().getName());
+        }
     }
 
 
 
     /**
-     * <p>Returns the title (or <code>null</code>).</p>
-     *
-     * @return The title or <code>null</code>
+     * @return The title or {@code null}
      */
-    public String getTitle()
-    {
+    public String getTitle() {
         return getPropertyStringValue(PropertyIDMap.PID_TITLE);
     }
 
 
 
     /**
-     * <p>Sets the title.</p>
+     * Sets the title.
      *
      * @param title The title to set.
      */
-    public void setTitle(final String title)
-    {
-        final MutableSection s = (MutableSection) getFirstSection();
-        s.setProperty(PropertyIDMap.PID_TITLE, title);
+    public void setTitle(final String title) {
+        set1stProperty(PropertyIDMap.PID_TITLE, title);
     }
 
 
 
     /**
-     * <p>Removes the title.</p>
+     * Removes the title.
      */
-    public void removeTitle()
-    {
-        final MutableSection s = (MutableSection) getFirstSection();
-        s.removeProperty(PropertyIDMap.PID_TITLE);
+    public void removeTitle() {
+        remove1stProperty(PropertyIDMap.PID_TITLE);
     }
 
 
 
     /**
-     * <p>Returns the subject (or <code>null</code>).</p>
+     * Returns the subject (or {@code null}).
      *
-     * @return The subject or <code>null</code>
+     * @return The subject or {@code null}
      */
-    public String getSubject()
-    {
+    public String getSubject() {
         return getPropertyStringValue(PropertyIDMap.PID_SUBJECT);
     }
 
 
 
     /**
-     * <p>Sets the subject.</p>
+     * Sets the subject.
      *
      * @param subject The subject to set.
      */
-    public void setSubject(final String subject)
-    {
-        final MutableSection s = (MutableSection) getFirstSection();
-        s.setProperty(PropertyIDMap.PID_SUBJECT, subject);
+    public void setSubject(final String subject) {
+        set1stProperty(PropertyIDMap.PID_SUBJECT, subject);
     }
 
 
 
     /**
-     * <p>Removes the subject.</p>
+     * Removes the subject.
      */
-    public void removeSubject()
-    {
-        final MutableSection s = (MutableSection) getFirstSection();
-        s.removeProperty(PropertyIDMap.PID_SUBJECT);
+    public void removeSubject() {
+        remove1stProperty(PropertyIDMap.PID_SUBJECT);
     }
 
 
 
     /**
-     * <p>Returns the author (or <code>null</code>).</p>
+     * Returns the author (or {@code null}).
      *
-     * @return The author or <code>null</code>
+     * @return The author or {@code null}
      */
-    public String getAuthor()
-    {
+    public String getAuthor() {
         return getPropertyStringValue(PropertyIDMap.PID_AUTHOR);
     }
 
 
 
     /**
-     * <p>Sets the author.</p>
+     * Sets the author.
      *
      * @param author The author to set.
      */
-    public void setAuthor(final String author)
-    {
-        final MutableSection s = (MutableSection) getFirstSection();
-        s.setProperty(PropertyIDMap.PID_AUTHOR, author);
+    public void setAuthor(final String author) {
+        set1stProperty(PropertyIDMap.PID_AUTHOR, author);
     }
 
 
 
     /**
-     * <p>Removes the author.</p>
+     * Removes the author.
      */
-    public void removeAuthor()
-    {
-        final MutableSection s = (MutableSection) getFirstSection();
-        s.removeProperty(PropertyIDMap.PID_AUTHOR);
+    public void removeAuthor() {
+        remove1stProperty(PropertyIDMap.PID_AUTHOR);
     }
 
 
 
     /**
-     * <p>Returns the keywords (or <code>null</code>).</p>
+     * Returns the keywords (or {@code null}).
      *
-     * @return The keywords or <code>null</code>
+     * @return The keywords or {@code null}
      */
-    public String getKeywords()
-    {
+    public String getKeywords() {
         return getPropertyStringValue(PropertyIDMap.PID_KEYWORDS);
     }
 
 
 
     /**
-     * <p>Sets the keywords.</p>
+     * Sets the keywords.
      *
      * @param keywords The keywords to set.
      */
-    public void setKeywords(final String keywords)
-    {
-        final MutableSection s = (MutableSection) getFirstSection();
-        s.setProperty(PropertyIDMap.PID_KEYWORDS, keywords);
+    public void setKeywords(final String keywords) {
+        set1stProperty(PropertyIDMap.PID_KEYWORDS, keywords);
     }
 
 
 
     /**
-     * <p>Removes the keywords.</p>
+     * Removes the keywords.
      */
-    public void removeKeywords()
-    {
-        final MutableSection s = (MutableSection) getFirstSection();
-        s.removeProperty(PropertyIDMap.PID_KEYWORDS);
+    public void removeKeywords() {
+        remove1stProperty(PropertyIDMap.PID_KEYWORDS);
     }
 
 
 
     /**
-     * <p>Returns the comments (or <code>null</code>).</p>
+     * Returns the comments (or {@code null}).
      *
-     * @return The comments or <code>null</code>
+     * @return The comments or {@code null}
      */
-    public String getComments()
-    {
+    public String getComments() {
         return getPropertyStringValue(PropertyIDMap.PID_COMMENTS);
     }
 
 
 
     /**
-     * <p>Sets the comments.</p>
+     * Sets the comments.
      *
      * @param comments The comments to set.
      */
-    public void setComments(final String comments)
-    {
-        final MutableSection s = (MutableSection) getFirstSection();
-        s.setProperty(PropertyIDMap.PID_COMMENTS, comments);
+    public void setComments(final String comments) {
+        set1stProperty(PropertyIDMap.PID_COMMENTS, comments);
     }
 
 
 
     /**
-     * <p>Removes the comments.</p>
+     * Removes the comments.
      */
-    public void removeComments()
-    {
-        final MutableSection s = (MutableSection) getFirstSection();
-        s.removeProperty(PropertyIDMap.PID_COMMENTS);
+    public void removeComments() {
+        remove1stProperty(PropertyIDMap.PID_COMMENTS);
     }
 
 
 
     /**
-     * <p>Returns the template (or <code>null</code>).</p>
+     * Returns the template (or {@code null}).
      *
-     * @return The template or <code>null</code>
+     * @return The template or {@code null}
      */
-    public String getTemplate()
-    {
+    public String getTemplate() {
         return getPropertyStringValue(PropertyIDMap.PID_TEMPLATE);
     }
 
 
 
     /**
-     * <p>Sets the template.</p>
+     * Sets the template.
      *
      * @param template The template to set.
      */
-    public void setTemplate(final String template)
-    {
-        final MutableSection s = (MutableSection) getFirstSection();
-        s.setProperty(PropertyIDMap.PID_TEMPLATE, template);
+    public void setTemplate(final String template) {
+        set1stProperty(PropertyIDMap.PID_TEMPLATE, template);
     }
 
 
 
     /**
-     * <p>Removes the template.</p>
+     * Removes the template.
      */
-    public void removeTemplate()
-    {
-        final MutableSection s = (MutableSection) getFirstSection();
-        s.removeProperty(PropertyIDMap.PID_TEMPLATE);
+    public void removeTemplate() {
+        remove1stProperty(PropertyIDMap.PID_TEMPLATE);
     }
 
 
 
     /**
-     * <p>Returns the last author (or <code>null</code>).</p>
+     * Returns the last author (or {@code null}).
      *
-     * @return The last author or <code>null</code>
+     * @return The last author or {@code null}
      */
-    public String getLastAuthor()
-    {
+    public String getLastAuthor() {
         return getPropertyStringValue(PropertyIDMap.PID_LASTAUTHOR);
     }
 
 
 
     /**
-     * <p>Sets the last author.</p>
+     * Sets the last author.
      *
      * @param lastAuthor The last author to set.
      */
-    public void setLastAuthor(final String lastAuthor)
-    {
-        final MutableSection s = (MutableSection) getFirstSection();
-        s.setProperty(PropertyIDMap.PID_LASTAUTHOR, lastAuthor);
+    public void setLastAuthor(final String lastAuthor) {
+        set1stProperty(PropertyIDMap.PID_LASTAUTHOR, lastAuthor);
     }
 
 
 
     /**
-     * <p>Removes the last author.</p>
+     * Removes the last author.
      */
-    public void removeLastAuthor()
-    {
-        final MutableSection s = (MutableSection) getFirstSection();
-        s.removeProperty(PropertyIDMap.PID_LASTAUTHOR);
+    public void removeLastAuthor() {
+        remove1stProperty(PropertyIDMap.PID_LASTAUTHOR);
     }
 
 
 
     /**
-     * <p>Returns the revision number (or <code>null</code>). </p>
+     * Returns the revision number (or {@code null}). 
      *
-     * @return The revision number or <code>null</code>
+     * @return The revision number or {@code null}
      */
-    public String getRevNumber()
-    {
+    public String getRevNumber() {
         return getPropertyStringValue(PropertyIDMap.PID_REVNUMBER);
     }
 
 
 
     /**
-     * <p>Sets the revision number.</p>
+     * Sets the revision number.
      *
      * @param revNumber The revision number to set.
      */
-    public void setRevNumber(final String revNumber)
-    {
-        final MutableSection s = (MutableSection) getFirstSection();
-        s.setProperty(PropertyIDMap.PID_REVNUMBER, revNumber);
+    public void setRevNumber(final String revNumber) {
+        set1stProperty(PropertyIDMap.PID_REVNUMBER, revNumber);
     }
 
 
 
     /**
-     * <p>Removes the revision number.</p>
+     * Removes the revision number.
      */
-    public void removeRevNumber()
-    {
-        final MutableSection s = (MutableSection) getFirstSection();
-        s.removeProperty(PropertyIDMap.PID_REVNUMBER);
+    public void removeRevNumber() {
+        remove1stProperty(PropertyIDMap.PID_REVNUMBER);
     }
 
 
 
     /**
-     * <p>Returns the total time spent in editing the document (or
-     * <code>0</code>).</p>
+     * Returns the total time spent in editing the document (or
+     * {@code 0}).
      *
      * @return The total time spent in editing the document or 0 if the {@link
      *         SummaryInformation} does not contain this information.
      */
-    public long getEditTime()
-    {
+    public long getEditTime() {
         final Date d = (Date) getProperty(PropertyIDMap.PID_EDITTIME);
         if (d == null) {
             return 0;
@@ -367,124 +334,106 @@ public final class SummaryInformation ex
 
 
     /**
-     * <p>Sets the total time spent in editing the document.</p>
+     * Sets the total time spent in editing the document.
      *
      * @param time The time to set.
      */
-    public void setEditTime(final long time)
-    {
+    public void setEditTime(final long time) {
         final Date d = Util.filetimeToDate(time);
-        final MutableSection s = (MutableSection) getFirstSection();
-        s.setProperty(PropertyIDMap.PID_EDITTIME, Variant.VT_FILETIME, d);
+        getFirstSection().setProperty(PropertyIDMap.PID_EDITTIME, Variant.VT_FILETIME, d);
     }
 
 
 
     /**
-     * <p>Remove the total time spent in editing the document.</p>
+     * Remove the total time spent in editing the document.
      */
-    public void removeEditTime()
-    {
-        final MutableSection s = (MutableSection) getFirstSection();
-        s.removeProperty(PropertyIDMap.PID_EDITTIME);
+    public void removeEditTime() {
+        remove1stProperty(PropertyIDMap.PID_EDITTIME);
     }
 
 
 
     /**
-     * <p>Returns the last printed time (or <code>null</code>).</p>
+     * Returns the last printed time (or {@code null}).
      *
-     * @return The last printed time or <code>null</code>
+     * @return The last printed time or {@code null}
      */
-    public Date getLastPrinted()
-    {
+    public Date getLastPrinted() {
         return (Date) getProperty(PropertyIDMap.PID_LASTPRINTED);
     }
 
 
 
     /**
-     * <p>Sets the lastPrinted.</p>
+     * Sets the lastPrinted.
      *
      * @param lastPrinted The lastPrinted to set.
      */
-    public void setLastPrinted(final Date lastPrinted)
-    {
-        final MutableSection s = (MutableSection) getFirstSection();
-        s.setProperty(PropertyIDMap.PID_LASTPRINTED, Variant.VT_FILETIME,
-                lastPrinted);
+    public void setLastPrinted(final Date lastPrinted) {
+        getFirstSection().setProperty(PropertyIDMap.PID_LASTPRINTED, Variant.VT_FILETIME, lastPrinted);
     }
 
 
 
     /**
-     * <p>Removes the lastPrinted.</p>
+     * Removes the lastPrinted.
      */
-    public void removeLastPrinted()
-    {
-        final MutableSection s = (MutableSection) getFirstSection();
-        s.removeProperty(PropertyIDMap.PID_LASTPRINTED);
+    public void removeLastPrinted() {
+        remove1stProperty(PropertyIDMap.PID_LASTPRINTED);
     }
 
 
 
     /**
-     * <p>Returns the creation time (or <code>null</code>).</p>
+     * Returns the creation time (or {@code null}).
      *
-     * @return The creation time or <code>null</code>
+     * @return The creation time or {@code null}
      */
-    public Date getCreateDateTime()
-    {
+    public Date getCreateDateTime() {
         return (Date) getProperty(PropertyIDMap.PID_CREATE_DTM);
     }
 
 
 
     /**
-     * <p>Sets the creation time.</p>
+     * Sets the creation time.
      *
      * @param createDateTime The creation time to set.
      */
-    public void setCreateDateTime(final Date createDateTime)
-    {
-        final MutableSection s = (MutableSection) getFirstSection();
-        s.setProperty(PropertyIDMap.PID_CREATE_DTM, Variant.VT_FILETIME,
-                createDateTime);
+    public void setCreateDateTime(final Date createDateTime) {
+        getFirstSection().setProperty(PropertyIDMap.PID_CREATE_DTM, Variant.VT_FILETIME, createDateTime);
     }
 
 
 
     /**
-     * <p>Removes the creation time.</p>
+     * Removes the creation time.
      */
-    public void removeCreateDateTime()
-    {
-        final MutableSection s = (MutableSection) getFirstSection();
-        s.removeProperty(PropertyIDMap.PID_CREATE_DTM);
+    public void removeCreateDateTime() {
+        remove1stProperty(PropertyIDMap.PID_CREATE_DTM);
     }
 
 
 
     /**
-     * <p>Returns the last save time (or <code>null</code>).</p>
+     * Returns the last save time (or {@code null}).
      *
-     * @return The last save time or <code>null</code>
+     * @return The last save time or {@code null}
      */
-    public Date getLastSaveDateTime()
-    {
+    public Date getLastSaveDateTime() {
         return (Date) getProperty(PropertyIDMap.PID_LASTSAVE_DTM);
     }
 
 
 
     /**
-     * <p>Sets the total time spent in editing the document.</p>
+     * Sets the total time spent in editing the document.
      *
      * @param time The time to set.
      */
-    public void setLastSaveDateTime(final Date time)
-    {
-        final MutableSection s = (MutableSection) getFirstSection();
+    public void setLastSaveDateTime(final Date time) {
+        final Section s = getFirstSection();
         s
                 .setProperty(PropertyIDMap.PID_LASTSAVE_DTM,
                         Variant.VT_FILETIME, time);
@@ -493,153 +442,134 @@ public final class SummaryInformation ex
 
 
     /**
-     * <p>Remove the total time spent in editing the document.</p>
+     * Remove the total time spent in editing the document.
      */
-    public void removeLastSaveDateTime()
-    {
-        final MutableSection s = (MutableSection) getFirstSection();
-        s.removeProperty(PropertyIDMap.PID_LASTSAVE_DTM);
+    public void removeLastSaveDateTime() {
+        remove1stProperty(PropertyIDMap.PID_LASTSAVE_DTM);
     }
 
 
 
     /**
-     * <p>Returns the page count or 0 if the {@link SummaryInformation} does
-     * not contain a page count.</p>
+     * Returns the page count or 0 if the {@link SummaryInformation} does
+     * not contain a page count.
      *
      * @return The page count or 0 if the {@link SummaryInformation} does not
      *         contain a page count.
      */
-    public int getPageCount()
-    {
+    public int getPageCount() {
         return getPropertyIntValue(PropertyIDMap.PID_PAGECOUNT);
     }
 
 
 
     /**
-     * <p>Sets the page count.</p>
+     * Sets the page count.
      *
      * @param pageCount The page count to set.
      */
-    public void setPageCount(final int pageCount)
-    {
-        final MutableSection s = (MutableSection) getFirstSection();
-        s.setProperty(PropertyIDMap.PID_PAGECOUNT, pageCount);
+    public void setPageCount(final int pageCount) {
+        set1stProperty(PropertyIDMap.PID_PAGECOUNT, pageCount);
     }
 
 
 
     /**
-     * <p>Removes the page count.</p>
+     * Removes the page count.
      */
-    public void removePageCount()
-    {
-        final MutableSection s = (MutableSection) getFirstSection();
-        s.removeProperty(PropertyIDMap.PID_PAGECOUNT);
+    public void removePageCount() {
+        remove1stProperty(PropertyIDMap.PID_PAGECOUNT);
     }
 
 
 
     /**
-     * <p>Returns the word count or 0 if the {@link SummaryInformation} does
-     * not contain a word count.</p>
+     * Returns the word count or 0 if the {@link SummaryInformation} does
+     * not contain a word count.
      *
-     * @return The word count or <code>null</code>
+     * @return The word count or {@code null}
      */
-    public int getWordCount()
-    {
+    public int getWordCount() {
         return getPropertyIntValue(PropertyIDMap.PID_WORDCOUNT);
     }
 
 
 
     /**
-     * <p>Sets the word count.</p>
+     * Sets the word count.
      *
      * @param wordCount The word count to set.
      */
-    public void setWordCount(final int wordCount)
-    {
-        final MutableSection s = (MutableSection) getFirstSection();
-        s.setProperty(PropertyIDMap.PID_WORDCOUNT, wordCount);
+    public void setWordCount(final int wordCount) {
+        set1stProperty(PropertyIDMap.PID_WORDCOUNT, wordCount);
     }
 
 
 
     /**
-     * <p>Removes the word count.</p>
+     * Removes the word count.
      */
-    public void removeWordCount()
-    {
-        final MutableSection s = (MutableSection) getFirstSection();
-        s.removeProperty(PropertyIDMap.PID_WORDCOUNT);
+    public void removeWordCount() {
+        remove1stProperty(PropertyIDMap.PID_WORDCOUNT);
     }
 
 
 
     /**
-     * <p>Returns the character count or 0 if the {@link SummaryInformation}
-     * does not contain a char count.</p>
+     * Returns the character count or 0 if the {@link SummaryInformation}
+     * does not contain a char count.
      *
-     * @return The character count or <code>null</code>
+     * @return The character count or {@code null}
      */
-    public int getCharCount()
-    {
+    public int getCharCount() {
         return getPropertyIntValue(PropertyIDMap.PID_CHARCOUNT);
     }
 
 
 
     /**
-     * <p>Sets the character count.</p>
+     * Sets the character count.
      *
      * @param charCount The character count to set.
      */
-    public void setCharCount(final int charCount)
-    {
-        final MutableSection s = (MutableSection) getFirstSection();
-        s.setProperty(PropertyIDMap.PID_CHARCOUNT, charCount);
+    public void setCharCount(final int charCount) {
+        set1stProperty(PropertyIDMap.PID_CHARCOUNT, charCount);
     }
 
 
 
     /**
-     * <p>Removes the character count.</p>
+     * Removes the character count.
      */
-    public void removeCharCount()
-    {
-        final MutableSection s = (MutableSection) getFirstSection();
-        s.removeProperty(PropertyIDMap.PID_CHARCOUNT);
+    public void removeCharCount() {
+        remove1stProperty(PropertyIDMap.PID_CHARCOUNT);
     }
 
 
 
     /**
-     * <p>Returns the thumbnail (or <code>null</code>) <strong>when this
+     * Returns the thumbnail (or {@code null}) <strong>when this
      * method is implemented. Please note that the return type is likely to
-     * change!</strong></p>
+     * change!</strong><p>
      *
-     * <p>To process this data, you may wish to make use of the
+     * To process this data, you may wish to make use of the
      *  {@link Thumbnail} class. The raw data is generally 
-     *  an image in WMF or Clipboard (BMP?) format</p>
+     *  an image in WMF or Clipboard (BMP?) format
      *
-     * @return The thumbnail or <code>null</code>
+     * @return The thumbnail or {@code null}
      */
-    public byte[] getThumbnail()
-    {
+    public byte[] getThumbnail() {
         return (byte[]) getProperty(PropertyIDMap.PID_THUMBNAIL);
     }
 
     /**
-     * <p>Returns the thumbnail (or <code>null</code>), processed
+     * Returns the thumbnail (or {@code null}), processed
      *  as an object which is (largely) able to unpack the thumbnail
-     *  image data.</p>
+     *  image data.
      *
-     * @return The thumbnail or <code>null</code>
+     * @return The thumbnail or {@code null}
      */
-    public Thumbnail getThumbnailThumbnail()
-    {
+    public Thumbnail getThumbnailThumbnail() {
         byte[] data = getThumbnail();
         if (data == null) return null;
         return new Thumbnail(data);
@@ -648,115 +578,100 @@ public final class SummaryInformation ex
 
 
     /**
-     * <p>Sets the thumbnail.</p>
+     * Sets the thumbnail.
      *
      * @param thumbnail The thumbnail to set.
      */
-    public void setThumbnail(final byte[] thumbnail)
-    {
-        final MutableSection s = (MutableSection) getFirstSection();
-        s.setProperty(PropertyIDMap.PID_THUMBNAIL, /* FIXME: */
-                Variant.VT_LPSTR, thumbnail);
+    public void setThumbnail(final byte[] thumbnail) {
+        getFirstSection().setProperty(PropertyIDMap.PID_THUMBNAIL, /* FIXME: */ Variant.VT_LPSTR, thumbnail);
     }
 
 
 
     /**
-     * <p>Removes the thumbnail.</p>
+     * Removes the thumbnail.
      */
-    public void removeThumbnail()
-    {
-        final MutableSection s = (MutableSection) getFirstSection();
-        s.removeProperty(PropertyIDMap.PID_THUMBNAIL);
+    public void removeThumbnail() {
+        remove1stProperty(PropertyIDMap.PID_THUMBNAIL);
     }
 
 
 
     /**
-     * <p>Returns the application name (or <code>null</code>).</p>
+     * Returns the application name (or {@code null}).
      *
-     * @return The application name or <code>null</code>
+     * @return The application name or {@code null}
      */
-    public String getApplicationName()
-    {
+    public String getApplicationName() {
         return getPropertyStringValue(PropertyIDMap.PID_APPNAME);
     }
 
 
 
     /**
-     * <p>Sets the application name.</p>
+     * Sets the application name.
      *
      * @param applicationName The application name to set.
      */
-    public void setApplicationName(final String applicationName)
-    {
-        final MutableSection s = (MutableSection) getFirstSection();
-        s.setProperty(PropertyIDMap.PID_APPNAME, applicationName);
+    public void setApplicationName(final String applicationName) {
+        set1stProperty(PropertyIDMap.PID_APPNAME, applicationName);
     }
 
 
 
     /**
-     * <p>Removes the application name.</p>
+     * Removes the application name.
      */
-    public void removeApplicationName()
-    {
-        final MutableSection s = (MutableSection) getFirstSection();
-        s.removeProperty(PropertyIDMap.PID_APPNAME);
+    public void removeApplicationName() {
+        remove1stProperty(PropertyIDMap.PID_APPNAME);
     }
 
 
 
     /**
-     * <p>Returns a security code which is one of the following values:</p>
+     * Returns a security code which is one of the following values:
      *
      * <ul>
      *
-     * <li><p>0 if the {@link SummaryInformation} does not contain a
+     * <li>0 if the {@link SummaryInformation} does not contain a
      * security field or if there is no security on the document. Use
      * {@link PropertySet#wasNull()} to distinguish between the two
-     * cases!</p></li>
+     * cases!
      *
-     * <li><p>1 if the document is password protected</p></li>
+     * <li>1 if the document is password protected
      *
-     * <li><p>2 if the document is read-only recommended</p></li>
+     * <li>2 if the document is read-only recommended
      *
-     * <li><p>4 if the document is read-only enforced</p></li>
+     * <li>4 if the document is read-only enforced
      *
-     * <li><p>8 if the document is locked for annotations</p></li>
+     * <li>8 if the document is locked for annotations
      *
      * </ul>
      *
-     * @return The security code or <code>null</code>
+     * @return The security code or {@code null}
      */
-    public int getSecurity()
-    {
+    public int getSecurity() {
         return getPropertyIntValue(PropertyIDMap.PID_SECURITY);
     }
 
 
 
     /**
-     * <p>Sets the security code.</p>
+     * Sets the security code.
      *
      * @param security The security code to set.
      */
-    public void setSecurity(final int security)
-    {
-        final MutableSection s = (MutableSection) getFirstSection();
-        s.setProperty(PropertyIDMap.PID_SECURITY, security);
+    public void setSecurity(final int security) {
+        set1stProperty(PropertyIDMap.PID_SECURITY, security);
     }
 
 
 
     /**
-     * <p>Removes the security code.</p>
+     * Removes the security code.
      */
-    public void removeSecurity()
-    {
-        final MutableSection s = (MutableSection) getFirstSection();
-        s.removeProperty(PropertyIDMap.PID_SECURITY);
+    public void removeSecurity() {
+        remove1stProperty(PropertyIDMap.PID_SECURITY);
     }
 
 }




---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@poi.apache.org
For additional commands, e-mail: commits-help@poi.apache.org