** 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-ffa450edef68tags/REL_3_0_ALPHA3
@@ -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); | |||
} | |||
@@ -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 | |||
{ |
@@ -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; |
@@ -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> | |||
@@ -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> |
@@ -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(); | |||
} | |||
} |
@@ -1,4 +1,3 @@ | |||
/* ==================================================================== | |||
Copyright 2002-2004 Apache Software Foundation | |||
@@ -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; | |||
/** |
@@ -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"><klute@rainer-klute.de></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; | |||
} | |||
} |
@@ -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"><klute@rainer-klute.de></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(); | |||
} | |||
} |
@@ -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."); | |||
} | |||
} |
@@ -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"><klute@rainer-klute.de></a> | |||
* href="mailto:klute@rainer-klute.de"><klute@rainer-klute.de></a> | |||
* @version $Id$ | |||
* @since 2002-02-09 | |||
*/ |
@@ -1,4 +1,3 @@ | |||
/* ==================================================================== | |||
Copyright 2002-2004 Apache Software Foundation | |||
@@ -1,4 +1,3 @@ | |||
/* ==================================================================== | |||
Copyright 2002-2004 Apache Software Foundation | |||
@@ -1,4 +1,3 @@ | |||
/* ==================================================================== | |||
Copyright 2002-2004 Apache Software Foundation | |||
@@ -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"><klute@rainer-klute.de></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); | |||
} | |||
} |
@@ -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()); | |||
} | |||
} |
@@ -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)); | |||
} | |||
} |
@@ -1,4 +1,3 @@ | |||
/* ==================================================================== | |||
Copyright 2002-2004 Apache Software Foundation | |||
@@ -1,4 +1,3 @@ | |||
/* ==================================================================== | |||
Copyright 2002-2004 Apache Software Foundation | |||
@@ -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: |
@@ -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)); | |||
} | |||
@@ -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); | |||
} | |||
} | |||
} |
@@ -1,4 +1,3 @@ | |||
/* ==================================================================== | |||
Copyright 2002-2004 Apache Software Foundation | |||
@@ -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; | |||
} | |||
} |
@@ -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(); | |||
} | |||
} |
@@ -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"><klute@rainer-klute.de></a> | |||
* href="mailto:klute@rainer-klute.de"><klute@rainer-klute.de></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 <Drew.Varner -at- | |||
* sc.edu> 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 <Drew.Varner | |||
* -at- sc.edu> 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); | |||
} | |||
} |
@@ -1,4 +1,3 @@ | |||
/* ==================================================================== | |||
Copyright 2002-2004 Apache Software Foundation | |||
@@ -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); |
@@ -1,4 +1,3 @@ | |||
/* ==================================================================== | |||
Copyright 2002-2004 Apache Software Foundation | |||
@@ -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> | |||
* |
@@ -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) | |||
{ |
@@ -1,4 +1,3 @@ | |||
/* ==================================================================== | |||
Copyright 2002-2004 Apache Software Foundation | |||
@@ -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: |
@@ -1,4 +1,3 @@ | |||
/* ==================================================================== | |||
Copyright 2002-2004 Apache Software Foundation | |||
@@ -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; | |||
@@ -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. |
@@ -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; | |||
} | |||
} |
@@ -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"><klute@rainer-klute.de></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"><klute@rainer-klute.de></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); | |||
} | |||
} | |||
} |
@@ -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()); | |||
} | |||
@@ -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); | |||
} | |||
} |
@@ -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"><klute@rainer-klute.de></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++) |
@@ -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(); | |||
} | |||
} | |||
} |
@@ -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), |
@@ -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 | |||
{ |
@@ -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); | |||
} | |||
} |