You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@poi.apache.org by kl...@apache.org on 2006/03/03 17:58:00 UTC

svn commit: r382887 [1/3] - in /jakarta/poi/trunk/src: contrib/src/org/apache/poi/contrib/poibrowser/ documentation/content/xdocs/hpsf/ examples/src/org/apache/poi/hpsf/examples/ java/org/apache/poi/hpsf/ java/org/apache/poi/hpsf/wellknown/ java/org/ap...

Author: klute
Date: Fri Mar  3 08:57:55 2006
New Revision: 382887

URL: http://svn.apache.org/viewcvs?rev=382887&view=rev
Log:
* Writing support added to the SummaryInformation and DocumentSummaryInformation classes. These classes now have methods for setting and removing properties. Coherent extensions are:
** Documentation section about writing standard properties added to the HPSF HOW-TO.
** Example application added showing how to modify the document summary information.
** Testcases added for testing modifying summary information and document summary information.
** PropertySetFactory extended to create SummaryInformation and DocumentSummaryInformation instances.

* Added MutablePropertySet.write(DirectoryEntry, String) to ease writing a property set to a POI filesystem document.

* Improved codepage handling.

* Bug fixed: Integral values were read and written as unsigned instead of signed.

* Reworked the mapping between variant types and Java types: Variant.VT_I4 is mapped to Integer now and Variant.VT_I8 to Long. This might cause incompatibilities if you are doing low-level HPSF programming. 

* Changed SectionIDMap.DOCUMENT_SUMMARY_INFORMATION_ID from a byte[] to a byte[][] in order to contain the format ID of the first and the second section. This is an incompatible change!

* Added PropertySet.getFirstSection(). This method is similar to getSingleSection() won't choke if the property set has more than one section.

* Support for low-level reading and writing of Variant.VT_I8 type properties added.

* Unnecessary casts removed.

* Poibrowser's display format changed slightly.


Added:
    jakarta/poi/trunk/src/examples/src/org/apache/poi/hpsf/examples/ModifyDocumentSummaryInformation.java
    jakarta/poi/trunk/src/java/org/apache/poi/hpsf/CustomProperties.java
    jakarta/poi/trunk/src/java/org/apache/poi/hpsf/CustomProperty.java
    jakarta/poi/trunk/src/java/org/apache/poi/hpsf/MissingSectionException.java
    jakarta/poi/trunk/src/testcases/org/apache/poi/hpsf/basic/AllDataFilesTester.java   (with props)
    jakarta/poi/trunk/src/testcases/org/apache/poi/hpsf/basic/TestMetaDataIPI.java
    jakarta/poi/trunk/src/testcases/org/apache/poi/hpsf/basic/TestWriteWellKnown.java   (with props)
    jakarta/poi/trunk/src/testcases/org/apache/poi/hpsf/data/TestWriteWellKnown.doc   (with props)
Modified:
    jakarta/poi/trunk/src/contrib/src/org/apache/poi/contrib/poibrowser/POIBrowser.java
    jakarta/poi/trunk/src/contrib/src/org/apache/poi/contrib/poibrowser/PropertySetDescriptor.java
    jakarta/poi/trunk/src/contrib/src/org/apache/poi/contrib/poibrowser/PropertySetDescriptorRenderer.java
    jakarta/poi/trunk/src/documentation/content/xdocs/hpsf/how-to.xml
    jakarta/poi/trunk/src/documentation/content/xdocs/hpsf/internals.xml
    jakarta/poi/trunk/src/java/org/apache/poi/hpsf/ClassID.java
    jakarta/poi/trunk/src/java/org/apache/poi/hpsf/Constants.java
    jakarta/poi/trunk/src/java/org/apache/poi/hpsf/DocumentSummaryInformation.java
    jakarta/poi/trunk/src/java/org/apache/poi/hpsf/HPSFException.java
    jakarta/poi/trunk/src/java/org/apache/poi/hpsf/HPSFRuntimeException.java
    jakarta/poi/trunk/src/java/org/apache/poi/hpsf/IllegalPropertySetDataException.java
    jakarta/poi/trunk/src/java/org/apache/poi/hpsf/MarkUnsupportedException.java
    jakarta/poi/trunk/src/java/org/apache/poi/hpsf/MutablePropertySet.java
    jakarta/poi/trunk/src/java/org/apache/poi/hpsf/MutableSection.java
    jakarta/poi/trunk/src/java/org/apache/poi/hpsf/NoFormatIDException.java
    jakarta/poi/trunk/src/java/org/apache/poi/hpsf/NoSingleSectionException.java
    jakarta/poi/trunk/src/java/org/apache/poi/hpsf/Property.java
    jakarta/poi/trunk/src/java/org/apache/poi/hpsf/PropertySet.java
    jakarta/poi/trunk/src/java/org/apache/poi/hpsf/PropertySetFactory.java
    jakarta/poi/trunk/src/java/org/apache/poi/hpsf/ReadingNotSupportedException.java
    jakarta/poi/trunk/src/java/org/apache/poi/hpsf/Section.java
    jakarta/poi/trunk/src/java/org/apache/poi/hpsf/SpecialPropertySet.java
    jakarta/poi/trunk/src/java/org/apache/poi/hpsf/SummaryInformation.java
    jakarta/poi/trunk/src/java/org/apache/poi/hpsf/Thumbnail.java
    jakarta/poi/trunk/src/java/org/apache/poi/hpsf/TypeWriter.java
    jakarta/poi/trunk/src/java/org/apache/poi/hpsf/UnexpectedPropertySetTypeException.java
    jakarta/poi/trunk/src/java/org/apache/poi/hpsf/UnsupportedVariantTypeException.java
    jakarta/poi/trunk/src/java/org/apache/poi/hpsf/Util.java
    jakarta/poi/trunk/src/java/org/apache/poi/hpsf/Variant.java
    jakarta/poi/trunk/src/java/org/apache/poi/hpsf/VariantSupport.java
    jakarta/poi/trunk/src/java/org/apache/poi/hpsf/WritingNotSupportedException.java
    jakarta/poi/trunk/src/java/org/apache/poi/hpsf/wellknown/PropertyIDMap.java
    jakarta/poi/trunk/src/java/org/apache/poi/hpsf/wellknown/SectionIDMap.java
    jakarta/poi/trunk/src/java/org/apache/poi/util/LittleEndian.java
    jakarta/poi/trunk/src/testcases/org/apache/poi/hpsf/basic/TestBasic.java
    jakarta/poi/trunk/src/testcases/org/apache/poi/hpsf/basic/TestClassID.java   (contents, props changed)
    jakarta/poi/trunk/src/testcases/org/apache/poi/hpsf/basic/TestEmptyProperties.java
    jakarta/poi/trunk/src/testcases/org/apache/poi/hpsf/basic/TestUnicode.java
    jakarta/poi/trunk/src/testcases/org/apache/poi/hpsf/basic/TestWrite.java
    jakarta/poi/trunk/src/testcases/org/apache/poi/hpsf/data/TestCorel.shw   (props changed)
    jakarta/poi/trunk/src/testcases/org/apache/poi/hpsf/data/TestGermanWord90.doc   (props changed)
    jakarta/poi/trunk/src/testcases/org/apache/poi/hpsf/data/TestRobert_Flaherty.doc   (props changed)

Modified: jakarta/poi/trunk/src/contrib/src/org/apache/poi/contrib/poibrowser/POIBrowser.java
URL: http://svn.apache.org/viewcvs/jakarta/poi/trunk/src/contrib/src/org/apache/poi/contrib/poibrowser/POIBrowser.java?rev=382887&r1=382886&r2=382887&view=diff
==============================================================================
--- jakarta/poi/trunk/src/contrib/src/org/apache/poi/contrib/poibrowser/POIBrowser.java (original)
+++ jakarta/poi/trunk/src/contrib/src/org/apache/poi/contrib/poibrowser/POIBrowser.java Fri Mar  3 08:57:55 2006
@@ -128,7 +128,7 @@
                       new PropertySetDescriptorRenderer());
         treeUI.setCellRenderer(etcr);
         setSize(600, 450);
-        setTitle("POI Browser 0.08");
+        setTitle("POI Browser 0.09");
         setVisible(true);
     }
 

Modified: jakarta/poi/trunk/src/contrib/src/org/apache/poi/contrib/poibrowser/PropertySetDescriptor.java
URL: http://svn.apache.org/viewcvs/jakarta/poi/trunk/src/contrib/src/org/apache/poi/contrib/poibrowser/PropertySetDescriptor.java?rev=382887&r1=382886&r2=382887&view=diff
==============================================================================
--- jakarta/poi/trunk/src/contrib/src/org/apache/poi/contrib/poibrowser/PropertySetDescriptor.java (original)
+++ jakarta/poi/trunk/src/contrib/src/org/apache/poi/contrib/poibrowser/PropertySetDescriptor.java Fri Mar  3 08:57:55 2006
@@ -25,7 +25,6 @@
 import org.apache.poi.hpsf.NoPropertySetStreamException;
 import org.apache.poi.hpsf.PropertySet;
 import org.apache.poi.hpsf.PropertySetFactory;
-import org.apache.poi.hpsf.UnexpectedPropertySetTypeException;
 import org.apache.poi.poifs.filesystem.DocumentInputStream;
 import org.apache.poi.poifs.filesystem.POIFSDocumentPath;
 
@@ -70,7 +69,7 @@
                                  final POIFSDocumentPath path,
                                  final DocumentInputStream stream,
                                  final int nrOfBytesToDump)
-        throws UnexpectedPropertySetTypeException, NoPropertySetStreamException,
+        throws NoPropertySetStreamException,
                MarkUnsupportedException, UnsupportedEncodingException,
                IOException
     {

Modified: jakarta/poi/trunk/src/contrib/src/org/apache/poi/contrib/poibrowser/PropertySetDescriptorRenderer.java
URL: http://svn.apache.org/viewcvs/jakarta/poi/trunk/src/contrib/src/org/apache/poi/contrib/poibrowser/PropertySetDescriptorRenderer.java?rev=382887&r1=382886&r2=382887&view=diff
==============================================================================
--- jakarta/poi/trunk/src/contrib/src/org/apache/poi/contrib/poibrowser/PropertySetDescriptorRenderer.java (original)
+++ jakarta/poi/trunk/src/contrib/src/org/apache/poi/contrib/poibrowser/PropertySetDescriptorRenderer.java Fri Mar  3 08:57:55 2006
@@ -127,6 +127,9 @@
 
     /**
      * <p>Returns a string representation of a {@link Section}.</p>
+     * @param s the section
+     * @param name the section's name
+     * @return a string representation of the {@link Section}
      */
     protected String toString(final Section s, final String name)
     {
@@ -141,12 +144,18 @@
         for (int i = 0; i < properties.length; i++)
         {
             final Property p = properties[i];
+            final long id = p.getID();
+            final long type = p.getType();
             final Object value = p.getValue();
-            b.append("\n" + name + " ");
-            b.append("PID_");
-            b.append(p.getID());
-            b.append(' ');
-            b.append(s.getPIDString(p.getID()) + ": ");
+            b.append('\n');
+            b.append(name);
+            b.append(", Name: ");
+            b.append(id);
+            b.append(" (");
+            b.append(s.getPIDString(id));
+            b.append("), Type: ");
+            b.append(type);
+            b.append(", Value: ");
             if (value instanceof byte[])
             {
                 byte[] b2 = (byte[]) value;

Modified: jakarta/poi/trunk/src/documentation/content/xdocs/hpsf/how-to.xml
URL: http://svn.apache.org/viewcvs/jakarta/poi/trunk/src/documentation/content/xdocs/hpsf/how-to.xml?rev=382887&r1=382886&r2=382887&view=diff
==============================================================================
--- jakarta/poi/trunk/src/documentation/content/xdocs/hpsf/how-to.xml (original)
+++ jakarta/poi/trunk/src/documentation/content/xdocs/hpsf/how-to.xml Fri Mar  3 08:57:55 2006
@@ -19,8 +19,8 @@
 
    <ol>
     <li>
-     The <link href="#sec1">first section</link> explains how to read
-      the most important standard properties of a Microsoft Office
+     The <link href="#sec1">first section</link> explains how to <strong>read
+      the most important standard properties</strong> of a Microsoft Office
       document. Standard properties are things like title, author, creation
       date etc. It is quite likely that you will find here what you need and
       don't have to read the other sections.
@@ -28,58 +28,78 @@
 
     <li>
      The <link href="#sec2">second section</link> goes a small step
-      further and focusses on reading additional standard properties. It also
-      talks about exceptions that may be thrown when dealing with HPSF and
-      shows how you can read properties of embedded objects.
+     further and focusses on <strong>reading additional standard
+      properties</strong>. It also talks about <strong>exceptions</strong> that
+     may be thrown when dealing with HPSF and shows how you can <strong>read
+      properties of embedded objects</strong>.
      </li>
 
     <li>
-     The <link href="#sec3">third section</link> tells how to read
-     non-standard properties. Non-standard properties are application-specific
-     triples consisting of an ID, a type, and a value.
+     The <link href="#sec3">third section</link> explains how to <strong>write
+      standard properties</strong>. HPSF provides some high-level classes and
+     methods which make writing of standard properties easy. They are based on
+     the low-level writing functions explained in the <link href="#sec3">fifth
+      section</link>.
     </li>
 
     <li>
-     The <link href="#sec4">fourth section</link> tells you how to write
-     property set streams. At this time HPSF provides low-level methods only
-     for writing properties. Therefore you have to understand the <link
-      href="#sec3">third section</link> before you should think about writing
-     properties. Check the Javadoc API documentation to find out about the
-     details!  <strong>Please note:</strong> HPSF's writing functionality is
-     <strong>not</strong> present in POI releases up to and including 2.5. In
-     order to write properties you have to download a later POI release (when
-     available) or retrieve the POI development version from the Subversion
-     repository.
+     The <link href="#sec4">fourth section</link> tells how to <strong>read
+     non-standard properties</strong>. Non-standard properties are
+     application-specific triples consisting of an ID, a type, and a value.
+    </li>
+
+    <li>
+     The <link href="#sec5">fifth section</link> tells you how to <strong>write
+     property set streams</strong> using HPSF's low-level methods. You have to
+     understand the <link href="#sec3">fourth section</link> before you should
+     think about low-level writing properties. Check the Javadoc API
+     documentation to find out about the details!
     </li>
    </ol>
 
+   <note><strong>Please note:</strong> HPSF's writing functionality is
+    <strong>not</strong> present in POI releases up to and including 2.5. In
+    order to write properties you have to download a later POI release (when
+    available) or retrieve the POI development version from the <link
+     href="http://jakarta.apache.org/site/cvsindex.html">Subversion
+     repository</link>.</note>
+
 
 
    <anchor id="sec1"/>
    <section><title>Reading Standard Properties</title>
 
-    <note>This section explains how to read
-      the most important standard properties of a Microsoft Office
-      document. Standard properties are things like title, author, creation
-      date etc. Chances are that you will find here what you need and
-      don't have to read the other sections.</note>
-
-    <p>The first thing you should understand is that properties are stored in
-     separate documents inside the POI filesystem. (If you don't know what a
-     POI filesystem is, read the <link href="../poifs/index.html">POIFS
-      documentation</link>.)  A document in a POI filesystem is also called a
-     <strong>stream</strong>.</p>
-
-    <p>The following example shows how to read a POI filesystem's
-     "title" property. Reading other properties is similar. Consider the API
-     documentation of <code>org.apache.poi.hpsf.SummaryInformation</code> to
-     learn which methods are available!</p>
+    <note>This section explains how to read the most important standard
+     properties of a Microsoft Office document. Standard properties are things
+     like title, author, creation date etc. This section introduces the
+     <strong>summary information stream</strong> which is used to keep these
+     properties. Chances are that you will find here what you need and don't
+     have to read the other sections.</note>
+
+    <p>The first thing you should understand is that a Microsoft Office file is
+     not one large bunch of bytes but has an internal filesystem structure with
+     files and directories. You can access these files and directories using
+     the <link href="../poifs/index.html">POI filesystem (POIFS)</link>
+     provides. A file or document in a POI filesystem is also called a
+     <strong>stream</strong> - The properties of, say, an Excel document are
+     stored apart of the actual spreadsheet data in separate streams. The good
+     new is that this separation makes the properties independent of the
+     concrete Microsoft Office file. In the following text we will always say
+     "POI filesystem" instead of "Microsoft Office file" because a POI
+     filesystem is not necessarily created by or for a Microsoft Office
+     application, because it is shorter, and because we want to avoid the name
+     of That Redmond Company.</p>
+
+    <p>The following example shows how to read the "title" property. Reading
+     other properties is similar. Consider the API documentation of the class
+     <code>org.apache.poi.hpsf.SummaryInformation</code> to learn which methods
+     are available.</p>
 
     <p>The standard properties this section focusses on can be found in a
      document called <em>\005SummaryInformation</em> located in the root of the
      POI filesystem. The notation <em>\005</em> in the document's name means
-     the character with the decimal value of 5. In order to read the title, an
-     application has to perform the following steps:</p>
+     the character with a decimal value of 5. In order to read the "title"
+     property, an application has to perform the following steps:</p>
 
     <ol>
      <li>
@@ -103,7 +123,7 @@
        POI filesystem</title>
 
      <p>An application that wants to open a document in a POI filesystem
-      (POIFS) proceeds as shown by the following code fragment. (The full
+      (POIFS) proceeds as shown by the following code fragment. The full
       source code of the sample application is available in the
       <em>examples</em> section of the POI source tree as
       <em>ReadTitle.java</em>.</p>
@@ -144,14 +164,15 @@
      <p>This method call registers a
       <code>org.apache.poi.poifs.eventfilesystem.POIFSReaderListener</code>
       with the <code>POIFSReader</code>. The <code>POIFSReaderListener</code>
-      interface specifies the method <code>processPOIFSReaderEvent</code>
+      interface specifies the method <code>processPOIFSReaderEvent()</code>
       which processes a document. The class
       <code>MyPOIFSReaderListener</code> implements the
       <code>POIFSReaderListener</code> and thus the
-      <code>processPOIFSReaderEvent</code> method. The eventing POI filesystem
-      calls this method when it finds the <em>\005SummaryInformation</em>
-      document. In the sample application <code>MyPOIFSReaderListener</code> is
-      a static class in the <em>ReadTitle.java</em> source file.</p>
+      <code>processPOIFSReaderEvent()</code> method. The eventing POI
+      filesystem calls this method when it finds the
+      <em>\005SummaryInformation</em> document. In the sample application
+      <code>MyPOIFSReaderListener</code> is a static class in the
+      <em>ReadTitle.java</em> source file.</p>
 
      <p>Now everything is prepared and reading the POI filesystem can
       start:</p>
@@ -209,7 +230,7 @@
       convenience class with methods like <code>getTitle()</code>,
       <code>getAuthor()</code> etc.</p>
 
-     <p>The <code>PropertySetFactory.create</code> method may throw all sorts
+     <p>The <code>PropertySetFactory.create()</code> method may throw all sorts
       of exceptions. We'll deal with them in the next sections. For now we just
       catch all exceptions and throw a <code>RuntimeException</code>
       containing the message text of the origin exception.</p>
@@ -224,10 +245,10 @@
 else
     System.out.println("Document has no title.");</source>
 
-     <p>Please note that a Microsoft Office document does not necessarily
-      contain the <em>\005SummaryInformation</em> stream. The documents created
-      by the Microsoft Office suite have one, as far as I know. However, an
-      Excel spreadsheet exported from StarOffice 5.2 won't have a
+     <p>Please note that a POI filesystem does not necessarily contain the
+      <em>\005SummaryInformation</em> stream. The documents created by the
+      Microsoft Office suite have one, as far as I know. However, an Excel
+      spreadsheet exported from StarOffice 5.2 won't have a
       <em>\005SummaryInformation</em> stream. In this case the applications
       won't throw an exception but simply does not call the
       <code>processPOIFSReaderEvent</code> method. You have been warned!</p>
@@ -238,14 +259,16 @@
    <section><title>Additional Standard Properties, Exceptions And Embedded
     Objects</title>
 
-    <note>This section focusses on reading additional standard properties. It
+    <note>This section focusses on reading additional standard properties which
+     are kept in the <strong>document summary information</strong> stream. It
      also talks about exceptions that may be thrown when dealing with HPSF and
      shows how you can read properties of embedded objects.</note>
 
     <p>A couple of <strong>additional standard properties</strong> are not
-     contained in the <em>\005SummaryInformation</em> stream explained above,
-     for example a document's category or the number of multimedia clips in a
-     PowerPoint presentation. Microsoft has invented an additional stream named
+     contained in the <em>\005SummaryInformation</em> stream explained
+     above. Examples for such properties are a document's category or the
+     number of multimedia clips in a PowerPoint presentation. Microsoft has
+     invented an additional stream named
      <em>\005DocumentSummaryInformation</em> to hold these properties. With two
      minor exceptions you can proceed exactly as described above to read the
      properties stored in <em>\005DocumentSummaryInformation</em>:</p>
@@ -259,13 +282,14 @@
     </ul>
 
     <p>And of course you cannot call <code>getTitle()</code> because
-     <code>DocumentSummaryInformation</code> has different query methods. See
-     the Javadoc API documentation for the details!</p>
+     <code>DocumentSummaryInformation</code> has different query methods,
+     e.g. <code>getCategory</code>. See the Javadoc API documentation for the
+     details.</p>
 
     <p>In the previous section the application simply caught all
      <strong>exceptions</strong> and was in no way interested in any
      details. However, a real application will likely want to know what went
-     wrong and act appropriately. Besides any IO exceptions there are three
+     wrong and act appropriately. Besides any I/O exceptions there are three
      HPSF resp. POI specific exceptions you should know about:</p>
 
     <dl>
@@ -279,9 +303,9 @@
        being a property set stream at all. An application should be prepared to
        deal with this case even if it opens streams named
        <em>\005SummaryInformation</em> or
-       <em>\005DocumentSummaryInformation</em> only. These are just names. A
-       stream's name by itself does not ensure that the stream contains the
-       expected contents and that this contents is correct.
+       <em>\005DocumentSummaryInformation</em>. These are just names. A
+      stream's name by itself does not ensure that the stream contains the
+      expected contents and that this contents is correct.
      </dd>
 
      <dt><code>UnexpectedPropertySetTypeException</code></dt>
@@ -301,7 +325,7 @@
     </dl>
 
     <p>Many Microsoft Office documents contain <strong>embedded
-      objects</strong>, for example an Excel sheet on a page in a Word
+      objects</strong>, for example an Excel sheet within a Word
      document. Embedded objects may have property sets of their own. An
      application can open these property set streams as described above. The
      only difference is that they are not located in the POI filesystem's root
@@ -313,7 +337,252 @@
      properties.</p>
    </section>
 
+
+
    <anchor id="sec3"/>
+   <section><title>Writing Standard Properties</title>
+
+    <note>This section explains how to <strong>write standard
+      properties</strong>. HPSF provides some high-level classes and methods
+     which make writing of standard properties easy. They are based on the
+     low-level writing functions explained in <link href="#sec4">another 
+      section</link>.</note>
+
+    <p>As explained above, standard properties are located in the summary
+     information and document summary information streams of typical POI
+     filesystems. You have already learned about the classes
+     <code>SummaryInformation</code> and
+     <code>DocumentSummaryInformation</code> and their <code>get...()</code>
+     methods for reading standard properties. These classes also provide
+     <code>set...()</code> methods for writing properties.</p>
+
+    <p>After setting properties in <code>SummaryInformation</code> or
+     <code>DocumentSummaryInformation</code> you have to write them to a disk
+     file. The following sample program shows how you can</p>
+
+    <ol>
+     <li>read a disk file into a POI filesystem,</li>
+     <li>read the document summary information from the POI filesystem,</li>
+     <li>set a property to a new value,</li>
+     <li>write the modified document summary information back to the POI
+      filesystem, and</li>
+     <li>write the POI filesystem to a disk file.</li>
+    </ol>
+
+    <p>The complete source code of this program is available as
+     <em>ModifyDocumentSummaryInformation.java</em> in the <em>examples</em>
+     section of the POI source tree.</p>
+
+    <note>Dealing with the summary information stream is analogous to handling
+     the document summary information and therefore does not need to be
+     explained here in detailed. See the HPSF API documentation to learn about
+     the <code>set...()</code> methods of the class
+     <code>SummaryInformation</code>.</note>
+
+    <p>The first step is to read the POI filesystem into memory:</p>
+
+    <source>InputStream is = new FileInputStream(poiFilesystem);
+POIFSFileSystem poifs = new POIFSFileSystem(is);
+is.close();</source>
+
+    <p>The code snippet above assumes that the variable
+     <code>poiFilesystem</code> holds the name of a disk file. It reads the
+     file from an input stream and creates a <code>POIFSFileSystem</code>
+     object in memory. After having read the file, the input stream should be
+     closed as shown.</p>
+
+    <p>In order to read the document summary information stream the application
+     must open the element <em>\005DocumentSummaryInformation</em> in the POI
+     filesystem's root directory. However, the POI filesystem does not
+     necessarily contain a document summary information stream, and the
+     application should be able to deal with that situation. The following
+     code does so by creating a new <code>DocumentSummaryInformation</code> if
+     there is none in the POI filesystem:</p>
+
+    <source>DirectoryEntry dir = poifs.getRoot();
+DocumentSummaryInformation dsi;
+try
+{
+    DocumentEntry dsiEntry = (DocumentEntry)
+        dir.getEntry(DocumentSummaryInformation.DEFAULT_STREAM_NAME);
+    DocumentInputStream dis = new DocumentInputStream(dsiEntry);
+    PropertySet ps = new PropertySet(dis);
+    dis.close();
+    dsi = new DocumentSummaryInformation(ps);
+}
+catch (FileNotFoundException ex)
+{
+    /* There is no document summary information. We have to create a
+     * new one. */
+    dsi = PropertySetFactory.newDocumentSummaryInformation();
+}
+    </source>
+
+    <p>In the source code above the statement</p>
+
+    <source>DirectoryEntry dir = poifs.getRoot();</source>
+
+    <p>gets hold of the POI filesystem's root directory as a
+     <code>DirectoryEntry</code>. The <code>getEntry()</code> method of this
+     class is used to access a file or directory entry in a directory. However,
+     if the file to be opened does not exist, a
+     <code>FileNotFoundException</code> will be thrown. Therefore opening the
+     document summary information entry should be done in a <code>try</code>
+     block:</p>
+
+    <source>    DocumentEntry dsiEntry = (DocumentEntry)
+        dir.getEntry(DocumentSummaryInformation.DEFAULT_STREAM_NAME);</source>
+
+    <p><code>DocumentSummaryInformation.DEFAULT_STREAM_NAME</code> represents
+     the string "\005DocumentSummaryInformation", i.e. the standard name of a
+     document summary information stream. If this stream exists, the
+     <code>getEntry()</code> method returns a <code>DocumentEntry</code>. To
+     read the <code>DocumentEntry</code>'s contents, create a
+     <code>DocumentInputStream</code>:</p>
+
+    <source>     DocumentInputStream dis = new DocumentInputStream(dsiEntry);</source>
+
+    <p>Up to this point we have used POI's <link
+      href="../poifs/index.html">POIFS component</link>. Now HPSF enters the
+     stage. A property set is created from the input stream's data:</p>
+
+    <source>    PropertySet ps = new PropertySet(dis);
+    dis.close();
+    dsi = new DocumentSummaryInformation(ps);    </source>
+
+    <p>If the data really constitutes a property set, a
+     <code>PropertySet</code> object is created. Otherwise a
+     <code>NoPropertySetStreamException</code> is thrown. After having read the
+     data from the input stream the latter should be closed.</p>
+
+    <p>Since we know - or at least hope - that the stream named
+     "\005DocumentSummaryInformation" is not just any property set but really
+     contains the document summary information, we try to create a new
+     <code>DocumentSummaryInformation</code> from the property set. If the
+     stream is not document summary information stream the sample application
+     fails with a <code>UnexpectedPropertySetTypeException</code>.</p>
+
+    <p>If the POI document does not contain a document summary information
+     stream, we can create a new one in the <code>catch</code> clause. The
+     <code>PropertySetFactory</code>'s method
+     <code>newDocumentSummaryInformation()</code> establishes a new and empty
+     <code>DocumentSummaryInformation</code> instance:</p>
+
+    <source>    dsi = PropertySetFactory.newDocumentSummaryInformation();</source>
+
+    <p>Whether we read the document summary information from the POI filesystem
+     or created it from scratch, in either case we now have a
+     <code>DocumentSummaryInformation</code> instance we can write to. Writing
+     is quite simple, as the following line of code shows:</p>
+
+    <source>dsi.setCategory("POI example");</source>
+
+    <p>This statement sets the "category" property to "POI example". Any
+     former "category" value will be lost. If there hasn't been a "category"
+     property yet, a new one will be created.</p>
+
+    <p><code>DocumentSummaryInformation</code> of course has methods to set the
+     other standard properties, too - look into the API documentation to see
+     all of them.</p>
+
+    <p>Once all properties are set as needed, they should be stored into the
+     file on disk. The first step is to write the
+     <code>DocumentSummaryInformation</code> into the POI filesystem:</p>
+
+    <source>dsi.write(dir, DocumentSummaryInformation.DEFAULT_STREAM_NAME);</source>
+
+    <p>The <code>DocumentSummaryInformation</code>'s <code>write()</code>
+     method takes two parameters: The first is the <code>DirectoryEntry</code>
+     in the POI filesystem, the second is the name of the stream to create in
+     the directory. If this stream already exists, it will be overwritten.</p>
+
+    <note>If you not only modified the document summary information but also
+     the summary information you have to write both of them to the POI
+     filesystem.</note>
+
+    <p>Still the POI filesystem is a data structure in memory only and must be
+     written to a disk file to make it permanent. The following lines write
+     back the POI filesystem to the file it was read from before. Please note
+     that in production-quality code you should never write directly to the
+     origin file, because in case of an error everything would be lost. Here it
+     is done this way to keep the example short.</p>
+
+    <source>OutputStream out = new FileOutputStream(poiFilesystem);
+poifs.writeFilesystem(out);
+out.close();</source>
+
+    <section><title>User-Defined Properties</title>
+
+     <p>If you compare the source code excerpts above with the file containing
+      the full source code, you will notice that I left out some following
+      lines of code. The are dealing with the special topic of custom
+      properties.</p>
+
+     <source>DocumentSummaryInformation dsi = ...
+...
+CustomProperties customProperties = dsi.getCustomProperties();
+if (customProperties == null)
+    customProperties = new CustomProperties();
+
+/* Insert some custom properties into the container. */
+customProperties.put("Key 1", "Value 1");
+customProperties.put("Schlüssel 2", "Wert 2");
+customProperties.put("Sample Number", new Integer(12345));
+customProperties.put("Sample Boolean", new Boolean(true));
+customProperties.put("Sample Date", new Date());
+
+/* Read a custom property. */
+Object value = customProperties.get("Sample Number");
+
+/* Write the custom properties back to the document summary
+ * information. */
+dsi.setCustomProperties(customProperties);</source>
+
+     <p>Custom properties are properties the user can define himself. Using for
+      example Microsoft Word he can define these extra properties and give
+      each of them a <strong>name</strong>, a <strong>type</strong> and a
+      <strong>value</strong>. The custom properties are stored in the document
+      information summary along with the standard properties.</p>
+
+     <p>The source code example shows how to retrieve the custom properties
+      as a whole from a <code>DocumentSummaryInformation</code> instance using
+      the <code>getCustomProperties()</code> method. The result is a
+      <code>CustomProperties</code> instance or <code>null</code> if no
+      user-defined properties exist.</p>
+
+     <p>Since <code>CustomProperties</code> implements the <code>Map</code>
+      interface you can read and write properties with the usual
+      <code>Map</code> methods. However, <code>CustomProperties</code> poses
+      some restrictions on the types of keys and values.</p>
+
+     <ul>
+      <li>The <strong>key</strong> is a string.</li>
+      <li>The <strong>value</strong> is one of <code>String</code>,
+       <code>Boolean</code>, <code>Long</code>, <code>Integer</code>,
+       <code>Short</code>, or <code>java.util.Date</code>.</li>
+     </ul>
+
+     <p>The <code>CustomProperties</code> class has been designed for easy
+      access using just keys and values. The underlying Microsoft-specific
+      custom properties data structure is more complicated. However, it does
+      not provide noteworthy additional benefits. It is possible to have
+      multiple properties with the same name or properties without a
+      name at all. When reading custom properties from a document summary
+      information stream, the <code>CustomProperties</code> class ignores
+      properties without a name and keeps only the "last" (whatever that means)
+      of those properties having the same name. You can find out whether a
+      <code>CustomProperties</code> instance dropped any properties with the
+      <code>isPure()</code> method.</p>
+
+     <p>You can read and write the full spectrum of custom properties with
+      HPSF's low-level methods. They are explained in the <link
+       href="#sec4">next section</link>.</p>
+    </section>
+   </section>
+
+
+
+   <anchor id="sec4"/>
    <section><title>Reading Non-Standard Properties</title>
 
     <note>This section tells how to read non-standard properties. Non-standard
@@ -863,7 +1132,8 @@
 
      <p>There are some exceptions to the rule saying that a character
       encoding's name is derived from the codepage number by prepending the
-      string "cp" to it:</p>
+      string "cp" to it. In these cases the codepage number is mapped to a
+      well-known character encoding name. Here are a few examples:</p>
 
      <dl>
       <dt>Codepage 932</dt>
@@ -874,26 +1144,32 @@
       <dd>is mapped to the character encoding "UTF-8".</dd>
      </dl>
 
-     <p>Probably there will be a need to add more mappings between codepage
-      numbers and character encoding names. They should be added to the method
-      <code>codepageToEncoding</code> in the class
-      <code>org.apache.poi.hpsf.VariantSupport</code>. The HPSF author will
-      appreciate any advices for mappings to be added.</p>
+     <p>More of these mappings between codepage and character encoding name are
+      hard-coded in the classes <code>org.apache.poi.hpsf.Constants</code> and
+      <code>org.apache.poi.hpsf.VariantSupport</code>. Probably there will be a
+      need to add more mappings. The HPSF author will appreciate any hints.</p>
     </section>
    </section>
 
-   <anchor id="sec4"/>
+   <anchor id="sec5"/>
    <section><title>Writing Properties</title>
 
     <note>This section describes how to write properties.</note>
 
     <section><title>Overview of Writing Properties</title>
-     <p>Writing properties is possible at a low level only at the moment. You
-      have to deal with things like property IDs and variant types to write
-      properties. There are no convenience classes or convenience methods for
-      dealing with summary information and document summary information streams
-      yet. Therefore you should have read <link href="#sec3">section 3</link>
-      to understand what follows in this section.</p>
+     <p>Writing properties is possible at a high level and at a low level:</p>
+
+     <ul>
+
+      <li>Most users will want to create or change entries in the summary
+       information or document summary information streams. </li>
+
+      <li>On the low level, there are no convenience classes or methods. You
+       have to deal with things like property IDs and variant types to write
+       properties. Therefore you should have read <link href="#sec3">section
+	3</link> to understand the description of the low-level writing
+       functions.</li>
+     </ul>
 
      <p>HPSF's writing capabilities come with the classes
       <code>MutablePropertySet</code>, <code>MutableSection</code>,
@@ -903,7 +1179,10 @@
       "write" methods, following the <link
        href="http://en.wikipedia.org/wiki/Decorator_pattern">Decorator
        pattern</link>.</p>
+    </section>
+
 
+    <section><title>Low-Level Writing: An Overview</title>
      <p>When you are going to write a property set stream your application has
       to perform the following steps:</p>
 

Modified: jakarta/poi/trunk/src/documentation/content/xdocs/hpsf/internals.xml
URL: http://svn.apache.org/viewcvs/jakarta/poi/trunk/src/documentation/content/xdocs/hpsf/internals.xml?rev=382887&r1=382886&r2=382887&view=diff
==============================================================================
--- jakarta/poi/trunk/src/documentation/content/xdocs/hpsf/internals.xml (original)
+++ jakarta/poi/trunk/src/documentation/content/xdocs/hpsf/internals.xml Fri Mar  3 08:57:55 2006
@@ -1006,41 +1006,40 @@
      helpful. If you have any amendments or corrections, please let us know!
      Thank you!</p>
 
-   <ol>
+    <ol>
 
-    <li>In
+     <li>In
       <link href="http://www.kyler.com/pubs/ddj9894.html"><em>Understanding OLE
-	 documents</em></link>, Ken Kyler gives an introduction to OLE2
-       documents
-      and especially to property sets. He names the property names, types, and
-      IDs of the Summary Information and Document Summary Information
-      stream.</li>
-
-    <li>The
-      <link href="http://www.dwam.net/docs/oleref/"><em>ActiveX Programmer's
-	Reference</em></link> at
-      <link href="http://www.dwam.net/docs/oleref/">http://www.dwam.net/docs/oleref/</link>
+	documents</em></link>, Ken Kyler gives an introduction to OLE2
+      documents and especially to property sets. He names the property names,
+      types, and IDs of the Summary Information and Document Summary
+      Information stream.</li>
+
+     <li>The <link href="http://www.dwam.net/docs/oleref/"><em>ActiveX
+	Programmer's Reference</em></link> at <link
+       href="http://www.dwam.net/docs/oleref/">http://www.dwam.net/docs/oleref/</link>
       seems a little outdated, but that's what I have found.</li>
 
-    <li>An overview of the <code>VT_</code> types is in
+     <li>An overview of the <code>VT_</code> types is in
       <link href="http://www.marin.clara.net/COM/variant_type_definitions.htm"><em>Variant
 	Type Definitions</em></link>.</li>
 
-    <li>What is a <code>FILETIME</code>? The answer can be found
-     under <link
-      href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/sysinfo/base/filetime_str.asp"></link>, <link href="http://www.vbapi.com/ref/f/filetime.html">http://www.vbapi.com/ref/f/filetime.html</link> or
+     <li>What is a <code>FILETIME</code>? The answer can be found
+      under <link
+       href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/sysinfo/base/filetime_str.asp"></link>, <link href="http://www.vbapi.com/ref/f/filetime.html">http://www.vbapi.com/ref/f/filetime.html</link> or
       <link href="http://www.cs.rpi.edu/courses/fall01/os/FILETIME.html">http://www.cs.rpi.edu/courses/fall01/os/FILETIME.html</link>.
       In short: <em>The FILETIME structure holds a date and time associated
-      with a file. The structure identifies a 64-bit integer specifying the
-      number of 100-nanosecond intervals which have passed since January 1,
-      1601. This 64-bit value is split into the two dwords stored in the
-      structure.</em></li>
-
-    <li>Information about the code page property in the
-     DocumentSummaryInformation stream is available at <link
-      href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/stg/stg/property_id_1.asp">http://msdn.microsoft.com/library/default.asp?url=/library/en-us/stg/stg/property_id_1.asp</link>.</li>
+       with a file. The structure identifies a 64-bit integer specifying the
+       number of 100-nanosecond intervals which have passed since January 1,
+       1601. This 64-bit value is split into the two dwords stored in the
+       structure.</em></li>
+
+     <li>Microsoft provides some public information in the <link
+       href="http://msdn.microsoft.com/library/default.asp">MSDN
+       Library</link>. Use the search function to try to find what you are
+      looking for, e.g. "codepage" or "document summary information" etc.</li>
 
-    <li>This documentation origins from the <link href="http://www.rainer-klute.de/~klute/Software/poibrowser/doc/HPSF-Description.html">HPSF description</link> available at <link href="http://www.rainer-klute.de/~klute/Software/poibrowser/doc/HPSF-Description.html">http://www.rainer-klute.de/~klute/Software/poibrowser/doc/HPSF-Description.html</link>.</li>
+     <li>This documentation origins from the <link href="http://www.rainer-klute.de/~klute/Software/poibrowser/doc/HPSF-Description.html">HPSF description</link> available at <link href="http://www.rainer-klute.de/~klute/Software/poibrowser/doc/HPSF-Description.html">http://www.rainer-klute.de/~klute/Software/poibrowser/doc/HPSF-Description.html</link>.</li>
     </ol>
    </section>
   </section>

Added: jakarta/poi/trunk/src/examples/src/org/apache/poi/hpsf/examples/ModifyDocumentSummaryInformation.java
URL: http://svn.apache.org/viewcvs/jakarta/poi/trunk/src/examples/src/org/apache/poi/hpsf/examples/ModifyDocumentSummaryInformation.java?rev=382887&view=auto
==============================================================================
--- jakarta/poi/trunk/src/examples/src/org/apache/poi/hpsf/examples/ModifyDocumentSummaryInformation.java (added)
+++ jakarta/poi/trunk/src/examples/src/org/apache/poi/hpsf/examples/ModifyDocumentSummaryInformation.java Fri Mar  3 08:57:55 2006
@@ -0,0 +1,200 @@
+/* ====================================================================
+   Copyright 2002-2006   Apache Software Foundation
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+==================================================================== */
+        
+
+package org.apache.poi.hpsf.examples;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Date;
+
+import org.apache.poi.hpsf.CustomProperties;
+import org.apache.poi.hpsf.DocumentSummaryInformation;
+import org.apache.poi.hpsf.MarkUnsupportedException;
+import org.apache.poi.hpsf.NoPropertySetStreamException;
+import org.apache.poi.hpsf.PropertySet;
+import org.apache.poi.hpsf.PropertySetFactory;
+import org.apache.poi.hpsf.SummaryInformation;
+import org.apache.poi.hpsf.UnexpectedPropertySetTypeException;
+import org.apache.poi.hpsf.WritingNotSupportedException;
+import org.apache.poi.poifs.filesystem.DirectoryEntry;
+import org.apache.poi.poifs.filesystem.DocumentEntry;
+import org.apache.poi.poifs.filesystem.DocumentInputStream;
+import org.apache.poi.poifs.filesystem.POIFSFileSystem;
+
+
+
+/**
+ * <p>This is a sample application showing how to easily modify properties in
+ * the summary information and in the document summary information. The
+ * application reads the name of a POI filesystem from the command line and
+ * performs the following actions:</p>
+ * 
+ * <ul>
+ * 
+ * <li><p>Open the POI filesystem.</p></li>
+ * 
+ * <li><p>Read the summary information.</p></li>
+ * 
+ * <li><p>Read and print the "author" property.</p></li>
+ * 
+ * <li><p>Change the author to "Rainer Klute".</p></li>
+ * 
+ * <li><p>Read the document summary information.</p></li>
+ * 
+ * <li><p>Read and print the "category" property.</p></li>
+ * 
+ * <li><p>Change the category to "POI example".</p></li>
+ * 
+ * <li><p>Read the custom properties (if available).</p></li>
+ * 
+ * <li><p>Insert a new custom property.</p></li>
+ * 
+ * <li><p>Write the custom properties back to the document summary
+ * information.</p></li>
+ * 
+ * <li><p>Write the summary information to the POI filesystem.</p></li>
+ * 
+ * <li><p>Write the document summary information to the POI filesystem.</p></li>
+ * 
+ * <li><p>Write the POI filesystem back to the original file.</p></li>
+ * 
+ * </ol>
+ * 
+ * @author Rainer Klute <a
+ *         href="mailto:klute@rainer-klute.de">klute@rainer-klute.de</a>
+ * @since 2006-02-09
+ * @version $Id: TestWrite.java 353637 2005-04-13 16:33:22Z klute $
+ */
+public class ModifyDocumentSummaryInformation
+{
+
+    /**
+     * <p>Main method - see class description.</p>
+     *
+     * @param args The command-line parameters.
+     * @throws IOException 
+     * @throws MarkUnsupportedException 
+     * @throws NoPropertySetStreamException 
+     * @throws UnexpectedPropertySetTypeException 
+     * @throws WritingNotSupportedException 
+     */
+    public static void main(final String[] args) throws IOException,
+            NoPropertySetStreamException, MarkUnsupportedException,
+            UnexpectedPropertySetTypeException, WritingNotSupportedException
+    {
+        /* Read the name of the POI filesystem to modify from the command line.
+         * For brevity to boundary check is performed on the command-line
+         * arguments. */
+        File poiFilesystem = new File(args[0]);
+
+        /* Open the POI filesystem. */
+        InputStream is = new FileInputStream(poiFilesystem);
+        POIFSFileSystem poifs = new POIFSFileSystem(is);
+        is.close();
+
+        /* Read the summary information. */
+        DirectoryEntry dir = poifs.getRoot();
+        SummaryInformation si;
+        try
+        {
+            DocumentEntry siEntry = (DocumentEntry)
+                dir.getEntry(SummaryInformation.DEFAULT_STREAM_NAME);
+            DocumentInputStream dis = new DocumentInputStream(siEntry);
+            PropertySet ps = new PropertySet(dis);
+            dis.close();
+            si = new SummaryInformation(ps);
+        }
+        catch (FileNotFoundException ex)
+        {
+            /* There is no summary information yet. We have to create a new
+             * one. */
+            si = PropertySetFactory.newSummaryInformation();
+        }
+
+        /* Change the author to "Rainer Klute". Any former author value will
+         * be lost. If there has been no author yet, it will be created. */
+        si.setAuthor("Rainer Klute");
+        System.out.println("Author changed to " + si.getAuthor() + ".");
+
+
+        /* Handling the document summary information is analogous to handling
+         * the summary information. An additional feature, however, are the
+         * custom properties. */
+
+        /* Read the document summary information. */
+        DocumentSummaryInformation dsi;
+        try
+        {
+            DocumentEntry dsiEntry = (DocumentEntry)
+                dir.getEntry(DocumentSummaryInformation.DEFAULT_STREAM_NAME);
+            DocumentInputStream dis = new DocumentInputStream(dsiEntry);
+            PropertySet ps = new PropertySet(dis);
+            dis.close();
+            dsi = new DocumentSummaryInformation(ps);
+        }
+        catch (FileNotFoundException ex)
+        {
+            /* There is no document summary information yet. We have to create a
+             * new one. */
+            dsi = PropertySetFactory.newDocumentSummaryInformation();
+        }
+
+        /* Change the category to "POI example". Any former category value will
+         * be lost. If there has been no category yet, it will be created. */
+        dsi.setCategory("POI example");
+        System.out.println("Category changed to " + dsi.getCategory() + ".");
+
+        /* Read the custom properties. If there are no custom properties yet,
+         * the application has to create a new CustomProperties object. It will
+         * serve as a container for custom properties. */
+        CustomProperties customProperties = dsi.getCustomProperties();
+        if (customProperties == null)
+            customProperties = new CustomProperties();
+        
+        /* Insert some custom properties into the container. */
+        customProperties.put("Key 1", "Value 1");
+        customProperties.put("Schlüssel 2", "Wert 2");
+        customProperties.put("Sample Number", new Integer(12345));
+        customProperties.put("Sample Boolean", new Boolean(true));
+        customProperties.put("Sample Date", new Date());
+
+        /* Read a custom property. */
+        Object value = customProperties.get("Sample Number");
+
+        /* Write the custom properties back to the document summary
+         * information. */
+        dsi.setCustomProperties(customProperties);
+
+        /* Write the summary information and the document summary information
+         * to the POI filesystem. */
+        si.write(dir, SummaryInformation.DEFAULT_STREAM_NAME);
+        dsi.write(dir, DocumentSummaryInformation.DEFAULT_STREAM_NAME);
+
+        /* Write the POI filesystem back to the original file. Please note that
+         * in production code you should never write directly to the origin
+         * file! In case of a writing error everything would be lost. */
+        OutputStream out = new FileOutputStream(poiFilesystem);
+        poifs.writeFilesystem(out);
+        out.close();
+        }
+
+}

Modified: jakarta/poi/trunk/src/java/org/apache/poi/hpsf/ClassID.java
URL: http://svn.apache.org/viewcvs/jakarta/poi/trunk/src/java/org/apache/poi/hpsf/ClassID.java?rev=382887&r1=382886&r2=382887&view=diff
==============================================================================
--- jakarta/poi/trunk/src/java/org/apache/poi/hpsf/ClassID.java (original)
+++ jakarta/poi/trunk/src/java/org/apache/poi/hpsf/ClassID.java Fri Mar  3 08:57:55 2006
@@ -1,4 +1,3 @@
-
 /* ====================================================================
    Copyright 2002-2004   Apache Software Foundation
 

Modified: jakarta/poi/trunk/src/java/org/apache/poi/hpsf/Constants.java
URL: http://svn.apache.org/viewcvs/jakarta/poi/trunk/src/java/org/apache/poi/hpsf/Constants.java?rev=382887&r1=382886&r2=382887&view=diff
==============================================================================
--- jakarta/poi/trunk/src/java/org/apache/poi/hpsf/Constants.java (original)
+++ jakarta/poi/trunk/src/java/org/apache/poi/hpsf/Constants.java Fri Mar  3 08:57:55 2006
@@ -1,3 +1,19 @@
+/* ====================================================================
+   Copyright 2002-2006   Apache Software Foundation
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+==================================================================== */
+
 package org.apache.poi.hpsf;
 
 /**

Added: jakarta/poi/trunk/src/java/org/apache/poi/hpsf/CustomProperties.java
URL: http://svn.apache.org/viewcvs/jakarta/poi/trunk/src/java/org/apache/poi/hpsf/CustomProperties.java?rev=382887&view=auto
==============================================================================
--- jakarta/poi/trunk/src/java/org/apache/poi/hpsf/CustomProperties.java (added)
+++ jakarta/poi/trunk/src/java/org/apache/poi/hpsf/CustomProperties.java Fri Mar  3 08:57:55 2006
@@ -0,0 +1,367 @@
+/* ====================================================================
+   Copyright 2002-2006   Apache Software Foundation
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+==================================================================== */
+
+package org.apache.poi.hpsf;
+
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.apache.poi.hpsf.wellknown.PropertyIDMap;
+
+/**
+ * <p>Maintains the instances of {@link CustomProperty} that belong to a
+ * {@link DocumentSummaryInformation}. The class maintains the names of the
+ * custom properties in a dictionary. It implements the {@link Map} interface
+ * and by this provides a simplified view on custom properties: A property's
+ * name is the key that maps to a typed value. This implementation hides
+ * property IDs from the developer and regards the property names as keys to
+ * typed values.</p>
+ * 
+ * <p>While this class provides a simple API to custom properties, it ignores
+ * the fact that not names, but IDs are the real keys to properties. Under the
+ * hood this class maintains a 1:1 relationship between IDs and names. Therefore
+ * you should not use this class to process property sets with several IDs
+ * mapping to the same name or with properties without a name: the result will
+ * contain only a subset of the original properties. If you really need to deal
+ * such property sets, use HPSF's low-level access methods.</p>
+ * 
+ * <p>An application can call the {@link #isPure} method to check whether a
+ * property set parsed by {@link CustomProperties} is still pure (i.e.
+ * unmodified) or whether one or more properties have been dropped.</p>
+ * 
+ * <p>This class is not thread-safe; concurrent access to instances of this
+ * class must be syncronized.</p>
+ * 
+ * @author Rainer Klute <a
+ *         href="mailto:klute@rainer-klute.de">&lt;klute@rainer-klute.de&gt;</a>
+ * @since 2006-02-09
+ * @version $Id$
+ */
+public class CustomProperties extends HashMap
+{
+
+    /**
+     * <p>Maps property IDs to property names.</p>
+     */
+    private Map dictionaryIDToName = new HashMap();
+
+    /**
+     * <p>Maps property names to property IDs.</p>
+     */
+    private Map dictionaryNameToID = new HashMap();
+    
+    /**
+     * <p>Tells whether this object is pure or not.</p>
+     */
+    private boolean isPure = true;
+
+
+
+    /**
+     * <p>Puts a {@link CustomProperty} into this map. It is assumed that the
+     * {@link CustomProperty} already has a valid ID. Otherwise use
+     * {@link #put(CustomProperty)}.</p>
+     */
+    public Object put(final Object name, final Object customProperty) throws ClassCastException
+    {
+        final CustomProperty cp = (CustomProperty) customProperty;
+        if (name == null)
+        {
+            /* Ignoring a property without a name. */
+            isPure = false;
+            return null;
+        }
+        if (!(name instanceof String))
+            throw new ClassCastException("The name of a custom property must " +
+                    "be a java.lang.String, but it is a " +
+                    name.getClass().getName());
+        if (!(name.equals(cp.getName())))
+            throw new IllegalArgumentException("Parameter \"name\" (" + name +
+                    ") and custom property's name (" + cp.getName() +
+                    ") do not match.");
+
+        /* Register name and ID in the dictionary. Mapping in both directions is possible. If there is already a  */
+        final Long idKey = new Long(cp.getID());
+        final Object oldID = dictionaryNameToID.get(name);
+        dictionaryIDToName.remove(oldID);
+        dictionaryNameToID.put(name, idKey);
+        dictionaryIDToName.put(idKey, name);
+
+        /* Put the custom property into this map. */
+        final Object oldCp = super.remove(oldID);
+        super.put(idKey, cp);
+        return oldCp;
+    }
+
+
+
+    /**
+     * <p>Puts a {@link CustomProperty} that has not yet a valid ID into this
+     * map. The method will allocate a suitable ID for the custom property:</p>
+     * 
+     * <ul>
+     * 
+     * <li><p>If there is already a property with the same name, take the ID
+     * of that property.</p></li>
+     * 
+     * <li><p>Otherwise find the highest ID and use its value plus one.</p></li>
+     * 
+     * </ul>
+     * 
+     * @param customProperty
+     * @return If the was already a property with the same name, the
+     * @throws ClassCastException
+     */
+    private Object put(final CustomProperty customProperty) throws ClassCastException
+    {
+        final String name = customProperty.getName();
+
+        /* Check whether a property with this name is in the map already. */
+        final Long oldId = (Long) dictionaryNameToID.get(name);
+        if (oldId != null)
+            customProperty.setID(oldId.longValue());
+        else
+        {
+            long max = 1;
+            for (final Iterator i = dictionaryIDToName.keySet().iterator(); i.hasNext();)
+            {
+                final long id = ((Long) i.next()).longValue();
+                if (id > max)
+                    max = id;
+            }
+            customProperty.setID(max + 1);
+        }
+        return this.put(name, customProperty);
+    }
+
+
+
+    /**
+     * <p>Removes a custom property.</p>
+     * @param name The name of the custom property to remove 
+     * @return The removed property or <code>null</code> if the specified property was not found.
+     *
+     * @see java.util.HashSet#remove(java.lang.Object)
+     */
+    public Object remove(final String name)
+    {
+        final Long id = (Long) dictionaryNameToID.get(name);
+        if (id == null)
+            return null;
+        dictionaryIDToName.remove(id);
+        dictionaryNameToID.remove(name);
+        return super.remove(id);
+    }
+
+    /**
+     * <p>Adds a named string property.</p>
+     * 
+     * @param name The property's name.
+     * @param value The property's value.
+     * @return the property that was stored under the specified name before, or
+     *         <code>null</code> if there was no such property before.
+     */
+    public Object put(final String name, final String value)
+    {
+        final MutableProperty p = new MutableProperty();
+        p.setID(-1);
+        p.setType(Variant.VT_LPWSTR);
+        p.setValue(value);
+        final CustomProperty cp = new CustomProperty(p, name);
+        return put(cp);
+    }
+
+    /**
+     * <p>Adds a named long property.</p>
+     *
+     * @param name The property's name.
+     * @param value The property's value.
+     * @return the property that was stored under the specified name before, or
+     *         <code>null</code> if there was no such property before.
+     */
+    public Object put(final String name, final Long value)
+    {
+        final MutableProperty p = new MutableProperty();
+        p.setID(-1);
+        p.setType(Variant.VT_I8);
+        p.setValue(value);
+        final CustomProperty cp = new CustomProperty(p, name);
+        return put(cp);
+    }
+
+    /**
+     * <p>Adds a named double property.</p>
+     *
+     * @param name The property's name.
+     * @param value The property's value.
+     * @return the property that was stored under the specified name before, or
+     *         <code>null</code> if there was no such property before.
+     */
+    public Object put(final String name, final Double value)
+    {
+        final MutableProperty p = new MutableProperty();
+        p.setID(-1);
+        p.setType(Variant.VT_R8);
+        p.setValue(value);
+        final CustomProperty cp = new CustomProperty(p, name);
+        return put(cp);
+    }
+
+    /**
+     * <p>Adds a named integer property.</p>
+     *
+     * @param name The property's name.
+     * @param value The property's value.
+     * @return the property that was stored under the specified name before, or
+     *         <code>null</code> if there was no such property before.
+     */
+    public Object put(final String name, final Integer value)
+    {
+        final MutableProperty p = new MutableProperty();
+        p.setID(-1);
+        p.setType(Variant.VT_I4);
+        p.setValue(value);
+        final CustomProperty cp = new CustomProperty(p, name);
+        return put(cp);
+    }
+
+    /**
+     * <p>Adds a named boolean property.</p>
+     *
+     * @param name The property's name.
+     * @param value The property's value.
+     * @return the property that was stored under the specified name before, or
+     *         <code>null</code> if there was no such property before.
+     */
+    public Object put(final String name, final Boolean value)
+    {
+        final MutableProperty p = new MutableProperty();
+        p.setID(-1);
+        p.setType(Variant.VT_BOOL);
+        p.setValue(value);
+        final CustomProperty cp = new CustomProperty(p, name);
+        return put(cp);
+    }
+
+    
+    /**
+     * <p>Gets a named value from the custom properties.</p>
+     * 
+     * @param name the name of the value to get
+     * @return the value or <code>null</code> if a value with the specified
+     *         name is not found in the custom properties.
+     */
+    public Object get(final String name)
+    {
+        final Long id = (Long) dictionaryNameToID.get(name);
+        final CustomProperty cp = (CustomProperty) super.get(id);
+        return cp != null ? cp.getValue() : null;
+    }
+
+
+
+    /**
+     * <p>Adds a named date property.</p>
+     *
+     * @param name The property's name.
+     * @param value The property's value.
+     * @return the property that was stored under the specified name before, or
+     *         <code>null</code> if there was no such property before.
+     */
+    public Object put(final String name, final Date value)
+    {
+        final MutableProperty p = new MutableProperty();
+        p.setID(-1);
+        p.setType(Variant.VT_FILETIME);
+        p.setValue(value);
+        final CustomProperty cp = new CustomProperty(p, name);
+        return put(cp);
+    }
+
+    /**
+     * <p>Sets the codepage.</p>
+     *
+     * @param codepage the codepage
+     */
+    public void setCodepage(final int codepage)
+    {
+        final MutableProperty p = new MutableProperty();
+        p.setID(PropertyIDMap.PID_CODEPAGE);
+        p.setType(Variant.VT_I2);
+        p.setValue(new Integer(codepage));
+        put(new CustomProperty(p));
+    }
+
+
+
+    /**
+     * <p>Gets the dictionary which contains IDs and names of the named custom
+     * properties.
+     * 
+     * @return the dictionary.
+     */
+    Map getDictionary()
+    {
+        return dictionaryIDToName;
+    }
+
+
+
+    /**
+     * <p>Gets the codepage.</p>
+     *
+     * @return the codepage or -1 if the codepage is undefined.
+     */
+    public int getCodepage()
+    {
+        int codepage = -1;
+        for (final Iterator i = this.values().iterator(); codepage == -1 && i.hasNext();)
+        {
+            final CustomProperty cp = (CustomProperty) i.next();
+            if (cp.getID() == PropertyIDMap.PID_CODEPAGE)
+                codepage = ((Integer) cp.getValue()).intValue();
+        }
+        return codepage;
+    }
+
+
+
+    /**
+     * <p>Tells whether this {@link CustomProperties} instance is pure or one or
+     * more properties of the underlying low-level property set has been
+     * dropped.</p>
+     * 
+     * @return <code>true</code> if the {@link CustomProperties} is pure, else
+     *         <code>false</code>.
+     */
+    public boolean isPure()
+    {
+        return isPure;
+    }
+
+    /**
+     * <p>Sets the purity of the custom property set.</p>
+     *
+     * @param isPure the purity
+     */
+    public void setPure(final boolean isPure)
+    {
+        this.isPure = isPure;
+    }
+
+}

Added: jakarta/poi/trunk/src/java/org/apache/poi/hpsf/CustomProperty.java
URL: http://svn.apache.org/viewcvs/jakarta/poi/trunk/src/java/org/apache/poi/hpsf/CustomProperty.java?rev=382887&view=auto
==============================================================================
--- jakarta/poi/trunk/src/java/org/apache/poi/hpsf/CustomProperty.java (added)
+++ jakarta/poi/trunk/src/java/org/apache/poi/hpsf/CustomProperty.java Fri Mar  3 08:57:55 2006
@@ -0,0 +1,123 @@
+/* ====================================================================
+   Copyright 2002-2006   Apache Software Foundation
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+==================================================================== */
+
+package org.apache.poi.hpsf;
+
+/**
+ * <p>This class represents custum properties in the document summary
+ * information stream. The difference to normal properties is that custom
+ * properties have an optional name. If the name is not <code>null</code> it
+ * will be maintained in the section's dictionary.</p>
+ * 
+ * @author Rainer Klute <a
+ *         href="mailto:klute@rainer-klute.de">&lt;klute@rainer-klute.de&gt;</a>
+ * @since 2006-02-09
+ * @version $Id$
+ */
+public class CustomProperty extends MutableProperty
+{
+
+    private String name;
+
+    /**
+     * <p>Creates an empty {@link CustomProperty}. The set methods must be
+     * called to make it usable.</p>
+     */
+    public CustomProperty()
+    {
+        this.name = null;
+    }
+
+    /**
+     * <p>Creates a {@link CustomProperty} without a name by copying the
+     * underlying {@link Property}' attributes.</p>
+     * 
+     * @param property the property to copy
+     */
+    public CustomProperty(final Property property)
+    {
+        this(property, null);
+    }
+
+    /**
+     * <p>Creates a {@link CustomProperty} with a name.</p>
+     * 
+     * @param property This property's attributes are copied to the new custom
+     *        property.
+     * @param name The new custom property's name.
+     */
+    public CustomProperty(final Property property, final String name)
+    {
+        super(property);
+        this.name = name;
+    }
+
+    /**
+     * <p>Gets the property's name.</p>
+     *
+     * @return the property's name.
+     */
+    public String getName()
+    {
+        return name;
+    }
+
+    /**
+     * <p>Sets the property's name.</p>
+     *
+     * @param name The name to set.
+     */
+    public void setName(final String name)
+    {
+        this.name = name;
+    }
+
+
+    /**
+     * <p>Compares two custom properties for equality. The method returns
+     * <code>true</code> if all attributes of the two custom properties are
+     * equal.</p>
+     * 
+     * @param o The custom property to compare with.
+     * @return <code>true</code> if both custom properties are equal, else
+     *         <code>false</code>.
+     * 
+     * @see java.util.AbstractSet#equals(java.lang.Object)
+     */
+    public boolean equalsContents(final Object o)
+    {
+        final CustomProperty c = (CustomProperty) o;
+        final String name1 = c.getName();
+        final String name2 = this.getName();
+        boolean equalNames = true;
+        if (name1 == null)
+            equalNames = name2 == null;
+        else
+            equalNames = name1.equals(name2);
+        return equalNames && c.getID() == this.getID()
+                && c.getType() == this.getType()
+                && c.getValue().equals(this.getValue());
+    }
+
+    /**
+     * @see java.util.AbstractSet#hashCode()
+     */
+    public int hashCode()
+    {
+        return (int) this.getID();
+    }
+
+}

Modified: jakarta/poi/trunk/src/java/org/apache/poi/hpsf/DocumentSummaryInformation.java
URL: http://svn.apache.org/viewcvs/jakarta/poi/trunk/src/java/org/apache/poi/hpsf/DocumentSummaryInformation.java?rev=382887&r1=382886&r2=382887&view=diff
==============================================================================
--- jakarta/poi/trunk/src/java/org/apache/poi/hpsf/DocumentSummaryInformation.java (original)
+++ jakarta/poi/trunk/src/java/org/apache/poi/hpsf/DocumentSummaryInformation.java Fri Mar  3 08:57:55 2006
@@ -1,6 +1,5 @@
-
 /* ====================================================================
-   Copyright 2002-2004   Apache Software Foundation
+   Copyright 2002-2006   Apache Software Foundation
 
    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
@@ -17,11 +16,11 @@
         
 package org.apache.poi.hpsf;
 
-import java.util.HashMap;
 import java.util.Iterator;
 import java.util.Map;
 
 import org.apache.poi.hpsf.wellknown.PropertyIDMap;
+import org.apache.poi.hpsf.wellknown.SectionIDMap;
 
 /**
  * <p>Convenience class representing a DocumentSummary Information stream in a
@@ -68,7 +67,7 @@
 
 
     /**
-     * <p>Returns the stream's category (or <code>null</code>).</p>
+     * <p>Returns the category (or <code>null</code>).</p>
      *
      * @return The category value
      */
@@ -77,23 +76,63 @@
         return (String) getProperty(PropertyIDMap.PID_CATEGORY);
     }
 
+    /**
+     * <p>Sets the category.</p>
+     *
+     * @param category The category to set.
+     */
+    public void setCategory(final String category)
+    {
+        final MutableSection s = (MutableSection) getFirstSection();
+        s.setProperty(PropertyIDMap.PID_CATEGORY, category);
+    }
+
+    /**
+     * <p>Removes the category.</p>
+     */
+    public void removeCategory()
+    {
+        final MutableSection s = (MutableSection) getFirstSection();
+        s.removeProperty(PropertyIDMap.PID_CATEGORY);
+    }
+
 
 
     /**
-     * <p>Returns the stream's presentation format (or
+     * <p>Returns the presentation format (or
      * <code>null</code>).</p>
      *
-     * @return The presentationFormat value
+     * @return The presentation format value
      */
     public String getPresentationFormat()
     {
         return (String) getProperty(PropertyIDMap.PID_PRESFORMAT);
     }
 
+    /**
+     * <p>Sets the presentation format.</p>
+     *
+     * @param presentationFormat The presentation format to set.
+     */
+    public void setPresentationFormat(final String presentationFormat)
+    {
+        final MutableSection s = (MutableSection) getFirstSection();
+        s.setProperty(PropertyIDMap.PID_PRESFORMAT, presentationFormat);
+    }
+
+    /**
+     * <p>Removes the presentation format.</p>
+     */
+    public void removePresentationFormat()
+    {
+        final MutableSection s = (MutableSection) getFirstSection();
+        s.removeProperty(PropertyIDMap.PID_PRESFORMAT);
+    }
+
 
 
     /**
-     * <p>Returns the stream's byte count or 0 if the {@link
+     * <p>Returns the byte count or 0 if the {@link
      * DocumentSummaryInformation} does not contain a byte count.</p>
      *
      * @return The byteCount value
@@ -103,86 +142,226 @@
         return getPropertyIntValue(PropertyIDMap.PID_BYTECOUNT);
     }
 
+    /**
+     * <p>Sets the byte count.</p>
+     *
+     * @param byteCount The byte count to set.
+     */
+    public void setByteCount(final int byteCount)
+    {
+        final MutableSection s = (MutableSection) getFirstSection();
+        s.setProperty(PropertyIDMap.PID_BYTECOUNT, byteCount);
+    }
+
+    /**
+     * <p>Removes the byte count.</p>
+     */
+    public void removeByteCount()
+    {
+        final MutableSection s = (MutableSection) getFirstSection();
+        s.removeProperty(PropertyIDMap.PID_BYTECOUNT);
+    }
+
 
 
     /**
-     * <p>Returns the stream's line count or 0 if the {@link
+     * <p>Returns the line count or 0 if the {@link
      * DocumentSummaryInformation} does not contain a line count.</p>
      *
-     * @return The lineCount value
+     * @return The line count value
      */
     public int getLineCount()
     {
         return getPropertyIntValue(PropertyIDMap.PID_LINECOUNT);
     }
 
+    /**
+     * <p>Sets the line count.</p>
+     *
+     * @param lineCount The line count to set.
+     */
+    public void setLineCount(final int lineCount)
+    {
+        final MutableSection s = (MutableSection) getFirstSection();
+        s.setProperty(PropertyIDMap.PID_LINECOUNT, lineCount);
+    }
+
+    /**
+     * <p>Removes the line count.</p>
+     */
+    public void removeLineCount()
+    {
+        final MutableSection s = (MutableSection) getFirstSection();
+        s.removeProperty(PropertyIDMap.PID_LINECOUNT);
+    }
+
 
 
     /**
-     * <p>Returns the stream's par count or 0 if the {@link
+     * <p>Returns the par count or 0 if the {@link
      * DocumentSummaryInformation} does not contain a par count.</p>
      *
-     * @return The parCount value
+     * @return The par count value
      */
     public int getParCount()
     {
         return getPropertyIntValue(PropertyIDMap.PID_PARCOUNT);
     }
 
+    /**
+     * <p>Sets the par count.</p>
+     *
+     * @param parCount The par count to set.
+     */
+    public void setParCount(final int parCount)
+    {
+        final MutableSection s = (MutableSection) getFirstSection();
+        s.setProperty(PropertyIDMap.PID_PARCOUNT, parCount);
+    }
+
+    /**
+     * <p>Removes the par count.</p>
+     */
+    public void removeParCount()
+    {
+        final MutableSection s = (MutableSection) getFirstSection();
+        s.removeProperty(PropertyIDMap.PID_PARCOUNT);
+    }
+
 
 
     /**
-     * <p>Returns the stream's slide count or 0 if the {@link
+     * <p>Returns the slide count or 0 if the {@link
      * DocumentSummaryInformation} does not contain a slide count.</p>
      *
-     * @return The slideCount value
+     * @return The slide count value
      */
     public int getSlideCount()
     {
         return getPropertyIntValue(PropertyIDMap.PID_SLIDECOUNT);
     }
 
+    /**
+     * <p>Sets the slideCount.</p>
+     *
+     * @param slideCount The slide count to set.
+     */
+    public void setSlideCount(final int slideCount)
+    {
+        final MutableSection s = (MutableSection) getFirstSection();
+        s.setProperty(PropertyIDMap.PID_SLIDECOUNT, slideCount);
+    }
+
+    /**
+     * <p>Removes the slide count.</p>
+     */
+    public void removeSlideCount()
+    {
+        final MutableSection s = (MutableSection) getFirstSection();
+        s.removeProperty(PropertyIDMap.PID_SLIDECOUNT);
+    }
+
 
 
     /**
-     * <p>Returns the stream's note count or 0 if the {@link
+     * <p>Returns the note count or 0 if the {@link
      * DocumentSummaryInformation} does not contain a note count.</p>
      *
-     * @return The noteCount value
+     * @return The note count value
      */
     public int getNoteCount()
     {
         return getPropertyIntValue(PropertyIDMap.PID_NOTECOUNT);
     }
 
+    /**
+     * <p>Sets the note count.</p>
+     *
+     * @param noteCount The note count to set.
+     */
+    public void setNoteCount(final int noteCount)
+    {
+        final MutableSection s = (MutableSection) getFirstSection();
+        s.setProperty(PropertyIDMap.PID_NOTECOUNT, noteCount);
+    }
+
+    /**
+     * <p>Removes the noteCount.</p>
+     */
+    public void removeNoteCount()
+    {
+        final MutableSection s = (MutableSection) getFirstSection();
+        s.removeProperty(PropertyIDMap.PID_NOTECOUNT);
+    }
+
 
 
     /**
-     * <p>Returns the stream's hidden count or 0 if the {@link
+     * <p>Returns the hidden count or 0 if the {@link
      * DocumentSummaryInformation} does not contain a hidden
      * count.</p>
      *
-     * @return The hiddenCount value
+     * @return The hidden count value
      */
     public int getHiddenCount()
     {
         return getPropertyIntValue(PropertyIDMap.PID_HIDDENCOUNT);
     }
 
+    /**
+     * <p>Sets the hidden count.</p>
+     *
+     * @param hiddenCount The hidden count to set.
+     */
+    public void setHiddenCount(final int hiddenCount)
+    {
+        final MutableSection s = (MutableSection) getSections().get(0);
+        s.setProperty(PropertyIDMap.PID_HIDDENCOUNT, hiddenCount);
+    }
+
+    /**
+     * <p>Removes the hidden count.</p>
+     */
+    public void removeHiddenCount()
+    {
+        final MutableSection s = (MutableSection) getFirstSection();
+        s.removeProperty(PropertyIDMap.PID_HIDDENCOUNT);
+    }
+
 
 
     /**
-     * <p>Returns the stream's mmclip count or 0 if the {@link
+     * <p>Returns the mmclip count or 0 if the {@link
      * DocumentSummaryInformation} does not contain a mmclip
      * count.</p>
      *
-     * @return The mMClipCount value
+     * @return The mmclip count value
      */
     public int getMMClipCount()
     {
         return getPropertyIntValue(PropertyIDMap.PID_MMCLIPCOUNT);
     }
 
+    /**
+     * <p>Sets the mmclip count.</p>
+     *
+     * @param mmClipCount The mmclip count to set.
+     */
+    public void setMMClipCount(final int mmClipCount)
+    {
+        final MutableSection s = (MutableSection) getFirstSection();
+        s.setProperty(PropertyIDMap.PID_MMCLIPCOUNT, mmClipCount);
+    }
+
+    /**
+     * <p>Removes the mmclip count.</p>
+     */
+    public void removeMMClipCount()
+    {
+        final MutableSection s = (MutableSection) getFirstSection();
+        s.removeProperty(PropertyIDMap.PID_MMCLIPCOUNT);
+    }
+
 
 
     /**
@@ -196,42 +375,100 @@
         return getPropertyBooleanValue(PropertyIDMap.PID_SCALE);
     }
 
+    /**
+     * <p>Sets the scale.</p>
+     *
+     * @param scale The scale to set.
+     */
+    public void setScale(final boolean scale)
+    {
+        final MutableSection s = (MutableSection) getFirstSection();
+        s.setProperty(PropertyIDMap.PID_SCALE, scale);
+    }
+
+    /**
+     * <p>Removes the scale.</p>
+     */
+    public void removeScale()
+    {
+        final MutableSection s = (MutableSection) getFirstSection();
+        s.removeProperty(PropertyIDMap.PID_SCALE);
+    }
+
 
 
     /**
-     * <p>Returns the stream's heading pair (or <code>null</code>)
+     * <p>Returns the heading pair (or <code>null</code>)
      * <strong>when this method is implemented. Please note that the
      * return type is likely to change!</strong>
      *
-     * @return The headingPair value
+     * @return The heading pair value
      */
     public byte[] getHeadingPair()
     {
-        if (true)
-            throw new UnsupportedOperationException("FIXME");
+        notYetImplemented("Reading byte arrays ");
         return (byte[]) getProperty(PropertyIDMap.PID_HEADINGPAIR);
     }
 
+    /**
+     * <p>Sets the heading pair.</p>
+     *
+     * @param headingPair The heading pair to set.
+     */
+    public void setHeadingPair(final byte[] headingPair)
+    {
+        notYetImplemented("Writing byte arrays ");
+    }
+
+    /**
+     * <p>Removes the heading pair.</p>
+     */
+    public void removeHeadingPair()
+    {
+        final MutableSection s = (MutableSection) getFirstSection();
+        s.removeProperty(PropertyIDMap.PID_HEADINGPAIR);
+    }
+
 
 
     /**
-     * <p>Returns the stream's doc parts (or <code>null</code>)
+     * <p>Returns the doc parts (or <code>null</code>)
      * <strong>when this method is implemented. Please note that the
      * return type is likely to change!</strong>
      *
-     * @return The docparts value
+     * @return The doc parts value
      */
     public byte[] getDocparts()
     {
-        if (true)
-            throw new UnsupportedOperationException("FIXME");
+        notYetImplemented("Reading byte arrays");
         return (byte[]) getProperty(PropertyIDMap.PID_DOCPARTS);
     }
 
 
 
     /**
-     * <p>Returns the stream's manager (or <code>null</code>).</p>
+     * <p>Sets the doc parts.</p>
+     *
+     * @param docparts The doc parts to set.
+     */
+    public void setDocparts(final byte[] docparts)
+    {
+        notYetImplemented("Writing byte arrays");
+    }
+
+    /**
+     * <p>Removes the doc parts.</p>
+     */
+    public void removeDocparts()
+    {
+        final MutableSection s = (MutableSection) getFirstSection();
+        s.removeProperty(PropertyIDMap.PID_DOCPARTS);
+    }
+
+
+
+    /**
+     * <p>Returns the manager (or <code>null</code>).</p>
      *
      * @return The manager value
      */
@@ -240,10 +477,30 @@
         return (String) getProperty(PropertyIDMap.PID_MANAGER);
     }
 
+    /**
+     * <p>Sets the manager.</p>
+     *
+     * @param manager The manager to set.
+     */
+    public void setManager(final String manager)
+    {
+        final MutableSection s = (MutableSection) getFirstSection();
+        s.setProperty(PropertyIDMap.PID_MANAGER, manager);
+    }
+
+    /**
+     * <p>Removes the manager.</p>
+     */
+    public void removeManager()
+    {
+        final MutableSection s = (MutableSection) getFirstSection();
+        s.removeProperty(PropertyIDMap.PID_MANAGER);
+    }
+
 
 
     /**
-     * <p>Returns the stream's company (or <code>null</code>).</p>
+     * <p>Returns the company (or <code>null</code>).</p>
      *
      * @return The company value
      */
@@ -252,47 +509,168 @@
         return (String) getProperty(PropertyIDMap.PID_COMPANY);
     }
 
+    /**
+     * <p>Sets the company.</p>
+     *
+     * @param company The company to set.
+     */
+    public void setCompany(final String company)
+    {
+        final MutableSection s = (MutableSection) getFirstSection();
+        s.setProperty(PropertyIDMap.PID_COMPANY, company);
+    }
+
+    /**
+     * <p>Removes the company.</p>
+     */
+    public void removeCompany()
+    {
+        final MutableSection s = (MutableSection) getFirstSection();
+        s.removeProperty(PropertyIDMap.PID_COMPANY);
+    }
+
 
 
     /**
      * <p>Returns <code>true</code> if the custom links are dirty.</p> <p>
      *
-     * @return The linksDirty value
+     * @return The links dirty value
      */
     public boolean getLinksDirty()
     {
         return getPropertyBooleanValue(PropertyIDMap.PID_LINKSDIRTY);
     }
 
+    /**
+     * <p>Sets the linksDirty.</p>
+     *
+     * @param linksDirty The links dirty value to set.
+     */
+    public void setLinksDirty(final boolean linksDirty)
+    {
+        final MutableSection s = (MutableSection) getFirstSection();
+        s.setProperty(PropertyIDMap.PID_LINKSDIRTY, linksDirty);
+    }
+
+    /**
+     * <p>Removes the links dirty.</p>
+     */
+    public void removeLinksDirty()
+    {
+        final MutableSection s = (MutableSection) getFirstSection();
+        s.removeProperty(PropertyIDMap.PID_LINKSDIRTY);
+    }
+
 
 
     /**
-     * <p>Gets the custom properties as a map from the property name to
-     * value.</p>
+     * <p>Gets the custom properties.</p>
      * 
-     * @return The custom properties if any exist, <code>null</code> otherwise.
-     * @since 2003-10-22
+     * @return The custom properties.
+     * @since 2006-02-09
      */
-    public Map getCustomProperties()
+    public CustomProperties getCustomProperties()
     {
-        Map nameToValue = null;
+        CustomProperties cps = null;
         if (getSectionCount() >= 2)
         {
+            cps = new CustomProperties();
             final Section section = (Section) getSections().get(1);
-            final Map pidToName = 
-                      (Map) section.getProperty(PropertyIDMap.PID_DICTIONARY);
-            if (pidToName != null)
+            final Map dictionary = section.getDictionary();
+            final Property[] properties = section.getProperties();
+            int propertyCount = 0;
+            for (int i = 0; i < properties.length; i++)
             {
-                nameToValue = new HashMap(pidToName.size());
-                for (Iterator i = pidToName.entrySet().iterator(); i.hasNext();)
+                final Property p = properties[i];
+                final long id = p.getID();
+                if (id != 0 && id != 1)
                 {
-                    final Map.Entry e = (Map.Entry) i.next();
-                    final long pid = ((Number) e.getKey()).longValue();
-                    nameToValue.put(e.getValue(), section.getProperty(pid));
+                    propertyCount++;
+                    final CustomProperty cp = new CustomProperty(p,
+                            (String) dictionary.get(new Long(id)));
+                    cps.put(cp.getName(), cp);
                 }
             }
+            if (cps.size() != propertyCount)
+                cps.setPure(false);
+        }
+        return cps;
+    }
+
+    /**
+     * <p>Sets the custom properties.</p>
+     * 
+     * @param customProperties The custom properties
+     * @since 2006-02-07
+     */
+    public void setCustomProperties(final CustomProperties customProperties)
+    {
+        ensureSection2();
+        final MutableSection section = (MutableSection) getSections().get(1);
+        final Map dictionary = customProperties.getDictionary();
+        section.clear();
+
+        /* Set the codepage. If both custom properties and section have a
+         * codepage, the codepage from the custom properties wins, else take the
+         * one that is defined. If none is defined, take Unicode. */
+        int cpCodepage = customProperties.getCodepage();
+        if (cpCodepage < 0)
+            cpCodepage = section.getCodepage();
+        if (cpCodepage < 0)
+            cpCodepage = Constants.CP_UNICODE;
+        customProperties.setCodepage(cpCodepage);
+        section.setCodepage(cpCodepage);
+        section.setDictionary(dictionary);
+        for (final Iterator i = customProperties.values().iterator(); i.hasNext();)
+        {
+            final Property p = (Property) i.next();
+            section.setProperty(p);
+        }
+    }
+
+
+
+    /**
+     * <p>Creates section 2 if it is not already present.</p>
+     *
+     */
+    private void ensureSection2()
+    {
+        if (getSectionCount() < 2)
+        {
+            MutableSection s2 = new MutableSection();
+            s2.setFormatID(SectionIDMap.DOCUMENT_SUMMARY_INFORMATION_ID[1]);
+            addSection(s2);
         }
-        return nameToValue;
+    }
+
+
+
+    /**
+     * <p>Removes the custom properties.</p>
+     * 
+     * @since 2006-02-08
+     */
+    public void removeCustomProperties()
+    {
+        if (getSectionCount() >= 2)
+            getSections().remove(1);
+        else
+            throw new HPSFRuntimeException("Illegal internal format of Document SummaryInformation stream: second section is missing.");
+    }
+
+
+
+    /**
+     * <p>Throws an {@link UnsupportedOperationException} with a message text
+     * telling which functionality is not yet implemented.</p>
+     *
+     * @param msg text telling was leaves to be implemented, e.g.
+     * "Reading byte arrays".
+     */
+    private void notYetImplemented(final String msg)
+    {
+        throw new UnsupportedOperationException(msg + " is not yet implemented.");
     }
 
 }



---------------------------------------------------------------------
To unsubscribe, e-mail: poi-dev-unsubscribe@jakarta.apache.org
Mailing List:    http://jakarta.apache.org/site/mail2.html#poi
The Apache Jakarta POI Project: http://jakarta.apache.org/poi/