]> source.dussan.org Git - poi.git/commitdiff
* Writing support added to the SummaryInformation and DocumentSummaryInformation...
authorRainer Klute <klute@apache.org>
Fri, 3 Mar 2006 16:57:55 +0000 (16:57 +0000)
committerRainer Klute <klute@apache.org>
Fri, 3 Mar 2006 16:57:55 +0000 (16:57 +0000)
** 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.

git-svn-id: https://svn.apache.org/repos/asf/jakarta/poi/trunk@382887 13f79535-47bb-0310-9956-ffa450edef68

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

index 0c998c3235865a0750c57699b0df3e1aeb8cf23d..ffd45af237996c9e7e2da6355cc4d52481d30a77 100644 (file)
@@ -128,7 +128,7 @@ public class POIBrowser extends JFrame
                       new PropertySetDescriptorRenderer());
         treeUI.setCellRenderer(etcr);
         setSize(600, 450);
-        setTitle("POI Browser 0.08");
+        setTitle("POI Browser 0.09");
         setVisible(true);
     }
 
index 9e6500c1b783a1a7c01b0d74f9ac6f0011cf7758..bd7f584363fe81aae9923c51115d3c0ccd250f8c 100644 (file)
@@ -25,7 +25,6 @@ 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.UnexpectedPropertySetTypeException;
 import org.apache.poi.poifs.filesystem.DocumentInputStream;
 import org.apache.poi.poifs.filesystem.POIFSDocumentPath;
 
@@ -70,7 +69,7 @@ public class PropertySetDescriptor extends DocumentDescriptor
                                  final POIFSDocumentPath path,
                                  final DocumentInputStream stream,
                                  final int nrOfBytesToDump)
-        throws UnexpectedPropertySetTypeException, NoPropertySetStreamException,
+        throws NoPropertySetStreamException,
                MarkUnsupportedException, UnsupportedEncodingException,
                IOException
     {
index 3b17803b0d10c2ba53a333949acd0fcd30511b38..8aac2a1519c356c083ff8255bef4a8c81466a86a 100644 (file)
@@ -127,6 +127,9 @@ public class PropertySetDescriptorRenderer extends DocumentDescriptorRenderer
 
     /**
      * <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 @@ public class PropertySetDescriptorRenderer extends DocumentDescriptorRenderer
         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;
index b993685140741567e79f139439c1a4c9c2699fe6..93833430f54b59469ac6a8a20d2a202f1d81bf4f 100644 (file)
@@ -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.
 
     <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>
        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 @@ r.registerListener(new MyPOIFSReaderListener(),
      <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 @@ static class MyPOIFSReaderListener implements POIFSReaderListener
       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 @@ if (title != null)
 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 @@ else
    <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 @@ else
     </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 @@ else
        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 @@ else
     </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 @@ else
      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 @@ No property set stream: "/1Table"</source>
 
      <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 @@ No property set stream: "/1Table"</source>
       <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 @@ No property set stream: "/1Table"</source>
       "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>
 
index d317ffbffa612ddc271616eac9dca2dc6af1dc9d..3f314752ab04ca5f6537ddf4f117e640bda9a188 100644 (file)
      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>
+       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>
+     <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>
diff --git a/src/examples/src/org/apache/poi/hpsf/examples/ModifyDocumentSummaryInformation.java b/src/examples/src/org/apache/poi/hpsf/examples/ModifyDocumentSummaryInformation.java
new file mode 100644 (file)
index 0000000..c7e879c
--- /dev/null
@@ -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();
+        }
+
+}
index 0f8d12221bbe7b1775582a91ab4ef23d4fce7339..766453f584cb7ccad7e69b37551d2e72a20caa6f 100644 (file)
@@ -1,4 +1,3 @@
-
 /* ====================================================================
    Copyright 2002-2004   Apache Software Foundation
 
index 3cd1138c5d1bd6c8d492ab5ff8dba10be8230d49..32a0addf209e1a8492042c3ae2f63cdafe758592 100644 (file)
@@ -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;
 
 /**
diff --git a/src/java/org/apache/poi/hpsf/CustomProperties.java b/src/java/org/apache/poi/hpsf/CustomProperties.java
new file mode 100644 (file)
index 0000000..77f577d
--- /dev/null
@@ -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;
+    }
+
+}
diff --git a/src/java/org/apache/poi/hpsf/CustomProperty.java b/src/java/org/apache/poi/hpsf/CustomProperty.java
new file mode 100644 (file)
index 0000000..7c87e1f
--- /dev/null
@@ -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();
+    }
+
+}
index 21b1b2a9fec278ea4bbf3bdd0a37111cd2d7e8d6..1fce462d628b72ae17d9a94ac2362a25cb7b3ea4 100644 (file)
@@ -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.
         
 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 @@ public class DocumentSummaryInformation extends SpecialPropertySet
 
 
     /**
-     * <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 @@ public class DocumentSummaryInformation extends SpecialPropertySet
         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 @@ public class DocumentSummaryInformation extends SpecialPropertySet
         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 @@ public class DocumentSummaryInformation extends SpecialPropertySet
         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 @@ public class DocumentSummaryInformation extends SpecialPropertySet
         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 @@ public class DocumentSummaryInformation extends SpecialPropertySet
         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.");
     }
 
 }
index ed6aa4ea44c454e1fa9c3b3a6f274e4c7c8b34fe..7588be679e1bb59b23d051611e66789dfa9c5ebf 100644 (file)
@@ -1,4 +1,3 @@
-
 /* ====================================================================
    Copyright 2002-2004   Apache Software Foundation
 
 package org.apache.poi.hpsf;
 
 /**
- * <p>This exception is the superclass of all other checked exceptions
- * thrown in this package. It supports a nested "reason" throwable,
- * i.e. an exception that caused this one to be thrown.</p>
- *
+ * <p>This exception is the superclass of all other checked exceptions thrown
+ * in this package. It supports a nested "reason" throwable, i.e. an exception
+ * that caused this one to be thrown.</p>
+ * 
  * @author Rainer Klute <a
- * href="mailto:klute@rainer-klute.de">&lt;klute@rainer-klute.de&gt;</a>
+ *         href="mailto:klute@rainer-klute.de">&lt;klute@rainer-klute.de&gt;</a>
  * @version $Id$
  * @since 2002-02-09
  */
index 57c1a68f84d35f82662a18660a3fa89b2246854b..b997226bb1c20ef701123f21fb47b752f972521c 100644 (file)
@@ -1,4 +1,3 @@
-
 /* ====================================================================
    Copyright 2002-2004   Apache Software Foundation
 
index a0ad11df71a351417e1e0c33c874b7679ca461bc..6e2b06d5bad512948b5e321313cc58e641b488d0 100644 (file)
@@ -1,4 +1,3 @@
-
 /* ====================================================================
    Copyright 2002-2004   Apache Software Foundation
 
index ced098c3239b6a0a9816049a7a535651012dbf59..a03c1738b8efa790a6bf1b22751b9116d0784ab1 100644 (file)
@@ -1,4 +1,3 @@
-
 /* ====================================================================
    Copyright 2002-2004   Apache Software Foundation
 
diff --git a/src/java/org/apache/poi/hpsf/MissingSectionException.java b/src/java/org/apache/poi/hpsf/MissingSectionException.java
new file mode 100644 (file)
index 0000000..76aba80
--- /dev/null
@@ -0,0 +1,76 @@
+/* ====================================================================
+   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 exception is thrown if one of the {@link PropertySet}'s
+ * convenience methods does not find a required {@link Section}.</p>
+ *
+ * <p>The constructors of this class are analogous to those of its
+ * superclass and documented there.</p>
+ *
+ * @author Rainer Klute <a
+ * href="mailto:klute@rainer-klute.de">&lt;klute@rainer-klute.de&gt;</a>
+ * @version $Id: NoSingleSectionException.java 353545 2004-04-09 13:05:39Z glens $
+ * @since 2006-02-08
+ */
+public class MissingSectionException extends HPSFRuntimeException
+{
+
+    /**
+     * <p>Constructor</p>
+     */
+    public MissingSectionException()
+    {
+        super();
+    }
+
+
+    /**
+     * <p>Constructor</p>
+     * 
+     * @param msg The exception's message string
+     */
+    public MissingSectionException(final String msg)
+    {
+        super(msg);
+    }
+
+
+    /**
+     * <p>Constructor</p>
+     * 
+     * @param reason This exception's underlying reason
+     */
+    public MissingSectionException(final Throwable reason)
+    {
+        super(reason);
+    }
+
+
+    /**
+     * <p>Constructor</p>
+     * 
+     * @param msg The exception's message string
+     * @param reason This exception's underlying reason
+     */
+    public MissingSectionException(final String msg, final Throwable reason)
+    {
+        super(msg, reason);
+    }
+
+}
index d1dccf29495d095fed71cb2bc1ab9c2acf42895e..bff912be3f50be8677934e917eb05f5fd58801e4 100644 (file)
@@ -1,5 +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.
@@ -18,6 +18,7 @@ package org.apache.poi.hpsf;
 
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
+import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
@@ -26,6 +27,8 @@ import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.ListIterator;
 
+import org.apache.poi.poifs.filesystem.DirectoryEntry;
+import org.apache.poi.poifs.filesystem.Entry;
 import org.apache.poi.util.LittleEndian;
 import org.apache.poi.util.LittleEndianConsts;
 
@@ -151,7 +154,7 @@ public class MutablePropertySet extends PropertySet
      *
      * @param classID The property set stream's low-level "class ID" field.
      *
-     * @see #getClassID
+     * @see PropertySet#getClassID()
      */
     public void setClassID(final ClassID classID)
     {
@@ -205,9 +208,9 @@ public class MutablePropertySet extends PropertySet
         /* Write the property set's header. */
         length += TypeWriter.writeToStream(out, (short) getByteOrder());
         length += TypeWriter.writeToStream(out, (short) getFormat());
-        length += TypeWriter.writeToStream(out, (int) getOSVersion());
+        length += TypeWriter.writeToStream(out, getOSVersion());
         length += TypeWriter.writeToStream(out, getClassID());
-        length += TypeWriter.writeToStream(out, (int) nrSections);
+        length += TypeWriter.writeToStream(out, nrSections);
         int offset = OFFSET_HEADER;
 
         /* Write the section list, i.e. the references to the sections. Each
@@ -272,4 +275,31 @@ public class MutablePropertySet extends PropertySet
         return new ByteArrayInputStream(streamData);
     }
 
+    /**
+     * <p>Writes a property set to a document in a POI filesystem directory.</p>
+     *
+     * @param dir The directory in the POI filesystem to write the document to.
+     * @param name The document's name. If there is already a document with the
+     * same name in the directory the latter will be overwritten.
+     * 
+     * @throws WritingNotSupportedException
+     * @throws IOException
+     */
+    public void write(final DirectoryEntry dir, final String name)
+    throws WritingNotSupportedException, IOException
+    {
+        /* If there is already an entry with the same name, remove it. */
+        try
+        {
+            final Entry e = dir.getEntry(name);
+            e.delete();
+        }
+        catch (FileNotFoundException ex)
+        {
+            /* Entry not found, no need to remove it. */
+        }
+        /* Create the new entry. */
+        dir.createDocument(name, toInputStream());
+    }
+
 }
index 6d6c1b11e9c2f6f93ed7db6e9e6bb4ffe588cc1f..96928051ab11509dc540b9b9a8442139511efd8a 100644 (file)
@@ -1,5 +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.
@@ -21,6 +21,7 @@ import java.io.IOException;
 import java.io.OutputStream;
 import java.util.Collections;
 import java.util.Comparator;
+import java.util.Date;
 import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
@@ -107,7 +108,7 @@ public class MutableSection extends Section
      * @param formatID The section's format ID
      *
      * @see #setFormatID(byte[])
-     * @see #getFormatID
+     * @see Section#getFormatID
      */
     public void setFormatID(final ClassID formatID)
     {
@@ -123,7 +124,7 @@ public class MutableSection extends Section
      * are in big-endian format.
      *
      * @see #setFormatID(ClassID)
-     * @see #getFormatID
+     * @see Section#getFormatID
      */
     public void setFormatID(final byte[] formatID)
     {
@@ -155,10 +156,7 @@ public class MutableSection extends Section
 
 
     /**
-     * <p>Sets the value 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.</p>
+     * <p>Sets the string value of the property with the specified ID.</p>
      *
      * @param id The property's ID
      * @param value The property's value. It will be written as a Unicode
@@ -175,6 +173,57 @@ public class MutableSection extends Section
 
 
 
+    /**
+     * <p>Sets the int value of the property with the specified ID.</p>
+     *
+     * @param id The property's ID
+     * @param value The property's value.
+     *
+     * @see #setProperty(int, long, Object)
+     * @see #getProperty
+     */
+    public void setProperty(final int id, final int value)
+    {
+        setProperty(id, Variant.VT_I4, new Integer(value));
+        dirty = true;
+    }
+
+
+
+    /**
+     * <p>Sets the long value of the property with the specified ID.</p>
+     *
+     * @param id The property's ID
+     * @param value The property's value.
+     *
+     * @see #setProperty(int, long, Object)
+     * @see #getProperty
+     */
+    public void setProperty(final int id, final long value)
+    {
+        setProperty(id, Variant.VT_I8, new Long(value));
+        dirty = true;
+    }
+
+
+
+    /**
+     * <p>Sets the boolean value of the property with the specified ID.</p>
+     *
+     * @param id The property's ID
+     * @param value The property's value.
+     *
+     * @see #setProperty(int, long, Object)
+     * @see #getProperty
+     */
+    public void setProperty(final int id, final boolean value)
+    {
+        setProperty(id, Variant.VT_BOOL, new Boolean(value));
+        dirty = true;
+    }
+
+
+
     /**
      * <p>Sets the value and the variant type of the property with the
      * specified ID. If a property with this ID is not yet present in
@@ -204,15 +253,11 @@ public class MutableSection extends Section
 
 
     /**
-     * <p>Sets a property. If a property with the same ID is not yet present in
-     * the section, the property will be added to the section. If there is
-     * already a property with the same ID present in the section, it will be
-     * overwritten.</p>
+     * <p>Sets a property.</p>
      *
-     * @param p The property to be added to the section
+     * @param p The property to be set.
      *
      * @see #setProperty(int, long, Object)
-     * @see #setProperty(int, String)
      * @see #getProperty
      * @see Variant
      */
@@ -257,7 +302,7 @@ public class MutableSection extends Section
      */
     protected void setPropertyBooleanValue(final int id, final boolean value)
     {
-        setProperty(id, (long) Variant.VT_BOOL, new Boolean(value));
+        setProperty(id, Variant.VT_BOOL, new Boolean(value));
     }
 
 
@@ -296,6 +341,8 @@ public class MutableSection extends Section
      * properties) and the properties themselves.</p>
      *
      * @return the section's length in bytes.
+     * @throws WritingNotSupportedException 
+     * @throws IOException 
      */
     private int calcSize() throws WritingNotSupportedException, IOException 
     {
@@ -372,7 +419,7 @@ public class MutableSection extends Section
                 /* 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, (long) Variant.VT_I2,
+                setProperty(PropertyIDMap.PID_CODEPAGE, Variant.VT_I2,
                             new Integer(Constants.CP_UNICODE));
             codepage = getCodepage();
         }
@@ -474,16 +521,15 @@ public class MutableSection extends Section
                     sLength++;
                 length += TypeWriter.writeUIntToStream(out, key.longValue());
                 length += TypeWriter.writeUIntToStream(out, sLength);
-                final char[] ca = value.toCharArray();
-                for (int j = 0; j < ca.length; j++)
+                final byte[] ca =
+                    value.getBytes(VariantSupport.codepageToEncoding(codepage));
+                for (int j = 2; j < ca.length; j += 2)
                 {
-                    int high = (ca[j] & 0x0ff00) >> 8;
-                    int low  = (ca[j] & 0x000ff);
-                    out.write(low);
-                    out.write(high);
+                    out.write(ca[j+1]);
+                    out.write(ca[j]);
                     length += 2;
-                    sLength--;
                 }
+                sLength -= value.length();
                 while (sLength > 0)
                 {
                     out.write(0x00);
@@ -610,4 +656,60 @@ public class MutableSection extends Section
             removeProperty(PropertyIDMap.PID_DICTIONARY);
     }
 
+
+
+    /**
+     * <p>Sets a property.</p>
+     * 
+     * @param id The property ID.
+     * @param value The property's value. The value's class must be one of those
+     *        supported by HPSF.
+     */
+    public void setProperty(final int id, final Object value)
+    {
+        if (value instanceof String)
+            setProperty(id, (String) value);
+        else if (value instanceof Long)
+            setProperty(id, ((Long) value).longValue());
+        else if (value instanceof Integer)
+            setProperty(id, ((Integer) value).intValue());
+        else if (value instanceof Short)
+            setProperty(id, ((Short) value).intValue());
+        else if (value instanceof Boolean)
+            setProperty(id, ((Boolean) value).booleanValue());
+        else if (value instanceof Date)
+            setProperty(id, Variant.VT_FILETIME, value);
+        else
+            throw new HPSFRuntimeException(
+                    "HPSF does not support properties of type " +
+                    value.getClass().getName() + ".");
+    }
+
+
+
+    /**
+     * <p>Removes all properties from the section including 0 (dictionary) and
+     * 1 (codepage).</p>
+     */
+    public void clear()
+    {
+        final Property[] properties = getProperties();
+        for (int i = 0; i < properties.length; i++)
+        {
+            final Property p = properties[i];
+            removeProperty(p.getID());
+        }
+    }
+
+    /**
+     * <p>Sets the codepage.</p>
+     *
+     * @param codepage the codepage
+     */
+    public void setCodepage(final int codepage)
+    {
+        setProperty(PropertyIDMap.PID_CODEPAGE, Variant.VT_I2,
+                new Integer(codepage));
+    }
+
 }
index 661189d7b7b0d96e618a38f7397683fbc4ea3433..4bcdf3311c1087abd241fd7c31053d3cbd03dd74 100644 (file)
@@ -1,4 +1,3 @@
-
 /* ====================================================================
    Copyright 2002-2004   Apache Software Foundation
 
index 9e5de03ab3e4f83f5a38a70ed8f1c489270f8c14..7cdf0d2d97375bd4375fcd8386fac96ace7266e6 100644 (file)
@@ -1,4 +1,3 @@
-
 /* ====================================================================
    Copyright 2002-2004   Apache Software Foundation
 
index 81c346b8159286bf90e10355a9462ff6fd7e414e..e27b8beb351d569715fc2f45db8179ef2e18e2cd 100644 (file)
@@ -1,5 +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.
@@ -107,6 +107,22 @@ public class Property
 
 
 
+    /**
+     * <p>Creates a property.</p>
+     * 
+     * @param id the property's ID.
+     * @param type the property's type, see {@link Variant}.
+     * @param value the property's value. Only certain types are allowed, see {@link Variant}. 
+     */
+    public Property(final long id, final long type, final Object value)
+    {
+        this.id = id;
+        this.type = type;
+        this.value = value;
+    }
+
+
+
     /**
      * <p>Creates a {@link Property} instance by reading its bytes
      * from the property set stream.</p>
@@ -222,12 +238,15 @@ public class Property
                 {
                     /* The length is the number of characters, i.e. the number
                      * of bytes is twice the number of the characters. */
-                    for (int j = 0; j < sLength; j++)
+                    final int nrBytes = (int) (sLength * 2);
+                    final byte[] h = new byte[nrBytes];
+                    for (int i2 = 0; i2 < nrBytes; i2 += 2)
                     {
-                        final int i1 = o + (j * 2);
-                        final int i2 = i1 + 1;
-                        b.append((char) ((src[i2] << 8) + src[i1]));
+                        h[i2] = src[o + i2 + 1];
+                        h[i2 + 1] = src[o + i2];
                     }
+                    b.append(new String(h, 0, nrBytes,
+                            VariantSupport.codepageToEncoding(codepage)));
                     break;
                 }
                 default:
index 134df3564b099edbcafca286968970ebf79f589e..821acf2d40f6430c9f5f27ff71d2d80da4653fc3 100644 (file)
@@ -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.
@@ -402,8 +401,10 @@ public class PropertySet
      *
      * @param src Byte array containing the property set stream
      * @param offset The property set stream starts at this offset
-     * from the beginning of <var>src</src>
+     * from the beginning of <var>src</var>
      * @param length Length of the property set stream.
+     * @throws UnsupportedEncodingException if HPSF does not (yet) support the
+     * property set's character encoding.
      */
     private void init(final byte[] src, final int offset, final int length)
     throws UnsupportedEncodingException
@@ -482,7 +483,7 @@ public class PropertySet
     public boolean isDocumentSummaryInformation()
     {
         return Util.equal(((Section) sections.get(0)).getFormatID().getBytes(),
-                          SectionIDMap.DOCUMENT_SUMMARY_INFORMATION_ID);
+                          SectionIDMap.DOCUMENT_SUMMARY_INFORMATION_ID[0]);
     }
 
 
@@ -492,9 +493,7 @@ public class PropertySet
      * contained in this property set. It is a shortcut for getting
      * the {@link PropertySet}'s {@link Section}s list and then
      * getting the {@link Property} array from the first {@link
-     * Section}. However, it can only be used if the {@link
-     * PropertySet} contains exactly one {@link Section}, so check
-     * {@link #getSectionCount} first!</p>
+     * Section}.</p>
      *
      * @return The properties of the only {@link Section} of this
      * {@link PropertySet}.
@@ -504,7 +503,7 @@ public class PropertySet
     public Property[] getProperties()
         throws NoSingleSectionException
     {
-        return getSingleSection().getProperties();
+        return getFirstSection().getProperties();
     }
 
 
@@ -522,7 +521,7 @@ public class PropertySet
      */
     protected Object getProperty(final int id) throws NoSingleSectionException
     {
-        return getSingleSection().getProperty(id);
+        return getFirstSection().getProperty(id);
     }
 
 
@@ -543,7 +542,7 @@ public class PropertySet
     protected boolean getPropertyBooleanValue(final int id)
         throws NoSingleSectionException
     {
-        return getSingleSection().getPropertyBooleanValue(id);
+        return getFirstSection().getPropertyBooleanValue(id);
     }
 
 
@@ -563,7 +562,7 @@ public class PropertySet
     protected int getPropertyIntValue(final int id)
         throws NoSingleSectionException
     {
-        return getSingleSection().getPropertyIntValue(id);
+        return getFirstSection().getPropertyIntValue(id);
     }
 
 
@@ -585,7 +584,21 @@ public class PropertySet
      */
     public boolean wasNull() throws NoSingleSectionException
     {
-        return getSingleSection().wasNull();
+        return getFirstSection().wasNull();
+    }
+
+
+
+    /**
+     * <p>Gets the {@link PropertySet}'s first section.</p>
+     *
+     * @return The {@link PropertySet}'s first section.
+     */
+    public Section getFirstSection()
+    {
+        if (getSectionCount() < 1)
+            throw new MissingSectionException("Property set does not contain any sections.");
+        return ((Section) sections.get(0));
     }
 
 
index e7b95767926a93a325d207df66c638cef69a3016..0a939820434f9236e22064a526189d760ffc7ece 100644 (file)
@@ -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.
@@ -22,6 +21,8 @@ import java.io.IOException;
 import java.io.UnsupportedEncodingException;
 import java.rmi.UnexpectedException;
 
+import org.apache.poi.hpsf.wellknown.SectionIDMap;
+
 /**
  * <p>Factory class to create instances of {@link SummaryInformation},
  * {@link DocumentSummaryInformation} and {@link PropertySet}.</p>
@@ -74,4 +75,50 @@ public class PropertySetFactory
         }
     }
 
+
+
+    /**
+     * <p>Creates a new summary information.</p>
+     *
+     * @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);
+        }
+    }
+
+
+
+    /**
+     * <p>Creates a new document summary information.</p>
+     *
+     * @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);
+        }
+    }
+
 }
index a6a97f1ee7cf437d9527fc920e91569b66e23537..c7fed7fd0c6d11e26e50b7403dca0ca959cda510 100644 (file)
@@ -1,4 +1,3 @@
-
 /* ====================================================================
    Copyright 2002-2004   Apache Software Foundation
 
index 913fc9116baf87e4543da4f690b227026aa9b9e7..ee28158107c396f9242167fb170202e30b52fbb3 100644 (file)
@@ -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.
@@ -278,9 +277,12 @@ public class Section
         for (final Iterator i = propertyList.iterator(); i.hasNext();)
         {
             ple = (PropertyListEntry) i.next();
-            properties[i1++] = new Property(ple.id, src,
-                                            this.offset + ple.offset,
-                                            ple.length, codepage);
+            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(), new Integer(codepage));
+            properties[i1++] = p;
         }
 
         /*
@@ -359,15 +361,15 @@ public class Section
      */
     protected int getPropertyIntValue(final long id)
     {
-        final Long i;
+        final Number i;
         final Object o = getProperty(id);
         if (o == null)
             return 0;
-        if (!(o instanceof Long))
+        if (!(o instanceof Long || o instanceof Integer))
             throw new HPSFRuntimeException
                 ("This property is not an integer type, but " +
                  o.getClass().getName() + ".");
-        i = (Long) o;
+        i = (Number) o;
         return i.intValue();
     }
 
@@ -545,6 +547,10 @@ public class Section
     /**
      * <p>Removes a field from a property array. The resulting array is
      * compactified and returned.</p>
+     *
+     * @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)
     {
@@ -629,7 +635,10 @@ public class Section
     {
         final Integer codepage =
             (Integer) getProperty(PropertyIDMap.PID_CODEPAGE);
-        return codepage != null ? codepage.intValue() : -1;
+        if (codepage == null)
+            return -1;
+        int cp = codepage.intValue();
+        return cp;
     }
 
 }
index d114e41abf6f2e168e2f295c193ee1227b27fa10..e65de3c11b3b1cab091fba86155992fe55b9001b 100644 (file)
@@ -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.
         
 package org.apache.poi.hpsf;
 
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
 import java.util.List;
 
+import org.apache.poi.poifs.filesystem.DirectoryEntry;
+
 /**
  * <p>Abstract superclass for the convenience classes {@link
  * SummaryInformation} and {@link DocumentSummaryInformation}.</p>
@@ -50,24 +54,37 @@ import java.util.List;
  * @version $Id$
  * @since 2002-02-09
  */
-public abstract class SpecialPropertySet extends PropertySet
+public abstract class SpecialPropertySet extends MutablePropertySet
 {
 
     /**
      * <p>The "real" property set <code>SpecialPropertySet</code>
      * delegates to.</p>
      */
-    private PropertySet delegate;
+    private MutablePropertySet delegate;
 
 
 
     /**
      * <p>Creates a <code>SpecialPropertySet</code>.
      *
-     * @param ps The property set encapsulated by the
+     * @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;
     }
@@ -157,9 +174,178 @@ public abstract class SpecialPropertySet extends PropertySet
     /**
      * @see PropertySet#getSingleSection
      */
-    public Section getSingleSection()
+    public Section getFirstSection()
+    {
+        return delegate.getFirstSection();
+    }
+
+
+    /**
+     * @see org.apache.poi.hpsf.MutablePropertySet#addSection(org.apache.poi.hpsf.Section)
+     */
+    public void addSection(final Section section)
+    {
+        delegate.addSection(section);
+    }
+
+
+
+    /**
+     * @see org.apache.poi.hpsf.MutablePropertySet#clearSections()
+     */
+    public void clearSections()
+    {
+        delegate.clearSections();
+    }
+
+
+
+    /**
+     * @see org.apache.poi.hpsf.MutablePropertySet#setByteOrder(int)
+     */
+    public void setByteOrder(final int byteOrder)
+    {
+        delegate.setByteOrder(byteOrder);
+    }
+
+
+
+    /**
+     * @see org.apache.poi.hpsf.MutablePropertySet#setClassID(org.apache.poi.hpsf.ClassID)
+     */
+    public void setClassID(final ClassID classID)
+    {
+        delegate.setClassID(classID);
+    }
+
+
+
+    /**
+     * @see org.apache.poi.hpsf.MutablePropertySet#setFormat(int)
+     */
+    public void setFormat(final int format)
+    {
+        delegate.setFormat(format);
+    }
+
+
+
+    /**
+     * @see org.apache.poi.hpsf.MutablePropertySet#setOSVersion(int)
+     */
+    public void setOSVersion(final int osVersion)
+    {
+        delegate.setOSVersion(osVersion);
+    }
+
+
+
+    /**
+     * @see org.apache.poi.hpsf.MutablePropertySet#toInputStream()
+     */
+    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)
+     */
+    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)
+     */
+    public void write(final OutputStream out) throws WritingNotSupportedException, IOException
+    {
+        delegate.write(out);
+    }
+
+
+
+    /**
+     * @see org.apache.poi.hpsf.PropertySet#equals(java.lang.Object)
+     */
+    public boolean equals(final Object o)
+    {
+        return delegate.equals(o);
+    }
+
+
+
+    /**
+     * @see org.apache.poi.hpsf.PropertySet#getProperties()
+     */
+    public Property[] getProperties() throws NoSingleSectionException
+    {
+        return delegate.getProperties();
+    }
+
+
+
+    /**
+     * @see org.apache.poi.hpsf.PropertySet#getProperty(int)
+     */
+    protected Object getProperty(final int id) throws NoSingleSectionException
+    {
+        return delegate.getProperty(id);
+    }
+
+
+
+    /**
+     * @see org.apache.poi.hpsf.PropertySet#getPropertyBooleanValue(int)
+     */
+    protected boolean getPropertyBooleanValue(final int id) throws NoSingleSectionException
+    {
+        return delegate.getPropertyBooleanValue(id);
+    }
+
+
+
+    /**
+     * @see org.apache.poi.hpsf.PropertySet#getPropertyIntValue(int)
+     */
+    protected int getPropertyIntValue(final int id) throws NoSingleSectionException
+    {
+        return delegate.getPropertyIntValue(id);
+    }
+
+
+
+    /**
+     * @see org.apache.poi.hpsf.PropertySet#hashCode()
+     */
+    public int hashCode()
+    {
+        return delegate.hashCode();
+    }
+
+
+
+    /**
+     * @see org.apache.poi.hpsf.PropertySet#toString()
+     */
+    public String toString()
+    {
+        return delegate.toString();
+    }
+
+
+
+    /**
+     * @see org.apache.poi.hpsf.PropertySet#wasNull()
+     */
+    public boolean wasNull() throws NoSingleSectionException
     {
-        return delegate.getSingleSection();
+        return delegate.wasNull();
     }
 
 }
index a170225df095ececc89a653ecdecca27a0f573e5..7a4941448edf2ffdcd353a9e146149ea89a47e52 100644 (file)
@@ -1,31 +1,33 @@
+/*
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ */
 
-/* ====================================================================
-   Copyright 2002-2004   Apache Software Foundation
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-   you may not use this file except in compliance with the License.
-   You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-   Unless required by applicable law or agreed to in writing, software
-   distributed under the License is distributed on an "AS IS" BASIS,
-   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-   See the License for the specific language governing permissions and
-   limitations under the License.
-==================================================================== */
-        
 package org.apache.poi.hpsf;
 
 import java.util.Date;
+
 import org.apache.poi.hpsf.wellknown.PropertyIDMap;
 
 /**
  * <p>Convenience class representing a Summary Information stream in a
  * Microsoft Office document.</p>
- *
+ * 
  * @author Rainer Klute <a
- * href="mailto:klute@rainer-klute.de">&lt;klute@rainer-klute.de&gt;</a>
+ *         href="mailto:klute@rainer-klute.de">&lt;klute@rainer-klute.de&gt;</a>
  * @see DocumentSummaryInformation
  * @version $Id$
  * @since 2002-02-09
@@ -34,8 +36,8 @@ public class SummaryInformation extends SpecialPropertySet
 {
 
     /**
-     * <p>The document name a summary information stream usually has
-     * in a POIFS filesystem.</p>
+     * <p>The document name a summary information stream usually has in a POIFS
+     * filesystem.</p>
      */
     public static final String DEFAULT_STREAM_NAME = "\005SummaryInformation";
 
@@ -44,26 +46,26 @@ public class SummaryInformation extends SpecialPropertySet
     /**
      * <p>Creates a {@link SummaryInformation} from a given {@link
      * PropertySet}.</p>
-     *
+     * 
      * @param ps A property set which should be created from a summary
-     * information stream.
-     * @throws UnexpectedPropertySetTypeException if <var>ps</var>
-     * does not contain a summary information stream.
+     *        information stream.
+     * @throws UnexpectedPropertySetTypeException if <var>ps</var> does not
+     *         contain a summary information stream.
      */
     public SummaryInformation(final PropertySet ps)
-        throws UnexpectedPropertySetTypeException
+            throws UnexpectedPropertySetTypeException
     {
         super(ps);
         if (!isSummaryInformation())
-            throw new UnexpectedPropertySetTypeException
-                ("Not a " + getClass().getName());
+            throw new UnexpectedPropertySetTypeException("Not a "
+                    + getClass().getName());
     }
 
 
 
     /**
-     * <p>Returns the stream's title (or <code>null</code>).</p>
-     *
+     * <p>Returns the title (or <code>null</code>).</p>
+     * 
      * @return The title or <code>null</code>
      */
     public String getTitle()
@@ -74,8 +76,32 @@ public class SummaryInformation extends SpecialPropertySet
 
 
     /**
-     * <p>Returns the stream's subject (or <code>null</code>).</p>
-     *
+     * <p>Sets the title.</p>
+     * 
+     * @param title The title to set.
+     */
+    public void setTitle(final String title)
+    {
+        final MutableSection s = (MutableSection) getFirstSection();
+        s.setProperty(PropertyIDMap.PID_TITLE, title);
+    }
+
+
+
+    /**
+     * <p>Removes the title.</p>
+     */
+    public void removeTitle()
+    {
+        final MutableSection s = (MutableSection) getFirstSection();
+        s.removeProperty(PropertyIDMap.PID_TITLE);
+    }
+
+
+
+    /**
+     * <p>Returns the subject (or <code>null</code>).</p>
+     * 
      * @return The subject or <code>null</code>
      */
     public String getSubject()
@@ -86,8 +112,32 @@ public class SummaryInformation extends SpecialPropertySet
 
 
     /**
-     * <p>Returns the stream's author (or <code>null</code>).</p>
-     *
+     * <p>Sets the subject.</p>
+     * 
+     * @param subject The subject to set.
+     */
+    public void setSubject(final String subject)
+    {
+        final MutableSection s = (MutableSection) getFirstSection();
+        s.setProperty(PropertyIDMap.PID_SUBJECT, subject);
+    }
+
+
+
+    /**
+     * <p>Removes the subject.</p>
+     */
+    public void removeSubject()
+    {
+        final MutableSection s = (MutableSection) getFirstSection();
+        s.removeProperty(PropertyIDMap.PID_SUBJECT);
+    }
+
+
+
+    /**
+     * <p>Returns the author (or <code>null</code>).</p>
+     * 
      * @return The author or <code>null</code>
      */
     public String getAuthor()
@@ -98,8 +148,32 @@ public class SummaryInformation extends SpecialPropertySet
 
 
     /**
-     * <p>Returns the stream's keywords (or <code>null</code>).</p>
-     *
+     * <p>Sets the author.</p>
+     * 
+     * @param author The author to set.
+     */
+    public void setAuthor(final String author)
+    {
+        final MutableSection s = (MutableSection) getFirstSection();
+        s.setProperty(PropertyIDMap.PID_AUTHOR, author);
+    }
+
+
+
+    /**
+     * <p>Removes the author.</p>
+     */
+    public void removeAuthor()
+    {
+        final MutableSection s = (MutableSection) getFirstSection();
+        s.removeProperty(PropertyIDMap.PID_AUTHOR);
+    }
+
+
+
+    /**
+     * <p>Returns the keywords (or <code>null</code>).</p>
+     * 
      * @return The keywords or <code>null</code>
      */
     public String getKeywords()
@@ -110,8 +184,32 @@ public class SummaryInformation extends SpecialPropertySet
 
 
     /**
-     * <p>Returns the stream's comments (or <code>null</code>).</p>
-     *
+     * <p>Sets the keywords.</p>
+     * 
+     * @param keywords The keywords to set.
+     */
+    public void setKeywords(final String keywords)
+    {
+        final MutableSection s = (MutableSection) getFirstSection();
+        s.setProperty(PropertyIDMap.PID_KEYWORDS, keywords);
+    }
+
+
+
+    /**
+     * <p>Removes the keywords.</p>
+     */
+    public void removeKeywords()
+    {
+        final MutableSection s = (MutableSection) getFirstSection();
+        s.removeProperty(PropertyIDMap.PID_KEYWORDS);
+    }
+
+
+
+    /**
+     * <p>Returns the comments (or <code>null</code>).</p>
+     * 
      * @return The comments or <code>null</code>
      */
     public String getComments()
@@ -122,8 +220,32 @@ public class SummaryInformation extends SpecialPropertySet
 
 
     /**
-     * <p>Returns the stream's template (or <code>null</code>).</p>
-     *
+     * <p>Sets the comments.</p>
+     * 
+     * @param comments The comments to set.
+     */
+    public void setComments(final String comments)
+    {
+        final MutableSection s = (MutableSection) getFirstSection();
+        s.setProperty(PropertyIDMap.PID_COMMENTS, comments);
+    }
+
+
+
+    /**
+     * <p>Removes the comments.</p>
+     */
+    public void removeComments()
+    {
+        final MutableSection s = (MutableSection) getFirstSection();
+        s.removeProperty(PropertyIDMap.PID_COMMENTS);
+    }
+
+
+
+    /**
+     * <p>Returns the template (or <code>null</code>).</p>
+     * 
      * @return The template or <code>null</code>
      */
     public String getTemplate()
@@ -134,8 +256,32 @@ public class SummaryInformation extends SpecialPropertySet
 
 
     /**
-     * <p>Returns the stream's last author (or <code>null</code>).</p>
-     *
+     * <p>Sets the template.</p>
+     * 
+     * @param template The template to set.
+     */
+    public void setTemplate(final String template)
+    {
+        final MutableSection s = (MutableSection) getFirstSection();
+        s.setProperty(PropertyIDMap.PID_TEMPLATE, template);
+    }
+
+
+
+    /**
+     * <p>Removes the template.</p>
+     */
+    public void removeTemplate()
+    {
+        final MutableSection s = (MutableSection) getFirstSection();
+        s.removeProperty(PropertyIDMap.PID_TEMPLATE);
+    }
+
+
+
+    /**
+     * <p>Returns the last author (or <code>null</code>).</p>
+     * 
      * @return The last author or <code>null</code>
      */
     public String getLastAuthor()
@@ -146,9 +292,32 @@ public class SummaryInformation extends SpecialPropertySet
 
 
     /**
-     * <p>Returns the stream's revision number (or
-     * <code>null</code>). </p>
-     *
+     * <p>Sets the last author.</p>
+     * 
+     * @param lastAuthor The last author to set.
+     */
+    public void setLastAuthor(final String lastAuthor)
+    {
+        final MutableSection s = (MutableSection) getFirstSection();
+        s.setProperty(PropertyIDMap.PID_LASTAUTHOR, lastAuthor);
+    }
+
+
+
+    /**
+     * <p>Removes the last author.</p>
+     */
+    public void removeLastAuthor()
+    {
+        final MutableSection s = (MutableSection) getFirstSection();
+        s.removeProperty(PropertyIDMap.PID_LASTAUTHOR);
+    }
+
+
+
+    /**
+     * <p>Returns the revision number (or <code>null</code>). </p>
+     * 
      * @return The revision number or <code>null</code>
      */
     public String getRevNumber()
@@ -159,11 +328,35 @@ public class SummaryInformation extends SpecialPropertySet
 
 
     /**
-     * <p>Returns the total time spent in editing the document
-     * (or <code>0</code>).</p>
-     *
+     * <p>Sets the revision number.</p>
+     * 
+     * @param revNumber The revision number to set.
+     */
+    public void setRevNumber(final String revNumber)
+    {
+        final MutableSection s = (MutableSection) getFirstSection();
+        s.setProperty(PropertyIDMap.PID_REVNUMBER, revNumber);
+    }
+
+
+
+    /**
+     * <p>Removes the revision number.</p>
+     */
+    public void removeRevNumber()
+    {
+        final MutableSection s = (MutableSection) getFirstSection();
+        s.removeProperty(PropertyIDMap.PID_REVNUMBER);
+    }
+
+
+
+    /**
+     * <p>Returns the total time spent in editing the document (or
+     * <code>0</code>).</p>
+     * 
      * @return The total time spent in editing the document or 0 if the {@link
-     * SummaryInformation} does not contain this information.
+     *         SummaryInformation} does not contain this information.
      */
     public long getEditTime()
     {
@@ -177,9 +370,33 @@ public class SummaryInformation extends SpecialPropertySet
 
 
     /**
-     * <p>Returns the stream's last printed time (or
-     * <code>null</code>).</p>
-     *
+     * <p>Sets the total time spent in editing the document.</p>
+     * 
+     * @param time The time to set.
+     */
+    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);
+    }
+
+
+
+    /**
+     * <p>Remove the total time spent in editing the document.</p>
+     */
+    public void removeEditTime()
+    {
+        final MutableSection s = (MutableSection) getFirstSection();
+        s.removeProperty(PropertyIDMap.PID_EDITTIME);
+    }
+
+
+
+    /**
+     * <p>Returns the last printed time (or <code>null</code>).</p>
+     * 
      * @return The last printed time or <code>null</code>
      */
     public Date getLastPrinted()
@@ -190,9 +407,33 @@ public class SummaryInformation extends SpecialPropertySet
 
 
     /**
-     * <p>Returns the stream's creation time (or
-     * <code>null</code>).</p>
-     *
+     * <p>Sets the lastPrinted.</p>
+     * 
+     * @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);
+    }
+
+
+
+    /**
+     * <p>Removes the lastPrinted.</p>
+     */
+    public void removeLastPrinted()
+    {
+        final MutableSection s = (MutableSection) getFirstSection();
+        s.removeProperty(PropertyIDMap.PID_LASTPRINTED);
+    }
+
+
+
+    /**
+     * <p>Returns the creation time (or <code>null</code>).</p>
+     * 
      * @return The creation time or <code>null</code>
      */
     public Date getCreateDateTime()
@@ -203,9 +444,33 @@ public class SummaryInformation extends SpecialPropertySet
 
 
     /**
-     * <p>Returns the stream's last save time (or
-     * <code>null</code>).</p>
-     *
+     * <p>Sets the creation time.</p>
+     * 
+     * @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);
+    }
+
+
+
+    /**
+     * <p>Removes the creation time.</p>
+     */
+    public void removeCreateDateTime()
+    {
+        final MutableSection s = (MutableSection) getFirstSection();
+        s.removeProperty(PropertyIDMap.PID_CREATE_DTM);
+    }
+
+
+
+    /**
+     * <p>Returns the last save time (or <code>null</code>).</p>
+     * 
      * @return The last save time or <code>null</code>
      */
     public Date getLastSaveDateTime()
@@ -216,11 +481,37 @@ public class SummaryInformation extends SpecialPropertySet
 
 
     /**
-     * <p>Returns the stream's page count or 0 if the {@link
-     * SummaryInformation} does not contain a page count.</p>
-     *
+     * <p>Sets the total time spent in editing the document.</p>
+     * 
+     * @param time The time to set.
+     */
+    public void setLastSaveDateTime(final Date time)
+    {
+        final MutableSection s = (MutableSection) getFirstSection();
+        s
+                .setProperty(PropertyIDMap.PID_LASTSAVE_DTM,
+                        Variant.VT_FILETIME, time);
+    }
+
+
+
+    /**
+     * <p>Remove the total time spent in editing the document.</p>
+     */
+    public void removeLastSaveDateTime()
+    {
+        final MutableSection s = (MutableSection) getFirstSection();
+        s.removeProperty(PropertyIDMap.PID_LASTSAVE_DTM);
+    }
+
+
+
+    /**
+     * <p>Returns the page count or 0 if the {@link SummaryInformation} does
+     * not contain a page count.</p>
+     * 
      * @return The page count or 0 if the {@link SummaryInformation} does not
-     * contain a page count.
+     *         contain a page count.
      */
     public int getPageCount()
     {
@@ -230,9 +521,33 @@ public class SummaryInformation extends SpecialPropertySet
 
 
     /**
-     * <p>Returns the stream's word count or 0 if the {@link
-     * SummaryInformation} does not contain a word count.</p>
-     *
+     * <p>Sets the page count.</p>
+     * 
+     * @param pageCount The page count to set.
+     */
+    public void setPageCount(final int pageCount)
+    {
+        final MutableSection s = (MutableSection) getFirstSection();
+        s.setProperty(PropertyIDMap.PID_PAGECOUNT, pageCount);
+    }
+
+
+
+    /**
+     * <p>Removes the page count.</p>
+     */
+    public void removePageCount()
+    {
+        final MutableSection s = (MutableSection) getFirstSection();
+        s.removeProperty(PropertyIDMap.PID_PAGECOUNT);
+    }
+
+
+
+    /**
+     * <p>Returns the word count or 0 if the {@link SummaryInformation} does
+     * not contain a word count.</p>
+     * 
      * @return The word count or <code>null</code>
      */
     public int getWordCount()
@@ -243,9 +558,33 @@ public class SummaryInformation extends SpecialPropertySet
 
 
     /**
-     * <p>Returns the stream's character count or 0 if the {@link
-     * SummaryInformation} does not contain a char count.</p>
-     *
+     * <p>Sets the word count.</p>
+     * 
+     * @param wordCount The word count to set.
+     */
+    public void setWordCount(final int wordCount)
+    {
+        final MutableSection s = (MutableSection) getFirstSection();
+        s.setProperty(PropertyIDMap.PID_WORDCOUNT, wordCount);
+    }
+
+
+
+    /**
+     * <p>Removes the word count.</p>
+     */
+    public void removeWordCount()
+    {
+        final MutableSection s = (MutableSection) getFirstSection();
+        s.removeProperty(PropertyIDMap.PID_WORDCOUNT);
+    }
+
+
+
+    /**
+     * <p>Returns the character count or 0 if the {@link SummaryInformation}
+     * does not contain a char count.</p>
+     * 
      * @return The character count or <code>null</code>
      */
     public int getCharCount()
@@ -256,15 +595,39 @@ public class SummaryInformation extends SpecialPropertySet
 
 
     /**
-     * <p>Returns the stream's thumbnail (or <code>null</code>)
-     * <strong>when this method is implemented. Please note that the
-     * return type is likely to change!</strong></p>
-     *
-     * <p><strong>Hint to developers:</strong> Drew Varner &lt;Drew.Varner -at-
-     * sc.edu&gt; said that this is an image in WMF or Clipboard (BMP?) format.
-     * However, we won't do any conversion into any image type but instead just
-     * return a byte array.</p>
-     *
+     * <p>Sets the character count.</p>
+     * 
+     * @param charCount The character count to set.
+     */
+    public void setCharCount(final int charCount)
+    {
+        final MutableSection s = (MutableSection) getFirstSection();
+        s.setProperty(PropertyIDMap.PID_CHARCOUNT, charCount);
+    }
+
+
+
+    /**
+     * <p>Removes the character count.</p>
+     */
+    public void removeCharCount()
+    {
+        final MutableSection s = (MutableSection) getFirstSection();
+        s.removeProperty(PropertyIDMap.PID_CHARCOUNT);
+    }
+
+
+
+    /**
+     * <p>Returns the thumbnail (or <code>null</code>) <strong>when this
+     * method is implemented. Please note that the return type is likely to
+     * change!</strong></p>
+     * 
+     * <p><strong>Hint to developers:</strong> Drew Varner &lt;Drew.Varner
+     * -at- sc.edu&gt; said that this is an image in WMF or Clipboard (BMP?)
+     * format. However, we won't do any conversion into any image type but
+     * instead just return a byte array.</p>
+     * 
      * @return The thumbnail or <code>null</code>
      */
     public byte[] getThumbnail()
@@ -275,9 +638,33 @@ public class SummaryInformation extends SpecialPropertySet
 
 
     /**
-     * <p>Returns the stream's application name (or
-     * <code>null</code>).</p>
-     *
+     * <p>Sets the thumbnail.</p>
+     * 
+     * @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);
+    }
+
+
+
+    /**
+     * <p>Removes the thumbnail.</p>
+     */
+    public void removeThumbnail()
+    {
+        final MutableSection s = (MutableSection) getFirstSection();
+        s.removeProperty(PropertyIDMap.PID_THUMBNAIL);
+    }
+
+
+
+    /**
+     * <p>Returns the application name (or <code>null</code>).</p>
+     * 
      * @return The application name or <code>null</code>
      */
     public String getApplicationName()
@@ -288,35 +675,49 @@ public class SummaryInformation extends SpecialPropertySet
 
 
     /**
-     * <p>Returns a security code which is one of the following
-     * values:</p>
-     *
+     * <p>Sets the application name.</p>
+     * 
+     * @param applicationName The application name to set.
+     */
+    public void setApplicationName(final String applicationName)
+    {
+        final MutableSection s = (MutableSection) getFirstSection();
+        s.setProperty(PropertyIDMap.PID_APPNAME, applicationName);
+    }
+
+
+
+    /**
+     * <p>Removes the application name.</p>
+     */
+    public void removeApplicationName()
+    {
+        final MutableSection s = (MutableSection) getFirstSection();
+        s.removeProperty(PropertyIDMap.PID_APPNAME);
+    }
+
+
+
+    /**
+     * <p>Returns a security code which is one of the following values:</p>
+     * 
      * <ul>
-     *  <li>
-     *   <p>0 if the {@link SummaryInformation} does not contain a
-     *   security field or if there is no security on the
-     *   document. Use {@link #wasNull} to distinguish between the
-     *   two cases!</p>
-     *  </li>
-     *
-     *  <li>
-     *   <p>1 if the document is password protected</p>
-     *  </li>
-     *
-     *   <li>
-     *    <p>2 if the document is read-only recommended</p>
-     *   </li>
-     *
-     *   <li>
-     *    <p>4 if the document is read-only enforced</p>
-     *   </li>
-     *
-     *   <li>
-     *    <p>8 if the document is locked for annotations</p>
-     *   </li>
-     *
+     * 
+     * <li><p>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>
+     * 
+     * <li><p>1 if the document is password protected</p></li>
+     * 
+     * <li><p>2 if the document is read-only recommended</p></li>
+     * 
+     * <li><p>4 if the document is read-only enforced</p></li>
+     * 
+     * <li><p>8 if the document is locked for annotations</p></li>
+     * 
      * </ul>
-     *
+     * 
      * @return The security code or <code>null</code>
      */
     public int getSecurity()
@@ -324,4 +725,28 @@ public class SummaryInformation extends SpecialPropertySet
         return getPropertyIntValue(PropertyIDMap.PID_SECURITY);
     }
 
+
+
+    /**
+     * <p>Sets the security code.</p>
+     * 
+     * @param security The security code to set.
+     */
+    public void setSecurity(final int security)
+    {
+        final MutableSection s = (MutableSection) getFirstSection();
+        s.setProperty(PropertyIDMap.PID_SECURITY, security);
+    }
+
+
+
+    /**
+     * <p>Removes the security code.</p>
+     */
+    public void removeSecurity()
+    {
+        final MutableSection s = (MutableSection) getFirstSection();
+        s.removeProperty(PropertyIDMap.PID_SECURITY);
+    }
+
 }
index fd168822a87e48021ba4a59e19b1fae57d5692d2..14d1a0f3f791f18a4fef39fcbfed4d64141ae3ab 100644 (file)
@@ -1,4 +1,3 @@
-
 /* ====================================================================
    Copyright 2002-2004   Apache Software Foundation
 
index c88e98d07baafbe519e839ca04199089504e2ff0..acfcfa6c34ca435985f140c706f9387ddd212d5d 100644 (file)
@@ -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.
@@ -46,7 +45,7 @@ public class TypeWriter
     {
         final int length = LittleEndian.SHORT_SIZE;
         byte[] buffer = new byte[length];
-        LittleEndian.putUShort(buffer, 0, n);
+        LittleEndian.putShort(buffer, 0, n); // FIXME: unsigned
         out.write(buffer, 0, length);
         return length;
     }
@@ -74,6 +73,27 @@ public class TypeWriter
 
 
 
+    /**
+     * <p>Writes a eight-byte value to an output stream.</p>
+     *
+     * @param out The stream to write to.
+     * @param n The value to write.
+     * @exception IOException if an I/O error occurs
+     * @return The number of bytes written to the output stream. 
+     */
+    public static int writeToStream(final OutputStream out, final long n)
+        throws IOException
+    {
+        final int l = LittleEndian.LONG_SIZE;
+        final byte[] buffer = new byte[l];
+        LittleEndian.putLong(buffer, 0, n);
+        out.write(buffer, 0, l);
+        return l;
+        
+    }
+
+
+
     /**
      * <p>Writes an unsigned two-byte value to an output stream.</p>
      *
@@ -118,6 +138,7 @@ public class TypeWriter
      *
      * @param out The stream to write to
      * @param n The value to write
+     * @return The number of bytes written
      * @exception IOException if an I/O error occurs
      */
     public static int writeToStream(final OutputStream out, final ClassID n)
@@ -134,10 +155,13 @@ public class TypeWriter
     /**
      * <p>Writes an array of {@link Property} instances to an output stream
      * according to the Horrible Property Stream Format.</p>
-     *
+     * 
      * @param out The stream to write to
      * @param properties The array to write to the stream
+     * @param codepage The codepage number to use for writing strings
      * @exception IOException if an I/O error occurs
+     * @throws UnsupportedVariantTypeException if HPSF does not support some
+     *         variant type.
      */
     public static void writeToStream(final OutputStream out,
                                      final Property[] properties,
@@ -152,7 +176,7 @@ public class TypeWriter
          * ID and offset into the stream. */
         for (int i = 0; i < properties.length; i++)
         {
-            final Property p = (Property) properties[i];
+            final Property p = properties[i];
             writeUIntToStream(out, p.getID());
             writeUIntToStream(out, p.getSize());
         }
@@ -160,7 +184,7 @@ public class TypeWriter
         /* Write the properties themselves. */
         for (int i = 0; i < properties.length; i++)
         {
-            final Property p = (Property) properties[i];
+            final Property p = properties[i];
             long type = p.getType();
             writeUIntToStream(out, type);
             VariantSupport.write(out, (int) type, p.getValue(), codepage);
index 02a47b1986ce8dd8d4e29cac0b8b63a4ee7fa3eb..4f0aa8a9e20b650c6c7ec520308fb57e43fa9fc2 100644 (file)
@@ -1,4 +1,3 @@
-
 /* ====================================================================
    Copyright 2002-2004   Apache Software Foundation
 
index 8503fdfb03955ad4e769e4dc489eb034a1dcdb6e..9cdf1805bdd108f0c423c7ba88fc427e7fd458ba 100644 (file)
@@ -21,7 +21,7 @@ import org.apache.poi.util.HexDump;
 /**
  * <p>This exception is thrown if HPSF encounters a variant type that isn't
  * supported yet. Although a variant type is unsupported the value can still be
- * retrieved using the {@link #getValue} method.</p>
+ * retrieved using the {@link VariantTypeException#getValue} method.</p>
  * 
  * <p>Obviously this class should disappear some day.</p>
  *
index 60e9e9b9d354ef3101560801b92c0318e94efb36..d58e254256afdbeb387a735ccd1e9216a77a0dfb 100644 (file)
@@ -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.
@@ -152,6 +151,21 @@ public class Util
     public static Date filetimeToDate(final int high, final int low)
     {
         final long filetime = ((long) high) << 32 | (low & 0xffffffffL);
+        return filetimeToDate(filetime);
+    }
+
+    /**
+     * <p>Converts a Windows FILETIME into a {@link Date}. The Windows
+     * 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.</p>
+     *
+     * @param filetime The filetime to convert.
+     * @return The Windows FILETIME as a {@link Date}.
+     */
+    public static Date filetimeToDate(final long filetime)
+    {
         final long ms_since_16010101 = filetime / (1000 * 10);
         final long ms_since_19700101 = ms_since_16010101 - EPOCH_DIFF;
         return new Date(ms_since_19700101);
@@ -165,7 +179,8 @@ public class Util
      * @param date The date to be converted
      * @return The filetime
      * 
-     * @see #filetimeToDate
+     * @see #filetimeToDate(long)
+     * @see #filetimeToDate(int, int)
      */
     public static long dateToFileTime(final Date date)
     {
index 7b300bc0e1b78866f505275e65a3ab70fab84dbc..a64b05b661471aad9d1e9d44daf7d735a9398f40 100644 (file)
@@ -1,4 +1,3 @@
-
 /* ====================================================================
    Copyright 2002-2004   Apache Software Foundation
 
index 703f925de1260f01c692ec1a8c0751d32e9c1701..8994bb2fa1edcb84371e7b951c013288b0850955 100644 (file)
@@ -1,5 +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.
@@ -157,16 +157,25 @@ public class VariantSupport extends Variant
                  * Read a short. In Java it is represented as an
                  * Integer object.
                  */
-                value = new Integer(LittleEndian.getUShort(src, o1));
+                value = new Integer(LittleEndian.getShort(src, o1));
                 break;
             }
             case Variant.VT_I4:
             {
                 /*
-                 * Read a word. In Java it is represented as a
+                 * Read a word. In Java it is represented as an
+                 * Integer object.
+                 */
+                value = new Integer(LittleEndian.getInt(src, o1));
+                break;
+            }
+            case Variant.VT_I8:
+            {
+                /*
+                 * Read a double word. In Java it is represented as a
                  * Long object.
                  */
-                value = new Long(LittleEndian.getUInt(src, o1));
+                value = new Long(LittleEndian.getLong(src, o1));
                 break;
             }
             case Variant.VT_R8:
@@ -204,9 +213,9 @@ public class VariantSupport extends Variant
                     last--;
                 final int l = (int) (last - first + 1);
                 value = codepage != -1 ?
-                    new String(src, (int) first, l,
+                    new String(src, first, l,
                                codepageToEncoding(codepage)) :
-                    new String(src, (int) first, l);
+                    new String(src, first, l);
                 break;
             }
             case Variant.VT_LPWSTR:
@@ -240,7 +249,7 @@ public class VariantSupport extends Variant
             {
                 final byte[] v = new byte[l1];
                 for (int i = 0; i < l1; i++)
-                    v[i] = src[(int) (o1 + i)];
+                    v[i] = src[(o1 + i)];
                 value = v;
                 break;
             }
@@ -263,7 +272,7 @@ public class VariantSupport extends Variant
             {
                 final byte[] v = new byte[l1];
                 for (int i = 0; i < l1; i++)
-                    v[i] = src[(int) (o1 + i)];
+                    v[i] = src[(o1 + i)];
                 throw new ReadingNotSupportedException(type, v);
             }
         }
@@ -397,8 +406,8 @@ public class VariantSupport extends Variant
                 char[] s = Util.pad4((String) value);
                 for (int i = 0; i < s.length; i++)
                 {
-                    final int high = (int) ((s[i] & 0x0000ff00) >> 8);
-                    final int low = (int) (s[i] & 0x000000ff);
+                    final int high = ((s[i] & 0x0000ff00) >> 8);
+                    final int low = (s[i] & 0x000000ff);
                     final byte highb = (byte) high;
                     final byte lowb = (byte) low;
                     out.write(lowb);
@@ -431,8 +440,21 @@ public class VariantSupport extends Variant
             }
             case Variant.VT_I4:
             {
+                if (!(value instanceof Integer))
+                {
+                    throw new ClassCastException("Could not cast an object to "
+                            + Integer.class.toString() + ": "
+                            + value.getClass().toString() + ", "
+                            + value.toString());
+                }
                 length += TypeWriter.writeToStream(out, 
-                          ((Long) value).intValue());
+                          ((Integer) value).intValue());
+                break;
+            }
+            case Variant.VT_I8:
+            {
+                TypeWriter.writeToStream(out, ((Long) value).longValue());
+                length = LittleEndianConsts.LONG_SIZE;
                 break;
             }
             case Variant.VT_R8:
index 64e955a703f8ab21527aaf067cb1fa051d9bc126..f35b5c607052650cffdc32ef932358fbb2bbc572 100644 (file)
@@ -1,4 +1,3 @@
-
 /* ====================================================================
    Copyright 2002-2004   Apache Software Foundation
 
index 77a17b5224fb55bffa6b14b37b51b120d9fd7dde..517af3b1edfe26da621aac8ff609f435590aabfd 100644 (file)
@@ -1,4 +1,3 @@
-
 /* ====================================================================
    Copyright 2002-2004   Apache Software Foundation
 
@@ -231,6 +230,11 @@ public class PropertyIDMap extends HashMap
      * re-evaluated.</p>
      */
     public static final int PID_LINKSDIRTY = 16;
+    
+    /**
+     * <p>The highest well-known property ID. Applications are free to use higher values for custom purposes.</p>
+     */
+    public static final int PID_MAX = PID_LINKSDIRTY;
 
 
 
index 93e837f2c66a8f554734a8e5e24d010795153d12..e5e2045896114bceab8ddde0026685867380f57c 100644 (file)
@@ -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.
@@ -53,16 +52,23 @@ public class SectionIDMap extends HashMap
     };
 
     /**
-     * <p>The DocumentSummaryInformation's first section's format
-     * ID. The second section has a different format ID which is not
-     * well-known.</p>
+     * <p>The DocumentSummaryInformation's first and second sections' format
+     * ID.</p>
      */
-    public static final byte[] DOCUMENT_SUMMARY_INFORMATION_ID = new byte[]
+    public static final byte[][] DOCUMENT_SUMMARY_INFORMATION_ID = new byte[][]
     {
-        (byte) 0xD5, (byte) 0xCD, (byte) 0xD5, (byte) 0x02,
-        (byte) 0x2E, (byte) 0x9C, (byte) 0x10, (byte) 0x1B,
-        (byte) 0x93, (byte) 0x97, (byte) 0x08, (byte) 0x00,
-        (byte) 0x2B, (byte) 0x2C, (byte) 0xF9, (byte) 0xAE
+        {
+            (byte) 0xD5, (byte) 0xCD, (byte) 0xD5, (byte) 0x02,
+            (byte) 0x2E, (byte) 0x9C, (byte) 0x10, (byte) 0x1B,
+            (byte) 0x93, (byte) 0x97, (byte) 0x08, (byte) 0x00,
+            (byte) 0x2B, (byte) 0x2C, (byte) 0xF9, (byte) 0xAE
+        },
+        {
+            (byte) 0xD5, (byte) 0xCD, (byte) 0xD5, (byte) 0x05,
+            (byte) 0x2E, (byte) 0x9C, (byte) 0x10, (byte) 0x1B,
+            (byte) 0x93, (byte) 0x97, (byte) 0x08, (byte) 0x00,
+            (byte) 0x2B, (byte) 0x2C, (byte) 0xF9, (byte) 0xAE
+        }
     };
 
     /**
@@ -91,7 +97,7 @@ public class SectionIDMap extends HashMap
             final SectionIDMap m = new SectionIDMap();
             m.put(SUMMARY_INFORMATION_ID,
                   PropertyIDMap.getSummaryInformationProperties());
-            m.put(DOCUMENT_SUMMARY_INFORMATION_ID,
+            m.put(DOCUMENT_SUMMARY_INFORMATION_ID[0],
                   PropertyIDMap.getDocumentSummaryInformationProperties());
             defaultMap = m;
         }
@@ -116,8 +122,7 @@ public class SectionIDMap extends HashMap
     public static String getPIDString(final byte[] sectionFormatID,
                                       final long pid)
     {
-        final PropertyIDMap m =
-            (PropertyIDMap) getInstance().get(sectionFormatID);
+        final PropertyIDMap m = getInstance().get(sectionFormatID);
         if (m == null)
             return UNDEFINED;
         else
@@ -178,7 +183,8 @@ public class SectionIDMap extends HashMap
 
     /**
      * @deprecated Use {@link #put(byte[], PropertyIDMap)} instead!
-     * @link #put(byte[], PropertyIDMap)
+     *
+     * @see #put(byte[], PropertyIDMap)
      * 
      * @param key This parameter remains undocumented since the method is
      * deprecated.
index d215469247e97b782e4b0b0d3f1b765819d501c2..f6c95d3d4156d67269fe35b3201a855f276a8f51 100644 (file)
@@ -1,5 +1,5 @@
 /* ====================================================================
-   Copyright 2003-2004   Apache Software Foundation
+   Copyright 2003-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.
@@ -64,9 +64,9 @@ public class LittleEndian
         short num = (short) getNumber(data, offset, SHORT_SIZE);
         int retNum;
         if (num < 0) {
-            retNum = ((int) Short.MAX_VALUE + 1) * 2 + (int) num;
+            retNum = (Short.MAX_VALUE + 1) * 2 + num;
         } else {
-            retNum = (int) num;
+            retNum = num;
         }
         return retNum;
     }
@@ -163,9 +163,9 @@ public class LittleEndian
         int num = (int) getNumber(data, offset, INT_SIZE);
         long retNum;
         if (num < 0) {
-            retNum = ((long) Integer.MAX_VALUE + 1) * 2 + (long) num;
+            retNum = ((long) Integer.MAX_VALUE + 1) * 2 + num;
         } else {
-            retNum = (int) num;
+            retNum = num;
         }
         return retNum;
     }
@@ -522,7 +522,7 @@ public class LittleEndian
      *@return    Description of the Return Value
      */
     public static int ubyteToInt(byte b) {
-        return ((b & 0x80) == 0 ? (int) b : (int) (b & (byte) 0x7f) + 0x80);
+        return ((b & 0x80) == 0 ? (int) b : (b & (byte) 0x7f) + 0x80);
     }
 
 
@@ -566,5 +566,22 @@ public class LittleEndian
         return copy;
     }
 
+    /**
+     * <p>Gets an unsigned int value (8 bytes) from a byte array.</p>
+     * 
+     * @param data the byte array
+     * @param offset a starting offset into the byte array
+     * @return the unsigned int (32-bit) value in a long
+     */
+    public static long getULong(final byte[] data, final int offset)
+    {
+        int num = (int) getNumber(data, offset, LONG_SIZE);
+        long retNum;
+        if (num < 0)
+            retNum = ((long) Integer.MAX_VALUE + 1) * 2 + num;
+        else
+            retNum = num;
+        return retNum;
+    }
 
 }
diff --git a/src/testcases/org/apache/poi/hpsf/basic/AllDataFilesTester.java b/src/testcases/org/apache/poi/hpsf/basic/AllDataFilesTester.java
new file mode 100644 (file)
index 0000000..7396f41
--- /dev/null
@@ -0,0 +1,82 @@
+/* ====================================================================
+   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.basic;
+
+import java.io.File;
+import java.io.FileFilter;
+import java.util.logging.Logger;
+
+/**
+ * <p>Processes a test method for all OLE2 files in the HPSF test data
+ * directory. Well, this class does not check whether a file is an OLE2 file but
+ * rather whether its name begins with "Test".</p>
+ * 
+ * @author Rainer Klute <a
+ *         href="mailto:klute@rainer-klute.de">&lt;klute@rainer-klute.de&gt;</a>
+ * @since 2006-02-11
+ * @version $Id$
+ */
+public class AllDataFilesTester
+{
+
+    /**
+     * <p>Interface specifying how to run a test on a single file.</p>
+     *
+     * @author Rainer Klute <a
+     * href="mailto:klute@rainer-klute.de">&lt;klute@rainer-klute.de&gt;</a>
+     * @since 2006-02-11
+     * @version $Id$
+     */
+    public interface TestTask
+    {
+        /**
+         * <p>Executes a test on a single file.</p>
+         *
+         * @param file the file
+         * @throws Throwable if the method throws anything.
+         */
+        void runTest(File file) throws Throwable;
+    }
+
+    /**
+     * <p>Tests the simplified custom properties.</p>
+     *
+     * @param task the task to execute
+     * @throws Throwable 
+     */
+    public void runTests(final TestTask task) throws Throwable
+    {
+        final String dataDirName = System.getProperty("HPSF.testdata.path");
+        final File dataDir = new File(dataDirName);
+        final File[] docs = dataDir.listFiles(new FileFilter()
+        {
+            public boolean accept(final File file)
+            {
+                return file.isFile() && file.getName().startsWith("Test");
+            }});
+        for (int i = 0; i < docs.length; i++)
+        {
+            final File doc = docs[i];
+            final Logger logger = Logger.getLogger(getClass().getName());
+            logger.info("Processing file \" " + doc.toString() + "\".");
+
+            /* Execute the test task. */
+            task.runTest(doc);
+        }
+    }
+
+}
index e3b66725ad56b35440d3dd0a4b3c061b4eae6b2b..0ac068dac2fcfee4ae6254ce571bc0412fe3b2d7 100644 (file)
@@ -112,10 +112,8 @@ public class TestBasic extends TestCase
     /**
      * <p>Checks the names of the files in the POI filesystem. They
      * are expected to be in a certain order.</p>
-     * 
-     * @exception IOException if an I/O exception occurs
      */
-    public void testReadFiles() throws IOException
+    public void testReadFiles()
     {
         String[] expected = POI_FILES;
         for (int i = 0; i < expected.length; i++)
@@ -166,7 +164,7 @@ public class TestBasic extends TestCase
                 o = ex;
             }
             in.close();
-            Assert.assertEquals(o.getClass(), expected[i]);
+            Assert.assertEquals(expected[i], o.getClass());
         }
     }
 
@@ -223,7 +221,7 @@ public class TestBasic extends TestCase
         Assert.assertNotNull(s.getProperties());
         Assert.assertEquals(17, s.getPropertyCount());
         Assert.assertEquals("Titel", s.getProperty(2));
-        Assert.assertEquals(1764, s.getSize());
+        Assert.assertEquals(1748, s.getSize());
     }
 
 
index 03678d940bc27cb3d75f485930c544714d6e13b4..490017c6d830f2e7f55e0b5905c4dfef2402dd93 100644 (file)
-\r
-/* ====================================================================\r
-   Copyright 2002-2004   Apache Software Foundation\r
-\r
-   Licensed under the Apache License, Version 2.0 (the "License");\r
-   you may not use this file except in compliance with the License.\r
-   You may obtain a copy of the License at\r
-\r
-       http://www.apache.org/licenses/LICENSE-2.0\r
-\r
-   Unless required by applicable law or agreed to in writing, software\r
-   distributed under the License is distributed on an "AS IS" BASIS,\r
-   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
-   See the License for the specific language governing permissions and\r
-   limitations under the License.\r
-==================================================================== */\r
-        \r
-\r
-package org.apache.poi.hpsf.basic;\r
-\r
-import junit.framework.Assert;\r
-import junit.framework.TestCase;\r
-\r
-import org.apache.poi.hpsf.ClassID;\r
-\r
-/**\r
- * <p>Tests ClassID structure.</p>\r
- *\r
- * @author Michael Zalewski (zalewski@optonline.net)\r
- */\r
-public class TestClassID extends TestCase\r
-{\r
-    /**\r
-     * <p>Constructor</p>\r
-     * \r
-     * @param name the test case's name\r
-     */\r
-    public TestClassID(final String name)\r
-    {\r
-        super(name);\r
-    }\r
-\r
-    /**\r
-     * Various tests of overridden .equals()\r
-     */\r
-    public void testEquals()\r
-    {\r
-        ClassID clsidTest1 = new ClassID(\r
-              new byte[] {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,\r
-                          0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10}\r
-            , 0\r
-        );\r
-        ClassID clsidTest2 = new ClassID(\r
-              new byte[] {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,\r
-                          0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10}\r
-            , 0\r
-        );\r
-        ClassID clsidTest3 = new ClassID(\r
-              new byte[] {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,\r
-                          0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x11 }\r
-            , 0\r
-        );\r
-        Assert.assertEquals(clsidTest1, clsidTest1);\r
-        Assert.assertEquals(clsidTest1, clsidTest2);\r
-        Assert.assertFalse(clsidTest1.equals(clsidTest3));\r
-        Assert.assertFalse(clsidTest1.equals(null));\r
-    }\r
-    /**\r
-     * Try to write to a buffer that is too small. This should\r
-     *   throw an Exception\r
-     */\r
-    public void testWriteArrayStoreException()\r
-    {\r
-        ClassID clsidTest = new ClassID(\r
-              new byte[] {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,\r
-                          0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10}\r
-            , 0\r
-        );\r
-        boolean bExceptionOccurred = false;\r
-        try\r
-        {\r
-            clsidTest.write(new byte[15], 0);\r
-        }\r
-        catch (Exception e)\r
-        {\r
-            bExceptionOccurred = true;\r
-        }\r
-        Assert.assertTrue(bExceptionOccurred);\r
-\r
-        bExceptionOccurred = false;\r
-        try\r
-        {\r
-            clsidTest.write(new byte[16], 1);\r
-        }\r
-        catch (Exception e)\r
-        {\r
-            bExceptionOccurred = true;\r
-        }\r
-        Assert.assertTrue(bExceptionOccurred);\r
-\r
-        // These should work without throwing an Exception\r
-        bExceptionOccurred = false;\r
-        try\r
-        {\r
-            clsidTest.write(new byte[16], 0);\r
-            clsidTest.write(new byte[17], 1);\r
-        }\r
-        catch (Exception e)\r
-        {\r
-            bExceptionOccurred = true;\r
-        }\r
-        Assert.assertFalse(bExceptionOccurred);\r
-    }\r
-    /**\r
-     * <p>Tests the {@link PropertySet} methods. The test file has two\r
-     * property set: the first one is a {@link SummaryInformation},\r
-     * the second one is a {@link DocumentSummaryInformation}.</p>\r
-     */\r
-    public void testClassID()\r
-    {\r
-        ClassID clsidTest = new ClassID(\r
-              new byte[] {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,\r
-                          0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10}\r
-            , 0\r
-        );\r
-        Assert.assertEquals(clsidTest.toString().toUpperCase(),\r
-                            "{04030201-0605-0807-090A-0B0C0D0E0F10}"\r
-        );\r
-    }\r
-\r
-\r
-\r
-    /**\r
-     * <p>Runs the test cases stand-alone.</p>\r
-     * \r
-     * @param args Command-line parameters (ignored)\r
-     */\r
-    public static void main(final String[] args)\r
-    {\r
-        System.setProperty("HPSF.testdata.path",\r
-                           "./src/testcases/org/apache/poi/hpsf/data");\r
-        junit.textui.TestRunner.run(TestClassID.class);\r
-    }\r
-\r
-}\r
+/* ====================================================================
+   Copyright 2002-2004   Apache Software Foundation
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+==================================================================== */
+        
+
+package org.apache.poi.hpsf.basic;
+
+import junit.framework.Assert;
+import junit.framework.TestCase;
+
+import org.apache.poi.hpsf.ClassID;
+import org.apache.poi.hpsf.DocumentSummaryInformation;
+import org.apache.poi.hpsf.PropertySet;
+import org.apache.poi.hpsf.SummaryInformation;
+
+/**
+ * <p>Tests ClassID structure.</p>
+ *
+ * @author Michael Zalewski (zalewski@optonline.net)
+ */
+public class TestClassID extends TestCase
+{
+    /**
+     * <p>Constructor</p>
+     * 
+     * @param name the test case's name
+     */
+    public TestClassID(final String name)
+    {
+        super(name);
+    }
+
+    /**
+     * Various tests of overridden .equals()
+     */
+    public void testEquals()
+    {
+        ClassID clsidTest1 = new ClassID(
+              new byte[] {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+                          0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10}
+            , 0
+        );
+        ClassID clsidTest2 = new ClassID(
+              new byte[] {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+                          0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10}
+            , 0
+        );
+        ClassID clsidTest3 = new ClassID(
+              new byte[] {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+                          0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x11 }
+            , 0
+        );
+        Assert.assertEquals(clsidTest1, clsidTest1);
+        Assert.assertEquals(clsidTest1, clsidTest2);
+        Assert.assertFalse(clsidTest1.equals(clsidTest3));
+        Assert.assertFalse(clsidTest1.equals(null));
+    }
+    /**
+     * Try to write to a buffer that is too small. This should
+     *   throw an Exception
+     */
+    public void testWriteArrayStoreException()
+    {
+        ClassID clsidTest = new ClassID(
+              new byte[] {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+                          0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10}
+            , 0
+        );
+        boolean bExceptionOccurred = false;
+        try
+        {
+            clsidTest.write(new byte[15], 0);
+        }
+        catch (Exception e)
+        {
+            bExceptionOccurred = true;
+        }
+        Assert.assertTrue(bExceptionOccurred);
+
+        bExceptionOccurred = false;
+        try
+        {
+            clsidTest.write(new byte[16], 1);
+        }
+        catch (Exception e)
+        {
+            bExceptionOccurred = true;
+        }
+        Assert.assertTrue(bExceptionOccurred);
+
+        // These should work without throwing an Exception
+        bExceptionOccurred = false;
+        try
+        {
+            clsidTest.write(new byte[16], 0);
+            clsidTest.write(new byte[17], 1);
+        }
+        catch (Exception e)
+        {
+            bExceptionOccurred = true;
+        }
+        Assert.assertFalse(bExceptionOccurred);
+    }
+    /**
+     * <p>Tests the {@link PropertySet} methods. The test file has two
+     * property set: the first one is a {@link SummaryInformation},
+     * the second one is a {@link DocumentSummaryInformation}.</p>
+     */
+    public void testClassID()
+    {
+        ClassID clsidTest = new ClassID(
+              new byte[] {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+                          0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10}
+            , 0
+        );
+        Assert.assertEquals(clsidTest.toString().toUpperCase(),
+                            "{04030201-0605-0807-090A-0B0C0D0E0F10}"
+        );
+    }
+
+
+
+    /**
+     * <p>Runs the test cases stand-alone.</p>
+     * 
+     * @param args Command-line parameters (ignored)
+     */
+    public static void main(final String[] args)
+    {
+        System.setProperty("HPSF.testdata.path",
+                           "./src/testcases/org/apache/poi/hpsf/data");
+        junit.textui.TestRunner.run(TestClassID.class);
+    }
+
+}
index 59fe77418c8de3f2a791addcd5041d6d9c387a03..9f813d3ce32a5811decce74c58c5dc5c4577341e 100644 (file)
@@ -1,4 +1,3 @@
-
 /* ====================================================================
    Copyright 2002-2004   Apache Software Foundation
 
@@ -27,16 +26,18 @@ import java.io.UnsupportedEncodingException;
 import junit.framework.Assert;
 import junit.framework.TestCase;
 
+import org.apache.poi.hpsf.DocumentSummaryInformation;
 import org.apache.poi.hpsf.HPSFException;
 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.Variant;
 
 /**
  * <p>Test case for OLE2 files with empty properties. An empty property's type
- * is {@link Variant.VT_EMPTY}.</p>
+ * is {@link Variant#VT_EMPTY}.</p>
  *
  * @author Rainer Klute <a
  * href="mailto:klute@rainer-klute.de">&lt;klute@rainer-klute.de&gt;</a>
@@ -96,10 +97,8 @@ public class TestEmptyProperties extends TestCase
     /**
      * <p>Checks the names of the files in the POI filesystem. They
      * are expected to be in a certain order.</p>
-     * 
-     * @exception IOException if an I/O exception occurs
      */
-    public void testReadFiles() throws IOException
+    public void testReadFiles()
     {
         String[] expected = POI_FILES;
         for (int i = 0; i < expected.length; i++)
diff --git a/src/testcases/org/apache/poi/hpsf/basic/TestMetaDataIPI.java b/src/testcases/org/apache/poi/hpsf/basic/TestMetaDataIPI.java
new file mode 100644 (file)
index 0000000..00fcd2e
--- /dev/null
@@ -0,0 +1,827 @@
+/* ====================================================================\r
+   Copyright 2002-2006   Apache Software Foundation\r
+\r
+   Licensed under the Apache License, Version 2.0 (the "License");\r
+   you may not use this file except in compliance with the License.\r
+   You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+\r
+package org.apache.poi.hpsf.basic;\r
+\r
+import java.io.ByteArrayInputStream;\r
+import java.io.ByteArrayOutputStream;\r
+import java.io.FileNotFoundException;\r
+import java.io.IOException;\r
+import java.io.InputStream;\r
+import java.util.Date;\r
+import java.util.Random;\r
+\r
+import junit.framework.TestCase;\r
+\r
+import org.apache.poi.hpsf.CustomProperties;\r
+import org.apache.poi.hpsf.DocumentSummaryInformation;\r
+import org.apache.poi.hpsf.MarkUnsupportedException;\r
+import org.apache.poi.hpsf.NoPropertySetStreamException;\r
+import org.apache.poi.hpsf.PropertySet;\r
+import org.apache.poi.hpsf.PropertySetFactory;\r
+import org.apache.poi.hpsf.SummaryInformation;\r
+import org.apache.poi.hpsf.UnexpectedPropertySetTypeException;\r
+import org.apache.poi.hpsf.WritingNotSupportedException;\r
+import org.apache.poi.poifs.filesystem.DirectoryEntry;\r
+import org.apache.poi.poifs.filesystem.DocumentEntry;\r
+import org.apache.poi.poifs.filesystem.DocumentInputStream;\r
+import org.apache.poi.poifs.filesystem.POIFSFileSystem;\r
+/**\r
+ * Basing on: src/examples/src/org/apache/poi/hpsf/examples/ModifyDocumentSummaryInformation.java\r
+ * This class tests reading and writing of meta data. No actual document is created. All information\r
+ * is stored in a virtal document in a ByteArrayOutputStream\r
+ * @author Matthias Günter\r
+ * @since 2006-03-03\r
+ * @version $Id: TestEmptyProperties.java 353563 2004-06-22 16:16:33Z klute $\r
+ */\r
+public class TestMetaDataIPI extends TestCase{\r
+\r
+ private ByteArrayOutputStream bout= null; //our store\r
+ private POIFSFileSystem poifs=null;\r
+ DirectoryEntry dir = null;\r
+ DocumentSummaryInformation dsi=null;\r
+ SummaryInformation si=null;\r
\r
+ /**\r
+  * Standard constructor\r
+  * @param s\r
+  */\r
+ public TestMetaDataIPI(String s ){\r
+        super(s);\r
+ }\r
+\r
+ /**\r
+  * Setup is used to get the document ready. Gets the DocumentSummaryInformation and the \r
+  * SummaryInformation to reasonable values\r
+  */\r
+ public void setUp(){\r
+        bout=new ByteArrayOutputStream();\r
+        poifs= new POIFSFileSystem();\r
+     dir = poifs.getRoot();\r
+     dsi=null;\r
+     try\r
+     {\r
+         DocumentEntry dsiEntry = (DocumentEntry)\r
+             dir.getEntry(DocumentSummaryInformation.DEFAULT_STREAM_NAME);\r
+         DocumentInputStream dis = new DocumentInputStream(dsiEntry);\r
+         PropertySet ps = new PropertySet(dis);\r
+         dis.close();\r
+         dsi = new DocumentSummaryInformation(ps);\r
+       \r
+         \r
+     }\r
+     catch (FileNotFoundException ex)\r
+     {\r
+         /* There is no document summary information yet. We have to create a\r
+          * new one. */\r
+         dsi = PropertySetFactory.newDocumentSummaryInformation();\r
+         assertNotNull(dsi);\r
+     } catch (IOException e) {\r
+               e.printStackTrace();\r
+               fail();\r
+       } catch (NoPropertySetStreamException e) {\r
+               e.printStackTrace();\r
+               fail();\r
+       } catch (MarkUnsupportedException e) {\r
+               e.printStackTrace();\r
+               fail();\r
+       } catch (UnexpectedPropertySetTypeException e) {\r
+               e.printStackTrace();\r
+               fail();\r
+       }\r
+    assertNotNull(dsi);\r
+    try\r
+    {\r
+        DocumentEntry dsiEntry = (DocumentEntry)\r
+            dir.getEntry(SummaryInformation.DEFAULT_STREAM_NAME);\r
+        DocumentInputStream dis = new DocumentInputStream(dsiEntry);\r
+        PropertySet ps = new PropertySet(dis);\r
+        dis.close();\r
+        si = new SummaryInformation(ps);\r
+      \r
+        \r
+    }\r
+    catch (FileNotFoundException ex)\r
+    {\r
+        /* There is no document summary information yet. We have to create a\r
+         * new one. */\r
+        si = PropertySetFactory.newSummaryInformation();\r
+        assertNotNull(si);\r
+    } catch (IOException e) {\r
+               e.printStackTrace();\r
+               fail();\r
+       } catch (NoPropertySetStreamException e) {\r
+               e.printStackTrace();\r
+               fail();\r
+       } catch (MarkUnsupportedException e) {\r
+               e.printStackTrace();\r
+               fail();\r
+       } catch (UnexpectedPropertySetTypeException e) {\r
+               e.printStackTrace();\r
+               fail();\r
+       }\r
+       assertNotNull(dsi);\r
+    \r
+    \r
+ }\r
\r
+ /**\r
+  * Setting a lot of things to null.\r
+  */\r
+ public void tearDown(){\r
+       bout=null;\r
+       poifs=null;\r
+       dir=null;\r
+       dsi=null;\r
+        \r
+ }\r
\r
\r
+ /**\r
+  * Closes the ByteArrayOutputStream and reads it into a ByteArrayInputStream.\r
+  * When finished writing information this method is used in the tests to\r
+  * start reading from the created document and then the see if the results match.\r
+  *\r
+  */\r
+  public void closeAndReOpen(){\r
+            \r
+            try {\r
+                       dsi.write(dir, DocumentSummaryInformation.DEFAULT_STREAM_NAME);\r
+                       si.write(dir,SummaryInformation.DEFAULT_STREAM_NAME);\r
+               } catch (WritingNotSupportedException e) {\r
+                       e.printStackTrace();\r
+                       fail();\r
+               } catch (IOException e) {\r
+                       e.printStackTrace();\r
+                       fail();\r
+               }\r
+\r
+        si=null;\r
+        dsi=null;\r
+               try {\r
+                       \r
+                       poifs.writeFilesystem(bout);\r
+                       bout.flush();\r
+                       \r
+               } catch (IOException e) {\r
+\r
+                       e.printStackTrace();\r
+                       fail();\r
+               }\r
+            \r
+            InputStream is=new ByteArrayInputStream(bout.toByteArray());\r
+          assertNotNull(is);\r
+          POIFSFileSystem poifs=null;\r
+               try {\r
+                       poifs = new POIFSFileSystem(is);\r
+               } catch (IOException e) {\r
+\r
+                       e.printStackTrace();\r
+                       fail();\r
+               }\r
+            try {\r
+                       is.close();\r
+               } catch (IOException e) {\r
+                       e.printStackTrace();\r
+                       fail();\r
+               }\r
+           assertNotNull(poifs);\r
+            /* Read the document summary information. */\r
+            DirectoryEntry dir = poifs.getRoot();\r
+            \r
+            try\r
+            {\r
+                DocumentEntry dsiEntry = (DocumentEntry)\r
+                    dir.getEntry(DocumentSummaryInformation.DEFAULT_STREAM_NAME);\r
+                DocumentInputStream dis = new DocumentInputStream(dsiEntry);\r
+                PropertySet ps = new PropertySet(dis);\r
+                dis.close();\r
+                dsi = new DocumentSummaryInformation(ps);\r
+            }\r
+            catch (FileNotFoundException ex)\r
+            {\r
+                fail();\r
+            } catch (IOException e) {\r
+                       e.printStackTrace();\r
+                       fail();\r
+               } catch (NoPropertySetStreamException e) {\r
+                       e.printStackTrace();\r
+                       fail();\r
+               } catch (MarkUnsupportedException e) {\r
+                       e.printStackTrace();\r
+                       fail();\r
+               } catch (UnexpectedPropertySetTypeException e) {\r
+                       e.printStackTrace();\r
+                       fail();\r
+               }\r
+                 try\r
+                   {\r
+                       DocumentEntry dsiEntry = (DocumentEntry)\r
+                           dir.getEntry(SummaryInformation.DEFAULT_STREAM_NAME);\r
+                       DocumentInputStream dis = new DocumentInputStream(dsiEntry);\r
+                       PropertySet ps = new PropertySet(dis);\r
+                       dis.close();\r
+                       si = new SummaryInformation(ps);\r
+                     \r
+                       \r
+                   }\r
+                   catch (FileNotFoundException ex)\r
+                   {\r
+                       /* There is no document summary information yet. We have to create a\r
+                        * new one. */\r
+                       si = PropertySetFactory.newSummaryInformation();\r
+                       assertNotNull(si);\r
+                   } catch (IOException e) {\r
+                               e.printStackTrace();\r
+                               fail();\r
+                       } catch (NoPropertySetStreamException e) {\r
+                               e.printStackTrace();\r
+                               fail();\r
+                       } catch (MarkUnsupportedException e) {\r
+                               e.printStackTrace();\r
+                               fail();\r
+                       } catch (UnexpectedPropertySetTypeException e) {\r
+                               e.printStackTrace();\r
+                               fail();\r
+                       }\r
+  }\r
+  \r
+  /**\r
+   * Sets the most important information in DocumentSummaryInformation and Summary Information and rereads it\r
+   *\r
+   */\r
+ public void testOne(){\r
+        \r
+        //DocumentSummaryInformation\r
+        dsi.setCompany("xxxCompanyxxx");\r
+        dsi.setManager("xxxManagerxxx");\r
+        dsi.setCategory("xxxCategoryxxx");\r
+        \r
+        //SummaryInformation\r
+        si.setTitle("xxxTitlexxx");\r
+        si.setAuthor("xxxAuthorxxx");\r
+        si.setComments("xxxCommentsxxx");\r
+        si.setKeywords("xxxKeyWordsxxx");\r
+        si.setSubject("xxxSubjectxxx");\r
+        \r
+        //Custom Properties (in DocumentSummaryInformation\r
+     CustomProperties customProperties = dsi.getCustomProperties();\r
+     if (customProperties == null){\r
+         customProperties = new CustomProperties();\r
+     }\r
+     \r
+     /* Insert some custom properties into the container. */\r
+     customProperties.put("Key1", "Value1");\r
+     customProperties.put("Schlüssel2", "Wert2");\r
+     customProperties.put("Sample Integer", new Integer(12345));\r
+     customProperties.put("Sample Boolean", new Boolean(true));\r
+     Date date=new Date();\r
+     customProperties.put("Sample Date", date);\r
+     customProperties.put("Sample Double", new Double(-1.0001));\r
+     customProperties.put("Sample Negative Integer", new Integer(-100000));\r
+\r
+     dsi.setCustomProperties(customProperties);\r
+\r
+     //start reading\r
+        closeAndReOpen();\r
+     \r
+        //testing\r
+     assertNotNull(dsi);\r
+     assertNotNull(si);\r
+     \r
+     assertEquals("Category","xxxCategoryxxx",dsi.getCategory());\r
+     assertEquals("Company","xxxCompanyxxx",dsi.getCompany());\r
+     assertEquals("Manager","xxxManagerxxx",dsi.getManager());\r
+     \r
+     assertEquals("","xxxAuthorxxx",si.getAuthor());\r
+     assertEquals("","xxxTitlexxx",si.getTitle());\r
+     assertEquals("","xxxCommentsxxx",si.getComments());\r
+     assertEquals("","xxxKeyWordsxxx",si.getKeywords());\r
+     assertEquals("","xxxSubjectxxx",si.getSubject());\r
+     \r
+\r
+     /* Read the custom properties. If there are no custom properties yet,\r
+      * the application has to create a new CustomProperties object. It will\r
+      * serve as a container for custom properties. */\r
+     customProperties = dsi.getCustomProperties();\r
+     if (customProperties == null){\r
+        fail();\r
+         }\r
+     \r
+     /* Insert some custom properties into the container. */\r
+     String a1=(String) customProperties.get("Key1");\r
+     assertEquals("Key1","Value1",a1);\r
+     String a2=(String) customProperties.get("Schlüssel2");\r
+     assertEquals("Schlüssel2","Wert2",a2);\r
+     Integer a3=(Integer) customProperties.get("Sample Integer");\r
+     assertEquals("Sample Number",new Integer(12345),a3);\r
+     Boolean a4=(Boolean) customProperties.get("Sample Boolean");\r
+     assertEquals("Sample Boolean",new Boolean(true),a4);\r
+     Date a5=(Date) customProperties.get("Sample Date");\r
+     assertEquals("Custom Date:",date,a5);\r
+     \r
+     Double a6=(Double) customProperties.get("Sample Double");\r
+     assertEquals("Custom Float",new Double(-1.0001),a6);\r
+     \r
+     Integer a7=(Integer) customProperties.get("Sample Negative Integer");\r
+     assertEquals("Neg", new Integer(-100000),a7);\r
+ }\r
+     \r
+\r
+ /**\r
+  * multiplies a string\r
+  * @param s Input String\r
+  * @return  the multiplied String\r
+  */\r
+ public String elongate(String s){\r
+        StringBuffer sb=new StringBuffer();\r
+        for (int i=0;i<10000;i++){\r
+                sb.append(s);\r
+                sb.append(" ");\r
+        }\r
+        return sb.toString();\r
+ }\r
\r
\r
\r
+ /**\r
+  * Test very long input in each of the fields (approx 30-60KB each)\r
+  *\r
+  */\r
+public void testTwo(){\r
+        \r
+       String company=elongate("company");\r
+       String manager=elongate("manager");\r
+       String category=elongate("category");\r
+       String title=elongate("title");\r
+       String author=elongate("author");\r
+       String comments=elongate("comments");\r
+       String keywords=elongate("keywords");\r
+       String subject=elongate("subject");\r
+       String p1=elongate("p1");\r
+       String p2=elongate("p2");\r
+       String k1=elongate("k1");\r
+       String k2=elongate("k2");\r
+       \r
+        dsi.setCompany(company);\r
+        dsi.setManager(manager);\r
+        dsi.setCategory(category);\r
+        \r
+        si.setTitle(title);\r
+        si.setAuthor(author);\r
+        si.setComments(comments);\r
+        si.setKeywords(keywords);\r
+        si.setSubject(subject);\r
+    CustomProperties customProperties = dsi.getCustomProperties();\r
+    if (customProperties == null){\r
+        customProperties = new CustomProperties();\r
+    }\r
+    \r
+    /* Insert some custom properties into the container. */\r
+    customProperties.put(k1, p1);\r
+    customProperties.put(k2, p2);\r
+    customProperties.put("Sample Number", new Integer(12345));\r
+    customProperties.put("Sample Boolean", new Boolean(true));\r
+    Date date=new Date();\r
+    customProperties.put("Sample Date", date);\r
+\r
+    dsi.setCustomProperties(customProperties);\r
+\r
+\r
+        closeAndReOpen();\r
+  \r
+   assertNotNull(dsi);\r
+   assertNotNull(si);\r
+    /* Change the category to "POI example". Any former category value will\r
+     * be lost. If there has been no category yet, it will be created. */\r
+    assertEquals("Category",category,dsi.getCategory());\r
+    assertEquals("Company",company,dsi.getCompany());\r
+    assertEquals("Manager",manager,dsi.getManager());\r
+    \r
+    assertEquals("",author,si.getAuthor());\r
+    assertEquals("",title,si.getTitle());\r
+    assertEquals("",comments,si.getComments());\r
+    assertEquals("",keywords,si.getKeywords());\r
+    assertEquals("",subject,si.getSubject());\r
+    \r
+    \r
+    /* Read the custom properties. If there are no custom properties\r
+     * yet, the application has to create a new CustomProperties object.\r
+     * It will serve as a container for custom properties. */\r
+    customProperties = dsi.getCustomProperties();\r
+    if (customProperties == null){\r
+        fail();\r
+        }\r
+    \r
+    /* Insert some custom properties into the container. */\r
+    String a1=(String) customProperties.get(k1);\r
+    assertEquals("Key1",p1,a1);\r
+    String a2=(String) customProperties.get(k2);\r
+    assertEquals("Schlüssel2",p2,a2);\r
+    Integer a3=(Integer) customProperties.get("Sample Number");\r
+    assertEquals("Sample Number",new Integer(12345),a3);\r
+    Boolean a4=(Boolean) customProperties.get("Sample Boolean");\r
+    assertEquals("Sample Boolean",new Boolean(true),a4);\r
+    Date a5=(Date) customProperties.get("Sample Date");\r
+    assertEquals("Custom Date:",date,a5);\r
+\r
+\r
+       }\r
+    \r
+\r
+/**\r
+ * adds strange characters to the string\r
+ * @param s Input String\r
+ * @return  the multiplied String\r
+ */\r
+public String strangize(String s){\r
+        StringBuffer sb=new StringBuffer();\r
+        String[] umlaute= {"ä","ü","ö","Ü","$","Ö","Ü","É","Ö","@","ç","&"};\r
+        char j=0;\r
+        Random rand=new Random();\r
+        for (int i=0;i<5;i++){\r
+                sb.append(s);\r
+                sb.append(" ");\r
+                j=(char) rand.nextInt(220);\r
+                j+=33;\r
+                // System.out.println(j);\r
+                sb.append(">");\r
+                sb.append(new Character(j));\r
+                sb.append("=");\r
+                sb.append(umlaute[rand.nextInt(umlaute.length)]);\r
+                sb.append("<");\r
+        }\r
+        \r
+        return sb.toString();\r
+}\r
+\r
+\r
+/**\r
+ * Tests with strange characters in keys and data (Umlaute etc.)\r
+ *\r
+ */\r
+public void testThree(){\r
+        \r
+       String company=strangize("company");\r
+       String manager=strangize("manager");\r
+       String category=strangize("category");\r
+       String title=strangize("title");\r
+       String author=strangize("author");\r
+       String comments=strangize("comments");\r
+       String keywords=strangize("keywords");\r
+       String subject=strangize("subject");\r
+       String p1=strangize("p1");\r
+       String p2=strangize("p2");\r
+       String k1=strangize("k1");\r
+       String k2=strangize("k2");\r
+       \r
+        dsi.setCompany(company);\r
+        dsi.setManager(manager);\r
+        dsi.setCategory(category);\r
+        \r
+        si.setTitle(title);\r
+        si.setAuthor(author);\r
+        si.setComments(comments);\r
+        si.setKeywords(keywords);\r
+        si.setSubject(subject);\r
+   CustomProperties customProperties = dsi.getCustomProperties();\r
+   if (customProperties == null){\r
+       customProperties = new CustomProperties();\r
+   }\r
+   \r
+   /* Insert some custom properties into the container. */\r
+   customProperties.put(k1, p1);\r
+   customProperties.put(k2, p2);\r
+   customProperties.put("Sample Number", new Integer(12345));\r
+   customProperties.put("Sample Boolean", new Boolean(false));\r
+   Date date=new Date(0);\r
+   customProperties.put("Sample Date", date);\r
+\r
+   dsi.setCustomProperties(customProperties);\r
+\r
+\r
+        closeAndReOpen();\r
\r
+  assertNotNull(dsi);\r
+  assertNotNull(si);\r
+   /* Change the category to "POI example". Any former category value will\r
+    * be lost. If there has been no category yet, it will be created. */\r
+   assertEquals("Category",category,dsi.getCategory());\r
+   assertEquals("Company",company,dsi.getCompany());\r
+   assertEquals("Manager",manager,dsi.getManager());\r
+   \r
+   assertEquals("",author,si.getAuthor());\r
+   assertEquals("",title,si.getTitle());\r
+   assertEquals("",comments,si.getComments());\r
+   assertEquals("",keywords,si.getKeywords());\r
+   assertEquals("",subject,si.getSubject());\r
+   \r
+   \r
+   /* Read the custom properties. If there are no custom properties yet,\r
+    * the application has to create a new CustomProperties object. It will\r
+    * serve as a container for custom properties. */\r
+   customProperties = dsi.getCustomProperties();\r
+   if (customProperties == null){\r
+        fail();\r
+       }\r
+   \r
+   /* Insert some custom properties into the container. */\r
+   // System.out.println(k1);\r
+   String a1=(String) customProperties.get(k1);\r
+   assertEquals("Key1",p1,a1);\r
+   String a2=(String) customProperties.get(k2);\r
+   assertEquals("Schlüssel2",p2,a2);\r
+   Integer a3=(Integer) customProperties.get("Sample Number");\r
+   assertEquals("Sample Number",new Integer(12345),a3);\r
+   Boolean a4=(Boolean) customProperties.get("Sample Boolean");\r
+   assertEquals("Sample Boolean",new Boolean(false),a4);\r
+   Date a5=(Date) customProperties.get("Sample Date");\r
+   assertEquals("Custom Date:",date,a5);\r
+  \r
+\r
+       }\r
+   \r
+   /**\r
+    * Iterative testing: writing, reading etc.\r
+    *\r
+    */\r
+   public void testFour(){\r
+          for (int i=1;i<100;i++){\r
+                  setUp();\r
+                  testThree();\r
+                  tearDown();\r
+          }\r
+   }\r
+   \r
+   \r
+\r
+   /**\r
+    * adds strange characters to the string with the adding of unicode characters\r
+    * @param s Input String\r
+    * @return  the multiplied String\r
+    */\r
+   public String strangizeU(String s){\r
+         \r
+        StringBuffer sb=new StringBuffer();\r
+        String[] umlaute= {"ä","ü","ö","Ü","$","Ö","Ü","É","Ö","@","ç","&"};\r
+        char j=0;\r
+        Random rand=new Random();\r
+        for (int i=0;i<5;i++){\r
+                sb.append(s);\r
+                sb.append(" ");\r
+                j=(char) rand.nextInt(220);\r
+                j+=33;\r
+                // System.out.println(j);\r
+                sb.append(">");\r
+                sb.append(new Character(j));\r
+                sb.append("=");\r
+                sb.append(umlaute[rand.nextInt(umlaute.length)]);\r
+                sb.append("<");\r
+        }\r
+        sb.append("äöü\uD840\uDC00");\r
+        return sb.toString();\r
+   }\r
+   /**\r
+    * Unicode test\r
+    *\r
+    */\r
+   public void testUnicode(){\r
+          String company=strangizeU("company");\r
+               String manager=strangizeU("manager");\r
+               String category=strangizeU("category");\r
+               String title=strangizeU("title");\r
+               String author=strangizeU("author");\r
+               String comments=strangizeU("comments");\r
+               String keywords=strangizeU("keywords");\r
+               String subject=strangizeU("subject");\r
+               String p1=strangizeU("p1");\r
+               String p2=strangizeU("p2");\r
+               String k1=strangizeU("k1");\r
+               String k2=strangizeU("k2");\r
+               \r
+                dsi.setCompany(company);\r
+                dsi.setManager(manager);\r
+                dsi.setCategory(category);\r
+                \r
+                si.setTitle(title);\r
+                si.setAuthor(author);\r
+                si.setComments(comments);\r
+                si.setKeywords(keywords);\r
+                si.setSubject(subject);\r
+          CustomProperties customProperties = dsi.getCustomProperties();\r
+          if (customProperties == null){\r
+              customProperties = new CustomProperties();\r
+          }\r
+          \r
+          /* Insert some custom properties into the container. */\r
+          customProperties.put(k1, p1);\r
+          customProperties.put(k2, p2);\r
+          customProperties.put("Sample Number", new Integer(12345));\r
+          customProperties.put("Sample Boolean", new Boolean(true));\r
+          Date date=new Date();\r
+          customProperties.put("Sample Date", date);\r
+\r
+          dsi.setCustomProperties(customProperties);\r
+\r
+\r
+                closeAndReOpen();\r
+        \r
+         assertNotNull(dsi);\r
+         assertNotNull(si);\r
+          /* Change the category to "POI example". Any former category value will\r
+           * be lost. If there has been no category yet, it will be created. */\r
+          assertEquals("Category",category,dsi.getCategory());\r
+          assertEquals("Company",company,dsi.getCompany());\r
+          assertEquals("Manager",manager,dsi.getManager());\r
+          \r
+          assertEquals("",author,si.getAuthor());\r
+          assertEquals("",title,si.getTitle());\r
+          assertEquals("",comments,si.getComments());\r
+          assertEquals("",keywords,si.getKeywords());\r
+          assertEquals("",subject,si.getSubject());\r
+          \r
+          \r
+       /* Read the custom properties. If there are no custom properties yet,\r
+        * the application has to create a new CustomProperties object. It will\r
+        * serve as a container for custom properties. */\r
+          customProperties = dsi.getCustomProperties();\r
+          if (customProperties == null){\r
+                fail();\r
+              }\r
+          \r
+          /* Insert some custom properties into the container. */\r
+          // System.out.println(k1);\r
+          String a1=(String) customProperties.get(k1);\r
+          assertEquals("Key1",p1,a1);\r
+          String a2=(String) customProperties.get(k2);\r
+          assertEquals("Schlüssel2",p2,a2);\r
+       Integer a3=(Integer) customProperties.get("Sample Number");\r
+          assertEquals("Sample Number",new Integer(12345),a3);\r
+          Boolean a4=(Boolean) customProperties.get("Sample Boolean");\r
+          assertEquals("Sample Boolean",new Boolean(true),a4);\r
+          Date a5=(Date) customProperties.get("Sample Date");\r
+          assertEquals("Custom Date:",date,a5);\r
+         \r
+       \r
+\r
+   }\r
+   \r
+   \r
+   /**\r
+    * Iterative testing of the unicode test\r
+    *\r
+    */\r
+   public void testSix(){\r
+          for (int i=1;i<100;i++){\r
+                  setUp();\r
+                  testUnicode();\r
+                  tearDown();\r
+          }\r
+   }\r
+   \r
+\r
+   /**\r
+    * Tests conversion in custom fields and errors\r
+    *\r
+    */\r
+  public void testConvAndExistance(){\r
+        \r
+       \r
+      CustomProperties customProperties = dsi.getCustomProperties();\r
+      if (customProperties == null){\r
+          customProperties = new CustomProperties();\r
+      }\r
+      \r
+      /* Insert some custom properties into the container. */\r
+      customProperties.put("int", new Integer(12345));\r
+      customProperties.put("negint", new Integer(-12345));\r
+      customProperties.put("long", new Long(12345));\r
+      customProperties.put("neglong", new Long(-12345));\r
+      customProperties.put("boolean", new Boolean(true));\r
+      customProperties.put("string", "a String");\r
+      //customProperties.put("float", new Float(12345.0));  is not valid\r
+      //customProperties.put("negfloat", new Float(-12345.1)); is not valid\r
+      customProperties.put("double", new Double(12345.2));\r
+      customProperties.put("negdouble", new Double(-12345.3));\r
+      //customProperties.put("char", new Character('a')); is not valid\r
+      \r
+      Date date=new Date();\r
+      customProperties.put("date", date);\r
+\r
+      dsi.setCustomProperties(customProperties);\r
+\r
+\r
+        closeAndReOpen();\r
+    \r
+     assertNotNull(dsi);\r
+     assertNotNull(si);\r
+      /* Change the category to "POI example". Any former category value will\r
+       * be lost. If there has been no category yet, it will be created. */\r
+      assertNull(dsi.getCategory());\r
+      assertNull(dsi.getCompany());\r
+      assertNull(dsi.getManager());\r
+      \r
+      assertNull(si.getAuthor());\r
+      assertNull(si.getTitle());\r
+      assertNull(si.getComments());\r
+      assertNull(si.getKeywords());\r
+      assertNull(si.getSubject());\r
+      \r
+      \r
+      /* Read the custom properties. If there are no custom properties\r
+       * yet, the application has to create a new CustomProperties object.\r
+       * It will serve as a container for custom properties. */\r
+      customProperties = dsi.getCustomProperties();\r
+      if (customProperties == null){\r
+        fail();\r
+          }\r
+      \r
+      /* Insert some custom properties into the container. */\r
+\r
+      Integer a3=(Integer) customProperties.get("int");\r
+      assertEquals("int",new Integer(12345),a3);\r
+      \r
+      a3=(Integer) customProperties.get("negint");\r
+      assertEquals("negint",new Integer(-12345),a3);\r
+      \r
+      Long al=(Long) customProperties.get("neglong");\r
+      assertEquals("neglong",new Long(-12345),al);\r
+      \r
+      al=(Long) customProperties.get("long");\r
+      assertEquals("long",new Long(12345),al);\r
+      \r
+      Boolean a4=(Boolean) customProperties.get("boolean");\r
+      assertEquals("boolean",new Boolean(true),a4);\r
+      \r
+      Date a5=(Date) customProperties.get("date");\r
+      assertEquals("Custom Date:",date,a5);\r
+  \r
+      Double d=(Double) customProperties.get("double");\r
+      assertEquals("int",new Double(12345.2),d);\r
+      \r
+      d=(Double) customProperties.get("negdouble");\r
+      assertEquals("string",new Double(-12345.3),d);\r
+\r
+      String s=(String) customProperties.get("string");\r
+      assertEquals("sring","a String",s);\r
+      \r
+      Object o=null;\r
+      \r
+      o=customProperties.get("string");\r
+      if (!(o instanceof String)){\r
+         fail();\r
+      }\r
+      o=customProperties.get("boolean");\r
+      if (!(o instanceof Boolean)){\r
+         fail();\r
+      }\r
+      \r
+      o=customProperties.get("int");\r
+      if (!(o instanceof Integer)){\r
+         fail();\r
+      }\r
+      o=customProperties.get("negint");\r
+      if (!(o instanceof Integer)){\r
+         fail();\r
+      }\r
+      \r
+      o=customProperties.get("long");\r
+      if (!(o instanceof Long)){\r
+         fail();\r
+      }\r
+      o=customProperties.get("neglong");\r
+      if (!(o instanceof Long)){\r
+         fail();\r
+      }\r
+      \r
+      o=customProperties.get("double");\r
+      if (!(o instanceof Double)){\r
+         fail();\r
+      }\r
+      o=customProperties.get("negdouble");\r
+      if (!(o instanceof Double)){\r
+         fail();\r
+      }\r
+      \r
+      o=customProperties.get("date");\r
+      if (!(o instanceof Date)){\r
+         fail();\r
+      }\r
+       }\r
+      \r
+\r
+   \r
+}
\ No newline at end of file
index 9c7a2b47ac6188436ec0a8c9917ba2021b804f72..82115455652c07c52d2ca4a1975a370d9e4f79fa 100644 (file)
@@ -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.
@@ -27,10 +26,12 @@ import junit.framework.Assert;
 import junit.framework.TestCase;
 
 import org.apache.poi.hpsf.Constants;
+import org.apache.poi.hpsf.DocumentSummaryInformation;
 import org.apache.poi.hpsf.HPSFException;
 import org.apache.poi.hpsf.PropertySet;
 import org.apache.poi.hpsf.PropertySetFactory;
 import org.apache.poi.hpsf.Section;
+import org.apache.poi.hpsf.SummaryInformation;
 
 
 
@@ -102,7 +103,7 @@ public class TestUnicode extends TestCase
         Assert.assertEquals(s.getProperty(1),
                             new Integer(Constants.CP_UTF16));
         Assert.assertEquals(s.getProperty(2),
-                            new Long(4198897018L));
+                            new Integer(-96070278));
         Assert.assertEquals(s.getProperty(3),
                             "MCon_Info zu Office bei Schreiner");
         Assert.assertEquals(s.getProperty(4),
index 3fa12d83f60aa73a5559f7aef603904a3708a5d8..5880328f46f67cbb6693339784d7a9283c1bdb6a 100644 (file)
@@ -1,5 +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.
@@ -13,7 +13,6 @@
    See the License for the specific language governing permissions and
    limitations under the License.
 ==================================================================== */
-        
 
 package org.apache.poi.hpsf.basic;
 
@@ -124,11 +123,8 @@ public class TestWrite extends TestCase
      * in.</p>
      * 
      * @exception IOException if an I/O exception occurs
-     * @exception UnsupportedVariantTypeException if HPSF does not yet support
-     * a variant type to be written
      */
-    public void testNoFormatID()
-        throws IOException, UnsupportedVariantTypeException
+    public void testNoFormatID() throws IOException
     {
         final String dataDirName = System.getProperty("HPSF.testdata.path");
         final File dataDir = new File(dataDirName);
@@ -409,10 +405,19 @@ public class TestWrite extends TestCase
             check(Variant.VT_CF, new byte[]{0, 1, 2, 3, 4, 5}, codepage);
             check(Variant.VT_CF, new byte[]{0, 1, 2, 3, 4, 5, 6}, codepage);
             check(Variant.VT_CF, new byte[]{0, 1, 2, 3, 4, 5, 6, 7}, codepage);
-            check(Variant.VT_I2, new Integer(27), codepage);
-            check(Variant.VT_I4, new Long(28), codepage);
+            check(Variant.VT_I4, new Integer(27), codepage);
+            check(Variant.VT_I8, new Long(28), codepage);
             check(Variant.VT_R8, new Double(29.0), codepage);
+            check(Variant.VT_I4, new Integer(-27), codepage);
+            check(Variant.VT_I8, new Long(-28), codepage);
+            check(Variant.VT_R8, new Double(-29.0), codepage);
             check(Variant.VT_FILETIME, new Date(), codepage);
+            check(Variant.VT_I4, new Integer(Integer.MAX_VALUE), codepage);
+            check(Variant.VT_I4, new Integer(Integer.MIN_VALUE), codepage);
+            check(Variant.VT_I8, new Long(Long.MAX_VALUE), codepage);
+            check(Variant.VT_I8, new Long(Long.MIN_VALUE), codepage);
+            check(Variant.VT_R8, new Double(Double.MAX_VALUE), codepage);
+            check(Variant.VT_R8, new Double(Double.MIN_VALUE), codepage);
 
             check(Variant.VT_LPSTR,
                   "", codepage);
@@ -602,8 +607,11 @@ public class TestWrite extends TestCase
      *
      * @param variantType The property's variant type.
      * @param value The property's value.
+     * @param codepage The codepage to use for writing and reading.
      * @throws UnsupportedVariantTypeException if the variant is not supported.
      * @throws IOException if an I/O exception occurs.
+     * @throws ReadingNotSupportedException 
+     * @throws UnsupportedEncodingException 
      */
     private void check(final long variantType, final Object value, 
                        final int codepage)
@@ -779,7 +787,7 @@ public class TestWrite extends TestCase
             m.put(new Long(2), "String 2");
             m.put(new Long(3), "String 3");
             s.setDictionary(m);
-            s.setFormatID(SectionIDMap.DOCUMENT_SUMMARY_INFORMATION_ID);
+            s.setFormatID(SectionIDMap.DOCUMENT_SUMMARY_INFORMATION_ID[0]);
             int codepage = Constants.CP_UNICODE;
             s.setProperty(PropertyIDMap.PID_CODEPAGE, Variant.VT_I2,
                           new Integer(codepage));
@@ -831,7 +839,7 @@ public class TestWrite extends TestCase
             m.put(new Long(2), "String 2");
             m.put(new Long(3), "String 3");
             s.setDictionary(m);
-            s.setFormatID(SectionIDMap.DOCUMENT_SUMMARY_INFORMATION_ID);
+            s.setFormatID(SectionIDMap.DOCUMENT_SUMMARY_INFORMATION_ID[0]);
             int codepage = 12345;
             s.setProperty(PropertyIDMap.PID_CODEPAGE, Variant.VT_I2,
                           new Integer(codepage));
@@ -902,8 +910,11 @@ public class TestWrite extends TestCase
 
     /**
      * <p>In order to execute tests with characters beyond US-ASCII, this
-     * method checks whether the application has is runing in an environment
+     * method checks whether the application is runing in an environment
      * where the default character set is 16-bit-capable.</p>
+     *
+     * @return <code>true</code> if the default character set is 16-bit-capable,
+     * else <code>false</code>.
      */
     private boolean hasProperDefaultCharset()
     {
@@ -916,6 +927,9 @@ public class TestWrite extends TestCase
 
     /**
      * <p>Runs the test cases stand-alone.</p>
+     * 
+     * @param args The command-line parameters.
+     * @throws Throwable if anything goes wrong.
      */
     public static void main(final String[] args) throws Throwable
     {
diff --git a/src/testcases/org/apache/poi/hpsf/basic/TestWriteWellKnown.java b/src/testcases/org/apache/poi/hpsf/basic/TestWriteWellKnown.java
new file mode 100644 (file)
index 0000000..db18510
--- /dev/null
@@ -0,0 +1,763 @@
+/* ====================================================================
+   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.basic;
+
+import java.io.File;
+import java.io.FileFilter;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.GregorianCalendar;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import junit.framework.TestCase;
+
+import org.apache.poi.hpsf.CustomProperties;
+import org.apache.poi.hpsf.CustomProperty;
+import org.apache.poi.hpsf.DocumentSummaryInformation;
+import org.apache.poi.hpsf.MarkUnsupportedException;
+import org.apache.poi.hpsf.MutableProperty;
+import org.apache.poi.hpsf.MutableSection;
+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.Variant;
+import org.apache.poi.hpsf.VariantSupport;
+import org.apache.poi.hpsf.WritingNotSupportedException;
+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;
+import org.apache.poi.poifs.filesystem.POIFSFileSystem;
+
+
+
+/**
+ * <p>Tests HPSF's high-level writing functionality for the well-known property
+ * set "SummaryInformation" and "DocumentSummaryInformation".</p>
+ * 
+ * @author Rainer Klute
+ *     <a href="mailto:klute@rainer-klute.de">klute@rainer-klute.de</a>
+ * @since 2006-02-01
+ * @version $Id$
+ */
+public class TestWriteWellKnown extends TestCase
+{
+
+    private static final String POI_FS = "TestWriteWellKnown.doc";
+
+    /**
+     * <p>Constructor</p>
+     * 
+     * @param name the test case's name
+     */
+    public TestWriteWellKnown(final String name)
+    {
+        super(name);
+    }
+
+
+
+    /**
+     * @see TestCase#setUp()
+     */
+    public void setUp()
+    {
+        VariantSupport.setLogUnsupportedTypes(false);
+    }
+
+
+
+    /**
+     * <p>This test method checks whether DocumentSummary information streams
+     * can be read. This is done by opening all "Test*" files in the directrory
+     * pointed to by the "HPSF.testdata.path" system property, trying to extract
+     * the document summary information stream in the root directory and calling
+     * its get... methods.</p>
+     * @throws IOException 
+     * @throws FileNotFoundException 
+     * @throws MarkUnsupportedException 
+     * @throws NoPropertySetStreamException 
+     * @throws UnexpectedPropertySetTypeException 
+     */
+    public void testReadDocumentSummaryInformation()
+            throws FileNotFoundException, IOException,
+            NoPropertySetStreamException, MarkUnsupportedException,
+            UnexpectedPropertySetTypeException
+    {
+        final String dataDirName = System.getProperty("HPSF.testdata.path");
+        final File dataDir = new File(dataDirName);
+        final File[] docs = dataDir.listFiles(new FileFilter()
+        {
+            public boolean accept(final File file)
+            {
+                return file.isFile() && file.getName().startsWith("Test");
+            }});
+        for (int i = 0; i < docs.length; i++)
+        {
+            final File doc = docs[i];
+            System.out.println("Reading file " + doc);
+
+            /* Read a test document <em>doc</em> into a POI filesystem. */
+            final POIFSFileSystem poifs = new POIFSFileSystem(new FileInputStream(doc));
+            final DirectoryEntry dir = poifs.getRoot();
+            DocumentEntry dsiEntry = null;
+            try
+            {
+                dsiEntry = (DocumentEntry) dir.getEntry(DocumentSummaryInformation.DEFAULT_STREAM_NAME);
+            }
+            catch (FileNotFoundException ex)
+            {
+                /*
+                 * A missing document summary information stream is not an error
+                 * and therefore silently ignored here.
+                 */
+            }
+
+            /*
+             * If there is a document summry information stream, read it from
+             * the POI filesystem.
+             */
+            if (dsiEntry != null)
+            {
+                final DocumentInputStream dis = new DocumentInputStream(dsiEntry);
+                final PropertySet ps = new PropertySet(dis);
+                final DocumentSummaryInformation dsi = new DocumentSummaryInformation(ps);
+                
+                /* Execute the get... methods. */
+                dsi.getByteCount();
+                dsi.getByteOrder();
+                dsi.getCategory();
+                dsi.getCompany();
+                dsi.getCustomProperties();
+                // FIXME dsi.getDocparts();
+                // FIXME dsi.getHeadingPair();
+                dsi.getHiddenCount();
+                dsi.getLineCount();
+                dsi.getLinksDirty();
+                dsi.getManager();
+                dsi.getMMClipCount();
+                dsi.getNoteCount();
+                dsi.getParCount();
+                dsi.getPresentationFormat();
+                dsi.getScale();
+                dsi.getSlideCount();
+            }
+        }
+    }
+
+
+    /**
+     * <p>This test method test the writing of properties in the well-known
+     * property set streams "SummaryInformation" and
+     * "DocumentSummaryInformation" by performing the following steps:</p>
+     * 
+     * <ol>
+     * 
+     * <li><p>Read a test document <em>doc1</em> into a POI filesystem.</p></li>
+     * 
+     * <li><p>Read the summary information stream and the document summary
+     * information stream from the POI filesystem.</p></li>
+     * 
+     * <li><p>Write all properties supported by HPSF to the summary
+     * information (e.g. author, edit date, application name) and to the
+     * document summary information (e.g. company, manager).</p></li>
+     * 
+     * <li><p>Write the summary information stream and the document summary
+     * information stream to the POI filesystem.</p></li>
+     * 
+     * <li><p>Write the POI filesystem to a (temporary) file <em>doc2</em>
+     * and close the latter.</p></li>
+     * 
+     * <li><p>Open <em>doc2</em> for reading and check summary information
+     * and document summary information. All properties written before must be
+     * found in the property streams of <em>doc2</em> and have the correct
+     * values.</p></li>
+     * 
+     * <li><p>Remove all properties supported by HPSF from the summary
+     * information (e.g. author, edit date, application name) and from the
+     * document summary information (e.g. company, manager).</p></li>
+     * 
+     * <li><p>Write the summary information stream and the document summary
+     * information stream to the POI filesystem.</p></li>
+     * 
+     * <li><p>Write the POI filesystem to a (temporary) file <em>doc3</em>
+     * and close the latter.</p></li>
+     * 
+     * <li><p>Open <em>doc3</em> for reading and check summary information
+     * and document summary information. All properties removed before must not
+     * be found in the property streams of <em>doc3</em>.</p></li> </ol>
+     * 
+     * @throws IOException if some I/O error occurred.
+     * @throws MarkUnsupportedException
+     * @throws NoPropertySetStreamException
+     * @throws UnexpectedPropertySetTypeException
+     * @throws WritingNotSupportedException
+     */
+    public void testWriteWellKnown() throws IOException,
+            NoPropertySetStreamException, MarkUnsupportedException,
+            UnexpectedPropertySetTypeException, WritingNotSupportedException
+    {
+        final String dataDirName = System.getProperty("HPSF.testdata.path");
+        final File dataDir = new File(dataDirName);
+        final File doc1 = new File(dataDir, POI_FS);
+    
+        /* Read a test document <em>doc1</em> into a POI filesystem. */
+        POIFSFileSystem poifs = new POIFSFileSystem(new FileInputStream(doc1));
+        DirectoryEntry dir = poifs.getRoot();
+        DocumentEntry siEntry = (DocumentEntry) dir.getEntry(SummaryInformation.DEFAULT_STREAM_NAME);
+        DocumentEntry dsiEntry = (DocumentEntry) dir.getEntry(DocumentSummaryInformation.DEFAULT_STREAM_NAME);
+    
+        /*
+         * Read the summary information stream and the document summary
+         * information stream from the POI filesystem.
+         * 
+         * Please note that the result consists of SummaryInformation and
+         * DocumentSummaryInformation instances which are in memory only. To
+         * make them permanent they have to be written to a POI filesystem
+         * explicitly (overwriting the former contents). Then the POI filesystem
+         * should be saved to a file.
+         */
+        DocumentInputStream dis = new DocumentInputStream(siEntry);
+        PropertySet ps = new PropertySet(dis);
+        SummaryInformation si = new SummaryInformation(ps);
+        dis = new DocumentInputStream(dsiEntry);
+        ps = new PropertySet(dis);
+        DocumentSummaryInformation dsi = new DocumentSummaryInformation(ps);
+    
+        /*
+         * Write all properties supported by HPSF to the summary information
+         * (e.g. author, edit date, application name) and to the document
+         * summary information (e.g. company, manager).
+         */
+        Calendar cal = new GregorianCalendar();
+        cal.set(2000, 6, 6, 6, 6, 6);
+        final long time1 = cal.getTimeInMillis();
+        cal.set(2001, 7, 7, 7, 7, 7);
+        final long time2 = cal.getTimeInMillis();
+        cal.set(2002, 8, 8, 8, 8, 8);
+        final long time3 = cal.getTimeInMillis();
+    
+        int nr = 4711;
+        final String P_APPLICATION_NAME = "ApplicationName";
+        final String P_AUTHOR = "Author";
+        final int    P_CHAR_COUNT = ++nr;
+        final String P_COMMENTS = "Comments";
+        final Date   P_CREATE_DATE_TIME = new Date(time1);
+        final long   P_EDIT_TIME = ++nr * 1000 * 10;
+        final String P_KEYWORDS = "Keywords";
+        final String P_LAST_AUTHOR = "LastAuthor";
+        final Date   P_LAST_PRINTED = new Date(time2);
+        final Date   P_LAST_SAVE_DATE_TIME = new Date(time3);
+        final int    P_PAGE_COUNT = ++nr;
+        final String P_REV_NUMBER = "RevNumber";
+        final int    P_SECURITY = 1;
+        final String P_SUBJECT = "Subject";
+        final String P_TEMPLATE = "Template";
+        // FIXME (byte array properties not yet implemented): final byte[] P_THUMBNAIL = new byte[123];
+        final String P_TITLE = "Title";
+        final int    P_WORD_COUNT = ++nr;
+    
+        final int     P_BYTE_COUNT = ++nr;
+        final String  P_CATEGORY = "Category";
+        final String  P_COMPANY = "Company";
+        // FIXME (byte array properties not yet implemented): final byte[]  P_DOCPARTS = new byte[123];
+        // FIXME (byte array properties not yet implemented): final byte[]  P_HEADING_PAIR = new byte[123];
+        final int     P_HIDDEN_COUNT = ++nr;
+        final int     P_LINE_COUNT = ++nr;
+        final boolean P_LINKS_DIRTY = true;
+        final String  P_MANAGER = "Manager";
+        final int     P_MM_CLIP_COUNT = ++nr;
+        final int     P_NOTE_COUNT = ++nr;
+        final int     P_PAR_COUNT = ++nr;
+        final String  P_PRESENTATION_FORMAT = "PresentationFormat";
+        final boolean P_SCALE = false;
+        final int     P_SLIDE_COUNT = ++nr;
+        final Date    now = new Date();
+
+        final Integer POSITIVE_INTEGER = new Integer(2222);
+        final Long POSITIVE_LONG = new  Long(3333);
+        final Double POSITIVE_DOUBLE = new  Double(4444);
+        final Integer NEGATIVE_INTEGER = new Integer(2222);
+        final Long NEGATIVE_LONG = new  Long(3333);
+        final Double NEGATIVE_DOUBLE = new  Double(4444);
+
+        final Integer MAX_INTEGER = new Integer(Integer.MAX_VALUE);
+        final Integer MIN_INTEGER = new Integer(Integer.MIN_VALUE);
+        final Long MAX_LONG = new Long(Long.MAX_VALUE);
+        final Long MIN_LONG = new Long(Long.MIN_VALUE);
+        final Double MAX_DOUBLE = new Double(Double.MAX_VALUE);
+        final Double MIN_DOUBLE = new Double(Double.MIN_VALUE);
+        
+        si.setApplicationName(P_APPLICATION_NAME);
+        si.setAuthor(P_AUTHOR);
+        si.setCharCount(P_CHAR_COUNT);
+        si.setComments(P_COMMENTS);
+        si.setCreateDateTime(P_CREATE_DATE_TIME);
+        si.setEditTime(P_EDIT_TIME);
+        si.setKeywords(P_KEYWORDS);
+        si.setLastAuthor(P_LAST_AUTHOR);
+        si.setLastPrinted(P_LAST_PRINTED);
+        si.setLastSaveDateTime(P_LAST_SAVE_DATE_TIME);
+        si.setPageCount(P_PAGE_COUNT);
+        si.setRevNumber(P_REV_NUMBER);
+        si.setSecurity(P_SECURITY);
+        si.setSubject(P_SUBJECT);
+        si.setTemplate(P_TEMPLATE);
+        // FIXME (byte array properties not yet implemented): si.setThumbnail(P_THUMBNAIL);
+        si.setTitle(P_TITLE);
+        si.setWordCount(P_WORD_COUNT);
+    
+        dsi.setByteCount(P_BYTE_COUNT);
+        dsi.setCategory(P_CATEGORY);
+        dsi.setCompany(P_COMPANY);
+        // FIXME (byte array properties not yet implemented): dsi.setDocparts(P_DOCPARTS);
+        // FIXME (byte array properties not yet implemented): dsi.setHeadingPair(P_HEADING_PAIR);
+        dsi.setHiddenCount(P_HIDDEN_COUNT);
+        dsi.setLineCount(P_LINE_COUNT);
+        dsi.setLinksDirty(P_LINKS_DIRTY);
+        dsi.setManager(P_MANAGER);
+        dsi.setMMClipCount(P_MM_CLIP_COUNT);
+        dsi.setNoteCount(P_NOTE_COUNT);
+        dsi.setParCount(P_PAR_COUNT);
+        dsi.setPresentationFormat(P_PRESENTATION_FORMAT);
+        dsi.setScale(P_SCALE);
+        dsi.setSlideCount(P_SLIDE_COUNT);
+
+        CustomProperties customProperties = dsi.getCustomProperties();
+        if (customProperties == null)
+            customProperties = new CustomProperties();
+        customProperties.put("Schlüssel ä",    "Wert ä");
+        customProperties.put("Schlüssel äö",   "Wert äö");
+        customProperties.put("Schlüssel äöü",  "Wert äöü");
+        customProperties.put("Schlüssel äöüß", "Wert äöüß");
+        customProperties.put("positive_Integer", POSITIVE_INTEGER);
+        customProperties.put("positive_Long", POSITIVE_LONG);
+        customProperties.put("positive_Double", POSITIVE_DOUBLE);
+        customProperties.put("negative_Integer", NEGATIVE_INTEGER);
+        customProperties.put("negative_Long", NEGATIVE_LONG);
+        customProperties.put("negative_Double", NEGATIVE_DOUBLE);
+        customProperties.put("Boolean", new Boolean(true));
+        customProperties.put("Date", now);
+        customProperties.put("max_Integer", MAX_INTEGER);
+        customProperties.put("min_Integer", MIN_INTEGER);
+        customProperties.put("max_Long", MAX_LONG);
+        customProperties.put("min_Long", MIN_LONG);
+        customProperties.put("max_Double", MAX_DOUBLE);
+        customProperties.put("min_Double", MIN_DOUBLE);
+        dsi.setCustomProperties(customProperties);
+
+        /* Write the summary information stream and the document summary
+         * information stream to the POI filesystem. */
+        si.write(dir, siEntry.getName());
+        dsi.write(dir, dsiEntry.getName());
+
+        /* Write the POI filesystem to a (temporary) file <em>doc2</em>
+         * and close the latter. */
+        final File doc2 = File.createTempFile("POI_HPSF_Test.", ".tmp");
+        doc2.deleteOnExit();
+        OutputStream out = new FileOutputStream(doc2);
+        poifs.writeFilesystem(out);
+        out.close();
+
+        /*
+         * Open <em>doc2</em> for reading and check summary information and
+         * document summary information. All properties written before must be
+         * found in the property streams of <em>doc2</em> and have the correct
+         * values.
+         */
+        poifs = new POIFSFileSystem(new FileInputStream(doc2));
+        dir = poifs.getRoot();
+        siEntry = (DocumentEntry) dir.getEntry(SummaryInformation.DEFAULT_STREAM_NAME);
+        dsiEntry = (DocumentEntry) dir.getEntry(DocumentSummaryInformation.DEFAULT_STREAM_NAME);
+    
+        dis = new DocumentInputStream(siEntry);
+        ps = new PropertySet(dis);
+        si = new SummaryInformation(ps);
+        dis = new DocumentInputStream(dsiEntry);
+        ps = new PropertySet(dis);
+        dsi = new DocumentSummaryInformation(ps);
+    
+        assertEquals(P_APPLICATION_NAME, si.getApplicationName());
+        assertEquals(P_AUTHOR, si.getAuthor());
+        assertEquals(P_CHAR_COUNT, si.getCharCount());
+        assertEquals(P_COMMENTS, si.getComments());
+        assertEquals(P_CREATE_DATE_TIME, si.getCreateDateTime());
+        assertEquals(P_EDIT_TIME, si.getEditTime());
+        assertEquals(P_KEYWORDS, si.getKeywords());
+        assertEquals(P_LAST_AUTHOR, si.getLastAuthor());
+        assertEquals(P_LAST_PRINTED, si.getLastPrinted());
+        assertEquals(P_LAST_SAVE_DATE_TIME, si.getLastSaveDateTime());
+        assertEquals(P_PAGE_COUNT, si.getPageCount());
+        assertEquals(P_REV_NUMBER, si.getRevNumber());
+        assertEquals(P_SECURITY, si.getSecurity());
+        assertEquals(P_SUBJECT, si.getSubject());
+        assertEquals(P_TEMPLATE, si.getTemplate());
+        // FIXME (byte array properties not yet implemented): assertEquals(P_THUMBNAIL, si.getThumbnail());
+        assertEquals(P_TITLE, si.getTitle());
+        assertEquals(P_WORD_COUNT, si.getWordCount());
+    
+        assertEquals(P_BYTE_COUNT, dsi.getByteCount());
+        assertEquals(P_CATEGORY, dsi.getCategory());
+        assertEquals(P_COMPANY, dsi.getCompany());
+        // FIXME (byte array properties not yet implemented): assertEquals(P_, dsi.getDocparts());
+        // FIXME (byte array properties not yet implemented): assertEquals(P_, dsi.getHeadingPair());
+        assertEquals(P_HIDDEN_COUNT, dsi.getHiddenCount());
+        assertEquals(P_LINE_COUNT, dsi.getLineCount());
+        assertEquals(P_LINKS_DIRTY, dsi.getLinksDirty());
+        assertEquals(P_MANAGER, dsi.getManager());
+        assertEquals(P_MM_CLIP_COUNT, dsi.getMMClipCount());
+        assertEquals(P_NOTE_COUNT, dsi.getNoteCount());
+        assertEquals(P_PAR_COUNT, dsi.getParCount());
+        assertEquals(P_PRESENTATION_FORMAT, dsi.getPresentationFormat());
+        assertEquals(P_SCALE, dsi.getScale());
+        assertEquals(P_SLIDE_COUNT, dsi.getSlideCount());
+
+        final CustomProperties cps = dsi.getCustomProperties();
+        assertEquals(customProperties, cps);
+        assertNull(cps.get("No value available"));
+        assertEquals("Wert ä", cps.get("Schlüssel ä"));
+        assertEquals("Wert äö", cps.get("Schlüssel äö"));
+        assertEquals("Wert äöü", cps.get("Schlüssel äöü"));
+        assertEquals("Wert äöüß", cps.get("Schlüssel äöüß"));
+        assertEquals(POSITIVE_INTEGER, cps.get("positive_Integer"));
+        assertEquals(POSITIVE_LONG, cps.get("positive_Long"));
+        assertEquals(POSITIVE_DOUBLE, cps.get("positive_Double"));
+        assertEquals(NEGATIVE_INTEGER, cps.get("negative_Integer"));
+        assertEquals(NEGATIVE_LONG, cps.get("negative_Long"));
+        assertEquals(NEGATIVE_DOUBLE, cps.get("negative_Double"));
+        assertEquals(new Boolean(true), cps.get("Boolean"));
+        assertEquals(now, cps.get("Date"));
+        assertEquals(MAX_INTEGER, cps.get("max_Integer"));
+        assertEquals(MIN_INTEGER, cps.get("min_Integer"));
+        assertEquals(MAX_LONG, cps.get("max_Long"));
+        assertEquals(MIN_LONG, cps.get("min_Long"));
+        assertEquals(MAX_DOUBLE, cps.get("max_Double"));
+        assertEquals(MIN_DOUBLE, cps.get("min_Double"));
+
+        /* Remove all properties supported by HPSF from the summary
+         * information (e.g. author, edit date, application name) and from the
+         * document summary information (e.g. company, manager). */
+        si.removeApplicationName();
+        si.removeAuthor();
+        si.removeCharCount();
+        si.removeComments();
+        si.removeCreateDateTime();
+        si.removeEditTime();
+        si.removeKeywords();
+        si.removeLastAuthor();
+        si.removeLastPrinted();
+        si.removeLastSaveDateTime();
+        si.removePageCount();
+        si.removeRevNumber();
+        si.removeSecurity();
+        si.removeSubject();
+        si.removeTemplate();
+        si.removeThumbnail();
+        si.removeTitle();
+        si.removeWordCount();
+    
+        dsi.removeByteCount();
+        dsi.removeCategory();
+        dsi.removeCompany();
+        dsi.removeCustomProperties();
+        dsi.removeDocparts();
+        dsi.removeHeadingPair();
+        dsi.removeHiddenCount();
+        dsi.removeLineCount();
+        dsi.removeLinksDirty();
+        dsi.removeManager();
+        dsi.removeMMClipCount();
+        dsi.removeNoteCount();
+        dsi.removeParCount();
+        dsi.removePresentationFormat();
+        dsi.removeScale();
+        dsi.removeSlideCount();
+    
+        /* 
+         * <li><p>Write the summary information stream and the document summary
+         * information stream to the POI filesystem. */
+        si.write(dir, siEntry.getName());
+        dsi.write(dir, dsiEntry.getName());
+    
+        /* 
+         * <li><p>Write the POI filesystem to a (temporary) file <em>doc3</em>
+         * and close the latter. */
+        final File doc3 = File.createTempFile("POI_HPSF_Test.", ".tmp");
+        doc3.deleteOnExit();
+        out = new FileOutputStream(doc3);
+        poifs.writeFilesystem(out);
+        out.close();
+    
+        /* 
+         * Open <em>doc3</em> for reading and check summary information
+         * and document summary information. All properties removed before must not
+         * be found in the property streams of <em>doc3</em>.
+         */
+        poifs = new POIFSFileSystem(new FileInputStream(doc3));
+        dir = poifs.getRoot();
+        siEntry = (DocumentEntry) dir.getEntry(SummaryInformation.DEFAULT_STREAM_NAME);
+        dsiEntry = (DocumentEntry) dir.getEntry(DocumentSummaryInformation.DEFAULT_STREAM_NAME);
+    
+        dis = new DocumentInputStream(siEntry);
+        ps = new PropertySet(dis);
+        si = new SummaryInformation(ps);
+        dis = new DocumentInputStream(dsiEntry);
+        ps = new PropertySet(dis);
+        dsi = new DocumentSummaryInformation(ps);
+    
+        assertEquals(null, si.getApplicationName());
+        assertEquals(null, si.getAuthor());
+        assertEquals(0, si.getCharCount());
+        assertTrue(si.wasNull());
+        assertEquals(null, si.getComments());
+        assertEquals(null, si.getCreateDateTime());
+        assertEquals(0, si.getEditTime());
+        assertTrue(si.wasNull());
+        assertEquals(null, si.getKeywords());
+        assertEquals(null, si.getLastAuthor());
+        assertEquals(null, si.getLastPrinted());
+        assertEquals(null, si.getLastSaveDateTime());
+        assertEquals(0, si.getPageCount());
+        assertTrue(si.wasNull());
+        assertEquals(null, si.getRevNumber());
+        assertEquals(0, si.getSecurity());
+        assertTrue(si.wasNull());
+        assertEquals(null, si.getSubject());
+        assertEquals(null, si.getTemplate());
+        assertEquals(null, si.getThumbnail());
+        assertEquals(null, si.getTitle());
+        assertEquals(0, si.getWordCount());
+        assertTrue(si.wasNull());
+    
+        assertEquals(0, dsi.getByteCount());
+        assertTrue(dsi.wasNull());
+        assertEquals(null, dsi.getCategory());
+        assertEquals(null, dsi.getCustomProperties());
+        // FIXME (byte array properties not yet implemented): assertEquals(null, dsi.getDocparts());
+        // FIXME (byte array properties not yet implemented): assertEquals(null, dsi.getHeadingPair());
+        assertEquals(0, dsi.getHiddenCount());
+        assertTrue(dsi.wasNull());
+        assertEquals(0, dsi.getLineCount());
+        assertTrue(dsi.wasNull());
+        assertEquals(false, dsi.getLinksDirty());
+        assertTrue(dsi.wasNull());
+        assertEquals(null, dsi.getManager());
+        assertEquals(0, dsi.getMMClipCount());
+        assertTrue(dsi.wasNull());
+        assertEquals(0, dsi.getNoteCount());
+        assertTrue(dsi.wasNull());
+        assertEquals(0, dsi.getParCount());
+        assertTrue(dsi.wasNull());
+        assertEquals(null, dsi.getPresentationFormat());
+        assertEquals(false, dsi.getScale());
+        assertTrue(dsi.wasNull());
+        assertEquals(0, dsi.getSlideCount());
+        assertTrue(dsi.wasNull());
+    }
+
+
+
+    /**
+     * <p>Tests the simplified custom properties by reading them from the
+     * available test files.</p>
+     *
+     * @throws Throwable if anything goes wrong.
+     */
+    public void testReadCustomPropertiesFromFiles() throws Throwable
+    {
+        final AllDataFilesTester.TestTask task = new AllDataFilesTester.TestTask()
+        {
+            public void runTest(final File file) throws FileNotFoundException,
+                    IOException, NoPropertySetStreamException,
+                    MarkUnsupportedException,
+                    UnexpectedPropertySetTypeException
+            {
+                /* Read a test document <em>doc</em> into a POI filesystem. */
+                final POIFSFileSystem poifs = new POIFSFileSystem(new FileInputStream(file));
+                final DirectoryEntry dir = poifs.getRoot();
+                DocumentEntry dsiEntry = null;
+                try
+                {
+                    dsiEntry = (DocumentEntry) dir.getEntry(DocumentSummaryInformation.DEFAULT_STREAM_NAME);
+                }
+                catch (FileNotFoundException ex)
+                {
+                    /*
+                     * A missing document summary information stream is not an error
+                     * and therefore silently ignored here.
+                     */
+                }
+
+                /*
+                 * If there is a document summry information stream, read it from
+                 * the POI filesystem, else create a new one.
+                 */
+                DocumentSummaryInformation dsi;
+                if (dsiEntry != null)
+                {
+                    final DocumentInputStream dis = new DocumentInputStream(dsiEntry);
+                    final PropertySet ps = new PropertySet(dis);
+                    dsi = new DocumentSummaryInformation(ps);
+                }
+                else
+                    dsi = PropertySetFactory.newDocumentSummaryInformation();
+                final CustomProperties cps = dsi.getCustomProperties();
+                
+                if (cps == null)
+                    /* The document does not have custom properties. */
+                    return;
+
+                for (final Iterator i = cps.entrySet().iterator(); i.hasNext();)
+                {
+                    final Map.Entry e = (Entry) i.next();
+                    final CustomProperty cp = (CustomProperty) e.getValue();
+                    cp.getName();
+                    cp.getValue();
+                }
+            }
+        };
+
+        final String dataDirName = System.getProperty("HPSF.testdata.path");
+        final File dataDir = new File(dataDirName);
+        final File[] docs = dataDir.listFiles(new FileFilter()
+        {
+            public boolean accept(final File file)
+            {
+                return file.isFile() && file.getName().startsWith("Test");
+            }
+        });
+
+        for (int i = 0; i < docs.length; i++)
+        {
+            task.runTest(docs[i]);
+        }
+    }
+
+
+
+    /**
+     * <p>Tests basic custom property features.</p>
+     */
+    public void testCustomerProperties()
+    {
+        final String KEY = "Schlüssel ä";
+        final String VALUE_1 = "Wert 1";
+        final String VALUE_2 = "Wert 2";
+
+        CustomProperty cp;
+        CustomProperties cps = new CustomProperties();
+        assertEquals(0, cps.size());
+
+        /* After adding a custom property the size must be 1 and it must be
+         * possible to extract the custom property from the map. */
+        cps.put(KEY, VALUE_1);
+        assertEquals(1, cps.size());
+        Object v1 = cps.get(KEY);
+        assertEquals(VALUE_1, v1);
+        
+        /* After adding a custom property with the same name the size must still
+         * be one. */
+        cps.put(KEY, VALUE_2);
+        assertEquals(1, cps.size());
+        Object v2 = cps.get(KEY);
+        assertEquals(VALUE_2, v2);
+        
+        /* Removing the custom property must return the remove property and
+         * reduce the size to 0. */
+        cp = (CustomProperty) cps.remove(KEY);
+        assertEquals(KEY, cp.getName());
+        assertEquals(VALUE_2, cp.getValue());
+        assertEquals(0, cps.size());
+    }
+
+
+
+    /**
+     * <p>Tests reading custom properties from a section including reading
+     * custom properties which are not pure.</p>
+     */
+    public void testGetCustomerProperties()
+    {
+        final int ID_1 = 2;
+        final int ID_2 = 3;
+        final String NAME_1 = "Schlüssel ä";
+        final String VALUE_1 = "Wert 1";
+        final Map dictionary = new HashMap();
+
+        DocumentSummaryInformation dsi = PropertySetFactory.newDocumentSummaryInformation();
+        CustomProperties cps;
+        MutableSection s;
+
+        /* A document summary information set stream by default does have custom properties. */
+        cps = dsi.getCustomProperties();
+        assertEquals(null, cps);
+
+        /* Test an empty custom properties set. */
+        s = new MutableSection();
+        s.setFormatID(SectionIDMap.DOCUMENT_SUMMARY_INFORMATION_ID[1]);
+        // s.setCodepage(Constants.CP_UNICODE);
+        dsi.addSection(s);
+        cps = dsi.getCustomProperties();
+        assertEquals(0, cps.size());
+
+        /* Add a custom property. */
+        MutableProperty p = new MutableProperty();
+        p.setID(ID_1);
+        p.setType(Variant.VT_LPWSTR);
+        p.setValue(VALUE_1);
+        s.setProperty(p);
+        dictionary.put(new Long(ID_1), NAME_1);
+        s.setDictionary(dictionary);
+        cps = dsi.getCustomProperties();
+        assertEquals(1, cps.size());
+        assertTrue(cps.isPure());
+
+        /* Add another custom property. */
+        s.setProperty(ID_2, Variant.VT_LPWSTR, VALUE_1);
+        dictionary.put(new Long(ID_2), NAME_1);
+        s.setDictionary(dictionary);
+        cps = dsi.getCustomProperties();
+        assertEquals(1, cps.size());
+        assertFalse(cps.isPure());
+    }
+
+
+
+    /**
+     * <p>Runs the test cases stand-alone.</p>
+     * 
+     * @param args The command-line parameters.
+     * @throws Throwable if anything goes wrong.
+     */
+    public static void main(final String[] args) throws Throwable
+    {
+        System.setProperty("HPSF.testdata.path",
+                           "./src/testcases/org/apache/poi/hpsf/data");
+        junit.textui.TestRunner.run(TestWriteWellKnown.class);
+    }
+
+}
diff --git a/src/testcases/org/apache/poi/hpsf/data/TestWriteWellKnown.doc b/src/testcases/org/apache/poi/hpsf/data/TestWriteWellKnown.doc
new file mode 100644 (file)
index 0000000..d3a0433
Binary files /dev/null and b/src/testcases/org/apache/poi/hpsf/data/TestWriteWellKnown.doc differ