diff --git a/src/contrib/src/org/apache/poi/contrib/poibrowser/POIBrowser.java b/src/contrib/src/org/apache/poi/contrib/poibrowser/POIBrowser.java index 0c998c323..ffd45af23 100644 --- a/src/contrib/src/org/apache/poi/contrib/poibrowser/POIBrowser.java +++ b/src/contrib/src/org/apache/poi/contrib/poibrowser/POIBrowser.java @@ -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); } diff --git a/src/contrib/src/org/apache/poi/contrib/poibrowser/PropertySetDescriptor.java b/src/contrib/src/org/apache/poi/contrib/poibrowser/PropertySetDescriptor.java index 9e6500c1b..bd7f58436 100644 --- a/src/contrib/src/org/apache/poi/contrib/poibrowser/PropertySetDescriptor.java +++ b/src/contrib/src/org/apache/poi/contrib/poibrowser/PropertySetDescriptor.java @@ -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 { diff --git a/src/contrib/src/org/apache/poi/contrib/poibrowser/PropertySetDescriptorRenderer.java b/src/contrib/src/org/apache/poi/contrib/poibrowser/PropertySetDescriptorRenderer.java index 3b17803b0..8aac2a151 100644 --- a/src/contrib/src/org/apache/poi/contrib/poibrowser/PropertySetDescriptorRenderer.java +++ b/src/contrib/src/org/apache/poi/contrib/poibrowser/PropertySetDescriptorRenderer.java @@ -127,6 +127,9 @@ public class PropertySetDescriptorRenderer extends DocumentDescriptorRenderer /** *

Returns a string representation of a {@link Section}.

+ * @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; diff --git a/src/documentation/content/xdocs/hpsf/how-to.xml b/src/documentation/content/xdocs/hpsf/how-to.xml index b99368514..93833430f 100644 --- a/src/documentation/content/xdocs/hpsf/how-to.xml +++ b/src/documentation/content/xdocs/hpsf/how-to.xml @@ -19,8 +19,8 @@
  1. - The first section explains how to read - the most important standard properties of a Microsoft Office + The first 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. 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 @@
  2. The second section 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 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.
  3. - The third section tells how to read - non-standard properties. Non-standard properties are application-specific - triples consisting of an ID, a type, and a value. + The third section explains how to write + standard properties. 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 fifth + section.
  4. - The fourth section 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 third section before you should think about writing - properties. Check the Javadoc API documentation to find out about the - details! Please note: HPSF's writing functionality is - not 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 fourth section tells how to read + non-standard properties. Non-standard properties are + application-specific triples consisting of an ID, a type, and a value. +
  5. + +
  6. + The fifth section tells you how to write + property set streams using HPSF's low-level methods. You have to + understand the fourth section before you should + think about low-level writing properties. Check the Javadoc API + documentation to find out about the details!
+ Please note: HPSF's writing functionality is + not 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. +
Reading Standard Properties - 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. + 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 + summary information stream 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. -

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 POIFS - documentation.) A document in a POI filesystem is also called a - stream.

+

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 POI filesystem (POIFS) + provides. A file or document in a POI filesystem is also called a + stream - 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.

-

The following example shows how to read a POI filesystem's - "title" property. Reading other properties is similar. Consider the API - documentation of org.apache.poi.hpsf.SummaryInformation to - learn which methods are available!

+

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

The standard properties this section focusses on can be found in a document called \005SummaryInformation located in the root of the POI filesystem. The notation \005 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:

+ the character with a decimal value of 5. In order to read the "title" + property, an application has to perform the following steps:

  1. @@ -103,7 +123,7 @@ POI filesystem

    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 examples section of the POI source tree as ReadTitle.java.

    @@ -144,14 +164,15 @@ r.registerListener(new MyPOIFSReaderListener(),

    This method call registers a org.apache.poi.poifs.eventfilesystem.POIFSReaderListener with the POIFSReader. The POIFSReaderListener - interface specifies the method processPOIFSReaderEvent + interface specifies the method processPOIFSReaderEvent() which processes a document. The class MyPOIFSReaderListener implements the POIFSReaderListener and thus the - processPOIFSReaderEvent method. The eventing POI filesystem - calls this method when it finds the \005SummaryInformation - document. In the sample application MyPOIFSReaderListener is - a static class in the ReadTitle.java source file.

    + processPOIFSReaderEvent() method. The eventing POI + filesystem calls this method when it finds the + \005SummaryInformation document. In the sample application + MyPOIFSReaderListener is a static class in the + ReadTitle.java source file.

    Now everything is prepared and reading the POI filesystem can start:

    @@ -209,7 +230,7 @@ static class MyPOIFSReaderListener implements POIFSReaderListener convenience class with methods like getTitle(), getAuthor() etc.

    -

    The PropertySetFactory.create method may throw all sorts +

    The PropertySetFactory.create() 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 RuntimeException containing the message text of the origin exception.

    @@ -224,10 +245,10 @@ if (title != null) else System.out.println("Document has no title."); -

    Please note that a Microsoft Office document does not necessarily - contain the \005SummaryInformation 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 +

    Please note that a POI filesystem does not necessarily contain the + \005SummaryInformation 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 \005SummaryInformation stream. In this case the applications won't throw an exception but simply does not call the processPOIFSReaderEvent method. You have been warned!

    @@ -238,14 +259,16 @@ else
    Additional Standard Properties, Exceptions And Embedded Objects - This section focusses on reading additional standard properties. It + This section focusses on reading additional standard properties which + are kept in the document summary information stream. It also talks about exceptions that may be thrown when dealing with HPSF and shows how you can read properties of embedded objects.

    A couple of additional standard properties are not - contained in the \005SummaryInformation 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 \005SummaryInformation 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 \005DocumentSummaryInformation to hold these properties. With two minor exceptions you can proceed exactly as described above to read the properties stored in \005DocumentSummaryInformation:

    @@ -259,13 +282,14 @@ else

    And of course you cannot call getTitle() because - DocumentSummaryInformation has different query methods. See - the Javadoc API documentation for the details!

    + DocumentSummaryInformation has different query methods, + e.g. getCategory. See the Javadoc API documentation for the + details.

    In the previous section the application simply caught all exceptions 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:

    @@ -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 \005SummaryInformation or - \005DocumentSummaryInformation 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. + \005DocumentSummaryInformation. 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.
    UnexpectedPropertySetTypeException
    @@ -301,7 +325,7 @@ else

    Many Microsoft Office documents contain embedded - objects, for example an Excel sheet on a page in a Word + objects, 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.

    + + +
    Writing Standard Properties + + This section explains how to write standard + properties. 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 another + section. + +

    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 + SummaryInformation and + DocumentSummaryInformation and their get...() + methods for reading standard properties. These classes also provide + set...() methods for writing properties.

    + +

    After setting properties in SummaryInformation or + DocumentSummaryInformation you have to write them to a disk + file. The following sample program shows how you can

    + +
      +
    1. read a disk file into a POI filesystem,
    2. +
    3. read the document summary information from the POI filesystem,
    4. +
    5. set a property to a new value,
    6. +
    7. write the modified document summary information back to the POI + filesystem, and
    8. +
    9. write the POI filesystem to a disk file.
    10. +
    + +

    The complete source code of this program is available as + ModifyDocumentSummaryInformation.java in the examples + section of the POI source tree.

    + + 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 set...() methods of the class + SummaryInformation. + +

    The first step is to read the POI filesystem into memory:

    + + InputStream is = new FileInputStream(poiFilesystem); +POIFSFileSystem poifs = new POIFSFileSystem(is); +is.close(); + +

    The code snippet above assumes that the variable + poiFilesystem holds the name of a disk file. It reads the + file from an input stream and creates a POIFSFileSystem + object in memory. After having read the file, the input stream should be + closed as shown.

    + +

    In order to read the document summary information stream the application + must open the element \005DocumentSummaryInformation 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 DocumentSummaryInformation if + there is none in the POI filesystem:

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

    In the source code above the statement

    + + DirectoryEntry dir = poifs.getRoot(); + +

    gets hold of the POI filesystem's root directory as a + DirectoryEntry. The getEntry() 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 + FileNotFoundException will be thrown. Therefore opening the + document summary information entry should be done in a try + block:

    + + DocumentEntry dsiEntry = (DocumentEntry) + dir.getEntry(DocumentSummaryInformation.DEFAULT_STREAM_NAME); + +

    DocumentSummaryInformation.DEFAULT_STREAM_NAME represents + the string "\005DocumentSummaryInformation", i.e. the standard name of a + document summary information stream. If this stream exists, the + getEntry() method returns a DocumentEntry. To + read the DocumentEntry's contents, create a + DocumentInputStream:

    + + DocumentInputStream dis = new DocumentInputStream(dsiEntry); + +

    Up to this point we have used POI's POIFS component. Now HPSF enters the + stage. A property set is created from the input stream's data:

    + + PropertySet ps = new PropertySet(dis); + dis.close(); + dsi = new DocumentSummaryInformation(ps); + +

    If the data really constitutes a property set, a + PropertySet object is created. Otherwise a + NoPropertySetStreamException is thrown. After having read the + data from the input stream the latter should be closed.

    + +

    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 + DocumentSummaryInformation from the property set. If the + stream is not document summary information stream the sample application + fails with a UnexpectedPropertySetTypeException.

    + +

    If the POI document does not contain a document summary information + stream, we can create a new one in the catch clause. The + PropertySetFactory's method + newDocumentSummaryInformation() establishes a new and empty + DocumentSummaryInformation instance:

    + + dsi = PropertySetFactory.newDocumentSummaryInformation(); + +

    Whether we read the document summary information from the POI filesystem + or created it from scratch, in either case we now have a + DocumentSummaryInformation instance we can write to. Writing + is quite simple, as the following line of code shows:

    + + dsi.setCategory("POI example"); + +

    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.

    + +

    DocumentSummaryInformation of course has methods to set the + other standard properties, too - look into the API documentation to see + all of them.

    + +

    Once all properties are set as needed, they should be stored into the + file on disk. The first step is to write the + DocumentSummaryInformation into the POI filesystem:

    + + dsi.write(dir, DocumentSummaryInformation.DEFAULT_STREAM_NAME); + +

    The DocumentSummaryInformation's write() + method takes two parameters: The first is the DirectoryEntry + 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.

    + + 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. + +

    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.

    + + OutputStream out = new FileOutputStream(poiFilesystem); +poifs.writeFilesystem(out); +out.close(); + +
    User-Defined Properties + +

    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.

    + + 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); + +

    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 name, a type and a + value. The custom properties are stored in the document + information summary along with the standard properties.

    + +

    The source code example shows how to retrieve the custom properties + as a whole from a DocumentSummaryInformation instance using + the getCustomProperties() method. The result is a + CustomProperties instance or null if no + user-defined properties exist.

    + +

    Since CustomProperties implements the Map + interface you can read and write properties with the usual + Map methods. However, CustomProperties poses + some restrictions on the types of keys and values.

    + +
      +
    • The key is a string.
    • +
    • The value is one of String, + Boolean, Long, Integer, + Short, or java.util.Date.
    • +
    + +

    The CustomProperties 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 CustomProperties 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 + CustomProperties instance dropped any properties with the + isPure() method.

    + +

    You can read and write the full spectrum of custom properties with + HPSF's low-level methods. They are explained in the next section.

    +
    +
    + + + +
    Reading Non-Standard Properties This section tells how to read non-standard properties. Non-standard @@ -863,7 +1132,8 @@ No property set stream: "/1Table"

    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:

    + string "cp" to it. In these cases the codepage number is mapped to a + well-known character encoding name. Here are a few examples:

    Codepage 932
    @@ -874,26 +1144,32 @@ No property set stream: "/1Table"
    is mapped to the character encoding "UTF-8".
    -

    Probably there will be a need to add more mappings between codepage - numbers and character encoding names. They should be added to the method - codepageToEncoding in the class - org.apache.poi.hpsf.VariantSupport. The HPSF author will - appreciate any advices for mappings to be added.

    +

    More of these mappings between codepage and character encoding name are + hard-coded in the classes org.apache.poi.hpsf.Constants and + org.apache.poi.hpsf.VariantSupport. Probably there will be a + need to add more mappings. The HPSF author will appreciate any hints.

- +
Writing Properties This section describes how to write properties.
Overview of Writing Properties -

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 section 3 - to understand what follows in this section.

+

Writing properties is possible at a high level and at a low level:

+ +
    + +
  • Most users will want to create or change entries in the summary + information or document summary information streams.
  • + +
  • 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 section + 3 to understand the description of the low-level writing + functions.
  • +

HPSF's writing capabilities come with the classes MutablePropertySet, MutableSection, @@ -903,7 +1179,10 @@ No property set stream: "/1Table" "write" methods, following the Decorator pattern.

+
+ +
Low-Level Writing: An Overview

When you are going to write a property set stream your application has to perform the following steps:

diff --git a/src/documentation/content/xdocs/hpsf/internals.xml b/src/documentation/content/xdocs/hpsf/internals.xml index d317ffbff..3f314752a 100644 --- a/src/documentation/content/xdocs/hpsf/internals.xml +++ b/src/documentation/content/xdocs/hpsf/internals.xml @@ -1006,41 +1006,40 @@ helpful. If you have any amendments or corrections, please let us know! Thank you!

-
    +
      -
    1. In +
    2. In Understanding OLE - documents, 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.
    3. + documents, 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. -
    4. The - ActiveX Programmer's - Reference at - http://www.dwam.net/docs/oleref/ +
    5. The ActiveX + Programmer's Reference at http://www.dwam.net/docs/oleref/ seems a little outdated, but that's what I have found.
    6. -
    7. An overview of the VT_ types is in +
    8. An overview of the VT_ types is in Variant Type Definitions.
    9. -
    10. What is a FILETIME? The answer can be found - under , http://www.vbapi.com/ref/f/filetime.html or +
    11. What is a FILETIME? The answer can be found + under , http://www.vbapi.com/ref/f/filetime.html or http://www.cs.rpi.edu/courses/fall01/os/FILETIME.html. In short: 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.
    12. + 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. -
    13. Information about the code page property in the - DocumentSummaryInformation stream is available at http://msdn.microsoft.com/library/default.asp?url=/library/en-us/stg/stg/property_id_1.asp.
    14. +
    15. Microsoft provides some public information in the MSDN + Library. Use the search function to try to find what you are + looking for, e.g. "codepage" or "document summary information" etc.
    16. -
    17. This documentation origins from the HPSF description available at http://www.rainer-klute.de/~klute/Software/poibrowser/doc/HPSF-Description.html.
    18. +
    19. This documentation origins from the HPSF description available at http://www.rainer-klute.de/~klute/Software/poibrowser/doc/HPSF-Description.html.
diff --git a/src/examples/src/org/apache/poi/hpsf/examples/ModifyDocumentSummaryInformation.java b/src/examples/src/org/apache/poi/hpsf/examples/ModifyDocumentSummaryInformation.java new file mode 100644 index 000000000..c7e879c8b --- /dev/null +++ b/src/examples/src/org/apache/poi/hpsf/examples/ModifyDocumentSummaryInformation.java @@ -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; + + + +/** + *

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:

+ * + *
    + * + *
  • Open the POI filesystem.

  • + * + *
  • Read the summary information.

  • + * + *
  • Read and print the "author" property.

  • + * + *
  • Change the author to "Rainer Klute".

  • + * + *
  • Read the document summary information.

  • + * + *
  • Read and print the "category" property.

  • + * + *
  • Change the category to "POI example".

  • + * + *
  • Read the custom properties (if available).

  • + * + *
  • Insert a new custom property.

  • + * + *
  • Write the custom properties back to the document summary + * information.

  • + * + *
  • Write the summary information to the POI filesystem.

  • + * + *
  • Write the document summary information to the POI filesystem.

  • + * + *
  • Write the POI filesystem back to the original file.

  • + * + * + * + * @author Rainer Klute klute@rainer-klute.de + * @since 2006-02-09 + * @version $Id: TestWrite.java 353637 2005-04-13 16:33:22Z klute $ + */ +public class ModifyDocumentSummaryInformation +{ + + /** + *

    Main method - see class description.

    + * + * @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(); + } + +} diff --git a/src/java/org/apache/poi/hpsf/ClassID.java b/src/java/org/apache/poi/hpsf/ClassID.java index 0f8d12221..766453f58 100644 --- a/src/java/org/apache/poi/hpsf/ClassID.java +++ b/src/java/org/apache/poi/hpsf/ClassID.java @@ -1,4 +1,3 @@ - /* ==================================================================== Copyright 2002-2004 Apache Software Foundation diff --git a/src/java/org/apache/poi/hpsf/Constants.java b/src/java/org/apache/poi/hpsf/Constants.java index 3cd1138c5..32a0addf2 100644 --- a/src/java/org/apache/poi/hpsf/Constants.java +++ b/src/java/org/apache/poi/hpsf/Constants.java @@ -1,3 +1,19 @@ +/* ==================================================================== + Copyright 2002-2006 Apache Software Foundation + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +==================================================================== */ + package org.apache.poi.hpsf; /** diff --git a/src/java/org/apache/poi/hpsf/CustomProperties.java b/src/java/org/apache/poi/hpsf/CustomProperties.java new file mode 100644 index 000000000..77f577d5c --- /dev/null +++ b/src/java/org/apache/poi/hpsf/CustomProperties.java @@ -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; + +/** + *

    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.

    + * + *

    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.

    + * + *

    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.

    + * + *

    This class is not thread-safe; concurrent access to instances of this + * class must be syncronized.

    + * + * @author Rainer Klute <klute@rainer-klute.de> + * @since 2006-02-09 + * @version $Id$ + */ +public class CustomProperties extends HashMap +{ + + /** + *

    Maps property IDs to property names.

    + */ + private Map dictionaryIDToName = new HashMap(); + + /** + *

    Maps property names to property IDs.

    + */ + private Map dictionaryNameToID = new HashMap(); + + /** + *

    Tells whether this object is pure or not.

    + */ + private boolean isPure = true; + + + + /** + *

    Puts a {@link CustomProperty} into this map. It is assumed that the + * {@link CustomProperty} already has a valid ID. Otherwise use + * {@link #put(CustomProperty)}.

    + */ + 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; + } + + + + /** + *

    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:

    + * + *
      + * + *
    • If there is already a property with the same name, take the ID + * of that property.

    • + * + *
    • Otherwise find the highest ID and use its value plus one.

    • + * + *
    + * + * @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); + } + + + + /** + *

    Removes a custom property.

    + * @param name The name of the custom property to remove + * @return The removed property or null 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); + } + + /** + *

    Adds a named string property.

    + * + * @param name The property's name. + * @param value The property's value. + * @return the property that was stored under the specified name before, or + * null 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); + } + + /** + *

    Adds a named long property.

    + * + * @param name The property's name. + * @param value The property's value. + * @return the property that was stored under the specified name before, or + * null 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); + } + + /** + *

    Adds a named double property.

    + * + * @param name The property's name. + * @param value The property's value. + * @return the property that was stored under the specified name before, or + * null 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); + } + + /** + *

    Adds a named integer property.

    + * + * @param name The property's name. + * @param value The property's value. + * @return the property that was stored under the specified name before, or + * null 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); + } + + /** + *

    Adds a named boolean property.

    + * + * @param name The property's name. + * @param value The property's value. + * @return the property that was stored under the specified name before, or + * null 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); + } + + + /** + *

    Gets a named value from the custom properties.

    + * + * @param name the name of the value to get + * @return the value or null 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; + } + + + + /** + *

    Adds a named date property.

    + * + * @param name The property's name. + * @param value The property's value. + * @return the property that was stored under the specified name before, or + * null 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); + } + + /** + *

    Sets the codepage.

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

    Gets the dictionary which contains IDs and names of the named custom + * properties. + * + * @return the dictionary. + */ + Map getDictionary() + { + return dictionaryIDToName; + } + + + + /** + *

    Gets the codepage.

    + * + * @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; + } + + + + /** + *

    Tells whether this {@link CustomProperties} instance is pure or one or + * more properties of the underlying low-level property set has been + * dropped.

    + * + * @return true if the {@link CustomProperties} is pure, else + * false. + */ + public boolean isPure() + { + return isPure; + } + + /** + *

    Sets the purity of the custom property set.

    + * + * @param isPure the purity + */ + public void setPure(final boolean isPure) + { + this.isPure = isPure; + } + +} diff --git a/src/java/org/apache/poi/hpsf/CustomProperty.java b/src/java/org/apache/poi/hpsf/CustomProperty.java new file mode 100644 index 000000000..7c87e1f9a --- /dev/null +++ b/src/java/org/apache/poi/hpsf/CustomProperty.java @@ -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; + +/** + *

    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 null it + * will be maintained in the section's dictionary.

    + * + * @author Rainer Klute <klute@rainer-klute.de> + * @since 2006-02-09 + * @version $Id$ + */ +public class CustomProperty extends MutableProperty +{ + + private String name; + + /** + *

    Creates an empty {@link CustomProperty}. The set methods must be + * called to make it usable.

    + */ + public CustomProperty() + { + this.name = null; + } + + /** + *

    Creates a {@link CustomProperty} without a name by copying the + * underlying {@link Property}' attributes.

    + * + * @param property the property to copy + */ + public CustomProperty(final Property property) + { + this(property, null); + } + + /** + *

    Creates a {@link CustomProperty} with a name.

    + * + * @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; + } + + /** + *

    Gets the property's name.

    + * + * @return the property's name. + */ + public String getName() + { + return name; + } + + /** + *

    Sets the property's name.

    + * + * @param name The name to set. + */ + public void setName(final String name) + { + this.name = name; + } + + + /** + *

    Compares two custom properties for equality. The method returns + * true if all attributes of the two custom properties are + * equal.

    + * + * @param o The custom property to compare with. + * @return true if both custom properties are equal, else + * false. + * + * @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(); + } + +} diff --git a/src/java/org/apache/poi/hpsf/DocumentSummaryInformation.java b/src/java/org/apache/poi/hpsf/DocumentSummaryInformation.java index 21b1b2a9f..1fce462d6 100644 --- a/src/java/org/apache/poi/hpsf/DocumentSummaryInformation.java +++ b/src/java/org/apache/poi/hpsf/DocumentSummaryInformation.java @@ -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; /** *

    Convenience class representing a DocumentSummary Information stream in a @@ -68,7 +67,7 @@ public class DocumentSummaryInformation extends SpecialPropertySet /** - *

    Returns the stream's category (or null).

    + *

    Returns the category (or null).

    * * @return The category value */ @@ -77,23 +76,63 @@ public class DocumentSummaryInformation extends SpecialPropertySet return (String) getProperty(PropertyIDMap.PID_CATEGORY); } + /** + *

    Sets the category.

    + * + * @param category The category to set. + */ + public void setCategory(final String category) + { + final MutableSection s = (MutableSection) getFirstSection(); + s.setProperty(PropertyIDMap.PID_CATEGORY, category); + } + + /** + *

    Removes the category.

    + */ + public void removeCategory() + { + final MutableSection s = (MutableSection) getFirstSection(); + s.removeProperty(PropertyIDMap.PID_CATEGORY); + } + /** - *

    Returns the stream's presentation format (or + *

    Returns the presentation format (or * null).

    * - * @return The presentationFormat value + * @return The presentation format value */ public String getPresentationFormat() { return (String) getProperty(PropertyIDMap.PID_PRESFORMAT); } + /** + *

    Sets the presentation format.

    + * + * @param presentationFormat The presentation format to set. + */ + public void setPresentationFormat(final String presentationFormat) + { + final MutableSection s = (MutableSection) getFirstSection(); + s.setProperty(PropertyIDMap.PID_PRESFORMAT, presentationFormat); + } + + /** + *

    Removes the presentation format.

    + */ + public void removePresentationFormat() + { + final MutableSection s = (MutableSection) getFirstSection(); + s.removeProperty(PropertyIDMap.PID_PRESFORMAT); + } + /** - *

    Returns the stream's byte count or 0 if the {@link + *

    Returns the byte count or 0 if the {@link * DocumentSummaryInformation} does not contain a byte count.

    * * @return The byteCount value @@ -103,86 +142,226 @@ public class DocumentSummaryInformation extends SpecialPropertySet return getPropertyIntValue(PropertyIDMap.PID_BYTECOUNT); } + /** + *

    Sets the byte count.

    + * + * @param byteCount The byte count to set. + */ + public void setByteCount(final int byteCount) + { + final MutableSection s = (MutableSection) getFirstSection(); + s.setProperty(PropertyIDMap.PID_BYTECOUNT, byteCount); + } + + /** + *

    Removes the byte count.

    + */ + public void removeByteCount() + { + final MutableSection s = (MutableSection) getFirstSection(); + s.removeProperty(PropertyIDMap.PID_BYTECOUNT); + } + /** - *

    Returns the stream's line count or 0 if the {@link + *

    Returns the line count or 0 if the {@link * DocumentSummaryInformation} does not contain a line count.

    * - * @return The lineCount value + * @return The line count value */ public int getLineCount() { return getPropertyIntValue(PropertyIDMap.PID_LINECOUNT); } + /** + *

    Sets the line count.

    + * + * @param lineCount The line count to set. + */ + public void setLineCount(final int lineCount) + { + final MutableSection s = (MutableSection) getFirstSection(); + s.setProperty(PropertyIDMap.PID_LINECOUNT, lineCount); + } + + /** + *

    Removes the line count.

    + */ + public void removeLineCount() + { + final MutableSection s = (MutableSection) getFirstSection(); + s.removeProperty(PropertyIDMap.PID_LINECOUNT); + } + /** - *

    Returns the stream's par count or 0 if the {@link + *

    Returns the par count or 0 if the {@link * DocumentSummaryInformation} does not contain a par count.

    * - * @return The parCount value + * @return The par count value */ public int getParCount() { return getPropertyIntValue(PropertyIDMap.PID_PARCOUNT); } + /** + *

    Sets the par count.

    + * + * @param parCount The par count to set. + */ + public void setParCount(final int parCount) + { + final MutableSection s = (MutableSection) getFirstSection(); + s.setProperty(PropertyIDMap.PID_PARCOUNT, parCount); + } + + /** + *

    Removes the par count.

    + */ + public void removeParCount() + { + final MutableSection s = (MutableSection) getFirstSection(); + s.removeProperty(PropertyIDMap.PID_PARCOUNT); + } + /** - *

    Returns the stream's slide count or 0 if the {@link + *

    Returns the slide count or 0 if the {@link * DocumentSummaryInformation} does not contain a slide count.

    * - * @return The slideCount value + * @return The slide count value */ public int getSlideCount() { return getPropertyIntValue(PropertyIDMap.PID_SLIDECOUNT); } + /** + *

    Sets the slideCount.

    + * + * @param slideCount The slide count to set. + */ + public void setSlideCount(final int slideCount) + { + final MutableSection s = (MutableSection) getFirstSection(); + s.setProperty(PropertyIDMap.PID_SLIDECOUNT, slideCount); + } + + /** + *

    Removes the slide count.

    + */ + public void removeSlideCount() + { + final MutableSection s = (MutableSection) getFirstSection(); + s.removeProperty(PropertyIDMap.PID_SLIDECOUNT); + } + /** - *

    Returns the stream's note count or 0 if the {@link + *

    Returns the note count or 0 if the {@link * DocumentSummaryInformation} does not contain a note count.

    * - * @return The noteCount value + * @return The note count value */ public int getNoteCount() { return getPropertyIntValue(PropertyIDMap.PID_NOTECOUNT); } + /** + *

    Sets the note count.

    + * + * @param noteCount The note count to set. + */ + public void setNoteCount(final int noteCount) + { + final MutableSection s = (MutableSection) getFirstSection(); + s.setProperty(PropertyIDMap.PID_NOTECOUNT, noteCount); + } + + /** + *

    Removes the noteCount.

    + */ + public void removeNoteCount() + { + final MutableSection s = (MutableSection) getFirstSection(); + s.removeProperty(PropertyIDMap.PID_NOTECOUNT); + } + /** - *

    Returns the stream's hidden count or 0 if the {@link + *

    Returns the hidden count or 0 if the {@link * DocumentSummaryInformation} does not contain a hidden * count.

    * - * @return The hiddenCount value + * @return The hidden count value */ public int getHiddenCount() { return getPropertyIntValue(PropertyIDMap.PID_HIDDENCOUNT); } + /** + *

    Sets the hidden count.

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

    Removes the hidden count.

    + */ + public void removeHiddenCount() + { + final MutableSection s = (MutableSection) getFirstSection(); + s.removeProperty(PropertyIDMap.PID_HIDDENCOUNT); + } + /** - *

    Returns the stream's mmclip count or 0 if the {@link + *

    Returns the mmclip count or 0 if the {@link * DocumentSummaryInformation} does not contain a mmclip * count.

    * - * @return The mMClipCount value + * @return The mmclip count value */ public int getMMClipCount() { return getPropertyIntValue(PropertyIDMap.PID_MMCLIPCOUNT); } + /** + *

    Sets the mmclip count.

    + * + * @param mmClipCount The mmclip count to set. + */ + public void setMMClipCount(final int mmClipCount) + { + final MutableSection s = (MutableSection) getFirstSection(); + s.setProperty(PropertyIDMap.PID_MMCLIPCOUNT, mmClipCount); + } + + /** + *

    Removes the mmclip count.

    + */ + 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); } - + /** + *

    Sets the scale.

    + * + * @param scale The scale to set. + */ + public void setScale(final boolean scale) + { + final MutableSection s = (MutableSection) getFirstSection(); + s.setProperty(PropertyIDMap.PID_SCALE, scale); + } /** - *

    Returns the stream's heading pair (or null) - * when this method is implemented. Please note that the - * return type is likely to change! - * - * @return The headingPair value + *

    Removes the scale.

    */ - public byte[] getHeadingPair() + public void removeScale() { - if (true) - throw new UnsupportedOperationException("FIXME"); - return (byte[]) getProperty(PropertyIDMap.PID_HEADINGPAIR); + final MutableSection s = (MutableSection) getFirstSection(); + s.removeProperty(PropertyIDMap.PID_SCALE); } /** - *

    Returns the stream's doc parts (or null) + *

    Returns the heading pair (or null) * when this method is implemented. Please note that the * return type is likely to change! * - * @return The docparts value + * @return The heading pair value + */ + public byte[] getHeadingPair() + { + notYetImplemented("Reading byte arrays "); + return (byte[]) getProperty(PropertyIDMap.PID_HEADINGPAIR); + } + + /** + *

    Sets the heading pair.

    + * + * @param headingPair The heading pair to set. + */ + public void setHeadingPair(final byte[] headingPair) + { + notYetImplemented("Writing byte arrays "); + } + + /** + *

    Removes the heading pair.

    + */ + public void removeHeadingPair() + { + final MutableSection s = (MutableSection) getFirstSection(); + s.removeProperty(PropertyIDMap.PID_HEADINGPAIR); + } + + + + /** + *

    Returns the doc parts (or null) + * when this method is implemented. Please note that the + * return type is likely to change! + * + * @return The doc parts value */ public byte[] getDocparts() { - if (true) - throw new UnsupportedOperationException("FIXME"); + notYetImplemented("Reading byte arrays"); return (byte[]) getProperty(PropertyIDMap.PID_DOCPARTS); } /** - *

    Returns the stream's manager (or null).

    + *

    Sets the doc parts.

    + * + * @param docparts The doc parts to set. + */ + public void setDocparts(final byte[] docparts) + { + notYetImplemented("Writing byte arrays"); + } + + /** + *

    Removes the doc parts.

    + */ + public void removeDocparts() + { + final MutableSection s = (MutableSection) getFirstSection(); + s.removeProperty(PropertyIDMap.PID_DOCPARTS); + } + + + + /** + *

    Returns the manager (or null).

    * * @return The manager value */ @@ -240,10 +477,30 @@ public class DocumentSummaryInformation extends SpecialPropertySet return (String) getProperty(PropertyIDMap.PID_MANAGER); } + /** + *

    Sets the manager.

    + * + * @param manager The manager to set. + */ + public void setManager(final String manager) + { + final MutableSection s = (MutableSection) getFirstSection(); + s.setProperty(PropertyIDMap.PID_MANAGER, manager); + } + + /** + *

    Removes the manager.

    + */ + public void removeManager() + { + final MutableSection s = (MutableSection) getFirstSection(); + s.removeProperty(PropertyIDMap.PID_MANAGER); + } + /** - *

    Returns the stream's company (or null).

    + *

    Returns the company (or null).

    * * @return The company value */ @@ -252,47 +509,168 @@ public class DocumentSummaryInformation extends SpecialPropertySet return (String) getProperty(PropertyIDMap.PID_COMPANY); } + /** + *

    Sets the company.

    + * + * @param company The company to set. + */ + public void setCompany(final String company) + { + final MutableSection s = (MutableSection) getFirstSection(); + s.setProperty(PropertyIDMap.PID_COMPANY, company); + } + + /** + *

    Removes the company.

    + */ + public void removeCompany() + { + final MutableSection s = (MutableSection) getFirstSection(); + s.removeProperty(PropertyIDMap.PID_COMPANY); + } + /** *

    Returns true if the custom links are dirty.

    * - * @return The linksDirty value + * @return The links dirty value */ public boolean getLinksDirty() { return getPropertyBooleanValue(PropertyIDMap.PID_LINKSDIRTY); } + /** + *

    Sets the linksDirty.

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

    Removes the links dirty.

    + */ + public void removeLinksDirty() + { + final MutableSection s = (MutableSection) getFirstSection(); + s.removeProperty(PropertyIDMap.PID_LINKSDIRTY); + } + /** - *

    Gets the custom properties as a map from the property name to - * value.

    + *

    Gets the custom properties.

    * - * @return The custom properties if any exist, null 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 nameToValue; + return cps; + } + + /** + *

    Sets the custom properties.

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

    Creates section 2 if it is not already present.

    + * + */ + private void ensureSection2() + { + if (getSectionCount() < 2) + { + MutableSection s2 = new MutableSection(); + s2.setFormatID(SectionIDMap.DOCUMENT_SUMMARY_INFORMATION_ID[1]); + addSection(s2); + } + } + + + + /** + *

    Removes the custom properties.

    + * + * @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."); + } + + + + /** + *

    Throws an {@link UnsupportedOperationException} with a message text + * telling which functionality is not yet implemented.

    + * + * @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."); } } diff --git a/src/java/org/apache/poi/hpsf/HPSFException.java b/src/java/org/apache/poi/hpsf/HPSFException.java index ed6aa4ea4..7588be679 100644 --- a/src/java/org/apache/poi/hpsf/HPSFException.java +++ b/src/java/org/apache/poi/hpsf/HPSFException.java @@ -1,4 +1,3 @@ - /* ==================================================================== Copyright 2002-2004 Apache Software Foundation @@ -18,12 +17,12 @@ package org.apache.poi.hpsf; /** - *

    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.

    - * + *

    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.

    + * * @author Rainer Klute <klute@rainer-klute.de> + * href="mailto:klute@rainer-klute.de"><klute@rainer-klute.de> * @version $Id$ * @since 2002-02-09 */ diff --git a/src/java/org/apache/poi/hpsf/HPSFRuntimeException.java b/src/java/org/apache/poi/hpsf/HPSFRuntimeException.java index 57c1a68f8..b997226bb 100644 --- a/src/java/org/apache/poi/hpsf/HPSFRuntimeException.java +++ b/src/java/org/apache/poi/hpsf/HPSFRuntimeException.java @@ -1,4 +1,3 @@ - /* ==================================================================== Copyright 2002-2004 Apache Software Foundation diff --git a/src/java/org/apache/poi/hpsf/IllegalPropertySetDataException.java b/src/java/org/apache/poi/hpsf/IllegalPropertySetDataException.java index a0ad11df7..6e2b06d5b 100644 --- a/src/java/org/apache/poi/hpsf/IllegalPropertySetDataException.java +++ b/src/java/org/apache/poi/hpsf/IllegalPropertySetDataException.java @@ -1,4 +1,3 @@ - /* ==================================================================== Copyright 2002-2004 Apache Software Foundation diff --git a/src/java/org/apache/poi/hpsf/MarkUnsupportedException.java b/src/java/org/apache/poi/hpsf/MarkUnsupportedException.java index ced098c32..a03c1738b 100644 --- a/src/java/org/apache/poi/hpsf/MarkUnsupportedException.java +++ b/src/java/org/apache/poi/hpsf/MarkUnsupportedException.java @@ -1,4 +1,3 @@ - /* ==================================================================== Copyright 2002-2004 Apache Software Foundation diff --git a/src/java/org/apache/poi/hpsf/MissingSectionException.java b/src/java/org/apache/poi/hpsf/MissingSectionException.java new file mode 100644 index 000000000..76aba80bd --- /dev/null +++ b/src/java/org/apache/poi/hpsf/MissingSectionException.java @@ -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; + +/** + *

    This exception is thrown if one of the {@link PropertySet}'s + * convenience methods does not find a required {@link Section}.

    + * + *

    The constructors of this class are analogous to those of its + * superclass and documented there.

    + * + * @author Rainer Klute <klute@rainer-klute.de> + * @version $Id: NoSingleSectionException.java 353545 2004-04-09 13:05:39Z glens $ + * @since 2006-02-08 + */ +public class MissingSectionException extends HPSFRuntimeException +{ + + /** + *

    Constructor

    + */ + public MissingSectionException() + { + super(); + } + + + /** + *

    Constructor

    + * + * @param msg The exception's message string + */ + public MissingSectionException(final String msg) + { + super(msg); + } + + + /** + *

    Constructor

    + * + * @param reason This exception's underlying reason + */ + public MissingSectionException(final Throwable reason) + { + super(reason); + } + + + /** + *

    Constructor

    + * + * @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); + } + +} diff --git a/src/java/org/apache/poi/hpsf/MutablePropertySet.java b/src/java/org/apache/poi/hpsf/MutablePropertySet.java index d1dccf294..bff912be3 100644 --- a/src/java/org/apache/poi/hpsf/MutablePropertySet.java +++ b/src/java/org/apache/poi/hpsf/MutablePropertySet.java @@ -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); } + /** + *

    Writes a property set to a document in a POI filesystem directory.

    + * + * @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()); + } + } diff --git a/src/java/org/apache/poi/hpsf/MutableSection.java b/src/java/org/apache/poi/hpsf/MutableSection.java index 6d6c1b11e..96928051a 100644 --- a/src/java/org/apache/poi/hpsf/MutableSection.java +++ b/src/java/org/apache/poi/hpsf/MutableSection.java @@ -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 /** - *

    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.

    + *

    Sets the string value of the property with the specified ID.

    * * @param id The property's ID * @param value The property's value. It will be written as a Unicode @@ -175,6 +173,57 @@ public class MutableSection extends Section + /** + *

    Sets the int value of the property with the specified ID.

    + * + * @param id The property's ID + * @param value The property's value. + * + * @see #setProperty(int, long, Object) + * @see #getProperty + */ + public void setProperty(final int id, final int value) + { + setProperty(id, Variant.VT_I4, new Integer(value)); + dirty = true; + } + + + + /** + *

    Sets the long value of the property with the specified ID.

    + * + * @param id The property's ID + * @param value The property's value. + * + * @see #setProperty(int, long, Object) + * @see #getProperty + */ + public void setProperty(final int id, final long value) + { + setProperty(id, Variant.VT_I8, new Long(value)); + dirty = true; + } + + + + /** + *

    Sets the boolean value of the property with the specified ID.

    + * + * @param id The property's ID + * @param value The property's value. + * + * @see #setProperty(int, long, Object) + * @see #getProperty + */ + public void setProperty(final int id, final boolean value) + { + setProperty(id, Variant.VT_BOOL, new Boolean(value)); + dirty = true; + } + + + /** *

    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 /** - *

    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.

    + *

    Sets a property.

    * - * @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.

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

    Sets a property.

    + * + * @param id The property ID. + * @param value The property's value. The value's class must be one of those + * supported by HPSF. + */ + public void setProperty(final int id, final Object value) + { + if (value instanceof String) + setProperty(id, (String) value); + else if (value instanceof Long) + setProperty(id, ((Long) value).longValue()); + else if (value instanceof Integer) + setProperty(id, ((Integer) value).intValue()); + else if (value instanceof Short) + setProperty(id, ((Short) value).intValue()); + else if (value instanceof Boolean) + setProperty(id, ((Boolean) value).booleanValue()); + else if (value instanceof Date) + setProperty(id, Variant.VT_FILETIME, value); + else + throw new HPSFRuntimeException( + "HPSF does not support properties of type " + + value.getClass().getName() + "."); + } + + + + /** + *

    Removes all properties from the section including 0 (dictionary) and + * 1 (codepage).

    + */ + public void clear() + { + final Property[] properties = getProperties(); + for (int i = 0; i < properties.length; i++) + { + final Property p = properties[i]; + removeProperty(p.getID()); + } + } + + /** + *

    Sets the codepage.

    + * + * @param codepage the codepage + */ + public void setCodepage(final int codepage) + { + setProperty(PropertyIDMap.PID_CODEPAGE, Variant.VT_I2, + new Integer(codepage)); + } + } diff --git a/src/java/org/apache/poi/hpsf/NoFormatIDException.java b/src/java/org/apache/poi/hpsf/NoFormatIDException.java index 661189d7b..4bcdf3311 100644 --- a/src/java/org/apache/poi/hpsf/NoFormatIDException.java +++ b/src/java/org/apache/poi/hpsf/NoFormatIDException.java @@ -1,4 +1,3 @@ - /* ==================================================================== Copyright 2002-2004 Apache Software Foundation diff --git a/src/java/org/apache/poi/hpsf/NoSingleSectionException.java b/src/java/org/apache/poi/hpsf/NoSingleSectionException.java index 9e5de03ab..7cdf0d2d9 100644 --- a/src/java/org/apache/poi/hpsf/NoSingleSectionException.java +++ b/src/java/org/apache/poi/hpsf/NoSingleSectionException.java @@ -1,4 +1,3 @@ - /* ==================================================================== Copyright 2002-2004 Apache Software Foundation diff --git a/src/java/org/apache/poi/hpsf/Property.java b/src/java/org/apache/poi/hpsf/Property.java index 81c346b81..e27b8beb3 100644 --- a/src/java/org/apache/poi/hpsf/Property.java +++ b/src/java/org/apache/poi/hpsf/Property.java @@ -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 + /** + *

    Creates a property.

    + * + * @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; + } + + + /** *

    Creates a {@link Property} instance by reading its bytes * from the property set stream.

    @@ -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: diff --git a/src/java/org/apache/poi/hpsf/PropertySet.java b/src/java/org/apache/poi/hpsf/PropertySet.java index 134df3564..821acf2d4 100644 --- a/src/java/org/apache/poi/hpsf/PropertySet.java +++ b/src/java/org/apache/poi/hpsf/PropertySet.java @@ -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 src + * from the beginning of src * @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!

    + * Section}.

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

    Gets the {@link PropertySet}'s first section.

    + * + * @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)); } diff --git a/src/java/org/apache/poi/hpsf/PropertySetFactory.java b/src/java/org/apache/poi/hpsf/PropertySetFactory.java index e7b957679..0a9398204 100644 --- a/src/java/org/apache/poi/hpsf/PropertySetFactory.java +++ b/src/java/org/apache/poi/hpsf/PropertySetFactory.java @@ -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; + /** *

    Factory class to create instances of {@link SummaryInformation}, * {@link DocumentSummaryInformation} and {@link PropertySet}.

    @@ -74,4 +75,50 @@ public class PropertySetFactory } } + + + /** + *

    Creates a new summary information.

    + * + * @return the new summary information. + */ + public static SummaryInformation newSummaryInformation() + { + final MutablePropertySet ps = new MutablePropertySet(); + final MutableSection s = (MutableSection) ps.getFirstSection(); + s.setFormatID(SectionIDMap.SUMMARY_INFORMATION_ID); + try + { + return new SummaryInformation(ps); + } + catch (UnexpectedPropertySetTypeException ex) + { + /* This should never happen. */ + throw new HPSFRuntimeException(ex); + } + } + + + + /** + *

    Creates a new document summary information.

    + * + * @return the new document summary information. + */ + public static DocumentSummaryInformation newDocumentSummaryInformation() + { + final MutablePropertySet ps = new MutablePropertySet(); + final MutableSection s = (MutableSection) ps.getFirstSection(); + s.setFormatID(SectionIDMap.DOCUMENT_SUMMARY_INFORMATION_ID[0]); + try + { + return new DocumentSummaryInformation(ps); + } + catch (UnexpectedPropertySetTypeException ex) + { + /* This should never happen. */ + throw new HPSFRuntimeException(ex); + } + } + } diff --git a/src/java/org/apache/poi/hpsf/ReadingNotSupportedException.java b/src/java/org/apache/poi/hpsf/ReadingNotSupportedException.java index a6a97f1ee..c7fed7fd0 100644 --- a/src/java/org/apache/poi/hpsf/ReadingNotSupportedException.java +++ b/src/java/org/apache/poi/hpsf/ReadingNotSupportedException.java @@ -1,4 +1,3 @@ - /* ==================================================================== Copyright 2002-2004 Apache Software Foundation diff --git a/src/java/org/apache/poi/hpsf/Section.java b/src/java/org/apache/poi/hpsf/Section.java index 913fc9116..ee2815810 100644 --- a/src/java/org/apache/poi/hpsf/Section.java +++ b/src/java/org/apache/poi/hpsf/Section.java @@ -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 /** *

    Removes a field from a property array. The resulting array is * compactified and returned.

    + * + * @param pa The property array. + * @param i The index of the field to be removed. + * @return the compactified array. */ private Property[] remove(final Property[] pa, final int i) { @@ -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; } } diff --git a/src/java/org/apache/poi/hpsf/SpecialPropertySet.java b/src/java/org/apache/poi/hpsf/SpecialPropertySet.java index d114e41ab..e65de3c11 100644 --- a/src/java/org/apache/poi/hpsf/SpecialPropertySet.java +++ b/src/java/org/apache/poi/hpsf/SpecialPropertySet.java @@ -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; + /** *

    Abstract superclass for the convenience classes {@link * SummaryInformation} and {@link DocumentSummaryInformation}.

    @@ -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 { /** *

    The "real" property set SpecialPropertySet * delegates to.

    */ - private PropertySet delegate; + private MutablePropertySet delegate; /** *

    Creates a SpecialPropertySet. * - * @param ps The property set encapsulated by the + * @param ps The property set to be encapsulated by the * SpecialPropertySet */ public SpecialPropertySet(final PropertySet ps) + { + delegate = new MutablePropertySet(ps); + } + + + + /** + *

    Creates a SpecialPropertySet. + * + * @param ps The mutable property set to be encapsulated by the + * SpecialPropertySet + */ + 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.getSingleSection(); + 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.wasNull(); } } diff --git a/src/java/org/apache/poi/hpsf/SummaryInformation.java b/src/java/org/apache/poi/hpsf/SummaryInformation.java index a170225df..7a4941448 100644 --- a/src/java/org/apache/poi/hpsf/SummaryInformation.java +++ b/src/java/org/apache/poi/hpsf/SummaryInformation.java @@ -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; /** *

    Convenience class representing a Summary Information stream in a * Microsoft Office document.

    - * + * * @author Rainer Klute <klute@rainer-klute.de> + * href="mailto:klute@rainer-klute.de"><klute@rainer-klute.de> * @see DocumentSummaryInformation * @version $Id$ * @since 2002-02-09 @@ -34,8 +36,8 @@ public class SummaryInformation extends SpecialPropertySet { /** - *

    The document name a summary information stream usually has - * in a POIFS filesystem.

    + *

    The document name a summary information stream usually has in a POIFS + * filesystem.

    */ public static final String DEFAULT_STREAM_NAME = "\005SummaryInformation"; @@ -44,26 +46,26 @@ public class SummaryInformation extends SpecialPropertySet /** *

    Creates a {@link SummaryInformation} from a given {@link * PropertySet}.

    - * + * * @param ps A property set which should be created from a summary - * information stream. - * @throws UnexpectedPropertySetTypeException if ps - * does not contain a summary information stream. + * information stream. + * @throws UnexpectedPropertySetTypeException if ps 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()); } /** - *

    Returns the stream's title (or null).

    - * + *

    Returns the title (or null).

    + * * @return The title or null */ public String getTitle() @@ -74,8 +76,32 @@ public class SummaryInformation extends SpecialPropertySet /** - *

    Returns the stream's subject (or null).

    - * + *

    Sets the title.

    + * + * @param title The title to set. + */ + public void setTitle(final String title) + { + final MutableSection s = (MutableSection) getFirstSection(); + s.setProperty(PropertyIDMap.PID_TITLE, title); + } + + + + /** + *

    Removes the title.

    + */ + public void removeTitle() + { + final MutableSection s = (MutableSection) getFirstSection(); + s.removeProperty(PropertyIDMap.PID_TITLE); + } + + + + /** + *

    Returns the subject (or null).

    + * * @return The subject or null */ public String getSubject() @@ -86,8 +112,32 @@ public class SummaryInformation extends SpecialPropertySet /** - *

    Returns the stream's author (or null).

    - * + *

    Sets the subject.

    + * + * @param subject The subject to set. + */ + public void setSubject(final String subject) + { + final MutableSection s = (MutableSection) getFirstSection(); + s.setProperty(PropertyIDMap.PID_SUBJECT, subject); + } + + + + /** + *

    Removes the subject.

    + */ + public void removeSubject() + { + final MutableSection s = (MutableSection) getFirstSection(); + s.removeProperty(PropertyIDMap.PID_SUBJECT); + } + + + + /** + *

    Returns the author (or null).

    + * * @return The author or null */ public String getAuthor() @@ -98,8 +148,32 @@ public class SummaryInformation extends SpecialPropertySet /** - *

    Returns the stream's keywords (or null).

    - * + *

    Sets the author.

    + * + * @param author The author to set. + */ + public void setAuthor(final String author) + { + final MutableSection s = (MutableSection) getFirstSection(); + s.setProperty(PropertyIDMap.PID_AUTHOR, author); + } + + + + /** + *

    Removes the author.

    + */ + public void removeAuthor() + { + final MutableSection s = (MutableSection) getFirstSection(); + s.removeProperty(PropertyIDMap.PID_AUTHOR); + } + + + + /** + *

    Returns the keywords (or null).

    + * * @return The keywords or null */ public String getKeywords() @@ -110,8 +184,32 @@ public class SummaryInformation extends SpecialPropertySet /** - *

    Returns the stream's comments (or null).

    - * + *

    Sets the keywords.

    + * + * @param keywords The keywords to set. + */ + public void setKeywords(final String keywords) + { + final MutableSection s = (MutableSection) getFirstSection(); + s.setProperty(PropertyIDMap.PID_KEYWORDS, keywords); + } + + + + /** + *

    Removes the keywords.

    + */ + public void removeKeywords() + { + final MutableSection s = (MutableSection) getFirstSection(); + s.removeProperty(PropertyIDMap.PID_KEYWORDS); + } + + + + /** + *

    Returns the comments (or null).

    + * * @return The comments or null */ public String getComments() @@ -122,8 +220,32 @@ public class SummaryInformation extends SpecialPropertySet /** - *

    Returns the stream's template (or null).

    - * + *

    Sets the comments.

    + * + * @param comments The comments to set. + */ + public void setComments(final String comments) + { + final MutableSection s = (MutableSection) getFirstSection(); + s.setProperty(PropertyIDMap.PID_COMMENTS, comments); + } + + + + /** + *

    Removes the comments.

    + */ + public void removeComments() + { + final MutableSection s = (MutableSection) getFirstSection(); + s.removeProperty(PropertyIDMap.PID_COMMENTS); + } + + + + /** + *

    Returns the template (or null).

    + * * @return The template or null */ public String getTemplate() @@ -134,8 +256,32 @@ public class SummaryInformation extends SpecialPropertySet /** - *

    Returns the stream's last author (or null).

    - * + *

    Sets the template.

    + * + * @param template The template to set. + */ + public void setTemplate(final String template) + { + final MutableSection s = (MutableSection) getFirstSection(); + s.setProperty(PropertyIDMap.PID_TEMPLATE, template); + } + + + + /** + *

    Removes the template.

    + */ + public void removeTemplate() + { + final MutableSection s = (MutableSection) getFirstSection(); + s.removeProperty(PropertyIDMap.PID_TEMPLATE); + } + + + + /** + *

    Returns the last author (or null).

    + * * @return The last author or null */ public String getLastAuthor() @@ -146,9 +292,32 @@ public class SummaryInformation extends SpecialPropertySet /** - *

    Returns the stream's revision number (or - * null).

    - * + *

    Sets the last author.

    + * + * @param lastAuthor The last author to set. + */ + public void setLastAuthor(final String lastAuthor) + { + final MutableSection s = (MutableSection) getFirstSection(); + s.setProperty(PropertyIDMap.PID_LASTAUTHOR, lastAuthor); + } + + + + /** + *

    Removes the last author.

    + */ + public void removeLastAuthor() + { + final MutableSection s = (MutableSection) getFirstSection(); + s.removeProperty(PropertyIDMap.PID_LASTAUTHOR); + } + + + + /** + *

    Returns the revision number (or null).

    + * * @return The revision number or null */ public String getRevNumber() @@ -159,11 +328,35 @@ public class SummaryInformation extends SpecialPropertySet /** - *

    Returns the total time spent in editing the document - * (or 0).

    - * + *

    Sets the revision number.

    + * + * @param revNumber The revision number to set. + */ + public void setRevNumber(final String revNumber) + { + final MutableSection s = (MutableSection) getFirstSection(); + s.setProperty(PropertyIDMap.PID_REVNUMBER, revNumber); + } + + + + /** + *

    Removes the revision number.

    + */ + public void removeRevNumber() + { + final MutableSection s = (MutableSection) getFirstSection(); + s.removeProperty(PropertyIDMap.PID_REVNUMBER); + } + + + + /** + *

    Returns the total time spent in editing the document (or + * 0).

    + * * @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 /** - *

    Returns the stream's last printed time (or - * null).

    - * + *

    Sets the total time spent in editing the document.

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

    Remove the total time spent in editing the document.

    + */ + public void removeEditTime() + { + final MutableSection s = (MutableSection) getFirstSection(); + s.removeProperty(PropertyIDMap.PID_EDITTIME); + } + + + + /** + *

    Returns the last printed time (or null).

    + * * @return The last printed time or null */ public Date getLastPrinted() @@ -190,9 +407,33 @@ public class SummaryInformation extends SpecialPropertySet /** - *

    Returns the stream's creation time (or - * null).

    - * + *

    Sets the lastPrinted.

    + * + * @param lastPrinted The lastPrinted to set. + */ + public void setLastPrinted(final Date lastPrinted) + { + final MutableSection s = (MutableSection) getFirstSection(); + s.setProperty(PropertyIDMap.PID_LASTPRINTED, Variant.VT_FILETIME, + lastPrinted); + } + + + + /** + *

    Removes the lastPrinted.

    + */ + public void removeLastPrinted() + { + final MutableSection s = (MutableSection) getFirstSection(); + s.removeProperty(PropertyIDMap.PID_LASTPRINTED); + } + + + + /** + *

    Returns the creation time (or null).

    + * * @return The creation time or null */ public Date getCreateDateTime() @@ -203,9 +444,33 @@ public class SummaryInformation extends SpecialPropertySet /** - *

    Returns the stream's last save time (or - * null).

    - * + *

    Sets the creation time.

    + * + * @param createDateTime The creation time to set. + */ + public void setCreateDateTime(final Date createDateTime) + { + final MutableSection s = (MutableSection) getFirstSection(); + s.setProperty(PropertyIDMap.PID_CREATE_DTM, Variant.VT_FILETIME, + createDateTime); + } + + + + /** + *

    Removes the creation time.

    + */ + public void removeCreateDateTime() + { + final MutableSection s = (MutableSection) getFirstSection(); + s.removeProperty(PropertyIDMap.PID_CREATE_DTM); + } + + + + /** + *

    Returns the last save time (or null).

    + * * @return The last save time or null */ public Date getLastSaveDateTime() @@ -216,11 +481,37 @@ public class SummaryInformation extends SpecialPropertySet /** - *

    Returns the stream's page count or 0 if the {@link - * SummaryInformation} does not contain a page count.

    - * + *

    Sets the total time spent in editing the document.

    + * + * @param time The time to set. + */ + public void setLastSaveDateTime(final Date time) + { + final MutableSection s = (MutableSection) getFirstSection(); + s + .setProperty(PropertyIDMap.PID_LASTSAVE_DTM, + Variant.VT_FILETIME, time); + } + + + + /** + *

    Remove the total time spent in editing the document.

    + */ + public void removeLastSaveDateTime() + { + final MutableSection s = (MutableSection) getFirstSection(); + s.removeProperty(PropertyIDMap.PID_LASTSAVE_DTM); + } + + + + /** + *

    Returns the page count or 0 if the {@link SummaryInformation} does + * not contain a page count.

    + * * @return The page count or 0 if the {@link SummaryInformation} does not - * contain a page count. + * contain a page count. */ public int getPageCount() { @@ -230,9 +521,33 @@ public class SummaryInformation extends SpecialPropertySet /** - *

    Returns the stream's word count or 0 if the {@link - * SummaryInformation} does not contain a word count.

    - * + *

    Sets the page count.

    + * + * @param pageCount The page count to set. + */ + public void setPageCount(final int pageCount) + { + final MutableSection s = (MutableSection) getFirstSection(); + s.setProperty(PropertyIDMap.PID_PAGECOUNT, pageCount); + } + + + + /** + *

    Removes the page count.

    + */ + public void removePageCount() + { + final MutableSection s = (MutableSection) getFirstSection(); + s.removeProperty(PropertyIDMap.PID_PAGECOUNT); + } + + + + /** + *

    Returns the word count or 0 if the {@link SummaryInformation} does + * not contain a word count.

    + * * @return The word count or null */ public int getWordCount() @@ -243,9 +558,33 @@ public class SummaryInformation extends SpecialPropertySet /** - *

    Returns the stream's character count or 0 if the {@link - * SummaryInformation} does not contain a char count.

    - * + *

    Sets the word count.

    + * + * @param wordCount The word count to set. + */ + public void setWordCount(final int wordCount) + { + final MutableSection s = (MutableSection) getFirstSection(); + s.setProperty(PropertyIDMap.PID_WORDCOUNT, wordCount); + } + + + + /** + *

    Removes the word count.

    + */ + public void removeWordCount() + { + final MutableSection s = (MutableSection) getFirstSection(); + s.removeProperty(PropertyIDMap.PID_WORDCOUNT); + } + + + + /** + *

    Returns the character count or 0 if the {@link SummaryInformation} + * does not contain a char count.

    + * * @return The character count or null */ public int getCharCount() @@ -256,15 +595,39 @@ public class SummaryInformation extends SpecialPropertySet /** - *

    Returns the stream's thumbnail (or null) - * when this method is implemented. Please note that the - * return type is likely to change!

    - * - *

    Hint to developers: 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.

    - * + *

    Sets the character count.

    + * + * @param charCount The character count to set. + */ + public void setCharCount(final int charCount) + { + final MutableSection s = (MutableSection) getFirstSection(); + s.setProperty(PropertyIDMap.PID_CHARCOUNT, charCount); + } + + + + /** + *

    Removes the character count.

    + */ + public void removeCharCount() + { + final MutableSection s = (MutableSection) getFirstSection(); + s.removeProperty(PropertyIDMap.PID_CHARCOUNT); + } + + + + /** + *

    Returns the thumbnail (or null) when this + * method is implemented. Please note that the return type is likely to + * change!

    + * + *

    Hint to developers: 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.

    + * * @return The thumbnail or null */ public byte[] getThumbnail() @@ -275,9 +638,33 @@ public class SummaryInformation extends SpecialPropertySet /** - *

    Returns the stream's application name (or - * null).

    - * + *

    Sets the thumbnail.

    + * + * @param thumbnail The thumbnail to set. + */ + public void setThumbnail(final byte[] thumbnail) + { + final MutableSection s = (MutableSection) getFirstSection(); + s.setProperty(PropertyIDMap.PID_THUMBNAIL, /* FIXME: */ + Variant.VT_LPSTR, thumbnail); + } + + + + /** + *

    Removes the thumbnail.

    + */ + public void removeThumbnail() + { + final MutableSection s = (MutableSection) getFirstSection(); + s.removeProperty(PropertyIDMap.PID_THUMBNAIL); + } + + + + /** + *

    Returns the application name (or null).

    + * * @return The application name or null */ public String getApplicationName() @@ -288,35 +675,49 @@ public class SummaryInformation extends SpecialPropertySet /** - *

    Returns a security code which is one of the following - * values:

    - * + *

    Sets the application name.

    + * + * @param applicationName The application name to set. + */ + public void setApplicationName(final String applicationName) + { + final MutableSection s = (MutableSection) getFirstSection(); + s.setProperty(PropertyIDMap.PID_APPNAME, applicationName); + } + + + + /** + *

    Removes the application name.

    + */ + public void removeApplicationName() + { + final MutableSection s = (MutableSection) getFirstSection(); + s.removeProperty(PropertyIDMap.PID_APPNAME); + } + + + + /** + *

    Returns a security code which is one of the following values:

    + * *
      - *
    • - *

      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!

      - *
    • - * - *
    • - *

      1 if the document is password protected

      - *
    • - * - *
    • - *

      2 if the document is read-only recommended

      - *
    • - * - *
    • - *

      4 if the document is read-only enforced

      - *
    • - * - *
    • - *

      8 if the document is locked for annotations

      - *
    • - * + * + *
    • 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!

    • + * + *
    • 1 if the document is password protected

    • + * + *
    • 2 if the document is read-only recommended

    • + * + *
    • 4 if the document is read-only enforced

    • + * + *
    • 8 if the document is locked for annotations

    • + * *
    - * + * * @return The security code or null */ public int getSecurity() @@ -324,4 +725,28 @@ public class SummaryInformation extends SpecialPropertySet return getPropertyIntValue(PropertyIDMap.PID_SECURITY); } + + + /** + *

    Sets the security code.

    + * + * @param security The security code to set. + */ + public void setSecurity(final int security) + { + final MutableSection s = (MutableSection) getFirstSection(); + s.setProperty(PropertyIDMap.PID_SECURITY, security); + } + + + + /** + *

    Removes the security code.

    + */ + public void removeSecurity() + { + final MutableSection s = (MutableSection) getFirstSection(); + s.removeProperty(PropertyIDMap.PID_SECURITY); + } + } diff --git a/src/java/org/apache/poi/hpsf/Thumbnail.java b/src/java/org/apache/poi/hpsf/Thumbnail.java index fd168822a..14d1a0f3f 100644 --- a/src/java/org/apache/poi/hpsf/Thumbnail.java +++ b/src/java/org/apache/poi/hpsf/Thumbnail.java @@ -1,4 +1,3 @@ - /* ==================================================================== Copyright 2002-2004 Apache Software Foundation diff --git a/src/java/org/apache/poi/hpsf/TypeWriter.java b/src/java/org/apache/poi/hpsf/TypeWriter.java index c88e98d07..acfcfa6c3 100644 --- a/src/java/org/apache/poi/hpsf/TypeWriter.java +++ b/src/java/org/apache/poi/hpsf/TypeWriter.java @@ -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 + /** + *

    Writes a eight-byte value to an output stream.

    + * + * @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; + + } + + + /** *

    Writes an unsigned two-byte value to an output stream.

    * @@ -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 /** *

    Writes an array of {@link Property} instances to an output stream * according to the Horrible Property Stream Format.

    - * + * * @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); diff --git a/src/java/org/apache/poi/hpsf/UnexpectedPropertySetTypeException.java b/src/java/org/apache/poi/hpsf/UnexpectedPropertySetTypeException.java index 02a47b198..4f0aa8a9e 100644 --- a/src/java/org/apache/poi/hpsf/UnexpectedPropertySetTypeException.java +++ b/src/java/org/apache/poi/hpsf/UnexpectedPropertySetTypeException.java @@ -1,4 +1,3 @@ - /* ==================================================================== Copyright 2002-2004 Apache Software Foundation diff --git a/src/java/org/apache/poi/hpsf/UnsupportedVariantTypeException.java b/src/java/org/apache/poi/hpsf/UnsupportedVariantTypeException.java index 8503fdfb0..9cdf1805b 100644 --- a/src/java/org/apache/poi/hpsf/UnsupportedVariantTypeException.java +++ b/src/java/org/apache/poi/hpsf/UnsupportedVariantTypeException.java @@ -21,7 +21,7 @@ import org.apache.poi.util.HexDump; /** *

    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.

    + * retrieved using the {@link VariantTypeException#getValue} method.

    * *

    Obviously this class should disappear some day.

    * diff --git a/src/java/org/apache/poi/hpsf/Util.java b/src/java/org/apache/poi/hpsf/Util.java index 60e9e9b9d..d58e25425 100644 --- a/src/java/org/apache/poi/hpsf/Util.java +++ b/src/java/org/apache/poi/hpsf/Util.java @@ -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); + } + + /** + *

    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.

    + * + * @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) { diff --git a/src/java/org/apache/poi/hpsf/Variant.java b/src/java/org/apache/poi/hpsf/Variant.java index 7b300bc0e..a64b05b66 100644 --- a/src/java/org/apache/poi/hpsf/Variant.java +++ b/src/java/org/apache/poi/hpsf/Variant.java @@ -1,4 +1,3 @@ - /* ==================================================================== Copyright 2002-2004 Apache Software Foundation diff --git a/src/java/org/apache/poi/hpsf/VariantSupport.java b/src/java/org/apache/poi/hpsf/VariantSupport.java index 703f925de..8994bb2fa 100644 --- a/src/java/org/apache/poi/hpsf/VariantSupport.java +++ b/src/java/org/apache/poi/hpsf/VariantSupport.java @@ -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: diff --git a/src/java/org/apache/poi/hpsf/WritingNotSupportedException.java b/src/java/org/apache/poi/hpsf/WritingNotSupportedException.java index 64e955a70..f35b5c607 100644 --- a/src/java/org/apache/poi/hpsf/WritingNotSupportedException.java +++ b/src/java/org/apache/poi/hpsf/WritingNotSupportedException.java @@ -1,4 +1,3 @@ - /* ==================================================================== Copyright 2002-2004 Apache Software Foundation diff --git a/src/java/org/apache/poi/hpsf/wellknown/PropertyIDMap.java b/src/java/org/apache/poi/hpsf/wellknown/PropertyIDMap.java index 77a17b522..517af3b1e 100644 --- a/src/java/org/apache/poi/hpsf/wellknown/PropertyIDMap.java +++ b/src/java/org/apache/poi/hpsf/wellknown/PropertyIDMap.java @@ -1,4 +1,3 @@ - /* ==================================================================== Copyright 2002-2004 Apache Software Foundation @@ -231,6 +230,11 @@ public class PropertyIDMap extends HashMap * re-evaluated.

    */ public static final int PID_LINKSDIRTY = 16; + + /** + *

    The highest well-known property ID. Applications are free to use higher values for custom purposes.

    + */ + public static final int PID_MAX = PID_LINKSDIRTY; diff --git a/src/java/org/apache/poi/hpsf/wellknown/SectionIDMap.java b/src/java/org/apache/poi/hpsf/wellknown/SectionIDMap.java index 93e837f2c..e5e204589 100644 --- a/src/java/org/apache/poi/hpsf/wellknown/SectionIDMap.java +++ b/src/java/org/apache/poi/hpsf/wellknown/SectionIDMap.java @@ -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 }; /** - *

    The DocumentSummaryInformation's first section's format - * ID. The second section has a different format ID which is not - * well-known.

    + *

    The DocumentSummaryInformation's first and second sections' format + * ID.

    */ - 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. diff --git a/src/java/org/apache/poi/util/LittleEndian.java b/src/java/org/apache/poi/util/LittleEndian.java index d21546924..f6c95d3d4 100644 --- a/src/java/org/apache/poi/util/LittleEndian.java +++ b/src/java/org/apache/poi/util/LittleEndian.java @@ -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; } + /** + *

    Gets an unsigned int value (8 bytes) from a byte array.

    + * + * @param data the byte array + * @param offset a starting offset into the byte array + * @return the unsigned int (32-bit) value in a long + */ + public static long getULong(final byte[] data, final int offset) + { + int num = (int) getNumber(data, offset, LONG_SIZE); + long retNum; + if (num < 0) + retNum = ((long) Integer.MAX_VALUE + 1) * 2 + num; + else + retNum = num; + return retNum; + } } diff --git a/src/testcases/org/apache/poi/hpsf/basic/AllDataFilesTester.java b/src/testcases/org/apache/poi/hpsf/basic/AllDataFilesTester.java new file mode 100644 index 000000000..7396f4167 --- /dev/null +++ b/src/testcases/org/apache/poi/hpsf/basic/AllDataFilesTester.java @@ -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; + +/** + *

    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".

    + * + * @author Rainer Klute <klute@rainer-klute.de> + * @since 2006-02-11 + * @version $Id$ + */ +public class AllDataFilesTester +{ + + /** + *

    Interface specifying how to run a test on a single file.

    + * + * @author Rainer Klute <klute@rainer-klute.de> + * @since 2006-02-11 + * @version $Id$ + */ + public interface TestTask + { + /** + *

    Executes a test on a single file.

    + * + * @param file the file + * @throws Throwable if the method throws anything. + */ + void runTest(File file) throws Throwable; + } + + /** + *

    Tests the simplified custom properties.

    + * + * @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); + } + } + +} diff --git a/src/testcases/org/apache/poi/hpsf/basic/TestBasic.java b/src/testcases/org/apache/poi/hpsf/basic/TestBasic.java index e3b66725a..0ac068dac 100644 --- a/src/testcases/org/apache/poi/hpsf/basic/TestBasic.java +++ b/src/testcases/org/apache/poi/hpsf/basic/TestBasic.java @@ -112,10 +112,8 @@ public class TestBasic extends TestCase /** *

    Checks the names of the files in the POI filesystem. They * are expected to be in a certain order.

    - * - * @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()); } diff --git a/src/testcases/org/apache/poi/hpsf/basic/TestClassID.java b/src/testcases/org/apache/poi/hpsf/basic/TestClassID.java index 03678d940..490017c6d 100644 --- a/src/testcases/org/apache/poi/hpsf/basic/TestClassID.java +++ b/src/testcases/org/apache/poi/hpsf/basic/TestClassID.java @@ -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; - -/** - *

    Tests ClassID structure.

    - * - * @author Michael Zalewski (zalewski@optonline.net) - */ -public class TestClassID extends TestCase -{ - /** - *

    Constructor

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

    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}.

    - */ - 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}" - ); - } - - - - /** - *

    Runs the test cases stand-alone.

    - * - * @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; + +/** + *

    Tests ClassID structure.

    + * + * @author Michael Zalewski (zalewski@optonline.net) + */ +public class TestClassID extends TestCase +{ + /** + *

    Constructor

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

    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}.

    + */ + 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}" + ); + } + + + + /** + *

    Runs the test cases stand-alone.

    + * + * @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); + } + +} diff --git a/src/testcases/org/apache/poi/hpsf/basic/TestEmptyProperties.java b/src/testcases/org/apache/poi/hpsf/basic/TestEmptyProperties.java index 59fe77418..9f813d3ce 100644 --- a/src/testcases/org/apache/poi/hpsf/basic/TestEmptyProperties.java +++ b/src/testcases/org/apache/poi/hpsf/basic/TestEmptyProperties.java @@ -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; /** *

    Test case for OLE2 files with empty properties. An empty property's type - * is {@link Variant.VT_EMPTY}.

    + * is {@link Variant#VT_EMPTY}.

    * * @author Rainer Klute <klute@rainer-klute.de> @@ -96,10 +97,8 @@ public class TestEmptyProperties extends TestCase /** *

    Checks the names of the files in the POI filesystem. They * are expected to be in a certain order.

    - * - * @exception IOException if an I/O exception occurs */ - public void testReadFiles() throws IOException + public void testReadFiles() { String[] expected = POI_FILES; for (int i = 0; i < expected.length; i++) diff --git a/src/testcases/org/apache/poi/hpsf/basic/TestMetaDataIPI.java b/src/testcases/org/apache/poi/hpsf/basic/TestMetaDataIPI.java new file mode 100644 index 000000000..00fcd2e50 --- /dev/null +++ b/src/testcases/org/apache/poi/hpsf/basic/TestMetaDataIPI.java @@ -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(); + } + } + + + +} \ No newline at end of file diff --git a/src/testcases/org/apache/poi/hpsf/basic/TestUnicode.java b/src/testcases/org/apache/poi/hpsf/basic/TestUnicode.java index 9c7a2b47a..821154556 100644 --- a/src/testcases/org/apache/poi/hpsf/basic/TestUnicode.java +++ b/src/testcases/org/apache/poi/hpsf/basic/TestUnicode.java @@ -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), diff --git a/src/testcases/org/apache/poi/hpsf/basic/TestWrite.java b/src/testcases/org/apache/poi/hpsf/basic/TestWrite.java index 3fa12d83f..5880328f4 100644 --- a/src/testcases/org/apache/poi/hpsf/basic/TestWrite.java +++ b/src/testcases/org/apache/poi/hpsf/basic/TestWrite.java @@ -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.

    * * @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 /** *

    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.

    + * + * @return true if the default character set is 16-bit-capable, + * else false. */ private boolean hasProperDefaultCharset() { @@ -916,6 +927,9 @@ public class TestWrite extends TestCase /** *

    Runs the test cases stand-alone.

    + * + * @param args The command-line parameters. + * @throws Throwable if anything goes wrong. */ public static void main(final String[] args) throws Throwable { diff --git a/src/testcases/org/apache/poi/hpsf/basic/TestWriteWellKnown.java b/src/testcases/org/apache/poi/hpsf/basic/TestWriteWellKnown.java new file mode 100644 index 000000000..db185109b --- /dev/null +++ b/src/testcases/org/apache/poi/hpsf/basic/TestWriteWellKnown.java @@ -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; + + + +/** + *

    Tests HPSF's high-level writing functionality for the well-known property + * set "SummaryInformation" and "DocumentSummaryInformation".

    + * + * @author Rainer Klute + * klute@rainer-klute.de + * @since 2006-02-01 + * @version $Id$ + */ +public class TestWriteWellKnown extends TestCase +{ + + private static final String POI_FS = "TestWriteWellKnown.doc"; + + /** + *

    Constructor

    + * + * @param name the test case's name + */ + public TestWriteWellKnown(final String name) + { + super(name); + } + + + + /** + * @see TestCase#setUp() + */ + public void setUp() + { + VariantSupport.setLogUnsupportedTypes(false); + } + + + + /** + *

    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.

    + * @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 doc 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(); + } + } + } + + + /** + *

    This test method test the writing of properties in the well-known + * property set streams "SummaryInformation" and + * "DocumentSummaryInformation" by performing the following steps:

    + * + *
      + * + *
    1. Read a test document doc1 into a POI filesystem.

    2. + * + *
    3. Read the summary information stream and the document summary + * information stream from the POI filesystem.

    4. + * + *
    5. 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).

    6. + * + *
    7. Write the summary information stream and the document summary + * information stream to the POI filesystem.

    8. + * + *
    9. Write the POI filesystem to a (temporary) file doc2 + * and close the latter.

    10. + * + *
    11. Open doc2 for reading and check summary information + * and document summary information. All properties written before must be + * found in the property streams of doc2 and have the correct + * values.

    12. + * + *
    13. 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).

    14. + * + *
    15. Write the summary information stream and the document summary + * information stream to the POI filesystem.

    16. + * + *
    17. Write the POI filesystem to a (temporary) file doc3 + * and close the latter.

    18. + * + *
    19. Open doc3 for reading and check summary information + * and document summary information. All properties removed before must not + * be found in the property streams of doc3.

    + * + * @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 doc1 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 doc2 + * 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 doc2 for reading and check summary information and + * document summary information. All properties written before must be + * found in the property streams of doc2 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(); + + /* + *
  • 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 doc3 + * 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 doc3 for reading and check summary information + * and document summary information. All properties removed before must not + * be found in the property streams of doc3. + */ + 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()); + } + + + + /** + *

    Tests the simplified custom properties by reading them from the + * available test files.

    + * + * @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 doc 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]); + } + } + + + + /** + *

    Tests basic custom property features.

    + */ + 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()); + } + + + + /** + *

    Tests reading custom properties from a section including reading + * custom properties which are not pure.

    + */ + 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()); + } + + + + /** + *

    Runs the test cases stand-alone.

    + * + * @param args The command-line parameters. + * @throws Throwable if anything goes wrong. + */ + public static void main(final String[] args) throws Throwable + { + System.setProperty("HPSF.testdata.path", + "./src/testcases/org/apache/poi/hpsf/data"); + junit.textui.TestRunner.run(TestWriteWellKnown.class); + } + +} diff --git a/src/testcases/org/apache/poi/hpsf/data/TestCorel.shw b/src/testcases/org/apache/poi/hpsf/data/TestCorel.shw old mode 100755 new mode 100644 diff --git a/src/testcases/org/apache/poi/hpsf/data/TestGermanWord90.doc b/src/testcases/org/apache/poi/hpsf/data/TestGermanWord90.doc old mode 100755 new mode 100644 diff --git a/src/testcases/org/apache/poi/hpsf/data/TestRobert_Flaherty.doc b/src/testcases/org/apache/poi/hpsf/data/TestRobert_Flaherty.doc old mode 100755 new mode 100644 diff --git a/src/testcases/org/apache/poi/hpsf/data/TestWriteWellKnown.doc b/src/testcases/org/apache/poi/hpsf/data/TestWriteWellKnown.doc new file mode 100644 index 000000000..d3a043304 Binary files /dev/null and b/src/testcases/org/apache/poi/hpsf/data/TestWriteWellKnown.doc differ