Browse Source

* Writing support added to the SummaryInformation and DocumentSummaryInformation classes. These classes now have methods for setting and removing properties. Coherent extensions are:

** Documentation section about writing standard properties added to the HPSF HOW-TO.
** Example application added showing how to modify the document summary information.
** Testcases added for testing modifying summary information and document summary information.
** PropertySetFactory extended to create SummaryInformation and DocumentSummaryInformation instances.

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

* Improved codepage handling.

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

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

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

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

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

* Unnecessary casts removed.

* Poibrowser's display format changed slightly.



git-svn-id: https://svn.apache.org/repos/asf/jakarta/poi/trunk@382887 13f79535-47bb-0310-9956-ffa450edef68
tags/REL_3_0_ALPHA3
Rainer Klute 18 years ago
parent
commit
73a9af683f
50 changed files with 4582 additions and 543 deletions
  1. 1
    1
      src/contrib/src/org/apache/poi/contrib/poibrowser/POIBrowser.java
  2. 1
    2
      src/contrib/src/org/apache/poi/contrib/poibrowser/PropertySetDescriptor.java
  3. 14
    5
      src/contrib/src/org/apache/poi/contrib/poibrowser/PropertySetDescriptorRenderer.java
  4. 350
    71
      src/documentation/content/xdocs/hpsf/how-to.xml
  5. 23
    24
      src/documentation/content/xdocs/hpsf/internals.xml
  6. 200
    0
      src/examples/src/org/apache/poi/hpsf/examples/ModifyDocumentSummaryInformation.java
  7. 0
    1
      src/java/org/apache/poi/hpsf/ClassID.java
  8. 16
    0
      src/java/org/apache/poi/hpsf/Constants.java
  9. 367
    0
      src/java/org/apache/poi/hpsf/CustomProperties.java
  10. 123
    0
      src/java/org/apache/poi/hpsf/CustomProperty.java
  11. 423
    45
      src/java/org/apache/poi/hpsf/DocumentSummaryInformation.java
  12. 5
    6
      src/java/org/apache/poi/hpsf/HPSFException.java
  13. 0
    1
      src/java/org/apache/poi/hpsf/HPSFRuntimeException.java
  14. 0
    1
      src/java/org/apache/poi/hpsf/IllegalPropertySetDataException.java
  15. 0
    1
      src/java/org/apache/poi/hpsf/MarkUnsupportedException.java
  16. 76
    0
      src/java/org/apache/poi/hpsf/MissingSectionException.java
  17. 34
    4
      src/java/org/apache/poi/hpsf/MutablePropertySet.java
  18. 124
    22
      src/java/org/apache/poi/hpsf/MutableSection.java
  19. 0
    1
      src/java/org/apache/poi/hpsf/NoFormatIDException.java
  20. 0
    1
      src/java/org/apache/poi/hpsf/NoSingleSectionException.java
  21. 24
    5
      src/java/org/apache/poi/hpsf/Property.java
  22. 25
    12
      src/java/org/apache/poi/hpsf/PropertySet.java
  23. 49
    2
      src/java/org/apache/poi/hpsf/PropertySetFactory.java
  24. 0
    1
      src/java/org/apache/poi/hpsf/ReadingNotSupportedException.java
  25. 18
    9
      src/java/org/apache/poi/hpsf/Section.java
  26. 193
    7
      src/java/org/apache/poi/hpsf/SpecialPropertySet.java
  27. 531
    106
      src/java/org/apache/poi/hpsf/SummaryInformation.java
  28. 0
    1
      src/java/org/apache/poi/hpsf/Thumbnail.java
  29. 30
    6
      src/java/org/apache/poi/hpsf/TypeWriter.java
  30. 0
    1
      src/java/org/apache/poi/hpsf/UnexpectedPropertySetTypeException.java
  31. 1
    1
      src/java/org/apache/poi/hpsf/UnsupportedVariantTypeException.java
  32. 18
    3
      src/java/org/apache/poi/hpsf/Util.java
  33. 0
    1
      src/java/org/apache/poi/hpsf/Variant.java
  34. 33
    11
      src/java/org/apache/poi/hpsf/VariantSupport.java
  35. 0
    1
      src/java/org/apache/poi/hpsf/WritingNotSupportedException.java
  36. 5
    1
      src/java/org/apache/poi/hpsf/wellknown/PropertyIDMap.java
  37. 20
    14
      src/java/org/apache/poi/hpsf/wellknown/SectionIDMap.java
  38. 23
    6
      src/java/org/apache/poi/util/LittleEndian.java
  39. 82
    0
      src/testcases/org/apache/poi/hpsf/basic/AllDataFilesTester.java
  40. 3
    5
      src/testcases/org/apache/poi/hpsf/basic/TestBasic.java
  41. 147
    145
      src/testcases/org/apache/poi/hpsf/basic/TestClassID.java
  42. 4
    5
      src/testcases/org/apache/poi/hpsf/basic/TestEmptyProperties.java
  43. 827
    0
      src/testcases/org/apache/poi/hpsf/basic/TestMetaDataIPI.java
  44. 4
    3
      src/testcases/org/apache/poi/hpsf/basic/TestUnicode.java
  45. 25
    11
      src/testcases/org/apache/poi/hpsf/basic/TestWrite.java
  46. 763
    0
      src/testcases/org/apache/poi/hpsf/basic/TestWriteWellKnown.java
  47. 0
    0
      src/testcases/org/apache/poi/hpsf/data/TestCorel.shw
  48. 0
    0
      src/testcases/org/apache/poi/hpsf/data/TestGermanWord90.doc
  49. 0
    0
      src/testcases/org/apache/poi/hpsf/data/TestRobert_Flaherty.doc
  50. BIN
      src/testcases/org/apache/poi/hpsf/data/TestWriteWellKnown.doc

+ 1
- 1
src/contrib/src/org/apache/poi/contrib/poibrowser/POIBrowser.java View 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);
}


+ 1
- 2
src/contrib/src/org/apache/poi/contrib/poibrowser/PropertySetDescriptor.java View 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
{

+ 14
- 5
src/contrib/src/org/apache/poi/contrib/poibrowser/PropertySetDescriptorRenderer.java View 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;

+ 350
- 71
src/documentation/content/xdocs/hpsf/how-to.xml View 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.
@@ -28,58 +28,78 @@

<li>
The <link href="#sec2">second section</link> goes a small step
further and focusses on reading additional standard properties. It also
talks about exceptions that may be thrown when dealing with HPSF and
shows how you can read properties of embedded objects.
further and focusses on <strong>reading additional standard
properties</strong>. It also talks about <strong>exceptions</strong> that
may be thrown when dealing with HPSF and shows how you can <strong>read
properties of embedded objects</strong>.
</li>

<li>
The <link href="#sec3">third section</link> tells how to read
non-standard properties. Non-standard properties are application-specific
triples consisting of an ID, a type, and a value.
The <link href="#sec3">third section</link> explains how to <strong>write
standard properties</strong>. HPSF provides some high-level classes and
methods which make writing of standard properties easy. They are based on
the low-level writing functions explained in the <link href="#sec3">fifth
section</link>.
</li>

<li>
The <link href="#sec4">fourth section</link> tells you how to write
property set streams. At this time HPSF provides low-level methods only
for writing properties. Therefore you have to understand the <link
href="#sec3">third section</link> before you should think about writing
properties. Check the Javadoc API documentation to find out about the
details! <strong>Please note:</strong> HPSF's writing functionality is
<strong>not</strong> present in POI releases up to and including 2.5. In
order to write properties you have to download a later POI release (when
available) or retrieve the POI development version from the Subversion
repository.
The <link href="#sec4">fourth section</link> tells how to <strong>read
non-standard properties</strong>. Non-standard properties are
application-specific triples consisting of an ID, a type, and a value.
</li>

<li>
The <link href="#sec5">fifth section</link> tells you how to <strong>write
property set streams</strong> using HPSF's low-level methods. You have to
understand the <link href="#sec3">fourth section</link> before you should
think about low-level writing properties. Check the Javadoc API
documentation to find out about the details!
</li>
</ol>

<note><strong>Please note:</strong> HPSF's writing functionality is
<strong>not</strong> present in POI releases up to and including 2.5. In
order to write properties you have to download a later POI release (when
available) or retrieve the POI development version from the <link
href="http://jakarta.apache.org/site/cvsindex.html">Subversion
repository</link>.</note>



<anchor id="sec1"/>
<section><title>Reading Standard Properties</title>

<note>This section explains how to read
the most important standard properties of a Microsoft Office
document. Standard properties are things like title, author, creation
date etc. Chances are that you will find here what you need and
don't have to read the other sections.</note>

<p>The first thing you should understand is that properties are stored in
separate documents inside the POI filesystem. (If you don't know what a
POI filesystem is, read the <link href="../poifs/index.html">POIFS
documentation</link>.) A document in a POI filesystem is also called a
<strong>stream</strong>.</p>

<p>The following example shows how to read a POI filesystem's
"title" property. Reading other properties is similar. Consider the API
documentation of <code>org.apache.poi.hpsf.SummaryInformation</code> to
learn which methods are available!</p>
<note>This section explains how to read the most important standard
properties of a Microsoft Office document. Standard properties are things
like title, author, creation date etc. This section introduces the
<strong>summary information stream</strong> which is used to keep these
properties. Chances are that you will find here what you need and don't
have to read the other sections.</note>

<p>The first thing you should understand is that a Microsoft Office file is
not one large bunch of bytes but has an internal filesystem structure with
files and directories. You can access these files and directories using
the <link href="../poifs/index.html">POI filesystem (POIFS)</link>
provides. A file or document in a POI filesystem is also called a
<strong>stream</strong> - The properties of, say, an Excel document are
stored apart of the actual spreadsheet data in separate streams. The good
new is that this separation makes the properties independent of the
concrete Microsoft Office file. In the following text we will always say
"POI filesystem" instead of "Microsoft Office file" because a POI
filesystem is not necessarily created by or for a Microsoft Office
application, because it is shorter, and because we want to avoid the name
of That Redmond Company.</p>

<p>The following example shows how to read the "title" property. Reading
other properties is similar. Consider the API documentation of the class
<code>org.apache.poi.hpsf.SummaryInformation</code> to learn which methods
are available.</p>

<p>The standard properties this section focusses on can be found in a
document called <em>\005SummaryInformation</em> located in the root of the
POI filesystem. The notation <em>\005</em> in the document's name means
the character with the decimal value of 5. In order to read the title, an
application has to perform the following steps:</p>
the character with a decimal value of 5. In order to read the "title"
property, an application has to perform the following steps:</p>

<ol>
<li>
@@ -103,7 +123,7 @@
POI filesystem</title>

<p>An application that wants to open a document in a POI filesystem
(POIFS) proceeds as shown by the following code fragment. (The full
(POIFS) proceeds as shown by the following code fragment. The full
source code of the sample application is available in the
<em>examples</em> section of the POI source tree as
<em>ReadTitle.java</em>.</p>
@@ -144,14 +164,15 @@ 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>


+ 23
- 24
src/documentation/content/xdocs/hpsf/internals.xml View File

@@ -1006,41 +1006,40 @@
helpful. If you have any amendments or corrections, please let us know!
Thank you!</p>

<ol>
<ol>

<li>In
<li>In
<link href="http://www.kyler.com/pubs/ddj9894.html"><em>Understanding OLE
documents</em></link>, Ken Kyler gives an introduction to OLE2
documents
and especially to property sets. He names the property names, types, and
IDs of the Summary Information and Document Summary Information
stream.</li>

<li>The
<link href="http://www.dwam.net/docs/oleref/"><em>ActiveX Programmer's
Reference</em></link> at
<link href="http://www.dwam.net/docs/oleref/">http://www.dwam.net/docs/oleref/</link>
documents</em></link>, Ken Kyler gives an introduction to OLE2
documents and especially to property sets. He names the property names,
types, and IDs of the Summary Information and Document Summary
Information stream.</li>

<li>The <link href="http://www.dwam.net/docs/oleref/"><em>ActiveX
Programmer's Reference</em></link> at <link
href="http://www.dwam.net/docs/oleref/">http://www.dwam.net/docs/oleref/</link>
seems a little outdated, but that's what I have found.</li>

<li>An overview of the <code>VT_</code> types is in
<li>An overview of the <code>VT_</code> types is in
<link href="http://www.marin.clara.net/COM/variant_type_definitions.htm"><em>Variant
Type Definitions</em></link>.</li>

<li>What is a <code>FILETIME</code>? The answer can be found
under <link
href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/sysinfo/base/filetime_str.asp"></link>, <link href="http://www.vbapi.com/ref/f/filetime.html">http://www.vbapi.com/ref/f/filetime.html</link> or
<li>What is a <code>FILETIME</code>? The answer can be found
under <link
href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/sysinfo/base/filetime_str.asp"></link>, <link href="http://www.vbapi.com/ref/f/filetime.html">http://www.vbapi.com/ref/f/filetime.html</link> or
<link href="http://www.cs.rpi.edu/courses/fall01/os/FILETIME.html">http://www.cs.rpi.edu/courses/fall01/os/FILETIME.html</link>.
In short: <em>The FILETIME structure holds a date and time associated
with a file. The structure identifies a 64-bit integer specifying the
number of 100-nanosecond intervals which have passed since January 1,
1601. This 64-bit value is split into the two dwords stored in the
structure.</em></li>
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>

+ 200
- 0
src/examples/src/org/apache/poi/hpsf/examples/ModifyDocumentSummaryInformation.java View File

@@ -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();
}

}

+ 0
- 1
src/java/org/apache/poi/hpsf/ClassID.java View File

@@ -1,4 +1,3 @@

/* ====================================================================
Copyright 2002-2004 Apache Software Foundation


+ 16
- 0
src/java/org/apache/poi/hpsf/Constants.java View 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;

/**

+ 367
- 0
src/java/org/apache/poi/hpsf/CustomProperties.java View File

@@ -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;
}

}

+ 123
- 0
src/java/org/apache/poi/hpsf/CustomProperty.java View File

@@ -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();
}

}

+ 423
- 45
src/java/org/apache/poi/hpsf/DocumentSummaryInformation.java View 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.
@@ -17,11 +16,11 @@
package org.apache.poi.hpsf;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import org.apache.poi.hpsf.wellknown.PropertyIDMap;
import org.apache.poi.hpsf.wellknown.SectionIDMap;

/**
* <p>Convenience class representing a DocumentSummary Information stream in a
@@ -68,7 +67,7 @@ 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.");
}

}

+ 5
- 6
src/java/org/apache/poi/hpsf/HPSFException.java View File

@@ -1,4 +1,3 @@

/* ====================================================================
Copyright 2002-2004 Apache Software Foundation

@@ -18,12 +17,12 @@
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
*/

+ 0
- 1
src/java/org/apache/poi/hpsf/HPSFRuntimeException.java View File

@@ -1,4 +1,3 @@

/* ====================================================================
Copyright 2002-2004 Apache Software Foundation


+ 0
- 1
src/java/org/apache/poi/hpsf/IllegalPropertySetDataException.java View File

@@ -1,4 +1,3 @@

/* ====================================================================
Copyright 2002-2004 Apache Software Foundation


+ 0
- 1
src/java/org/apache/poi/hpsf/MarkUnsupportedException.java View File

@@ -1,4 +1,3 @@

/* ====================================================================
Copyright 2002-2004 Apache Software Foundation


+ 76
- 0
src/java/org/apache/poi/hpsf/MissingSectionException.java View File

@@ -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);
}

}

+ 34
- 4
src/java/org/apache/poi/hpsf/MutablePropertySet.java View 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());
}

}

+ 124
- 22
src/java/org/apache/poi/hpsf/MutableSection.java View 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));
}

}

+ 0
- 1
src/java/org/apache/poi/hpsf/NoFormatIDException.java View File

@@ -1,4 +1,3 @@

/* ====================================================================
Copyright 2002-2004 Apache Software Foundation


+ 0
- 1
src/java/org/apache/poi/hpsf/NoSingleSectionException.java View File

@@ -1,4 +1,3 @@

/* ====================================================================
Copyright 2002-2004 Apache Software Foundation


+ 24
- 5
src/java/org/apache/poi/hpsf/Property.java View 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:

+ 25
- 12
src/java/org/apache/poi/hpsf/PropertySet.java View 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));
}



+ 49
- 2
src/java/org/apache/poi/hpsf/PropertySetFactory.java View 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);
}
}

}

+ 0
- 1
src/java/org/apache/poi/hpsf/ReadingNotSupportedException.java View File

@@ -1,4 +1,3 @@

/* ====================================================================
Copyright 2002-2004 Apache Software Foundation


+ 18
- 9
src/java/org/apache/poi/hpsf/Section.java View 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;
}

}

+ 193
- 7
src/java/org/apache/poi/hpsf/SpecialPropertySet.java View 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.
@@ -17,8 +16,13 @@
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();
}

}

+ 531
- 106
src/java/org/apache/poi/hpsf/SummaryInformation.java View 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);
}

}

+ 0
- 1
src/java/org/apache/poi/hpsf/Thumbnail.java View File

@@ -1,4 +1,3 @@

/* ====================================================================
Copyright 2002-2004 Apache Software Foundation


+ 30
- 6
src/java/org/apache/poi/hpsf/TypeWriter.java View 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);

+ 0
- 1
src/java/org/apache/poi/hpsf/UnexpectedPropertySetTypeException.java View File

@@ -1,4 +1,3 @@

/* ====================================================================
Copyright 2002-2004 Apache Software Foundation


+ 1
- 1
src/java/org/apache/poi/hpsf/UnsupportedVariantTypeException.java View 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>
*

+ 18
- 3
src/java/org/apache/poi/hpsf/Util.java View 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)
{

+ 0
- 1
src/java/org/apache/poi/hpsf/Variant.java View File

@@ -1,4 +1,3 @@

/* ====================================================================
Copyright 2002-2004 Apache Software Foundation


+ 33
- 11
src/java/org/apache/poi/hpsf/VariantSupport.java View 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:

+ 0
- 1
src/java/org/apache/poi/hpsf/WritingNotSupportedException.java View File

@@ -1,4 +1,3 @@

/* ====================================================================
Copyright 2002-2004 Apache Software Foundation


+ 5
- 1
src/java/org/apache/poi/hpsf/wellknown/PropertyIDMap.java View 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;




+ 20
- 14
src/java/org/apache/poi/hpsf/wellknown/SectionIDMap.java View 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.

+ 23
- 6
src/java/org/apache/poi/util/LittleEndian.java View 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;
}

}

+ 82
- 0
src/testcases/org/apache/poi/hpsf/basic/AllDataFilesTester.java View File

@@ -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);
}
}

}

+ 3
- 5
src/testcases/org/apache/poi/hpsf/basic/TestBasic.java View 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());
}



+ 147
- 145
src/testcases/org/apache/poi/hpsf/basic/TestClassID.java View File

@@ -1,145 +1,147 @@
/* ====================================================================
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;
/**
* <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);
}
}
/* ====================================================================
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);
}

}

+ 4
- 5
src/testcases/org/apache/poi/hpsf/basic/TestEmptyProperties.java View 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++)

+ 827
- 0
src/testcases/org/apache/poi/hpsf/basic/TestMetaDataIPI.java View File

@@ -0,0 +1,827 @@
/* ====================================================================
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.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.Date;
import java.util.Random;
import junit.framework.TestCase;
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;
/**
* Basing on: src/examples/src/org/apache/poi/hpsf/examples/ModifyDocumentSummaryInformation.java
* This class tests reading and writing of meta data. No actual document is created. All information
* is stored in a virtal document in a ByteArrayOutputStream
* @author Matthias Günter
* @since 2006-03-03
* @version $Id: TestEmptyProperties.java 353563 2004-06-22 16:16:33Z klute $
*/
public class TestMetaDataIPI extends TestCase{
private ByteArrayOutputStream bout= null; //our store
private POIFSFileSystem poifs=null;
DirectoryEntry dir = null;
DocumentSummaryInformation dsi=null;
SummaryInformation si=null;
/**
* Standard constructor
* @param s
*/
public TestMetaDataIPI(String s ){
super(s);
}
/**
* Setup is used to get the document ready. Gets the DocumentSummaryInformation and the
* SummaryInformation to reasonable values
*/
public void setUp(){
bout=new ByteArrayOutputStream();
poifs= new POIFSFileSystem();
dir = poifs.getRoot();
dsi=null;
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();
assertNotNull(dsi);
} catch (IOException e) {
e.printStackTrace();
fail();
} catch (NoPropertySetStreamException e) {
e.printStackTrace();
fail();
} catch (MarkUnsupportedException e) {
e.printStackTrace();
fail();
} catch (UnexpectedPropertySetTypeException e) {
e.printStackTrace();
fail();
}
assertNotNull(dsi);
try
{
DocumentEntry dsiEntry = (DocumentEntry)
dir.getEntry(SummaryInformation.DEFAULT_STREAM_NAME);
DocumentInputStream dis = new DocumentInputStream(dsiEntry);
PropertySet ps = new PropertySet(dis);
dis.close();
si = new SummaryInformation(ps);
}
catch (FileNotFoundException ex)
{
/* There is no document summary information yet. We have to create a
* new one. */
si = PropertySetFactory.newSummaryInformation();
assertNotNull(si);
} catch (IOException e) {
e.printStackTrace();
fail();
} catch (NoPropertySetStreamException e) {
e.printStackTrace();
fail();
} catch (MarkUnsupportedException e) {
e.printStackTrace();
fail();
} catch (UnexpectedPropertySetTypeException e) {
e.printStackTrace();
fail();
}
assertNotNull(dsi);
}
/**
* Setting a lot of things to null.
*/
public void tearDown(){
bout=null;
poifs=null;
dir=null;
dsi=null;
}
/**
* Closes the ByteArrayOutputStream and reads it into a ByteArrayInputStream.
* When finished writing information this method is used in the tests to
* start reading from the created document and then the see if the results match.
*
*/
public void closeAndReOpen(){
try {
dsi.write(dir, DocumentSummaryInformation.DEFAULT_STREAM_NAME);
si.write(dir,SummaryInformation.DEFAULT_STREAM_NAME);
} catch (WritingNotSupportedException e) {
e.printStackTrace();
fail();
} catch (IOException e) {
e.printStackTrace();
fail();
}
si=null;
dsi=null;
try {
poifs.writeFilesystem(bout);
bout.flush();
} catch (IOException e) {
e.printStackTrace();
fail();
}
InputStream is=new ByteArrayInputStream(bout.toByteArray());
assertNotNull(is);
POIFSFileSystem poifs=null;
try {
poifs = new POIFSFileSystem(is);
} catch (IOException e) {
e.printStackTrace();
fail();
}
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
fail();
}
assertNotNull(poifs);
/* Read the document summary information. */
DirectoryEntry dir = poifs.getRoot();
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)
{
fail();
} catch (IOException e) {
e.printStackTrace();
fail();
} catch (NoPropertySetStreamException e) {
e.printStackTrace();
fail();
} catch (MarkUnsupportedException e) {
e.printStackTrace();
fail();
} catch (UnexpectedPropertySetTypeException e) {
e.printStackTrace();
fail();
}
try
{
DocumentEntry dsiEntry = (DocumentEntry)
dir.getEntry(SummaryInformation.DEFAULT_STREAM_NAME);
DocumentInputStream dis = new DocumentInputStream(dsiEntry);
PropertySet ps = new PropertySet(dis);
dis.close();
si = new SummaryInformation(ps);
}
catch (FileNotFoundException ex)
{
/* There is no document summary information yet. We have to create a
* new one. */
si = PropertySetFactory.newSummaryInformation();
assertNotNull(si);
} catch (IOException e) {
e.printStackTrace();
fail();
} catch (NoPropertySetStreamException e) {
e.printStackTrace();
fail();
} catch (MarkUnsupportedException e) {
e.printStackTrace();
fail();
} catch (UnexpectedPropertySetTypeException e) {
e.printStackTrace();
fail();
}
}
/**
* Sets the most important information in DocumentSummaryInformation and Summary Information and rereads it
*
*/
public void testOne(){
//DocumentSummaryInformation
dsi.setCompany("xxxCompanyxxx");
dsi.setManager("xxxManagerxxx");
dsi.setCategory("xxxCategoryxxx");
//SummaryInformation
si.setTitle("xxxTitlexxx");
si.setAuthor("xxxAuthorxxx");
si.setComments("xxxCommentsxxx");
si.setKeywords("xxxKeyWordsxxx");
si.setSubject("xxxSubjectxxx");
//Custom Properties (in DocumentSummaryInformation
CustomProperties customProperties = dsi.getCustomProperties();
if (customProperties == null){
customProperties = new CustomProperties();
}
/* Insert some custom properties into the container. */
customProperties.put("Key1", "Value1");
customProperties.put("Schlüssel2", "Wert2");
customProperties.put("Sample Integer", new Integer(12345));
customProperties.put("Sample Boolean", new Boolean(true));
Date date=new Date();
customProperties.put("Sample Date", date);
customProperties.put("Sample Double", new Double(-1.0001));
customProperties.put("Sample Negative Integer", new Integer(-100000));
dsi.setCustomProperties(customProperties);
//start reading
closeAndReOpen();
//testing
assertNotNull(dsi);
assertNotNull(si);
assertEquals("Category","xxxCategoryxxx",dsi.getCategory());
assertEquals("Company","xxxCompanyxxx",dsi.getCompany());
assertEquals("Manager","xxxManagerxxx",dsi.getManager());
assertEquals("","xxxAuthorxxx",si.getAuthor());
assertEquals("","xxxTitlexxx",si.getTitle());
assertEquals("","xxxCommentsxxx",si.getComments());
assertEquals("","xxxKeyWordsxxx",si.getKeywords());
assertEquals("","xxxSubjectxxx",si.getSubject());
/* 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 = dsi.getCustomProperties();
if (customProperties == null){
fail();
}
/* Insert some custom properties into the container. */
String a1=(String) customProperties.get("Key1");
assertEquals("Key1","Value1",a1);
String a2=(String) customProperties.get("Schlüssel2");
assertEquals("Schlüssel2","Wert2",a2);
Integer a3=(Integer) customProperties.get("Sample Integer");
assertEquals("Sample Number",new Integer(12345),a3);
Boolean a4=(Boolean) customProperties.get("Sample Boolean");
assertEquals("Sample Boolean",new Boolean(true),a4);
Date a5=(Date) customProperties.get("Sample Date");
assertEquals("Custom Date:",date,a5);
Double a6=(Double) customProperties.get("Sample Double");
assertEquals("Custom Float",new Double(-1.0001),a6);
Integer a7=(Integer) customProperties.get("Sample Negative Integer");
assertEquals("Neg", new Integer(-100000),a7);
}
/**
* multiplies a string
* @param s Input String
* @return the multiplied String
*/
public String elongate(String s){
StringBuffer sb=new StringBuffer();
for (int i=0;i<10000;i++){
sb.append(s);
sb.append(" ");
}
return sb.toString();
}
/**
* Test very long input in each of the fields (approx 30-60KB each)
*
*/
public void testTwo(){
String company=elongate("company");
String manager=elongate("manager");
String category=elongate("category");
String title=elongate("title");
String author=elongate("author");
String comments=elongate("comments");
String keywords=elongate("keywords");
String subject=elongate("subject");
String p1=elongate("p1");
String p2=elongate("p2");
String k1=elongate("k1");
String k2=elongate("k2");
dsi.setCompany(company);
dsi.setManager(manager);
dsi.setCategory(category);
si.setTitle(title);
si.setAuthor(author);
si.setComments(comments);
si.setKeywords(keywords);
si.setSubject(subject);
CustomProperties customProperties = dsi.getCustomProperties();
if (customProperties == null){
customProperties = new CustomProperties();
}
/* Insert some custom properties into the container. */
customProperties.put(k1, p1);
customProperties.put(k2, p2);
customProperties.put("Sample Number", new Integer(12345));
customProperties.put("Sample Boolean", new Boolean(true));
Date date=new Date();
customProperties.put("Sample Date", date);
dsi.setCustomProperties(customProperties);
closeAndReOpen();
assertNotNull(dsi);
assertNotNull(si);
/* Change the category to "POI example". Any former category value will
* be lost. If there has been no category yet, it will be created. */
assertEquals("Category",category,dsi.getCategory());
assertEquals("Company",company,dsi.getCompany());
assertEquals("Manager",manager,dsi.getManager());
assertEquals("",author,si.getAuthor());
assertEquals("",title,si.getTitle());
assertEquals("",comments,si.getComments());
assertEquals("",keywords,si.getKeywords());
assertEquals("",subject,si.getSubject());
/* 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 = dsi.getCustomProperties();
if (customProperties == null){
fail();
}
/* Insert some custom properties into the container. */
String a1=(String) customProperties.get(k1);
assertEquals("Key1",p1,a1);
String a2=(String) customProperties.get(k2);
assertEquals("Schlüssel2",p2,a2);
Integer a3=(Integer) customProperties.get("Sample Number");
assertEquals("Sample Number",new Integer(12345),a3);
Boolean a4=(Boolean) customProperties.get("Sample Boolean");
assertEquals("Sample Boolean",new Boolean(true),a4);
Date a5=(Date) customProperties.get("Sample Date");
assertEquals("Custom Date:",date,a5);
}
/**
* adds strange characters to the string
* @param s Input String
* @return the multiplied String
*/
public String strangize(String s){
StringBuffer sb=new StringBuffer();
String[] umlaute= {"ä","ü","ö","Ü","$","Ö","Ü","É","Ö","@","ç","&"};
char j=0;
Random rand=new Random();
for (int i=0;i<5;i++){
sb.append(s);
sb.append(" ");
j=(char) rand.nextInt(220);
j+=33;
// System.out.println(j);
sb.append(">");
sb.append(new Character(j));
sb.append("=");
sb.append(umlaute[rand.nextInt(umlaute.length)]);
sb.append("<");
}
return sb.toString();
}
/**
* Tests with strange characters in keys and data (Umlaute etc.)
*
*/
public void testThree(){
String company=strangize("company");
String manager=strangize("manager");
String category=strangize("category");
String title=strangize("title");
String author=strangize("author");
String comments=strangize("comments");
String keywords=strangize("keywords");
String subject=strangize("subject");
String p1=strangize("p1");
String p2=strangize("p2");
String k1=strangize("k1");
String k2=strangize("k2");
dsi.setCompany(company);
dsi.setManager(manager);
dsi.setCategory(category);
si.setTitle(title);
si.setAuthor(author);
si.setComments(comments);
si.setKeywords(keywords);
si.setSubject(subject);
CustomProperties customProperties = dsi.getCustomProperties();
if (customProperties == null){
customProperties = new CustomProperties();
}
/* Insert some custom properties into the container. */
customProperties.put(k1, p1);
customProperties.put(k2, p2);
customProperties.put("Sample Number", new Integer(12345));
customProperties.put("Sample Boolean", new Boolean(false));
Date date=new Date(0);
customProperties.put("Sample Date", date);
dsi.setCustomProperties(customProperties);
closeAndReOpen();
assertNotNull(dsi);
assertNotNull(si);
/* Change the category to "POI example". Any former category value will
* be lost. If there has been no category yet, it will be created. */
assertEquals("Category",category,dsi.getCategory());
assertEquals("Company",company,dsi.getCompany());
assertEquals("Manager",manager,dsi.getManager());
assertEquals("",author,si.getAuthor());
assertEquals("",title,si.getTitle());
assertEquals("",comments,si.getComments());
assertEquals("",keywords,si.getKeywords());
assertEquals("",subject,si.getSubject());
/* 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 = dsi.getCustomProperties();
if (customProperties == null){
fail();
}
/* Insert some custom properties into the container. */
// System.out.println(k1);
String a1=(String) customProperties.get(k1);
assertEquals("Key1",p1,a1);
String a2=(String) customProperties.get(k2);
assertEquals("Schlüssel2",p2,a2);
Integer a3=(Integer) customProperties.get("Sample Number");
assertEquals("Sample Number",new Integer(12345),a3);
Boolean a4=(Boolean) customProperties.get("Sample Boolean");
assertEquals("Sample Boolean",new Boolean(false),a4);
Date a5=(Date) customProperties.get("Sample Date");
assertEquals("Custom Date:",date,a5);
}
/**
* Iterative testing: writing, reading etc.
*
*/
public void testFour(){
for (int i=1;i<100;i++){
setUp();
testThree();
tearDown();
}
}
/**
* adds strange characters to the string with the adding of unicode characters
* @param s Input String
* @return the multiplied String
*/
public String strangizeU(String s){
StringBuffer sb=new StringBuffer();
String[] umlaute= {"ä","ü","ö","Ü","$","Ö","Ü","É","Ö","@","ç","&"};
char j=0;
Random rand=new Random();
for (int i=0;i<5;i++){
sb.append(s);
sb.append(" ");
j=(char) rand.nextInt(220);
j+=33;
// System.out.println(j);
sb.append(">");
sb.append(new Character(j));
sb.append("=");
sb.append(umlaute[rand.nextInt(umlaute.length)]);
sb.append("<");
}
sb.append("äöü\uD840\uDC00");
return sb.toString();
}
/**
* Unicode test
*
*/
public void testUnicode(){
String company=strangizeU("company");
String manager=strangizeU("manager");
String category=strangizeU("category");
String title=strangizeU("title");
String author=strangizeU("author");
String comments=strangizeU("comments");
String keywords=strangizeU("keywords");
String subject=strangizeU("subject");
String p1=strangizeU("p1");
String p2=strangizeU("p2");
String k1=strangizeU("k1");
String k2=strangizeU("k2");
dsi.setCompany(company);
dsi.setManager(manager);
dsi.setCategory(category);
si.setTitle(title);
si.setAuthor(author);
si.setComments(comments);
si.setKeywords(keywords);
si.setSubject(subject);
CustomProperties customProperties = dsi.getCustomProperties();
if (customProperties == null){
customProperties = new CustomProperties();
}
/* Insert some custom properties into the container. */
customProperties.put(k1, p1);
customProperties.put(k2, p2);
customProperties.put("Sample Number", new Integer(12345));
customProperties.put("Sample Boolean", new Boolean(true));
Date date=new Date();
customProperties.put("Sample Date", date);
dsi.setCustomProperties(customProperties);
closeAndReOpen();
assertNotNull(dsi);
assertNotNull(si);
/* Change the category to "POI example". Any former category value will
* be lost. If there has been no category yet, it will be created. */
assertEquals("Category",category,dsi.getCategory());
assertEquals("Company",company,dsi.getCompany());
assertEquals("Manager",manager,dsi.getManager());
assertEquals("",author,si.getAuthor());
assertEquals("",title,si.getTitle());
assertEquals("",comments,si.getComments());
assertEquals("",keywords,si.getKeywords());
assertEquals("",subject,si.getSubject());
/* 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 = dsi.getCustomProperties();
if (customProperties == null){
fail();
}
/* Insert some custom properties into the container. */
// System.out.println(k1);
String a1=(String) customProperties.get(k1);
assertEquals("Key1",p1,a1);
String a2=(String) customProperties.get(k2);
assertEquals("Schlüssel2",p2,a2);
Integer a3=(Integer) customProperties.get("Sample Number");
assertEquals("Sample Number",new Integer(12345),a3);
Boolean a4=(Boolean) customProperties.get("Sample Boolean");
assertEquals("Sample Boolean",new Boolean(true),a4);
Date a5=(Date) customProperties.get("Sample Date");
assertEquals("Custom Date:",date,a5);
}
/**
* Iterative testing of the unicode test
*
*/
public void testSix(){
for (int i=1;i<100;i++){
setUp();
testUnicode();
tearDown();
}
}
/**
* Tests conversion in custom fields and errors
*
*/
public void testConvAndExistance(){
CustomProperties customProperties = dsi.getCustomProperties();
if (customProperties == null){
customProperties = new CustomProperties();
}
/* Insert some custom properties into the container. */
customProperties.put("int", new Integer(12345));
customProperties.put("negint", new Integer(-12345));
customProperties.put("long", new Long(12345));
customProperties.put("neglong", new Long(-12345));
customProperties.put("boolean", new Boolean(true));
customProperties.put("string", "a String");
//customProperties.put("float", new Float(12345.0)); is not valid
//customProperties.put("negfloat", new Float(-12345.1)); is not valid
customProperties.put("double", new Double(12345.2));
customProperties.put("negdouble", new Double(-12345.3));
//customProperties.put("char", new Character('a')); is not valid
Date date=new Date();
customProperties.put("date", date);
dsi.setCustomProperties(customProperties);
closeAndReOpen();
assertNotNull(dsi);
assertNotNull(si);
/* Change the category to "POI example". Any former category value will
* be lost. If there has been no category yet, it will be created. */
assertNull(dsi.getCategory());
assertNull(dsi.getCompany());
assertNull(dsi.getManager());
assertNull(si.getAuthor());
assertNull(si.getTitle());
assertNull(si.getComments());
assertNull(si.getKeywords());
assertNull(si.getSubject());
/* 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 = dsi.getCustomProperties();
if (customProperties == null){
fail();
}
/* Insert some custom properties into the container. */
Integer a3=(Integer) customProperties.get("int");
assertEquals("int",new Integer(12345),a3);
a3=(Integer) customProperties.get("negint");
assertEquals("negint",new Integer(-12345),a3);
Long al=(Long) customProperties.get("neglong");
assertEquals("neglong",new Long(-12345),al);
al=(Long) customProperties.get("long");
assertEquals("long",new Long(12345),al);
Boolean a4=(Boolean) customProperties.get("boolean");
assertEquals("boolean",new Boolean(true),a4);
Date a5=(Date) customProperties.get("date");
assertEquals("Custom Date:",date,a5);
Double d=(Double) customProperties.get("double");
assertEquals("int",new Double(12345.2),d);
d=(Double) customProperties.get("negdouble");
assertEquals("string",new Double(-12345.3),d);
String s=(String) customProperties.get("string");
assertEquals("sring","a String",s);
Object o=null;
o=customProperties.get("string");
if (!(o instanceof String)){
fail();
}
o=customProperties.get("boolean");
if (!(o instanceof Boolean)){
fail();
}
o=customProperties.get("int");
if (!(o instanceof Integer)){
fail();
}
o=customProperties.get("negint");
if (!(o instanceof Integer)){
fail();
}
o=customProperties.get("long");
if (!(o instanceof Long)){
fail();
}
o=customProperties.get("neglong");
if (!(o instanceof Long)){
fail();
}
o=customProperties.get("double");
if (!(o instanceof Double)){
fail();
}
o=customProperties.get("negdouble");
if (!(o instanceof Double)){
fail();
}
o=customProperties.get("date");
if (!(o instanceof Date)){
fail();
}
}
}

+ 4
- 3
src/testcases/org/apache/poi/hpsf/basic/TestUnicode.java View 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),

+ 25
- 11
src/testcases/org/apache/poi/hpsf/basic/TestWrite.java View 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
{

+ 763
- 0
src/testcases/org/apache/poi/hpsf/basic/TestWriteWellKnown.java View File

@@ -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);
}

}

+ 0
- 0
src/testcases/org/apache/poi/hpsf/data/TestCorel.shw View File


+ 0
- 0
src/testcases/org/apache/poi/hpsf/data/TestGermanWord90.doc View File


+ 0
- 0
src/testcases/org/apache/poi/hpsf/data/TestRobert_Flaherty.doc View File


BIN
src/testcases/org/apache/poi/hpsf/data/TestWriteWellKnown.doc View File


Loading…
Cancel
Save