diff --git a/src/examples/src/org/apache/poi/hpsf/examples/WriteAuthorAndTitle.java b/src/examples/src/org/apache/poi/hpsf/examples/WriteAuthorAndTitle.java index 64ea6c542..d0b203dfd 100644 --- a/src/examples/src/org/apache/poi/hpsf/examples/WriteAuthorAndTitle.java +++ b/src/examples/src/org/apache/poi/hpsf/examples/WriteAuthorAndTitle.java @@ -72,7 +72,6 @@ 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.UnexpectedPropertySetTypeException; import org.apache.poi.hpsf.Util; import org.apache.poi.hpsf.Variant; import org.apache.poi.hpsf.WritingNotSupportedException; diff --git a/src/examples/src/org/apache/poi/hpsf/examples/WriteTitle.java b/src/examples/src/org/apache/poi/hpsf/examples/WriteTitle.java index c98274829..df85917e6 100644 --- a/src/examples/src/org/apache/poi/hpsf/examples/WriteTitle.java +++ b/src/examples/src/org/apache/poi/hpsf/examples/WriteTitle.java @@ -54,11 +54,19 @@ */ package org.apache.poi.hpsf.examples; -import java.io.*; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; -import org.apache.poi.hpsf.*; -import org.apache.poi.hpsf.wellknown.*; -import org.apache.poi.poifs.filesystem.*; +import org.apache.poi.hpsf.MutableProperty; +import org.apache.poi.hpsf.MutablePropertySet; +import org.apache.poi.hpsf.MutableSection; +import org.apache.poi.hpsf.SummaryInformation; +import org.apache.poi.hpsf.Variant; +import org.apache.poi.hpsf.WritingNotSupportedException; +import org.apache.poi.hpsf.wellknown.PropertyIDMap; +import org.apache.poi.hpsf.wellknown.SectionIDMap; +import org.apache.poi.poifs.filesystem.POIFSFileSystem; /** *

This class is a simple sample application showing how to create a property diff --git a/src/java/org/apache/poi/hpsf/IllegalPropertySetDataException.java b/src/java/org/apache/poi/hpsf/IllegalPropertySetDataException.java index 378b1874d..3ad7de6e0 100644 --- a/src/java/org/apache/poi/hpsf/IllegalPropertySetDataException.java +++ b/src/java/org/apache/poi/hpsf/IllegalPropertySetDataException.java @@ -66,7 +66,7 @@ package org.apache.poi.hpsf; * @version $Id$ * @since 2002-05-26 */ -public class IllegalPropertySetDataException extends HPSFRuntimeException +public class IllegalPropertySetDataException extends HPSFRuntimeException { /** diff --git a/src/java/org/apache/poi/hpsf/MutablePropertySet.java b/src/java/org/apache/poi/hpsf/MutablePropertySet.java index 8a236643d..f9f1333ee 100644 --- a/src/java/org/apache/poi/hpsf/MutablePropertySet.java +++ b/src/java/org/apache/poi/hpsf/MutablePropertySet.java @@ -106,6 +106,7 @@ public class MutablePropertySet extends PropertySet * one section it is added right here. */ sections = new LinkedList(); sections.add(new MutableSection()); + sectionCount = 1; } @@ -123,7 +124,7 @@ public class MutablePropertySet extends PropertySet byteOrder = ps.getByteOrder(); format = ps.getFormat(); osVersion = ps.getOSVersion(); - classID = new ClassID(ps.getClassID().getBytes(), 0); + setClassID(ps.getClassID()); clearSections(); for (final Iterator i = ps.getSections().iterator(); i.hasNext();) { diff --git a/src/java/org/apache/poi/hpsf/MutableSection.java b/src/java/org/apache/poi/hpsf/MutableSection.java index fce86980e..e60edf5bd 100644 --- a/src/java/org/apache/poi/hpsf/MutableSection.java +++ b/src/java/org/apache/poi/hpsf/MutableSection.java @@ -60,7 +60,9 @@ import java.io.OutputStream; import java.util.Iterator; import java.util.LinkedList; import java.util.List; +import java.util.Map; +import org.apache.poi.hpsf.wellknown.PropertyIDMap; import org.apache.poi.util.LittleEndian; /** @@ -129,7 +131,7 @@ public class MutableSection extends Section for (int i = 0; i < pa.length; i++) mpa[i] = new MutableProperty(pa[i]); setProperties(mpa); - dictionary = s.dictionary; + setDictionary(s.getDictionary()); } @@ -247,13 +249,27 @@ public class MutableSection extends Section public void setProperty(final Property p) { final long id = p.getID(); + removeProperty(id); + preprops.add(p); + dirty = true; + propertyCount = preprops.size(); + } + + + + /** + *

Removes a property.

+ * + * @param id The ID of the property to be removed + */ + public void removeProperty(final long id) + { for (final Iterator i = preprops.iterator(); i.hasNext();) if (((Property) i.next()).getID() == id) { i.remove(); break; } - preprops.add(p); dirty = true; propertyCount = preprops.size(); } @@ -292,6 +308,10 @@ public class MutableSection extends Section size = calcSize(); dirty = false; } + catch (HPSFRuntimeException ex) + { + throw ex; + } catch (Exception ex) { throw new HPSFRuntimeException(ex); @@ -365,19 +385,55 @@ public class MutableSection extends Section position += 2 * LittleEndian.INT_SIZE + getPropertyCount() * 2 * LittleEndian.INT_SIZE; + /* Writing the section's dictionary it tricky. If there is a dictionary + * (property 0) the codepage property (property 1) has to be set, too. + * Since HPSF supports Unicode only, the codepage must be 1200. */ + if (getProperty(PropertyIDMap.PID_DICTIONARY) != null) + { + final Object p1 = getProperty(PropertyIDMap.PID_CODEPAGE); + if (p1 != null) + { + if (!(p1 instanceof Integer)) + throw new IllegalPropertySetDataException + ("The codepage property (ID = 1) must be an " + + "Integer object."); + else if (((Integer) p1).intValue() != Property.CP_UNICODE) + throw new IllegalPropertySetDataException + ("The codepage property (ID = 1) must be " + + "1200 (Unicode)."); + } + else + throw new IllegalPropertySetDataException + ("The codepage property (ID = 1) must be set."); + } + /* Write the properties and the property list into their respective * streams: */ for (final Iterator i = preprops.iterator(); i.hasNext();) { final MutableProperty p = (MutableProperty) i.next(); + final long id = p.getID(); /* Write the property list entry. */ TypeWriter.writeUIntToStream(propertyListStream, p.getID()); TypeWriter.writeUIntToStream(propertyListStream, position); - - /* Write the property and update the position to the next - * property. */ - position += p.write(propertyStream); + + /* If the property ID is not equal 0 we write the property and all + * is fine. However, if it equals 0 we have to write the section's + * dictionary which does not have a type but just a value. */ + if (id != 0) + /* Write the property and update the position to the next + * property. */ + position += p.write(propertyStream); + else + { + final Integer codepage = + (Integer) getProperty(PropertyIDMap.PID_CODEPAGE); + if (codepage == null) + throw new IllegalPropertySetDataException + ("Codepage (property 1) is undefined."); + position += writeDictionary(propertyStream, dictionary); + } } propertyStream.close(); propertyListStream.close(); @@ -405,6 +461,52 @@ public class MutableSection extends Section + /** + *

Writes the section's dictionary.

+ * + * @param out The output stream to write to. + * @param dictionary The dictionary. + * @return The number of bytes written + * @exception IOException if an I/O exception occurs. + */ + private static int writeDictionary(final OutputStream out, + final Map dictionary) + throws IOException + { + int length = 0; + length += TypeWriter.writeUIntToStream(out, dictionary.size()); + for (final Iterator i = dictionary.keySet().iterator(); i.hasNext();) + { + final Long key = (Long) i.next(); + final String value = (String) dictionary.get(key); + int sLength = value.length() + 1; + if (sLength % 2 == 1) + 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++) + { + int high = (ca[j] & 0x0ff00) >> 8; + int low = (ca[j] & 0x000ff); + out.write(low); + out.write(high); + length += 2; + sLength--; + } + while (sLength > 0) + { + out.write(0x00); + out.write(0x00); + length += 2; + sLength--; + } + } + return length; + } + + + /** *

Overwrites the super class' method to cope with a redundancy: * the property count is maintained in a separate member variable, but @@ -426,7 +528,77 @@ public class MutableSection extends Section */ public Property[] getProperties() { - return (Property[]) preprops.toArray(new Property[0]); + properties = (Property[]) preprops.toArray(new Property[0]); + return properties; + } + + + + /** + *

Gets a property.

+ * + *

FIXME (2): This method ensures that properties and + * preprops are in sync. Cleanup this awful stuff!

+ * + * @param id The ID of the property to get + * @return The property or null if there is no such property + */ + public Object getProperty(final long id) + { + getProperties(); + return super.getProperty(id); + } + + + + /** + *

Sets the section's dictionary. All keys in the dictionary must be + * {@see java.lang.Long} instances, all values must be + * {@see java.lang.String}s. This method overwrites the properties with IDs + * 0 and 1 since they are reserved for the dictionary and the dictionary's + * codepage. Setting these properties explicitly might have surprising + * effects. An application should never do this but always use this + * method.

+ * + * @param dictionary The dictionary + * + * @exception IllegalPropertySetDataException if the dictionary's key and + * value types are not correct. + * + * @see Section#getDictionary() + */ + public void setDictionary(final Map dictionary) + throws IllegalPropertySetDataException + { + if (dictionary != null) + { + for (final Iterator i = dictionary.keySet().iterator(); + i.hasNext();) + if (!(i.next() instanceof Long)) + throw new IllegalPropertySetDataException + ("Dictionary keys must be of type Long."); + for (final Iterator i = dictionary.values().iterator(); + i.hasNext();) + if (!(i.next() instanceof String)) + throw new IllegalPropertySetDataException + ("Dictionary values must be of type String."); + this.dictionary = dictionary; + + /* Set the dictionary property (ID 0). Please note that the second + * parameter in the method call below is unused because dictionaries + * don't have a type. */ + setProperty(PropertyIDMap.PID_DICTIONARY, -1, dictionary); + + /* Set the codepage property (ID 1) for the strings used in the + * dictionary. HPSF always writes Unicode strings to the + * dictionary. */ + setProperty(PropertyIDMap.PID_CODEPAGE, Variant.VT_I2, + new Integer(Property.CP_UNICODE)); + } + else + /* Setting the dictionary to null means to remove property 0. + * However, it does not mean to remove property 1 (codepage). */ + removeProperty(PropertyIDMap.PID_DICTIONARY); } } diff --git a/src/java/org/apache/poi/hpsf/Property.java b/src/java/org/apache/poi/hpsf/Property.java index 0a3bc97fa..e4d70bd1c 100644 --- a/src/java/org/apache/poi/hpsf/Property.java +++ b/src/java/org/apache/poi/hpsf/Property.java @@ -100,7 +100,7 @@ public class Property { /**

Codepage 1200 denotes Unicode.

*/ - private static final int CP_UNICODE = 1200; + public static final int CP_UNICODE = 1200; /**

The property's ID.

*/ protected long id; @@ -318,6 +318,10 @@ public class Property /** + *

Compares two properties. Please beware that a property with ID == 0 is + * a special case: It does not have a type, and its value is the section's + * dictionary.

+ * * @see Object#equals(java.lang.Object) */ public boolean equals(final Object o) @@ -326,13 +330,14 @@ public class Property return false; final Property p = (Property) o; final Object pValue = p.getValue(); - if (id != p.getID() || type != p.getType()) + final long pId = p.getID(); + if (id != pId || (id != 0 && type != p.getType())) return false; if (value == null && pValue == null) return true; if (value == null || pValue == null) return false; - + /* It's clear now that both values are non-null. */ final Class valueClass = value.getClass(); final Class pValueClass = pValue.getClass(); diff --git a/src/java/org/apache/poi/hpsf/PropertySetFactory.java b/src/java/org/apache/poi/hpsf/PropertySetFactory.java index 6f6d8e6ed..e399bceee 100644 --- a/src/java/org/apache/poi/hpsf/PropertySetFactory.java +++ b/src/java/org/apache/poi/hpsf/PropertySetFactory.java @@ -84,8 +84,6 @@ public class PropertySetFactory * contain a property set. * @throws MarkUnsupportedException if the stream does not support * the mark operation. - * @throws UnexpectedPropertySetTypeException if the property - * set's type is unexpected. * @throws IOException if some I/O problem occurs. */ public static PropertySet create(final InputStream stream) diff --git a/src/java/org/apache/poi/hpsf/Section.java b/src/java/org/apache/poi/hpsf/Section.java index 9a27a3ded..efe74015c 100644 --- a/src/java/org/apache/poi/hpsf/Section.java +++ b/src/java/org/apache/poi/hpsf/Section.java @@ -93,8 +93,7 @@ public class Section * section. For example, if the format ID of the first {@link * Section} contains the bytes specified by * org.apache.poi.hpsf.wellknown.SectionIDMap.SUMMARY_INFORMATION_ID - * the section (and thus the property set) is a - * SummaryInformation.

+ * the section (and thus the property set) is a SummaryInformation.

* * @return The format ID */ @@ -554,4 +553,21 @@ public class Section return b.toString(); } + + + /** + *

Gets the section's dictionary. A dictionary allows an application to + * use human-readable property names instead of numeric property IDs. It + * contains mappings from property IDs to their associated string + * values. The dictionary is stored as the property with ID 0. The codepage + * for the strings in the dictionary is defined by property with ID 1.

+ * + * @return the dictionary or null if the section does not have + * a dictionary. + */ + public Map getDictionary() + { + return dictionary; + } + } diff --git a/src/java/org/apache/poi/hpsf/VariantSupport.java b/src/java/org/apache/poi/hpsf/VariantSupport.java index 6e9d96c60..3bc0c39c3 100644 --- a/src/java/org/apache/poi/hpsf/VariantSupport.java +++ b/src/java/org/apache/poi/hpsf/VariantSupport.java @@ -252,7 +252,7 @@ public class VariantSupport extends Variant final int i1 = o1 + (i * 2); final int i2 = i1 + 1; final int high = src[i2] << 8; - final int low = src[i1] & 0xff; + final int low = src[i1] & 0x00ff; final char c = (char) (high | low); b.append(c); } @@ -352,8 +352,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] & 0xff00) >> 8); - final int low = (int) (s[i] & 0x00ff); + final int high = (int) ((s[i] & 0x0000ff00) >> 8); + final int low = (int) (s[i] & 0x000000ff); final byte highb = (byte) high; final byte lowb = (byte) low; out.write(lowb); @@ -386,13 +386,14 @@ public class VariantSupport extends Variant } case Variant.VT_I4: { - length += TypeWriter.writeToStream(out, ((Long) value).intValue()); + length += TypeWriter.writeToStream(out, + ((Long) value).intValue()); break; } case Variant.VT_FILETIME: { long filetime = Util.dateToFileTime((Date) value); - int high = (int) ((filetime >> 32) & 0xFFFFFFFFL); + int high = (int) ((filetime >> 32) & 0x00000000FFFFFFFFL); int low = (int) (filetime & 0x00000000FFFFFFFFL); length += TypeWriter.writeUIntToStream (out, 0x0000000FFFFFFFFL & low); diff --git a/src/java/org/apache/poi/hpsf/wellknown/PropertyIDMap.java b/src/java/org/apache/poi/hpsf/wellknown/PropertyIDMap.java index 30cb43e82..855e0c94e 100644 --- a/src/java/org/apache/poi/hpsf/wellknown/PropertyIDMap.java +++ b/src/java/org/apache/poi/hpsf/wellknown/PropertyIDMap.java @@ -141,7 +141,7 @@ public class PropertyIDMap extends HashMap * document

*/ public static final int PID_APPNAME = 18; - /**

ID of the property that denotes... FIXME (2)

*/ + /**

FIXME (2): ID of the property that denotes...

*/ public static final int PID_SECURITY = 19; diff --git a/src/testcases/org/apache/poi/hpsf/basic/TestBasic.java b/src/testcases/org/apache/poi/hpsf/basic/TestBasic.java index 5d7ff30e7..4e6d2a212 100644 --- a/src/testcases/org/apache/poi/hpsf/basic/TestBasic.java +++ b/src/testcases/org/apache/poi/hpsf/basic/TestBasic.java @@ -65,7 +65,6 @@ import java.util.List; 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.HPSFException; import org.apache.poi.hpsf.MarkUnsupportedException; @@ -288,7 +287,7 @@ public class TestBasic extends TestCase { final InputStream in = new ByteArrayInputStream(psf1[j].getBytes()); - final PropertySet psIn = PropertySetFactory.create(in); + PropertySetFactory.create(in); } } } diff --git a/src/testcases/org/apache/poi/hpsf/basic/TestEmptyProperties.java b/src/testcases/org/apache/poi/hpsf/basic/TestEmptyProperties.java index 9d8df67d8..7ca404f2d 100644 --- a/src/testcases/org/apache/poi/hpsf/basic/TestEmptyProperties.java +++ b/src/testcases/org/apache/poi/hpsf/basic/TestEmptyProperties.java @@ -15,7 +15,6 @@ 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; /** *

Test case for OLE2 files with empty properties. An empty property's type diff --git a/src/testcases/org/apache/poi/hpsf/basic/TestWrite.java b/src/testcases/org/apache/poi/hpsf/basic/TestWrite.java index 75f941d80..d62378dff 100644 --- a/src/testcases/org/apache/poi/hpsf/basic/TestWrite.java +++ b/src/testcases/org/apache/poi/hpsf/basic/TestWrite.java @@ -66,12 +66,14 @@ import java.io.OutputStream; import java.io.PrintWriter; import java.io.StringWriter; import java.util.Date; -import java.util.Iterator; +import java.util.HashMap; +import java.util.Map; import junit.framework.Assert; import junit.framework.TestCase; import org.apache.poi.hpsf.HPSFRuntimeException; +import org.apache.poi.hpsf.IllegalPropertySetDataException; import org.apache.poi.hpsf.MutableProperty; import org.apache.poi.hpsf.MutablePropertySet; import org.apache.poi.hpsf.MutableSection; @@ -570,7 +572,7 @@ public class TestWrite extends TestCase final InputStream in = new ByteArrayInputStream(psf1[i].getBytes()); final PropertySet psIn = PropertySetFactory.create(in); - final MutablePropertySet psOut = copy(psIn); + final MutablePropertySet psOut = new MutablePropertySet(psIn); final ByteArrayOutputStream psStream = new ByteArrayOutputStream(); psOut.write(psStream); @@ -602,75 +604,134 @@ public class TestWrite extends TestCase } catch (Exception ex) { - StringWriter sw = new StringWriter(); - PrintWriter pw = new PrintWriter(sw); - Throwable t = ex; - while (t != null) - { - t.printStackTrace(pw); - if (t instanceof HPSFRuntimeException) - t = ((HPSFRuntimeException) t).getReason(); - else - t = null; - if (t != null) - pw.println("Caused by:"); - } - pw.close(); - try - { - sw.close(); - } - catch (IOException ex2) - { - ex.printStackTrace(); - } - String msg = sw.toString(); - fail(msg); + handle(ex); } } /** - *

Creates a copy of a {@link PropertySet}.

- * - * @param ps the property set to copy - * @return the copy + *

Tests writing and reading back a proper dictionary.

*/ - private MutablePropertySet copy(final PropertySet ps) + public void testDictionary() { - MutablePropertySet copy = new MutablePropertySet(); - copy.setByteOrder(ps.getByteOrder()); - copy.setClassID(ps.getClassID()); - copy.setFormat(ps.getFormat()); - copy.setOSVersion(ps.getOSVersion()); - copy.clearSections(); - - /* Copy the sections. */ - for (final Iterator i1 = ps.getSections().iterator(); i1.hasNext();) + try { - final Section s1 = (Section) i1.next(); - final MutableSection s2 = new MutableSection(); - s2.setFormatID(s1.getFormatID()); + final File copy = File.createTempFile("Test-HPSF", "ole2"); + copy.deleteOnExit(); - /* Copy the properties. */ - final Property[] pa = s1.getProperties(); - for (int i2 = 0; i2 < pa.length; i2++) - { - final Property p1 = pa[i2]; - final MutableProperty p2 = new MutableProperty(); - p2.setID(p1.getID()); - p2.setType(p1.getType()); - p2.setValue(p1.getValue()); - s2.setProperty(p2); - } - copy.addSection(s2); + /* Write: */ + final OutputStream out = new FileOutputStream(copy); + final POIFSFileSystem poiFs = new POIFSFileSystem(); + final MutablePropertySet ps1 = new MutablePropertySet(); + final MutableSection s = (MutableSection) ps1.getSections().get(0); + final Map m = new HashMap(3, 1.0f); + m.put(new Long(1), "String 1"); + m.put(new Long(2), "String 2"); + m.put(new Long(3), "String 3"); + s.setDictionary(m); + s.setFormatID(SectionIDMap.DOCUMENT_SUMMARY_INFORMATION_ID); + int codepage = Property.CP_UNICODE; + s.setProperty(PropertyIDMap.PID_CODEPAGE, Variant.VT_I2, + new Integer(codepage)); + poiFs.createDocument(ps1.toInputStream(), "Test"); + poiFs.writeFilesystem(out); + out.close(); + + /* Read back: */ + final POIFile[] psf = Util.readPropertySets(copy); + Assert.assertEquals(1, psf.length); + final byte[] bytes = psf[0].getBytes(); + final InputStream in = new ByteArrayInputStream(bytes); + final PropertySet ps2 = PropertySetFactory.create(in); + + /* Compare the property set stream with the corresponding one + * from the origin file and check whether they are equal. */ + assertEquals(ps1, ps2); + } + catch (Exception ex) + { + handle(ex); } - return copy; } + /** + *

Tests writing and reading back a proper dictionary with an invalid + * codepage. (HPSF writes Unicode dictionaries only.)

+ */ + public void testDictionaryWithInvalidCodepage() + { + try + { + final File copy = File.createTempFile("Test-HPSF", "ole2"); + copy.deleteOnExit(); + + /* Write: */ + final OutputStream out = new FileOutputStream(copy); + final POIFSFileSystem poiFs = new POIFSFileSystem(); + final MutablePropertySet ps1 = new MutablePropertySet(); + final MutableSection s = (MutableSection) ps1.getSections().get(0); + final Map m = new HashMap(3, 1.0f); + m.put(new Long(1), "String 1"); + m.put(new Long(2), "String 2"); + m.put(new Long(3), "String 3"); + s.setDictionary(m); + s.setFormatID(SectionIDMap.DOCUMENT_SUMMARY_INFORMATION_ID); + int codepage = 12345; + s.setProperty(PropertyIDMap.PID_CODEPAGE, Variant.VT_I2, + new Integer(codepage)); + poiFs.createDocument(ps1.toInputStream(), "Test"); + poiFs.writeFilesystem(out); + out.close(); + fail("This testcase did not detect the invalid codepage value."); + } + catch (IllegalPropertySetDataException ex) + { + assertTrue(true); + } + catch (Exception ex) + { + handle(ex); + } + } + + + + /** + *

Handles unexpected exceptions in testcases.

+ * + * @param ex The exception that has been thrown. + */ + private void handle(final Exception ex) + { + final StringWriter sw = new StringWriter(); + final PrintWriter pw = new PrintWriter(sw); + Throwable t = ex; + while (t != null) + { + t.printStackTrace(pw); + if (t instanceof HPSFRuntimeException) + t = ((HPSFRuntimeException) t).getReason(); + else + t = null; + if (t != null) + pw.println("Caused by:"); + } + pw.close(); + try + { + sw.close(); + } + catch (IOException ex2) + { + ex.printStackTrace(); + } + fail(sw.toString()); + } + + /** *

Runs the test cases stand-alone.

*/ diff --git a/src/testcases/org/apache/poi/hpsf/data/TestSectionDictionary.doc b/src/testcases/org/apache/poi/hpsf/data/TestSectionDictionary.doc new file mode 100644 index 000000000..6114a7657 Binary files /dev/null and b/src/testcases/org/apache/poi/hpsf/data/TestSectionDictionary.doc differ