diff --git a/src/java/org/apache/poi/hpsf/MutablePropertySet.java b/src/java/org/apache/poi/hpsf/MutablePropertySet.java index 16978a4d6a..60f043af9f 100644 --- a/src/java/org/apache/poi/hpsf/MutablePropertySet.java +++ b/src/java/org/apache/poi/hpsf/MutablePropertySet.java @@ -17,6 +17,10 @@ package org.apache.poi.hpsf; +import java.io.IOException; +import java.io.InputStream; +import java.io.UnsupportedEncodingException; + import org.apache.poi.util.Removal; /** @@ -34,4 +38,9 @@ public class MutablePropertySet extends PropertySet { public MutablePropertySet(final PropertySet ps) { super(ps); } + + /* package */ MutablePropertySet(final InputStream stream) + throws NoPropertySetStreamException, MarkUnsupportedException, IOException, UnsupportedEncodingException { + super(stream); + } } diff --git a/src/java/org/apache/poi/hpsf/Property.java b/src/java/org/apache/poi/hpsf/Property.java index 1ae0e7430d..60a46b804d 100644 --- a/src/java/org/apache/poi/hpsf/Property.java +++ b/src/java/org/apache/poi/hpsf/Property.java @@ -17,17 +17,20 @@ package org.apache.poi.hpsf; +import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; import java.io.UnsupportedEncodingException; import java.nio.charset.Charset; import java.util.Arrays; -import java.util.LinkedHashMap; -import java.util.Map; +import org.apache.poi.hpsf.wellknown.PropertyIDMap; import org.apache.poi.util.CodePageUtil; import org.apache.poi.util.HexDump; import org.apache.poi.util.LittleEndian; +import org.apache.poi.util.LittleEndianByteArrayInputStream; +import org.apache.poi.util.LittleEndianConsts; +import org.apache.poi.util.LocaleUtil; import org.apache.poi.util.POILogFactory; import org.apache.poi.util.POILogger; @@ -58,6 +61,13 @@ import org.apache.poi.util.POILogger; */ public class Property { + /** + * Default codepage for {@link CodePageString CodePageStrings} + */ + public static final int DEFAULT_CODEPAGE = CodePageUtil.CP_WINDOWS_1252; + + private static final POILogger LOG = POILogFactory.getLogger(Property.class); + /** The property's ID. */ private long id; @@ -82,7 +92,7 @@ public class Property { public Property(Property p) { this(p.id, p.type, p.value); } - + /** * Creates a property. * @@ -120,13 +130,12 @@ public class Property { * property IDs and property names. */ if (id == 0) { - value = readDictionary(src, offset, length, codepage); - return; + throw new UnsupportedEncodingException("Dictionary not allowed here"); } int o = (int) offset; type = LittleEndian.getUInt(src, o); - o += LittleEndian.INT_SIZE; + o += LittleEndianConsts.INT_SIZE; try { value = VariantSupport.read(src, o, length, (int) type, codepage); @@ -136,6 +145,39 @@ public class Property { } } + /** + * Creates a {@link Property} instance by reading its bytes + * from the property set stream. + * + * @param id The property's ID. + * @param leis The bytes the property set stream consists of. + * @param length The property's type/value pair's length in bytes. + * @param codepage The section's and thus the property's + * codepage. It is needed only when reading string values. + * @exception UnsupportedEncodingException if the specified codepage is not + * supported. + */ + public Property(final long id, LittleEndianByteArrayInputStream leis, final int length, final int codepage) + throws UnsupportedEncodingException { + this.id = id; + + /* + * ID 0 is a special case since it specifies a dictionary of + * property IDs and property names. + */ + if (id == 0) { + throw new UnsupportedEncodingException("Dictionary not allowed here"); + } + + type = leis.readUInt(); + + try { + value = VariantSupport.read(leis, length, (int) type, codepage); + } catch (UnsupportedVariantTypeException ex) { + VariantSupport.writeUnsupportedTypeMessage(ex); + value = ex.getValue(); + } + } /** @@ -182,7 +224,7 @@ public class Property { public Object getValue() { return value; } - + /** * Sets the property's value. * @@ -194,102 +236,8 @@ public class Property { - - /** - * Reads a dictionary. - * - * @param src The byte array containing the bytes making out the dictionary. - * @param offset At this offset within {@code src} the dictionary - * starts. - * @param length The dictionary contains at most this many bytes. - * @param codepage The codepage of the string values. - * @return The dictonary - * @throws UnsupportedEncodingException if the dictionary's codepage is not - * (yet) supported. - */ - protected Map readDictionary(final byte[] src, final long offset, final int length, final int codepage) - throws UnsupportedEncodingException { - /* Check whether "offset" points into the "src" array". */ - if (offset < 0 || offset > src.length) { - throw new HPSFRuntimeException - ("Illegal offset " + offset + " while HPSF stream contains " + - length + " bytes."); - } - int o = (int) offset; - /* - * Read the number of dictionary entries. - */ - final long nrEntries = LittleEndian.getUInt(src, o); - o += LittleEndian.INT_SIZE; - - final Map m = new LinkedHashMap((int) nrEntries, (float) 1.0 ); - - try { - for (int i = 0; i < nrEntries; i++) { - /* The key. */ - final Long id = Long.valueOf(LittleEndian.getUInt(src, o)); - o += LittleEndian.INT_SIZE; - - /* The value (a string). The length is the either the - * number of (two-byte) characters if the character set is Unicode - * or the number of bytes if the character set is not Unicode. - * The length includes terminating 0x00 bytes which we have to strip - * off to create a Java string. */ - long sLength = LittleEndian.getUInt(src, o); - o += LittleEndian.INT_SIZE; - - /* Read the string. */ - final StringBuffer b = new StringBuffer(); - switch (codepage) { - case -1: - /* Without a codepage the length is equal to the number of - * bytes. */ - b.append(new String(src, o, (int) sLength, Charset.forName("ASCII"))); - break; - case CodePageUtil.CP_UNICODE: - /* The length is the number of characters, i.e. the number - * of bytes is twice the number of the characters. */ - final int nrBytes = (int) (sLength * 2); - final byte[] h = new byte[nrBytes]; - for (int i2 = 0; i2 < nrBytes; i2 += 2) - { - h[i2] = src[o + i2 + 1]; - h[i2 + 1] = src[o + i2]; - } - b.append(new String(h, 0, nrBytes, CodePageUtil.codepageToEncoding(codepage))); - break; - default: - /* For encodings other than Unicode the length is the number - * of bytes. */ - b.append(new String(src, o, (int) sLength, CodePageUtil.codepageToEncoding(codepage))); - break; - } - - /* Strip 0x00 characters from the end of the string: */ - while (b.length() > 0 && b.charAt(b.length() - 1) == 0x00) { - b.setLength(b.length() - 1); - } - if (codepage == CodePageUtil.CP_UNICODE) { - if (sLength % 2 == 1) { - sLength++; - } - o += (sLength + sLength); - } else { - o += sLength; - } - m.put(id, b.toString()); - } - } catch (RuntimeException ex) { - final POILogger l = POILogFactory.getLogger(getClass()); - l.log(POILogger.WARN, - "The property set's dictionary contains bogus data. " - + "All dictionary entries starting with the one with ID " - + id + " will be ignored.", ex); - } - return m; - } @@ -301,11 +249,12 @@ public class Property { * @exception WritingNotSupportedException if HPSF does not yet support the * property's variant type. */ - protected int getSize() throws WritingNotSupportedException + protected int getSize(int codepage) throws WritingNotSupportedException { - int length = VariantSupport.getVariantLength(type); - if (length >= 0) { - return length; /* Fixed length */ + int length = Variant.getVariantLength(type); + if (length >= 0 || type == Variant.VT_EMPTY) { + /* Fixed length */ + return length; } if (length == -2) { /* Unknown length */ @@ -313,29 +262,26 @@ public class Property { } /* Variable length: */ - final int PADDING = 4; /* Pad to multiples of 4. */ - switch ((int) type) { - case Variant.VT_LPSTR: { - int l = ((String) value).length() + 1; - int r = l % PADDING; - if (r > 0) - l += PADDING - r; - length += l; - break; - } - case Variant.VT_EMPTY: - break; - default: + if (type == Variant.VT_LPSTR || type == Variant.VT_LPWSTR) { + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + try { + length = write(bos, codepage) - 2*LittleEndianConsts.INT_SIZE; + /* Pad to multiples of 4. */ + length += (4 - (length & 0x3)) & 0x3; + return length; + } catch (IOException e) { throw new WritingNotSupportedException(type, value); + } } - return length; + + throw new WritingNotSupportedException(type, value); } /** * 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. Another special case are strings: Two properties @@ -343,6 +289,7 @@ public class Property { * * @see Object#equals(java.lang.Object) */ + @Override public boolean equals(final Object o) { if (!(o instanceof Property)) { return false; @@ -369,12 +316,35 @@ public class Property { } if (value instanceof byte[]) { - return Arrays.equals((byte[]) value, (byte[]) pValue); + // compare without padding bytes + byte[] thisVal = (byte[]) value, otherVal = (byte[]) pValue; + int len = unpaddedLength(thisVal); + if (len != unpaddedLength(otherVal)) { + return false; + } + for (int i=0; i 0 && len > buf.length-4 && buf[len-1] == 0; len--); + return len; + } private boolean typesAreEqual(final long t1, final long t2) { @@ -388,6 +358,7 @@ public class Property { /** * @see Object#hashCode() */ + @Override public int hashCode() { long hashCode = 0; hashCode += id; @@ -404,47 +375,128 @@ public class Property { /** * @see Object#toString() */ + @Override public String toString() { + return toString(Property.DEFAULT_CODEPAGE); + } + + public String toString(int codepage) { final StringBuffer b = new StringBuffer(); - b.append(getClass().getName()); - b.append('['); + b.append("Property["); b.append("id: "); b.append(getID()); + String idName = getNameFromID(); + if (idName != null) { + b.append(" ("); + b.append(idName); + b.append(")"); + } b.append(", type: "); b.append(getType()); + b.append(" ("); + b.append(getVariantName()); + b.append(") "); final Object value = getValue(); b.append(", value: "); if (value instanceof String) { - b.append(value); - final String s = (String) value; - final int l = s.length(); - final byte[] bytes = new byte[l * 2]; - for (int i = 0; i < l; i++) { - final char c = s.charAt(i); - final byte high = (byte) ((c & 0x00ff00) >> 8); - final byte low = (byte) ((c & 0x0000ff) >> 0); - bytes[i * 2] = high; - bytes[i * 2 + 1] = low; + b.append("\n"); + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + try { + write(bos, codepage); + } catch (Exception e) { + LOG.log(POILogger.WARN, "can't serialize string", e); } + b.append(" ["); - if(bytes.length > 0) { - final String hex = HexDump.dump(bytes, 0L, 0); + // skip length field + if(bos.size() > 2*LittleEndianConsts.INT_SIZE) { + final String hex = HexDump.dump(bos.toByteArray(), -2*LittleEndianConsts.INT_SIZE, 2*LittleEndianConsts.INT_SIZE); b.append(hex); } b.append("]"); } else if (value instanceof byte[]) { + b.append("\n"); byte[] bytes = (byte[])value; if(bytes.length > 0) { String hex = HexDump.dump(bytes, 0L, 0); b.append(hex); } + } else if (type == Variant.VT_EMPTY || type == Variant.VT_NULL) { + b.append("null"); } else { - b.append(value); + b.append(value.toString()); + + String decoded = decodeValueFromID(); + if (decoded != null) { + b.append(" ("); + b.append(decoded); + b.append(")"); + } } b.append(']'); return b.toString(); } + private String getVariantName() { + if (getID() == 0) { + return "dictionary"; + } + return Variant.getVariantName(getType()); + } + + private String decodeValueFromID() { + try { + switch((int)getID()) { + case PropertyIDMap.PID_CODEPAGE: + return CodePageUtil.codepageToEncoding(((Number)value).intValue()); + case PropertyIDMap.PID_LOCALE: + return LocaleUtil.getLocaleFromLCID(((Number)value).intValue()); + } + } catch (Exception e) { + LOG.log(POILogger.WARN, "Can't decode id "+getID()); + } + return null; + } + + private String getNameFromID() { + switch ((int)getID()) { + case PropertyIDMap.PID_DICTIONARY: return "PID_DICTIONARY"; + case PropertyIDMap.PID_CODEPAGE: return "PID_CODEPAGE"; + case PropertyIDMap.PID_CATEGORY: return "PID_CATEGORY"; + case PropertyIDMap.PID_PRESFORMAT: return "PID_PRESFORMAT"; + case PropertyIDMap.PID_BYTECOUNT: return "PID_BYTECOUNT"; + case PropertyIDMap.PID_LINECOUNT: return "PID_LINECOUNT"; + case PropertyIDMap.PID_PARCOUNT: return "PID_PARCOUNT"; + case PropertyIDMap.PID_SLIDECOUNT: return "PID_SLIDECOUNT"; + case PropertyIDMap.PID_NOTECOUNT: return "PID_NOTECOUNT"; + case PropertyIDMap.PID_HIDDENCOUNT: return "PID_HIDDENCOUNT"; + case PropertyIDMap.PID_MMCLIPCOUNT: return "PID_MMCLIPCOUNT"; + case PropertyIDMap.PID_SCALE: return "PID_SCALE"; + case PropertyIDMap.PID_HEADINGPAIR: return "PID_HEADINGPAIR"; + case PropertyIDMap.PID_DOCPARTS: return "PID_DOCPARTS"; + case PropertyIDMap.PID_MANAGER: return "PID_MANAGER"; + case PropertyIDMap.PID_COMPANY: return "PID_COMPANY"; + case PropertyIDMap.PID_LINKSDIRTY: return "PID_LINKSDIRTY"; + case PropertyIDMap.PID_CCHWITHSPACES: return "PID_CCHWITHSPACES"; + // 0x12 Unused + // 0x13 GKPIDDSI_SHAREDDOC - Must be False + // 0x14 GKPIDDSI_LINKBASE - Must not be written + // 0x15 GKPIDDSI_HLINKS - Must not be written + case PropertyIDMap.PID_HYPERLINKSCHANGED: return "PID_HYPERLINKSCHANGED"; + case PropertyIDMap.PID_VERSION: return "PID_VERSION"; + case PropertyIDMap.PID_DIGSIG: return "PID_DIGSIG"; + // 0x19 Unused + case PropertyIDMap.PID_CONTENTTYPE: return "PID_CONTENTTYPE"; + case PropertyIDMap.PID_CONTENTSTATUS: return "PID_CONTENTSTATUS"; + case PropertyIDMap.PID_LANGUAGE: return "PID_LANGUAGE"; + case PropertyIDMap.PID_DOCVERSION: return "PID_DOCVERSION"; + case PropertyIDMap.PID_MAX: return "PID_MAX"; + case PropertyIDMap.PID_LOCALE: return "PID_LOCALE"; + case PropertyIDMap.PID_BEHAVIOUR: return "PID_BEHAVIOUR"; + default: return null; + } + } + /** * Writes the property to an output stream. * @@ -462,13 +514,21 @@ public class Property { long variantType = getType(); /* Ensure that wide strings are written if the codepage is Unicode. */ - if (codepage == CodePageUtil.CP_UNICODE && variantType == Variant.VT_LPSTR) { - variantType = Variant.VT_LPWSTR; +// if (codepage == CodePageUtil.CP_UNICODE && variantType == Variant.VT_LPSTR) { +// variantType = Variant.VT_LPWSTR; +// } + + if (variantType == Variant.VT_LPSTR && codepage != CodePageUtil.CP_UTF16) { + String csStr = CodePageUtil.codepageToEncoding(codepage > 0 ? codepage : Property.DEFAULT_CODEPAGE); + if (!Charset.forName(csStr).newEncoder().canEncode((String)value)) { + variantType = Variant.VT_LPWSTR; + } } - length += TypeWriter.writeUIntToStream(out, variantType); + LittleEndian.putUInt(variantType, out); + length += LittleEndianConsts.INT_SIZE; length += VariantSupport.write(out, variantType, getValue(), codepage); return length; } - + } diff --git a/src/java/org/apache/poi/hpsf/PropertySetFactory.java b/src/java/org/apache/poi/hpsf/PropertySetFactory.java index db0a73d135..c3ced5d106 100644 --- a/src/java/org/apache/poi/hpsf/PropertySetFactory.java +++ b/src/java/org/apache/poi/hpsf/PropertySetFactory.java @@ -22,9 +22,11 @@ import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; +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.util.LittleEndianInputStream; /** * Factory class to create instances of {@link SummaryInformation}, @@ -87,19 +89,33 @@ public class PropertySetFactory { */ public static PropertySet create(final InputStream stream) throws NoPropertySetStreamException, MarkUnsupportedException, UnsupportedEncodingException, IOException { - final PropertySet ps = new PropertySet(stream); - try { - if (ps.isSummaryInformation()) { - return new SummaryInformation(ps); - } else if (ps.isDocumentSummaryInformation()) { - return new DocumentSummaryInformation(ps); - } else { - return ps; - } - } catch (UnexpectedPropertySetTypeException ex) { - /* This exception will never be throws because we already checked - * explicitly for this case above. */ - throw new IllegalStateException(ex); + stream.mark(PropertySet.OFFSET_HEADER+ClassID.LENGTH+1); + LittleEndianInputStream leis = new LittleEndianInputStream(stream); + int byteOrder = leis.readUShort(); + int format = leis.readUShort(); + int osVersion = (int)leis.readUInt(); + byte[] clsIdBuf = new byte[ClassID.LENGTH]; + leis.readFully(clsIdBuf); + int sectionCount = (int)leis.readUInt(); + + if (byteOrder != PropertySet.BYTE_ORDER_ASSERTION || + format != PropertySet.FORMAT_ASSERTION || + sectionCount < 0) { + throw new NoPropertySetStreamException(); + } + + if (sectionCount > 0) { + leis.readFully(clsIdBuf); + } + stream.reset(); + + ClassID clsId = new ClassID(clsIdBuf, 0); + if (sectionCount > 0 && PropertySet.matchesSummary(clsId, SectionIDMap.SUMMARY_INFORMATION_ID)) { + return new SummaryInformation(stream); + } else if (sectionCount > 0 && PropertySet.matchesSummary(clsId, SectionIDMap.DOCUMENT_SUMMARY_INFORMATION_ID)) { + return new DocumentSummaryInformation(stream); + } else { + return new PropertySet(stream); } } diff --git a/src/java/org/apache/poi/hpsf/SpecialPropertySet.java b/src/java/org/apache/poi/hpsf/SpecialPropertySet.java index 843d681ebc..0ba19b7982 100644 --- a/src/java/org/apache/poi/hpsf/SpecialPropertySet.java +++ b/src/java/org/apache/poi/hpsf/SpecialPropertySet.java @@ -17,6 +17,10 @@ package org.apache.poi.hpsf; +import java.io.IOException; +import java.io.InputStream; +import java.io.UnsupportedEncodingException; + import org.apache.poi.util.Removal; /** @@ -37,4 +41,9 @@ public class SpecialPropertySet extends MutablePropertySet { public SpecialPropertySet(final PropertySet ps) throws UnexpectedPropertySetTypeException { super(ps); } + + /* package */ SpecialPropertySet(final InputStream stream) + throws NoPropertySetStreamException, MarkUnsupportedException, IOException, UnsupportedEncodingException { + super(stream); + } } diff --git a/src/java/org/apache/poi/hpsf/SummaryInformation.java b/src/java/org/apache/poi/hpsf/SummaryInformation.java index e2ea8568b6..f59bd4872a 100644 --- a/src/java/org/apache/poi/hpsf/SummaryInformation.java +++ b/src/java/org/apache/poi/hpsf/SummaryInformation.java @@ -17,6 +17,9 @@ package org.apache.poi.hpsf; +import java.io.IOException; +import java.io.InputStream; +import java.io.UnsupportedEncodingException; import java.util.Date; import org.apache.poi.hpsf.wellknown.PropertyIDMap; @@ -62,8 +65,34 @@ public final class SummaryInformation extends SpecialPropertySet { } } + /** + * Creates a {@link SummaryInformation} instance from an {@link + * InputStream} in the Horrible Property Set Format.

+ * + * The constructor reads the first few bytes from the stream + * and determines whether it is really a property set stream. If + * it is, it parses the rest of the stream. If it is not, it + * resets the stream to its beginning in order to let other + * components mess around with the data and throws an + * exception. + * + * @param stream Holds the data making out the property set + * stream. + * @throws MarkUnsupportedException + * if the stream does not support the {@link InputStream#markSupported} method. + * @throws IOException + * if the {@link InputStream} cannot be accessed as needed. + * @exception NoPropertySetStreamException + * if the input stream does not contain a property set. + * @exception UnsupportedEncodingException + * if a character encoding is not supported. + */ + public SummaryInformation(final InputStream stream) + throws NoPropertySetStreamException, MarkUnsupportedException, IOException, UnsupportedEncodingException { + super(stream); + } - + /** * @return The title or {@code null} */ diff --git a/src/java/org/apache/poi/hpsf/wellknown/PropertyIDMap.java b/src/java/org/apache/poi/hpsf/wellknown/PropertyIDMap.java index 20e6d9f704..4a7da335d0 100644 --- a/src/java/org/apache/poi/hpsf/wellknown/PropertyIDMap.java +++ b/src/java/org/apache/poi/hpsf/wellknown/PropertyIDMap.java @@ -22,14 +22,14 @@ import java.util.HashMap; import java.util.Map; /** - *

This is a dictionary which maps property ID values to property - * ID strings.

+ * This is a dictionary which maps property ID values to property + * ID strings. * - *

The methods {@link #getSummaryInformationProperties} and {@link + * The methods {@link #getSummaryInformationProperties} and {@link * #getDocumentSummaryInformationProperties} return singleton {@link * PropertyIDMap}s. An application that wants to extend these maps * should treat them as unmodifiable, copy them and modifiy the - * copies.

+ * copies. */ public class PropertyIDMap extends HashMap { @@ -38,85 +38,85 @@ public class PropertyIDMap extends HashMap { * (and only) section of the Summary Information property set. */ - /**

ID of the property that denotes the document's title

*/ + /** ID of the property that denotes the document's title */ public static final int PID_TITLE = 2; - /**

ID of the property that denotes the document's subject

*/ + /** ID of the property that denotes the document's subject */ public static final int PID_SUBJECT = 3; - /**

ID of the property that denotes the document's author

*/ + /** ID of the property that denotes the document's author */ public static final int PID_AUTHOR = 4; - /**

ID of the property that denotes the document's keywords

*/ + /** ID of the property that denotes the document's keywords */ public static final int PID_KEYWORDS = 5; - /**

ID of the property that denotes the document's comments

*/ + /** ID of the property that denotes the document's comments */ public static final int PID_COMMENTS = 6; - /**

ID of the property that denotes the document's template

*/ + /** ID of the property that denotes the document's template */ public static final int PID_TEMPLATE = 7; - /**

ID of the property that denotes the document's last author

*/ + /** ID of the property that denotes the document's last author */ public static final int PID_LASTAUTHOR = 8; - /**

ID of the property that denotes the document's revision number

*/ + /** ID of the property that denotes the document's revision number */ public static final int PID_REVNUMBER = 9; - /**

ID of the property that denotes the document's edit time

*/ + /** ID of the property that denotes the document's edit time */ public static final int PID_EDITTIME = 10; - /**

ID of the property that denotes the date and time the document was - * last printed

*/ + /** ID of the property that denotes the date and time the document was + * last printed */ public static final int PID_LASTPRINTED = 11; - /**

ID of the property that denotes the date and time the document was - * created.

*/ + /** ID of the property that denotes the date and time the document was + * created. */ public static final int PID_CREATE_DTM = 12; - /**

ID of the property that denotes the date and time the document was - * saved

*/ + /** ID of the property that denotes the date and time the document was + * saved */ public static final int PID_LASTSAVE_DTM = 13; - /**

ID of the property that denotes the number of pages in the - * document

*/ + /** ID of the property that denotes the number of pages in the + * document */ public static final int PID_PAGECOUNT = 14; - /**

ID of the property that denotes the number of words in the - * document

*/ + /** ID of the property that denotes the number of words in the + * document */ public static final int PID_WORDCOUNT = 15; - /**

ID of the property that denotes the number of characters in the - * document

*/ + /** ID of the property that denotes the number of characters in the + * document */ public static final int PID_CHARCOUNT = 16; - /**

ID of the property that denotes the document's thumbnail

*/ + /** ID of the property that denotes the document's thumbnail */ public static final int PID_THUMBNAIL = 17; - /**

ID of the property that denotes the application that created the - * document

*/ + /** ID of the property that denotes the application that created the + * document */ public static final int PID_APPNAME = 18; - /**

ID of the property that denotes whether read/write access to the + /** ID of the property that denotes whether read/write access to the * document is allowed or whether is should be opened as read-only. It can - * have the following values:

+ * have the following values: * * * * - * - * + * + * * * - * - * + * + * * * - * - * + * + * * * - * - * + * + * * * *

Value

Description

ValueDescription

0

No restriction

0No restriction

2

Read-only recommended

2Read-only recommended

4

Read-only enforced

4Read-only enforced
@@ -131,103 +131,103 @@ public class PropertyIDMap extends HashMap { */ /** - *

The entry is a dictionary.

+ * The entry is a dictionary. */ public static final int PID_DICTIONARY = 0; /** - *

The entry denotes a code page.

+ * The entry denotes a code page. */ public static final int PID_CODEPAGE = 1; /** - *

The entry is a string denoting the category the file belongs + * The entry is a string denoting the category the file belongs * to, e.g. review, memo, etc. This is useful to find documents of - * same type.

+ * same type. */ public static final int PID_CATEGORY = 2; /** - *

Target format for power point presentation, e.g. 35mm, - * printer, video etc.

+ * Target format for power point presentation, e.g. 35mm, + * printer, video etc. */ public static final int PID_PRESFORMAT = 3; /** - *

Number of bytes.

+ * Number of bytes. */ public static final int PID_BYTECOUNT = 4; /** - *

Number of lines.

+ * Number of lines. */ public static final int PID_LINECOUNT = 5; /** - *

Number of paragraphs.

+ * Number of paragraphs. */ public static final int PID_PARCOUNT = 6; /** - *

Number of slides in a power point presentation.

+ * Number of slides in a power point presentation. */ public static final int PID_SLIDECOUNT = 7; /** - *

Number of slides with notes.

+ * Number of slides with notes. */ public static final int PID_NOTECOUNT = 8; /** - *

Number of hidden slides.

+ * Number of hidden slides. */ public static final int PID_HIDDENCOUNT = 9; /** - *

Number of multimedia clips, e.g. sound or video.

+ * Number of multimedia clips, e.g. sound or video. */ public static final int PID_MMCLIPCOUNT = 10; /** - *

This entry is set to -1 when scaling of the thumbnail is - * desired. Otherwise the thumbnail should be cropped.

+ * This entry is set to -1 when scaling of the thumbnail is + * desired. Otherwise the thumbnail should be cropped. */ public static final int PID_SCALE = 11; /** - *

This entry denotes an internally used property. It is a + * This entry denotes an internally used property. It is a * vector of variants consisting of pairs of a string (VT_LPSTR) * and a number (VT_I4). The string is a heading name, and the * number tells how many document parts are under that - * heading.

+ * heading. */ public static final int PID_HEADINGPAIR = 12; /** - *

This entry contains the names of document parts (word: names + * This entry contains the names of document parts (word: names * of the documents in the master document, excel: sheet names, - * power point: slide titles, binder: document names).

+ * power point: slide titles, binder: document names). */ public static final int PID_DOCPARTS = 13; /** - *

This entry contains the name of the project manager.

+ * This entry contains the name of the project manager. */ public static final int PID_MANAGER = 14; /** - *

This entry contains the company name.

+ * This entry contains the company name. */ public static final int PID_COMPANY = 15; /** - *

If this entry is -1 the links are dirty and should be - * re-evaluated.

+ * If this entry is -1 the links are dirty and should be + * re-evaluated. */ public static final int PID_LINKSDIRTY = 0x10; /** - *

The entry specifies an estimate of the number of characters + * The entry specifies an estimate of the number of characters * in the document, including whitespace, as an integer */ public static final int PID_CCHWITHSPACES = 0x11; @@ -238,21 +238,21 @@ public class PropertyIDMap extends HashMap { // 0x15 GKPIDDSI_HLINKS - Must not be written /** - *

This entry contains a boolean which marks if the User Defined + * This entry contains a boolean which marks if the User Defined * Property Set has been updated outside of the Application, if so the * hyperlinks should be updated on document load. */ public static final int PID_HYPERLINKSCHANGED = 0x16; /** - *

This entry contains the version of the Application which wrote the + * This entry contains the version of the Application which wrote the * Property set, stored with the two high order bytes having the major * version number, and the two low order bytes the minor version number. */ public static final int PID_VERSION = 0x17; /** - *

This entry contains the VBA digital signature for the VBA project + * This entry contains the VBA digital signature for the VBA project * embedded in the document. */ public static final int PID_DIGSIG = 0x18; @@ -260,54 +260,75 @@ public class PropertyIDMap extends HashMap { // 0x19 Unused /** - *

This entry contains a string of the content type of the file. + * This entry contains a string of the content type of the file. */ public static final int PID_CONTENTTYPE = 0x1A; /** - *

This entry contains a string of the document status. + * This entry contains a string of the document status. */ public static final int PID_CONTENTSTATUS = 0x1B; /** - *

This entry contains a string of the document language, but + * This entry contains a string of the document language, but * normally should be empty. */ public static final int PID_LANGUAGE = 0x1C; /** - *

This entry contains a string of the document version, but + * This entry contains a string of the document version, but * normally should be empty */ public static final int PID_DOCVERSION = 0x1D; /** - *

The highest well-known property ID. Applications are free to use + * The highest well-known property ID. Applications are free to use * higher values for custom purposes. (This value is based on Office 12, - * earlier versions of Office had lower values)

+ * earlier versions of Office had lower values) */ public static final int PID_MAX = 0x1F; + /** + * The Locale property, if present, MUST have the property identifier 0x80000000, + * MUST NOT have a property name, and MUST have type VT_UI4 (0x0013). + * If present, its value MUST be a valid language code identifier as specified in [MS-LCID]. + * Its value is selected in an implementation-specific manner. + */ + public static final int PID_LOCALE = 0x80000000; /** - *

Contains the summary information property ID values and + * The Behavior property, if present, MUST have the property identifier 0x80000003, + * MUST NOT have a property name, and MUST have type VT_UI4 (0x0013). + * A version 0 property set, indicated by the value 0x0000 for the Version field of + * the PropertySetStream packet, MUST NOT have a Behavior property. + * If the Behavior property is present, it MUST have one of the following values. + * + *

    + *
  • 0x00000000 = Property names are case-insensitive (default) + *
  • 0x00000001 = Property names are case-sensitive. + *
+ */ + public static final int PID_BEHAVIOUR = 0x80000003; + + /** + * Contains the summary information property ID values and * associated strings. See the overall HPSF documentation for - * details!

+ * details! */ private static PropertyIDMap summaryInformationProperties; /** - *

Contains the summary information property ID values and + * Contains the summary information property ID values and * associated strings. See the overall HPSF documentation for - * details!

+ * details! */ private static PropertyIDMap documentSummaryInformationProperties; /** - *

Creates a {@link PropertyIDMap}.

+ * Creates a {@link PropertyIDMap}. * * @param initialCapacity The initial capacity as defined for * {@link HashMap} @@ -321,7 +342,7 @@ public class PropertyIDMap extends HashMap { /** - *

Creates a {@link PropertyIDMap} backed by another map.

+ * Creates a {@link PropertyIDMap} backed by another map. * * @param map The instance to be created is backed by this map. */ @@ -333,8 +354,8 @@ public class PropertyIDMap extends HashMap { /** - *

Puts a ID string for an ID into the {@link - * PropertyIDMap}.

+ * Puts a ID string for an ID into the {@link + * PropertyIDMap}. * * @param id The ID. * @param idString The ID string. @@ -351,8 +372,8 @@ public class PropertyIDMap extends HashMap { /** - *

Gets the ID string for an ID from the {@link - * PropertyIDMap}.

+ * Gets the ID string for an ID from the {@link + * PropertyIDMap}. * * @param id The ID. * @return The ID string associated with id. @@ -367,7 +388,7 @@ public class PropertyIDMap extends HashMap { /** * @return the Summary Information properties singleton */ - public static PropertyIDMap getSummaryInformationProperties() + public static synchronized PropertyIDMap getSummaryInformationProperties() { if (summaryInformationProperties == null) { @@ -399,12 +420,12 @@ public class PropertyIDMap extends HashMap { /** - *

Returns the Document Summary Information properties - * singleton.

+ * Returns the Document Summary Information properties + * singleton. * * @return The Document Summary Information properties singleton. */ - public static PropertyIDMap getDocumentSummaryInformationProperties() + public static synchronized PropertyIDMap getDocumentSummaryInformationProperties() { if (documentSummaryInformationProperties == null) { @@ -435,7 +456,7 @@ public class PropertyIDMap extends HashMap { /** - *

For the most basic testing.

+ * For the most basic testing. * * @param args The command-line arguments */ diff --git a/src/java/org/apache/poi/hpsf/wellknown/SectionIDMap.java b/src/java/org/apache/poi/hpsf/wellknown/SectionIDMap.java index 1f5760df23..a3aff23d8f 100644 --- a/src/java/org/apache/poi/hpsf/wellknown/SectionIDMap.java +++ b/src/java/org/apache/poi/hpsf/wellknown/SectionIDMap.java @@ -18,8 +18,10 @@ package org.apache.poi.hpsf.wellknown; import java.util.HashMap; +import java.util.Map; -import org.apache.poi.util.StringUtil; +import org.apache.poi.hpsf.ClassID; +import org.apache.poi.util.Internal; /** *

Maps section format IDs to {@link PropertyIDMap}s. It is @@ -36,70 +38,53 @@ import org.apache.poi.util.StringUtil; * as keys. A key maps to a {@link PropertyIDMap} describing the * property IDs in sections with the specified section format ID.

*/ -@SuppressWarnings({"rawtypes","unchecked"}) // Java Generics have issues on this style of class... -public class SectionIDMap extends HashMap { +@Internal +public class SectionIDMap { + + /** + * The default section ID map. It maps section format IDs to {@link PropertyIDMap PropertyIDMaps} + */ + private static ThreadLocal> defaultMap = + new ThreadLocal>(); + /** *

The SummaryInformation's section's format ID.

*/ - public static final byte[] SUMMARY_INFORMATION_ID = new byte[] - { - (byte) 0xF2, (byte) 0x9F, (byte) 0x85, (byte) 0xE0, - (byte) 0x4F, (byte) 0xF9, (byte) 0x10, (byte) 0x68, - (byte) 0xAB, (byte) 0x91, (byte) 0x08, (byte) 0x00, - (byte) 0x2B, (byte) 0x27, (byte) 0xB3, (byte) 0xD9 - }; + public static final ClassID SUMMARY_INFORMATION_ID = + new ClassID("{F29F85E0-4FF9-1068-AB91-08002B27B3D9}"); /** - *

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

+ * The DocumentSummaryInformation's first and second sections' format ID. */ - 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) 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 - } + private static final ClassID DOC_SUMMARY_INFORMATION = + new ClassID("{D5CDD502-2E9C-101B-9397-08002B2CF9AE}"); + private static final ClassID USER_DEFINED_PROPERTIES = + new ClassID("{D5CDD505-2E9C-101B-9397-08002B2CF9AE}"); + + public static final ClassID[] DOCUMENT_SUMMARY_INFORMATION_ID = { + DOC_SUMMARY_INFORMATION, USER_DEFINED_PROPERTIES }; /** - *

A property without a known name is described by this string.

+ * A property without a known name is described by this string. */ public static final String UNDEFINED = "[undefined]"; - /** - *

The default section ID map. It maps section format IDs to - * {@link PropertyIDMap}s.

- */ - private static SectionIDMap defaultMap; - - - /** *

Returns the singleton instance of the default {@link * SectionIDMap}.

* * @return The instance value */ - public static SectionIDMap getInstance() - { - if (defaultMap == null) - { - final SectionIDMap m = new SectionIDMap(); - m.put(SUMMARY_INFORMATION_ID, - PropertyIDMap.getSummaryInformationProperties()); - m.put(DOCUMENT_SUMMARY_INFORMATION_ID[0], - PropertyIDMap.getDocumentSummaryInformationProperties()); - defaultMap = m; + public static SectionIDMap getInstance() { + Map m = defaultMap.get(); + if (m == null) { + m = new HashMap(); + m.put(SUMMARY_INFORMATION_ID, PropertyIDMap.getSummaryInformationProperties()); + m.put(DOCUMENT_SUMMARY_INFORMATION_ID[0], PropertyIDMap.getDocumentSummaryInformationProperties()); + defaultMap.set(m); } - return defaultMap; + return new SectionIDMap(); } @@ -117,16 +102,15 @@ public class SectionIDMap extends HashMap { * /sectionFormatID combination is not well-known, the * string "[undefined]" is returned. */ - public static String getPIDString(final byte[] sectionFormatID, - final long pid) - { + public static String getPIDString(ClassID sectionFormatID, long pid) { final PropertyIDMap m = getInstance().get(sectionFormatID); if (m == null) { return UNDEFINED; } final String s = (String) m.get(pid); - if (s == null) + if (s == null) { return UNDEFINED; + } return s; } @@ -139,28 +123,23 @@ public class SectionIDMap extends HashMap { * @param sectionFormatID the section format ID * @return the property ID map */ - public PropertyIDMap get(final byte[] sectionFormatID) - { - return (PropertyIDMap)super.get(new String(sectionFormatID, StringUtil.UTF8)); + public PropertyIDMap get(final ClassID sectionFormatID) { + return getInstance().get(sectionFormatID); } /** - *

Associates a section format ID with a {@link - * PropertyIDMap}.

+ * Associates a section format ID with a {@link PropertyIDMap}. * * @param sectionFormatID the section format ID * @param propertyIDMap the property ID map * @return as defined by {@link java.util.Map#put} */ - public PropertyIDMap put(final byte[] sectionFormatID, - final PropertyIDMap propertyIDMap) - { - return (PropertyIDMap)super.put(new String(sectionFormatID, StringUtil.UTF8), propertyIDMap); + public PropertyIDMap put(ClassID sectionFormatID, PropertyIDMap propertyIDMap) { + return getInstance().put(sectionFormatID, propertyIDMap); } /** - * Associates the string representation of a section - * format ID with a {@link PropertyIDMap} + * Associates the string representation of a section format ID with a {@link PropertyIDMap} * * @param key the key of the PropertyIDMap * @param value the PropertyIDMap itself @@ -168,6 +147,6 @@ public class SectionIDMap extends HashMap { * @return the previous PropertyIDMap stored under this key, or {@code null} if there wasn't one */ protected PropertyIDMap put(String key, PropertyIDMap value) { - return (PropertyIDMap)super.put(key, value); + return put(new ClassID(key), value); } } diff --git a/src/java/org/apache/poi/hssf/record/OldStringRecord.java b/src/java/org/apache/poi/hssf/record/OldStringRecord.java index 4964f84803..b28783c897 100644 --- a/src/java/org/apache/poi/hssf/record/OldStringRecord.java +++ b/src/java/org/apache/poi/hssf/record/OldStringRecord.java @@ -19,6 +19,7 @@ package org.apache.poi.hssf.record; import java.io.UnsupportedEncodingException; +import org.apache.poi.hpsf.Property; import org.apache.poi.util.CodePageUtil; @@ -73,7 +74,7 @@ public final class OldStringRecord { } protected static String getString(byte[] data, CodepageRecord codepage) { - int cp = CodePageUtil.CP_ISO_8859_1; + int cp = Property.DEFAULT_CODEPAGE; if (codepage != null) { cp = codepage.getCodepage() & 0xffff; } diff --git a/src/java/org/apache/poi/util/CodePageUtil.java b/src/java/org/apache/poi/util/CodePageUtil.java index 8d57a75df0..da8f8a9842 100644 --- a/src/java/org/apache/poi/util/CodePageUtil.java +++ b/src/java/org/apache/poi/util/CodePageUtil.java @@ -284,7 +284,7 @@ public class CodePageUtil switch (codepage) { case CP_UTF16: - return "UTF-16"; + return "UTF-16LE"; case CP_UTF16_BE: return "UTF-16BE"; case CP_UTF8: @@ -466,6 +466,7 @@ public class CodePageUtil sb.append((char)c); c = reader.read(); } + reader.close(); return sb.toString(); } } diff --git a/src/java/org/apache/poi/util/LittleEndianByteArrayInputStream.java b/src/java/org/apache/poi/util/LittleEndianByteArrayInputStream.java index 09e02c4e07..ff88e7164a 100644 --- a/src/java/org/apache/poi/util/LittleEndianByteArrayInputStream.java +++ b/src/java/org/apache/poi/util/LittleEndianByteArrayInputStream.java @@ -22,20 +22,20 @@ import java.io.ByteArrayInputStream; /** * Adapts a plain byte array to {@link LittleEndianInput} */ -public final class LittleEndianByteArrayInputStream extends ByteArrayInputStream implements LittleEndianInput { +public class LittleEndianByteArrayInputStream extends ByteArrayInputStream implements LittleEndianInput { public LittleEndianByteArrayInputStream(byte[] buf, int startOffset, int maxReadLen) { // NOSONAR super(buf, startOffset, maxReadLen); } public LittleEndianByteArrayInputStream(byte[] buf, int startOffset) { - super(buf, startOffset, buf.length - startOffset); + this(buf, startOffset, buf.length - startOffset); } public LittleEndianByteArrayInputStream(byte[] buf) { - super(buf); + this(buf, 0); } - private void checkPosition(int i) { + protected void checkPosition(int i) { if (i > count - pos) { throw new RuntimeException("Buffer overrun"); } @@ -45,6 +45,14 @@ public final class LittleEndianByteArrayInputStream extends ByteArrayInputStream return pos; } + public void setReadIndex(int pos) { + if (pos < 0 || pos >= count) { + throw new IndexOutOfBoundsException(); + } + this.pos = pos; + } + + @Override public byte readByte() { checkPosition(1); @@ -73,24 +81,28 @@ public final class LittleEndianByteArrayInputStream extends ByteArrayInputStream @Override public short readShort() { - return (short)readUShort(); - } - - @Override - public int readUByte() { - return readByte() & 0xFF; - } - - @Override - public int readUShort() { final int size = LittleEndianConsts.SHORT_SIZE; checkPosition(size); - int le = LittleEndian.getUShort(buf, pos); + short le = LittleEndian.getShort(buf, pos); long skipped = super.skip(size); assert skipped == size : "Buffer overrun"; return le; } + @Override + public int readUByte() { + return readByte() & 0x00FF; + } + + @Override + public int readUShort() { + return readShort() & 0x00FFFF; + } + + public long readUInt() { + return readInt() & 0x00FFFFFFFFL; + } + @Override public double readDouble() { return Double.longBitsToDouble(readLong()); diff --git a/src/java/org/apache/poi/util/LittleEndianCP950Reader.java b/src/java/org/apache/poi/util/LittleEndianCP950Reader.java index 1a680031bf..d3d29307c4 100644 --- a/src/java/org/apache/poi/util/LittleEndianCP950Reader.java +++ b/src/java/org/apache/poi/util/LittleEndianCP950Reader.java @@ -32,7 +32,7 @@ public class LittleEndianCP950Reader extends Reader { private static final POILogger LOGGER = POILogFactory.getLogger(LittleEndianCP950Reader.class); - private static final char UNMAPPABLE = (char) '?'; + private static final char UNMAPPABLE = '?'; private final ByteBuffer doubleByteBuffer = ByteBuffer.allocate(2); private final CharBuffer charBuffer = CharBuffer.allocate(2); private final CharsetDecoder decoder = StringUtil.BIG5.newDecoder(); @@ -131,8 +131,7 @@ public class LittleEndianCP950Reader extends Reader { } @Override - public void close() throws IOException { - + public void close() { } private int handleRange1(int leading, int trailing) { diff --git a/src/java/org/apache/poi/util/LocaleUtil.java b/src/java/org/apache/poi/util/LocaleUtil.java index 7ad6ca1483..933c7577cc 100644 --- a/src/java/org/apache/poi/util/LocaleUtil.java +++ b/src/java/org/apache/poi/util/LocaleUtil.java @@ -146,5 +146,475 @@ public final class LocaleUtil { public static Calendar getLocaleCalendar(TimeZone timeZone) { return Calendar.getInstance(timeZone, getUserLocale()); } + + /** + * Decode the language ID from LCID value + * + * @param lcid + * @return + */ + public static String getLocaleFromLCID(int lcid) { + int languageId = lcid & 0xFFFF; + switch (languageId) { + case 0x0001: return "ar"; + case 0x0002: return "bg"; + case 0x0003: return "ca"; + case 0x0004: return "zh-Hans"; + case 0x0005: return "cs"; + case 0x0006: return "da"; + case 0x0007: return "de"; + case 0x0008: return "el"; + case 0x0009: return "en"; + case 0x000a: return "es"; + case 0x000b: return "fi"; + case 0x000c: return "fr"; + case 0x000d: return "he"; + case 0x000e: return "hu"; + case 0x000f: return "is"; + case 0x0010: return "it"; + case 0x0011: return "ja"; + case 0x0012: return "ko"; + case 0x0013: return "nl"; + case 0x0014: return "no"; + case 0x0015: return "pl"; + case 0x0016: return "pt"; + case 0x0017: return "rm"; + case 0x0018: return "ro"; + case 0x0019: return "ru"; + case 0x001a: return "bs, hr, or sr"; + case 0x001b: return "sk"; + case 0x001c: return "sq"; + case 0x001d: return "sv"; + case 0x001e: return "th"; + case 0x001f: return "tr"; + case 0x0020: return "ur"; + case 0x0021: return "id"; + case 0x0022: return "uk"; + case 0x0023: return "be"; + case 0x0024: return "sl"; + case 0x0025: return "et"; + case 0x0026: return "lv"; + case 0x0027: return "lt"; + case 0x0028: return "tg"; + case 0x0029: return "fa"; + case 0x002a: return "vi"; + case 0x002b: return "hy"; + case 0x002c: return "az"; + case 0x002d: return "eu"; + case 0x002e: return "dsb or hsb"; + case 0x002f: return "mk"; + case 0x0030: return "st"; // reserved + case 0x0031: return "ts"; // reserved + case 0x0032: return "tn"; + case 0x0033: return "ve"; // reserved + case 0x0034: return "xh"; + case 0x0035: return "zu"; + case 0x0036: return "af"; + case 0x0037: return "ka"; + case 0x0038: return "fo"; + case 0x0039: return "hi"; + case 0x003a: return "mt"; + case 0x003b: return "se"; + case 0x003c: return "ga"; + case 0x003d: return "yi"; // reserved + case 0x003e: return "ms"; + case 0x003f: return "kk"; + case 0x0040: return "ky"; + case 0x0041: return "sw"; + case 0x0042: return "tk"; + case 0x0043: return "uz"; + case 0x0044: return "tt"; + case 0x0045: return "bn"; + case 0x0046: return "pa"; + case 0x0047: return "gu"; + case 0x0048: return "or"; + case 0x0049: return "ta"; + case 0x004a: return "te"; + case 0x004b: return "kn"; + case 0x004c: return "ml"; + case 0x004d: return "as"; + case 0x004e: return "mr"; + case 0x004f: return "sa"; + case 0x0050: return "mn"; + case 0x0051: return "bo"; + case 0x0052: return "cy"; + case 0x0053: return "km"; + case 0x0054: return "lo"; + case 0x0055: return "my"; // reserved + case 0x0056: return "gl"; + case 0x0057: return "kok"; + case 0x0058: return "mni"; // reserved + case 0x0059: return "sd"; + case 0x005a: return "syr"; + case 0x005b: return "si"; + case 0x005c: return "chr"; + case 0x005d: return "iu"; + case 0x005e: return "am"; + case 0x005f: return "tzm"; + case 0x0060: return "ks"; // reserved + case 0x0061: return "ne"; + case 0x0062: return "fy"; + case 0x0063: return "ps"; + case 0x0064: return "fil"; + case 0x0065: return "dv"; + case 0x0066: return "bin"; // reserved + case 0x0067: return "ff"; + case 0x0068: return "ha"; + case 0x0069: return "ibb"; // reserved + case 0x006a: return "yo"; + case 0x006b: return "quz"; + case 0x006c: return "nso"; + case 0x006d: return "ba"; + case 0x006e: return "lb"; + case 0x006f: return "kl"; + case 0x0070: return "ig"; + case 0x0071: return "kr"; // reserved + case 0x0072: return "om"; // reserved + case 0x0073: return "ti"; + case 0x0074: return "gn"; // reserved + case 0x0075: return "haw"; + case 0x0076: return "la"; // reserved + case 0x0077: return "so"; // reserved + case 0x0078: return "ii"; + case 0x0079: return "pap"; // reserved + case 0x007a: return "arn"; + case 0x007b: return "invalid"; // Neither defined nor reserved + case 0x007c: return "moh"; + case 0x007d: return "invalid"; // Neither defined nor reserved + case 0x007e: return "br"; + case 0x007f: return "invalid"; // Reserved or invariant locale behavior + case 0x0080: return "ug"; + case 0x0081: return "mi"; + case 0x0082: return "oc"; + case 0x0083: return "co"; + case 0x0084: return "gsw"; + case 0x0085: return "sah"; + case 0x0086: return "qut"; + case 0x0087: return "rw"; + case 0x0088: return "wo"; + case 0x0089: return "invalid"; // Neither defined nor reserved + case 0x008a: return "invalid"; // Neither defined nor reserved + case 0x008b: return "invalid"; // Neither defined nor reserved + case 0x008c: return "prs"; + case 0x008d: return "invalid"; // Neither defined nor reserved + case 0x008e: return "invalid"; // Neither defined nor reserved + case 0x008f: return "invalid"; // Neither defined nor reserved + case 0x0090: return "invalid"; // Neither defined nor reserved + case 0x0091: return "gd"; + case 0x0092: return "ku"; + case 0x0093: return "quc"; // reserved + case 0x0401: return "ar-SA"; + case 0x0402: return "bg-BG"; + case 0x0403: return "ca-ES"; + case 0x0404: return "zh-TW"; + case 0x0405: return "cs-CZ"; + case 0x0406: return "da-DK"; + case 0x0407: return "de-DE"; + case 0x0408: return "el-GR"; + case 0x0409: return "en-US"; + case 0x040a: return "es-ES_tradnl"; + case 0x040b: return "fi-FI"; + case 0x040c: return "fr-FR"; + case 0x040d: return "he-IL"; + case 0x040e: return "hu-HU"; + case 0x040f: return "is-IS"; + case 0x0410: return "it-IT"; + case 0x0411: return "ja-JP"; + case 0x0412: return "ko-KR"; + case 0x0413: return "nl-NL"; + case 0x0414: return "nb-NO"; + case 0x0415: return "pl-PL"; + case 0x0416: return "pt-BR"; + case 0x0417: return "rm-CH"; + case 0x0418: return "ro-RO"; + case 0x0419: return "ru-RU"; + case 0x041a: return "hr-HR"; + case 0x041b: return "sk-SK"; + case 0x041c: return "sq-AL"; + case 0x041d: return "sv-SE"; + case 0x041e: return "th-TH"; + case 0x041f: return "tr-TR"; + case 0x0420: return "ur-PK"; + case 0x0421: return "id-ID"; + case 0x0422: return "uk-UA"; + case 0x0423: return "be-BY"; + case 0x0424: return "sl-SI"; + case 0x0425: return "et-EE"; + case 0x0426: return "lv-LV"; + case 0x0427: return "lt-LT"; + case 0x0428: return "tg-Cyrl-TJ"; + case 0x0429: return "fa-IR"; + case 0x042a: return "vi-VN"; + case 0x042b: return "hy-AM"; + case 0x042c: return "az-Latn-AZ"; + case 0x042d: return "eu-ES"; + case 0x042e: return "hsb-DE"; + case 0x042f: return "mk-MK"; + case 0x0430: return "st-ZA"; // reserved + case 0x0431: return "ts-ZA"; // reserved + case 0x0432: return "tn-ZA"; + case 0x0433: return "ve-ZA"; // reserved + case 0x0434: return "xh-ZA"; + case 0x0435: return "zu-ZA"; + case 0x0436: return "af-ZA"; + case 0x0437: return "ka-GE"; + case 0x0438: return "fo-FO"; + case 0x0439: return "hi-IN"; + case 0x043a: return "mt-MT"; + case 0x043b: return "se-NO"; + case 0x043d: return "yi-Hebr"; // reserved + case 0x043e: return "ms-MY"; + case 0x043f: return "kk-KZ"; + case 0x0440: return "ky-KG"; + case 0x0441: return "sw-KE"; + case 0x0442: return "tk-TM"; + case 0x0443: return "uz-Latn-UZ"; + case 0x0444: return "tt-RU"; + case 0x0445: return "bn-IN"; + case 0x0446: return "pa-IN"; + case 0x0447: return "gu-IN"; + case 0x0448: return "or-IN"; + case 0x0449: return "ta-IN"; + case 0x044a: return "te-IN"; + case 0x044b: return "kn-IN"; + case 0x044c: return "ml-IN"; + case 0x044d: return "as-IN"; + case 0x044e: return "mr-IN"; + case 0x044f: return "sa-IN"; + case 0x0450: return "mn-MN"; + case 0x0451: return "bo-CN"; + case 0x0452: return "cy-GB"; + case 0x0453: return "km-KH"; + case 0x0454: return "lo-LA"; + case 0x0455: return "my-MM"; // reserved + case 0x0456: return "gl-ES"; + case 0x0457: return "kok-IN"; + case 0x0458: return "mni-IN"; // reserved + case 0x0459: return "sd-Deva-IN"; // reserved + case 0x045a: return "syr-SY"; + case 0x045b: return "si-LK"; + case 0x045c: return "chr-Cher-US"; + case 0x045d: return "iu-Cans-CA"; + case 0x045e: return "am-ET"; + case 0x045f: return "tzm-Arab-MA"; // reserved + case 0x0460: return "ks-Arab"; // reserved + case 0x0461: return "ne-NP"; + case 0x0462: return "fy-NL"; + case 0x0463: return "ps-AF"; + case 0x0464: return "fil-PH"; + case 0x0465: return "dv-MV"; + case 0x0466: return "bin-NG"; // reserved + case 0x0467: return "fuv-NG"; // reserved + case 0x0468: return "ha-Latn-NG"; + case 0x0469: return "ibb-NG"; // reserved + case 0x046a: return "yo-NG"; + case 0x046b: return "quz-BO"; + case 0x046c: return "nso-ZA"; + case 0x046d: return "ba-RU"; + case 0x046e: return "lb-LU"; + case 0x046f: return "kl-GL"; + case 0x0470: return "ig-NG"; + case 0x0471: return "kr-NG"; // reserved + case 0x0472: return "om-Ethi-ET"; // reserved + case 0x0473: return "ti-ET"; + case 0x0474: return "gn-PY"; // reserved + case 0x0475: return "haw-US"; + case 0x0476: return "la-Latn"; // reserved + case 0x0477: return "so-SO"; // reserved + case 0x0478: return "ii-CN"; + case 0x0479: return "pap-x029"; // reserved + case 0x047a: return "arn-CL"; + case 0x047c: return "moh-CA"; + case 0x047e: return "br-FR"; + case 0x0480: return "ug-CN"; + case 0x0481: return "mi-NZ"; + case 0x0482: return "oc-FR"; + case 0x0483: return "co-FR"; + case 0x0484: return "gsw-FR"; + case 0x0485: return "sah-RU"; + case 0x0486: return "qut-GT"; + case 0x0487: return "rw-RW"; + case 0x0488: return "wo-SN"; + case 0x048c: return "prs-AF"; + case 0x048d: return "plt-MG"; // reserved + case 0x048e: return "zh-yue-HK"; // reserved + case 0x048f: return "tdd-Tale-CN"; // reserved + case 0x0490: return "khb-Talu-CN"; // reserved + case 0x0491: return "gd-GB"; + case 0x0492: return "ku-Arab-IQ"; + case 0x0493: return "quc-CO"; // reserved + case 0x0501: return "qps-ploc"; + case 0x05fe: return "qps-ploca"; + case 0x0801: return "ar-IQ"; + case 0x0803: return "ca-ES-valencia"; + case 0x0804: return "zh-CN"; + case 0x0807: return "de-CH"; + case 0x0809: return "en-GB"; + case 0x080a: return "es-MX"; + case 0x080c: return "fr-BE"; + case 0x0810: return "it-CH"; + case 0x0811: return "ja-Ploc-JP"; // reserved + case 0x0813: return "nl-BE"; + case 0x0814: return "nn-NO"; + case 0x0816: return "pt-PT"; + case 0x0818: return "ro-MO"; // reserved + case 0x0819: return "ru-MO"; // reserved + case 0x081a: return "sr-Latn-CS"; + case 0x081d: return "sv-FI"; + case 0x0820: return "ur-IN"; // reserved + case 0x0827: return "invalid"; // Neither defined nor reserved + case 0x082c: return "az-Cyrl-AZ"; + case 0x082e: return "dsb-DE"; + case 0x0832: return "tn-BW"; + case 0x083b: return "se-SE"; + case 0x083c: return "ga-IE"; + case 0x083e: return "ms-BN"; + case 0x0843: return "uz-Cyrl-UZ"; + case 0x0845: return "bn-BD"; + case 0x0846: return "pa-Arab-PK"; + case 0x0849: return "ta-LK"; + case 0x0850: return "mn-Mong-CN"; + case 0x0851: return "bo-BT"; // reserved + case 0x0859: return "sd-Arab-PK"; + case 0x085d: return "iu-Latn-CA"; + case 0x085f: return "tzm-Latn-DZ"; + case 0x0860: return "ks-Deva"; // reserved + case 0x0861: return "ne-IN"; // reserved + case 0x0867: return "ff-Latn-SN"; + case 0x086b: return "quz-EC"; + case 0x0873: return "ti-ER"; + case 0x09ff: return "qps-plocm"; + case 0x0c01: return "ar-EG"; + case 0x0c04: return "zh-HK"; + case 0x0c07: return "de-AT"; + case 0x0c09: return "en-AU"; + case 0x0c0a: return "es-ES"; + case 0x0c0c: return "fr-CA"; + case 0x0c1a: return "sr-Cyrl-CS"; + case 0x0c3b: return "se-FI"; + case 0x0c5f: return "tmz-MA"; // reserved + case 0x0c6b: return "quz-PE"; + case 0x1001: return "ar-LY"; + case 0x1004: return "zh-SG"; + case 0x1007: return "de-LU"; + case 0x1009: return "en-CA"; + case 0x100a: return "es-GT"; + case 0x100c: return "fr-CH"; + case 0x101a: return "hr-BA"; + case 0x103b: return "smj-NO"; + case 0x1401: return "ar-DZ"; + case 0x1404: return "zh-MO"; + case 0x1407: return "de-LI"; + case 0x1409: return "en-NZ"; + case 0x140a: return "es-CR"; + case 0x140c: return "fr-LU"; + case 0x141a: return "bs-Latn-BA"; + case 0x143b: return "smj-SE"; + case 0x1801: return "ar-MA"; + case 0x1809: return "en-IE"; + case 0x180a: return "es-PA"; + case 0x180c: return "fr-MC"; + case 0x181a: return "sr-Latn-BA"; + case 0x183b: return "sma-NO"; + case 0x1c01: return "ar-TN"; + case 0x1c09: return "en-ZA"; + case 0x1c0a: return "es-DO"; + case 0x1c0c: return "invalid"; // Neither defined nor reserved + case 0x1c1a: return "sr-Cyrl-BA"; + case 0x1c3b: return "sma-SE"; + case 0x2001: return "ar-OM"; + case 0x2008: return "invalid"; // Neither defined nor reserved + case 0x2009: return "en-JM"; + case 0x200a: return "es-VE"; + case 0x200c: return "fr-RE"; // reserved + case 0x201a: return "bs-Cyrl-BA"; + case 0x203b: return "sms-FI"; + case 0x2401: return "ar-YE"; + case 0x2409: return "en-029"; + case 0x240a: return "es-CO"; + case 0x240c: return "fr-CG"; // reserved + case 0x241a: return "sr-Latn-RS"; + case 0x243b: return "smn-FI"; + case 0x2801: return "ar-SY"; + case 0x2809: return "en-BZ"; + case 0x280a: return "es-PE"; + case 0x280c: return "fr-SN"; // reserved + case 0x281a: return "sr-Cyrl-RS"; + case 0x2c01: return "ar-JO"; + case 0x2c09: return "en-TT"; + case 0x2c0a: return "es-AR"; + case 0x2c0c: return "fr-CM"; // reserved + case 0x2c1a: return "sr-Latn-ME"; + case 0x3001: return "ar-LB"; + case 0x3009: return "en-ZW"; + case 0x300a: return "es-EC"; + case 0x300c: return "fr-CI"; // reserved + case 0x301a: return "sr-Cyrl-ME"; + case 0x3401: return "ar-KW"; + case 0x3409: return "en-PH"; + case 0x340a: return "es-CL"; + case 0x340c: return "fr-ML"; // reserved + case 0x3801: return "ar-AE"; + case 0x3809: return "en-ID"; // reserved + case 0x380a: return "es-UY"; + case 0x380c: return "fr-MA"; // reserved + case 0x3c01: return "ar-BH"; + case 0x3c09: return "en-HK"; // reserved + case 0x3c0a: return "es-PY"; + case 0x3c0c: return "fr-HT"; // reserved + case 0x4001: return "ar-QA"; + case 0x4009: return "en-IN"; + case 0x400a: return "es-BO"; + case 0x4401: return "ar-Ploc-SA"; // reseærved + case 0x4409: return "en-MY"; + case 0x440a: return "es-SV"; + case 0x4801: return "ar-145"; // reserved + case 0x4809: return "en-SG"; + case 0x480a: return "es-HN"; + case 0x4c09: return "en-AE"; // reserved + case 0x4c0a: return "es-NI"; + case 0x5009: return "en-BH"; // reserved + case 0x500a: return "es-PR"; + case 0x5409: return "en-EG"; // reserved + case 0x540a: return "es-US"; + case 0x5809: return "en-JO"; // reserved + case 0x5c09: return "en-KW"; // reserved + case 0x6009: return "en-TR"; // reserved + case 0x6409: return "en-YE"; // reserved + case 0x641a: return "bs-Cyrl"; + case 0x681a: return "bs-Latn"; + case 0x6c1a: return "sr-Cyrl"; + case 0x701a: return "sr-Latn"; + case 0x703b: return "smn"; + case 0x742c: return "az-Cyrl"; + case 0x743b: return "sms"; + case 0x7804: return "zh"; + case 0x7814: return "nn"; + case 0x781a: return "bs"; + case 0x782c: return "az-Latn"; + case 0x783b: return "sma"; + case 0x7843: return "uz-Cyrl"; + case 0x7850: return "mn-Cyrl"; + case 0x785d: return "iu-Cans"; + case 0x7c04: return "zh-Hant"; + case 0x7c14: return "nb"; + case 0x7c1a: return "sr"; + case 0x7c28: return "tg-Cyrl"; + case 0x7c2e: return "dsb"; + case 0x7c3b: return "smj"; + case 0x7c43: return "uz-Latn"; + case 0x7c46: return "pa-Arab"; + case 0x7c50: return "mn-Mong"; + case 0x7c59: return "sd-Arab"; + case 0x7c5c: return "chr-Cher"; + case 0x7c5d: return "iu-Latn"; + case 0x7c5f: return "tzm-Latn"; + case 0x7c67: return "ff-Latn"; + case 0x7c68: return "ha-Latn"; + case 0x7c92: return "ku-Arab"; + default: return "invalid"; + } + } + } diff --git a/src/testcases/org/apache/poi/hpsf/basic/TestBasic.java b/src/testcases/org/apache/poi/hpsf/basic/TestBasic.java index b40e7a463b..a017ecc797 100644 --- a/src/testcases/org/apache/poi/hpsf/basic/TestBasic.java +++ b/src/testcases/org/apache/poi/hpsf/basic/TestBasic.java @@ -192,7 +192,7 @@ public final class TestBasic { (poiFiles[0].getBytes())); final List
sections = si.getSections(); final Section s = sections.get(0); - assertArrayEquals(s.getFormatID().getBytes(), SectionIDMap.SUMMARY_INFORMATION_ID); + assertEquals(s.getFormatID(), SectionIDMap.SUMMARY_INFORMATION_ID); assertNotNull(s.getProperties()); assertEquals(17, s.getPropertyCount()); assertEquals("Titel", s.getProperty(2)); diff --git a/src/testcases/org/apache/poi/hpsf/basic/TestMetaDataIPI.java b/src/testcases/org/apache/poi/hpsf/basic/TestMetaDataIPI.java index 3cb277f5de..ebcf7008ba 100644 --- a/src/testcases/org/apache/poi/hpsf/basic/TestMetaDataIPI.java +++ b/src/testcases/org/apache/poi/hpsf/basic/TestMetaDataIPI.java @@ -17,135 +17,62 @@ package org.apache.poi.hpsf.basic; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + 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.*; +import org.apache.poi.hpsf.CustomProperties; +import org.apache.poi.hpsf.DocumentSummaryInformation; +import org.apache.poi.hpsf.HPSFException; +import org.apache.poi.hpsf.PropertySetFactory; +import org.apache.poi.hpsf.SummaryInformation; 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; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; /** * 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 virtual document in a ByteArrayOutputStream - * @author Matthias G\u00fcnter */ -public final class TestMetaDataIPI extends TestCase{ +public final class TestMetaDataIPI { - private ByteArrayOutputStream bout; //our store - private POIFSFileSystem poifs; - private DirectoryEntry dir; + private POIFSFileSystem poifs ; private DocumentSummaryInformation dsi; private SummaryInformation si; - - + @After + public void tearDown() throws Exception { + poifs.close(); + } + /** * Setup is used to get the document ready. Gets the DocumentSummaryInformation and the * SummaryInformation to reasonable values */ - @Override + @Before public void setUp() throws Exception { - 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); - } - 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); - } - assertNotNull(dsi); - } - - /** - * 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() throws IOException, HPSFException { - - dsi.write(dir, DocumentSummaryInformation.DEFAULT_STREAM_NAME); - si.write(dir, SummaryInformation.DEFAULT_STREAM_NAME); - - si = null; - dsi = null; - poifs.writeFilesystem(bout); - bout.flush(); - - InputStream is = new ByteArrayInputStream(bout.toByteArray()); - assertNotNull(is); - POIFSFileSystem poifs = new POIFSFileSystem(is); - is.close(); - - assertNotNull(poifs); - /* Read the document summary information. */ - DirectoryEntry dir = poifs.getRoot(); - - 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); - - try { - dsiEntry = (DocumentEntry) dir - .getEntry(SummaryInformation.DEFAULT_STREAM_NAME); - dis = new DocumentInputStream(dsiEntry); - 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); - } + poifs = new POIFSFileSystem(); + dsi = PropertySetFactory.newDocumentSummaryInformation(); + si = PropertySetFactory.newSummaryInformation(); + dsi.write(poifs.getRoot(), DocumentSummaryInformation.DEFAULT_STREAM_NAME); + si.write(poifs.getRoot(), SummaryInformation.DEFAULT_STREAM_NAME); } /** * Sets the most important information in DocumentSummaryInformation and Summary Information and rereads it */ + @Test public void testOne() throws Exception { // DocumentSummaryInformation @@ -201,9 +128,7 @@ public final class TestMetaDataIPI extends TestCase{ * serve as a container for custom properties. */ customProperties = dsi.getCustomProperties(); - if (customProperties == null) { - fail(); - } + assertNotNull(customProperties); /* Insert some custom properties into the container. */ String a1 = (String) customProperties.get("Key1"); @@ -224,23 +149,10 @@ public final class TestMetaDataIPI extends TestCase{ assertEquals("Neg", new Integer(-100000), a7); } - /** - * multiplies a string - * @param s Input String - * @return the multiplied String - */ - private static String elongate(String s) { - StringBuilder sb = new StringBuilder(); - 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) */ + @Test public void testTwo() throws Exception { String company = elongate("company"); @@ -323,21 +235,10 @@ public final class TestMetaDataIPI extends TestCase{ } - /** - * adds strange characters to the string - * @param s Input String - * @return the multiplied String - */ - private static String strangize(String s) { - StringBuilder sb = strangizeInit(s); - - return sb.toString(); - } - - /** * Tests with strange characters in keys and data (Umlaute etc.) */ + @Test public void testThree() throws Exception { String company = strangize("company"); @@ -423,50 +324,18 @@ public final class TestMetaDataIPI extends TestCase{ /** * Iterative testing: writing, reading etc. */ + @Test public void testFour() throws Exception { for (int i = 1; i < 100; i++) { - setUp(); - testThree(); + testThree(); + closeAndReOpen(); } } - - - /** - * adds strange characters to the string with the adding of unicode characters - * @param s Input String - * @return the multiplied String - */ - private static String strangizeU(String s) { - - StringBuilder sb = strangizeInit(s); - sb.append("\u00e4\u00f6\u00fc\uD840\uDC00"); - return sb.toString(); - } - - private static StringBuilder strangizeInit(String s) { - StringBuilder sb = new StringBuilder(); - String[] umlaute = { "\u00e4", "\u00fc", "\u00f6", "\u00dc", "$", "\u00d6", "\u00dc", - "\u00c9", "\u00d6", "@", "\u00e7", "&" }; - Random rand = new Random(0); // TODO - no Random - tests should be completely deterministic - for (int i = 0; i < 5; i++) { - sb.append(s); - sb.append(" "); - char j = (char) rand.nextInt(220); - j += 33; - // System.out.println(j); - sb.append(">"); - sb.append(Character.valueOf(j)); - sb.append("="); - sb.append(umlaute[rand.nextInt(umlaute.length)]); - sb.append("<"); - } - return sb; - } - /** * Unicode test */ + @Test public void testUnicode() throws Exception { String company = strangizeU("company"); String manager = strangizeU("manager"); @@ -490,11 +359,8 @@ public final class TestMetaDataIPI extends TestCase{ si.setComments(comments); si.setKeywords(keywords); si.setSubject(subject); - CustomProperties customProperties = dsi.getCustomProperties(); - if (customProperties == null) { - customProperties = new CustomProperties(); - } - + + CustomProperties customProperties = new CustomProperties(); /* Insert some custom properties into the container. */ customProperties.put(k1, p1); customProperties.put(k2, p2); @@ -529,9 +395,7 @@ public final class TestMetaDataIPI extends TestCase{ * serve as a container for custom properties. */ customProperties = dsi.getCustomProperties(); - if (customProperties == null) { - fail(); - } + assertNotNull(customProperties); /* Insert some custom properties into the container. */ // System.out.println(k1); @@ -552,10 +416,11 @@ public final class TestMetaDataIPI extends TestCase{ * Iterative testing of the unicode test * */ + @Test public void testSix() throws Exception { for (int i = 1; i < 100; i++) { - setUp(); - testUnicode(); + testUnicode(); + closeAndReOpen(); } } @@ -563,6 +428,7 @@ public final class TestMetaDataIPI extends TestCase{ /** * Tests conversion in custom fields and errors */ + @Test public void testConvAndExistence() throws Exception { CustomProperties customProperties = dsi.getCustomProperties(); @@ -656,4 +522,86 @@ public final class TestMetaDataIPI extends TestCase{ assertTrue(customProperties.get("negdouble") instanceof Double); assertTrue(customProperties.get("date") instanceof Date); } + + + /** + * 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. + */ + private void closeAndReOpen() throws IOException, HPSFException { + dsi.write(poifs.getRoot(), DocumentSummaryInformation.DEFAULT_STREAM_NAME); + si.write(poifs.getRoot(), SummaryInformation.DEFAULT_STREAM_NAME); + + ByteArrayOutputStream bout = new ByteArrayOutputStream(); + poifs.writeFilesystem(bout); + poifs.close(); + + InputStream is = new ByteArrayInputStream(bout.toByteArray()); + poifs = new POIFSFileSystem(is); + is.close(); + + /* Read the document summary information. */ + DirectoryEntry dir = poifs.getRoot(); + + dsi = (DocumentSummaryInformation)PropertySetFactory.create(dir, DocumentSummaryInformation.DEFAULT_STREAM_NAME);; + si = (SummaryInformation)PropertySetFactory.create(dir, SummaryInformation.DEFAULT_STREAM_NAME);; + } + + /** + * multiplies a string + * @param s Input String + * @return the multiplied String + */ + private static String elongate(String s) { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < 10000; i++) { + sb.append(s); + sb.append(" "); + } + return sb.toString(); + } + + /** + * adds strange characters to the string + * @param s Input String + * @return the multiplied String + */ + private static String strangize(String s) { + StringBuilder sb = strangizeInit(s); + + return sb.toString(); + } + + /** + * adds strange characters to the string with the adding of unicode characters + * @param s Input String + * @return the multiplied String + */ + private static String strangizeU(String s) { + + StringBuilder sb = strangizeInit(s); + sb.append("\u00e4\u00f6\u00fc\uD840\uDC00"); + return sb.toString(); + } + + private static StringBuilder strangizeInit(String s) { + StringBuilder sb = new StringBuilder(); + String[] umlaute = { "\u00e4", "\u00fc", "\u00f6", "\u00dc", "$", "\u00d6", "\u00dc", + "\u00c9", "\u00d6", "@", "\u00e7", "&" }; + Random rand = new Random(0); // TODO - no Random - tests should be completely deterministic + for (int i = 0; i < 5; i++) { + sb.append(s); + sb.append(" "); + char j = (char) rand.nextInt(220); + j += 33; + // System.out.println(j); + sb.append(">"); + sb.append(Character.valueOf(j)); + sb.append("="); + sb.append(umlaute[rand.nextInt(umlaute.length)]); + sb.append("<"); + } + return sb; + } } diff --git a/src/testcases/org/apache/poi/hpsf/basic/TestReadAllFiles.java b/src/testcases/org/apache/poi/hpsf/basic/TestReadAllFiles.java index b419ad1da6..0df2f56cb2 100644 --- a/src/testcases/org/apache/poi/hpsf/basic/TestReadAllFiles.java +++ b/src/testcases/org/apache/poi/hpsf/basic/TestReadAllFiles.java @@ -17,77 +17,214 @@ package org.apache.poi.hpsf.basic; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileFilter; import java.io.IOException; import java.io.InputStream; - -import junit.framework.TestCase; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import org.apache.poi.POIDataSamples; +import org.apache.poi.hpsf.CustomProperties; +import org.apache.poi.hpsf.CustomProperty; +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.poifs.filesystem.DirectoryEntry; +import org.apache.poi.poifs.filesystem.NPOIFSFileSystem; +import org.apache.poi.poifs.filesystem.POIFSFileSystem; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameter; +import org.junit.runners.Parameterized.Parameters; /** - *

Tests some HPSF functionality by reading all property sets from all files + * Tests some HPSF functionality by reading all property sets from all files * in the "data" directory. If you want to ensure HPSF can deal with a certain - * OLE2 file, just add it to the "data" directory and run this test case.

+ * OLE2 file, just add it to the "data" directory and run this test case. */ -public class TestReadAllFiles extends TestCase { - private static String[] excludes = new String[] {}; - - /** - *

This test methods reads all property set streams from all POI - * filesystems in the "data" directory.

- * - * @throws IOException - * @throws HPSFException - */ - public void testReadAllFiles() throws IOException, HPSFException - { - POIDataSamples _samples = POIDataSamples.getHPSFInstance(); - final File dataDir = _samples.getFile(""); - final File[] fileList = dataDir.listFiles(new FileFilter() - { - @Override - public boolean accept(final File f) - { - // exclude files that we know will fail - return f.isFile() && checkExclude(f); +@RunWith(Parameterized.class) +public class TestReadAllFiles { + private static final POIDataSamples _samples = POIDataSamples.getHPSFInstance(); + + @Parameters(name="{index}: {0} using {1}") + public static Iterable files() { + final List files = new ArrayList(); + + _samples.getFile("").listFiles(new FileFilter() { + @Override + public boolean accept(final File f) { + if (f.getName().startsWith("Test")) { // && f.getName().equals("TestCorel.shw") + files.add(new Object[]{ f }); } - }); - - for (final File f : fileList) { - /* Read the POI filesystem's property set streams: */ - final POIFile[] psf1 = Util.readPropertySets(f); - - for (int j = 0; j < psf1.length; j++) - { - final InputStream in = - new ByteArrayInputStream(psf1[j].getBytes()); - try { - PropertySetFactory.create(in); - } catch (Exception e) { - throw new IOException("While handling file: " + f + " at " + j, e); - } - } - } - } - - /** - * Returns true if the file should be checked, false if it should be excluded. - * - * @param f - * @return - */ - public static boolean checkExclude(File f) { - for(String exclude : excludes) { - if(f.getAbsolutePath().endsWith(exclude)) { return false; } - } + }); - return true; + return files; } + + @Parameter(value=0) + public File file; + + /** + * This test methods reads all property set streams from all POI + * filesystems in the "data" directory. + */ + @Test + public void read() throws IOException, NoPropertySetStreamException, MarkUnsupportedException { + /* Read the POI filesystem's property set streams: */ + for (POIFile pf : Util.readPropertySets(file)) { + final InputStream in = new ByteArrayInputStream(pf.getBytes()); + try { + PropertySetFactory.create(in); + } finally { + in.close(); + } + } + } + + + /** + * This test method does a write and read back test with all POI + * filesystems in the "data" directory by performing the following + * actions for each file:

+ * + *

    + *
  • Read its property set streams. + *
  • Create a new POI filesystem containing the origin file's property set streams. + *
  • Read the property set streams from the POI filesystem just created. + *
  • Compare each property set stream with the corresponding one from + * the origin file and check whether they are equal. + *
+ */ + @Test + public void recreate() throws IOException, HPSFException { + /* Read the POI filesystem's property set streams: */ + Map psMap = new HashMap(); + + /* Create a new POI filesystem containing the origin file's + * property set streams: */ + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + final POIFSFileSystem poiFs = new POIFSFileSystem(); + for (POIFile poifile : Util.readPropertySets(file)) { + final InputStream in = new ByteArrayInputStream(poifile.getBytes()); + final PropertySet psIn = PropertySetFactory.create(in); + psMap.put(poifile.getName(), psIn); + bos.reset(); + psIn.write(bos); + poiFs.createDocument(new ByteArrayInputStream(bos.toByteArray()), poifile.getName()); + } + + /* Read the property set streams from the POI filesystem just + * created. */ + for (Map.Entry me : psMap.entrySet()) { + final PropertySet ps1 = me.getValue(); + final PropertySet ps2 = PropertySetFactory.create(poiFs.getRoot(), me.getKey()); + assertNotNull(ps2); + + /* Compare the property set stream with the corresponding one + * from the origin file and check whether they are equal. */ + + // Because of missing 0-paddings in the original input files, the bytes might differ. + // This fixes the comparison + String ps1str = ps1.toString().replace(" 00", " ").replace(".", " ").replaceAll("(?m)( +$|(size|offset): [0-9]+)",""); + String ps2str = ps2.toString().replace(" 00", " ").replace(".", " ").replaceAll("(?m)( +$|(size|offset): [0-9]+)",""); + + assertEquals("Equality for file " + file.getName(), ps1str, ps2str); + } + poiFs.close(); + } + + /** + *

This test method checks whether DocumentSummary information streams + * can be read. This is done by opening all "Test*" files in the 'poifs' directrory + * pointed to by the "POI.testdata.path" system property, trying to extract + * the document summary information stream in the root directory and calling + * its get... methods.

+ * @throws Exception + */ + @Test + public void readDocumentSummaryInformation() throws Exception { + /* Read a test document doc into a POI filesystem. */ + NPOIFSFileSystem poifs = new NPOIFSFileSystem(file, true); + try { + final DirectoryEntry dir = poifs.getRoot(); + /* + * If there is a document summry information stream, read it from + * the POI filesystem. + */ + if (dir.hasEntry(DocumentSummaryInformation.DEFAULT_STREAM_NAME)) { + final DocumentSummaryInformation dsi = TestWriteWellKnown.getDocumentSummaryInformation(poifs); + + /* 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(); + } + } finally { + poifs.close(); + } + } + + /** + *

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

+ * + * @throws Throwable if anything goes wrong. + */ + @Test + public void readCustomPropertiesFromFiles() throws Exception { + /* Read a test document doc into a POI filesystem. */ + NPOIFSFileSystem poifs = new NPOIFSFileSystem(file); + try { + /* + * If there is a document summry information stream, read it from + * the POI filesystem, else create a new one. + */ + DocumentSummaryInformation dsi = TestWriteWellKnown.getDocumentSummaryInformation(poifs); + if (dsi == null) { + dsi = PropertySetFactory.newDocumentSummaryInformation(); + } + final CustomProperties cps = dsi.getCustomProperties(); + + if (cps == null) { + /* The document does not have custom properties. */ + return; + } + + for (CustomProperty cp : cps.values()) { + cp.getName(); + cp.getValue(); + } + } finally { + poifs.close(); + } + } + } diff --git a/src/testcases/org/apache/poi/hpsf/basic/TestWrite.java b/src/testcases/org/apache/poi/hpsf/basic/TestWrite.java index 20f7d8fae6..43737a3b04 100644 --- a/src/testcases/org/apache/poi/hpsf/basic/TestWrite.java +++ b/src/testcases/org/apache/poi/hpsf/basic/TestWrite.java @@ -18,6 +18,7 @@ package org.apache.poi.hpsf.basic; import static org.hamcrest.core.IsEqual.equalTo; +import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertThat; @@ -27,7 +28,6 @@ import static org.junit.Assert.fail; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; -import java.io.FileFilter; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; @@ -37,6 +37,7 @@ import java.io.UnsupportedEncodingException; import java.nio.charset.Charset; import java.util.Date; import java.util.HashMap; +import java.util.List; import java.util.Locale; import java.util.Map; @@ -73,28 +74,29 @@ import org.apache.poi.poifs.filesystem.NPOIFSFileSystem; import org.apache.poi.poifs.filesystem.POIFSFileSystem; import org.apache.poi.util.CodePageUtil; import org.apache.poi.util.IOUtils; -import org.apache.poi.util.LittleEndian; +import org.apache.poi.util.LittleEndianConsts; import org.apache.poi.util.TempFile; -import org.junit.Before; +import org.junit.Assume; +import org.junit.BeforeClass; import org.junit.Test; /** - *

Tests HPSF's writing functionality.

+ * Tests HPSF's writing functionality */ -public class TestWrite -{ +public class TestWrite { private static final POIDataSamples _samples = POIDataSamples.getHPSFInstance(); + private static final int CODEPAGE_DEFAULT = -1; - static final String POI_FS = "TestHPSFWritingFunctionality.doc"; + private static final String POI_FS = "TestHPSFWritingFunctionality.doc"; - static final int BYTE_ORDER = 0xfffe; - static final int FORMAT = 0x0000; - static final int OS_VERSION = 0x00020A04; - static final int[] SECTION_COUNT = {1, 2}; - static final boolean[] IS_SUMMARY_INFORMATION = {true, false}; - static final boolean[] IS_DOCUMENT_SUMMARY_INFORMATION = {false, true}; + private static final int BYTE_ORDER = 0xfffe; + private static final int FORMAT = 0x0000; + private static final int OS_VERSION = 0x00020A04; + private static final int[] SECTION_COUNT = {1, 2}; + private static final boolean[] IS_SUMMARY_INFORMATION = {true, false}; + private static final boolean[] IS_DOCUMENT_SUMMARY_INFORMATION = {false, true}; - final String IMPROPER_DEFAULT_CHARSET_MESSAGE = + private static final String IMPROPER_DEFAULT_CHARSET_MESSAGE = "Your default character set is " + getDefaultCharsetName() + ". However, this testcase must be run in an environment " + "with a default character set supporting at least " + @@ -104,12 +106,11 @@ public class TestWrite POIFile[] poiFiles; - @Before - public void setUp() - { + @BeforeClass + public static void setUp() { VariantSupport.setLogUnsupportedTypes(false); } - + /** *

Writes an empty property set to a POIFS and reads it back * in.

@@ -117,8 +118,7 @@ public class TestWrite * @exception IOException if an I/O exception occurs */ @Test(expected=NoFormatIDException.class) - public void withoutAFormatID() throws Exception - { + public void withoutAFormatID() throws Exception { final File filename = TempFile.createTempFile(POI_FS, ".doc"); /* Create a mutable property set with a section that does not have the @@ -144,8 +144,6 @@ public class TestWrite } } - - /** *

Writes an empty property set to a POIFS and reads it back * in.

@@ -156,8 +154,7 @@ public class TestWrite */ @Test public void writeEmptyPropertySet() - throws IOException, UnsupportedVariantTypeException - { + throws IOException, UnsupportedVariantTypeException { final File dataDir = _samples.getFile(""); final File filename = new File(dataDir, POI_FS); filename.deleteOnExit(); @@ -203,8 +200,7 @@ public class TestWrite */ @Test public void writeSimplePropertySet() - throws IOException, UnsupportedVariantTypeException - { + throws IOException, UnsupportedVariantTypeException { final String AUTHOR = "Rainer Klute"; final String TITLE = "Test Document"; final File dataDir = _samples.getFile(""); @@ -235,24 +231,17 @@ public class TestWrite /* Read the POIFS: */ final PropertySet[] psa = new PropertySet[1]; final POIFSReader r = new POIFSReader(); - r.registerListener(new POIFSReaderListener() - { - @Override - public void processPOIFSReaderEvent - (final POIFSReaderEvent event) - { - try - { - psa[0] = PropertySetFactory.create(event.getStream()); - } - catch (Exception ex) - { - fail(org.apache.poi.hpsf.Util.toString(ex)); - } + r.registerListener(new POIFSReaderListener() { + @Override + public void processPOIFSReaderEvent(final POIFSReaderEvent event) { + try { + psa[0] = PropertySetFactory.create(event.getStream()); + } catch (Exception ex) { + fail(org.apache.poi.hpsf.Util.toString(ex)); } - - }, - SummaryInformation.DEFAULT_STREAM_NAME); + }}, + SummaryInformation.DEFAULT_STREAM_NAME + ); InputStream stream = new FileInputStream(filename); try { @@ -281,9 +270,7 @@ public class TestWrite * a variant type to be written */ @Test - public void writeTwoSections() - throws WritingNotSupportedException, IOException - { + public void writeTwoSections() throws WritingNotSupportedException, IOException { final String STREAM_NAME = "PropertySetStream"; final String SECTION1 = "Section 1"; final String SECTION2 = "Section 2"; @@ -318,18 +305,12 @@ public class TestWrite /* Read the POIFS: */ final PropertySet[] psa = new PropertySet[1]; final POIFSReader r = new POIFSReader(); - r.registerListener(new POIFSReaderListener() - { + r.registerListener(new POIFSReaderListener() { @Override - public void processPOIFSReaderEvent - (final POIFSReaderEvent event) - { - try - { + public void processPOIFSReaderEvent(final POIFSReaderEvent event) { + try { psa[0] = PropertySetFactory.create(event.getStream()); - } - catch (Exception ex) - { + } catch (Exception ex) { throw new RuntimeException(ex); } } @@ -353,17 +334,12 @@ public class TestWrite - static class MyPOIFSReaderListener implements POIFSReaderListener - { + static class MyPOIFSReaderListener implements POIFSReaderListener { @Override - public void processPOIFSReaderEvent(final POIFSReaderEvent event) - { - try - { + public void processPOIFSReaderEvent(final POIFSReaderEvent event) { + try { PropertySetFactory.create(event.getStream()); - } - catch (Exception ex) - { + } catch (Exception ex) { fail(org.apache.poi.hpsf.Util.toString(ex)); } } @@ -371,109 +347,52 @@ public class TestWrite - private static final int CODEPAGE_DEFAULT = -1; - private static final int CODEPAGE_1252 = 1252; - private static final int CODEPAGE_UTF8 = CodePageUtil.CP_UTF8; - private static final int CODEPAGE_UTF16 = CodePageUtil.CP_UTF16; - - - /** *

Writes and reads back various variant types and checks whether the * stuff that has been read back equals the stuff that was written.

+ * @throws IOException + * @throws UnsupportedEncodingException + * @throws UnsupportedVariantTypeException + * @throws ReadingNotSupportedException */ @Test - public void variantTypes() - { - Throwable t = null; + public void variantTypes() throws Exception { final int codepage = CODEPAGE_DEFAULT; - if (!hasProperDefaultCharset()) - { - System.err.println(IMPROPER_DEFAULT_CHARSET_MESSAGE + - " This testcase is skipped."); - return; - } + Assume.assumeTrue(IMPROPER_DEFAULT_CHARSET_MESSAGE, hasProperDefaultCharset()); - try - { - check(Variant.VT_EMPTY, null, codepage); - check(Variant.VT_BOOL, Boolean.TRUE, codepage); - check(Variant.VT_BOOL, Boolean.FALSE, codepage); - check( Variant.VT_CF, new byte[] { 8, 0, 0, 0, 1, 0, 0, 0, 1, 2, 3, - 4 }, codepage ); - check(Variant.VT_I4, Integer.valueOf(27), codepage); - check(Variant.VT_I8, Long.valueOf(28), codepage); - check(Variant.VT_R8, new Double(29.0), codepage); - check(Variant.VT_I4, Integer.valueOf(-27), codepage); - check(Variant.VT_I8, Long.valueOf(-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); - check(Variant.VT_LPSTR, - "\u00e4", codepage); - check(Variant.VT_LPSTR, - "\u00e4\u00f6", codepage); - check(Variant.VT_LPSTR, - "\u00e4\u00f6\u00fc", codepage); - check(Variant.VT_LPSTR, - "\u00e4\u00f6\u00fc\u00df", codepage); - check(Variant.VT_LPSTR, - "\u00e4\u00f6\u00fc\u00df\u00c4", codepage); - check(Variant.VT_LPSTR, - "\u00e4\u00f6\u00fc\u00df\u00c4\u00d6", codepage); - check(Variant.VT_LPSTR, - "\u00e4\u00f6\u00fc\u00df\u00c4\u00d6\u00dc", codepage); - - check(Variant.VT_LPWSTR, - "", codepage); - check(Variant.VT_LPWSTR, - "\u00e4", codepage); - check(Variant.VT_LPWSTR, - "\u00e4\u00f6", codepage); - check(Variant.VT_LPWSTR, - "\u00e4\u00f6\u00fc", codepage); - check(Variant.VT_LPWSTR, - "\u00e4\u00f6\u00fc\u00df", codepage); - check(Variant.VT_LPWSTR, - "\u00e4\u00f6\u00fc\u00df\u00c4", codepage); - check(Variant.VT_LPWSTR, - "\u00e4\u00f6\u00fc\u00df\u00c4\u00d6", codepage); - check(Variant.VT_LPWSTR, - "\u00e4\u00f6\u00fc\u00df\u00c4\u00d6\u00dc", codepage); - } - catch (Exception ex) - { - t = ex; - } - catch (Error ex) - { - t = ex; - } - if (t != null) - fail(org.apache.poi.hpsf.Util.toString(t)); + check(Variant.VT_EMPTY, null, codepage); + check(Variant.VT_BOOL, Boolean.TRUE, codepage); + check(Variant.VT_BOOL, Boolean.FALSE, codepage); + check( Variant.VT_CF, new byte[] { 8, 0, 0, 0, 1, 0, 0, 0, 1, 2, 3, 4 }, codepage ); + check(Variant.VT_I4, 27, codepage); + check(Variant.VT_I8, 28L, codepage); + check(Variant.VT_R8, 29.0d, codepage); + check(Variant.VT_I4, -27, codepage); + check(Variant.VT_I8, -28L, codepage); + check(Variant.VT_R8, -29.0d, codepage); + check(Variant.VT_FILETIME, new Date(), codepage); + check(Variant.VT_I4, Integer.MAX_VALUE, codepage); + check(Variant.VT_I4, Integer.MIN_VALUE, codepage); + check(Variant.VT_I8, Long.MAX_VALUE, codepage); + check(Variant.VT_I8, Long.MIN_VALUE, codepage); + check(Variant.VT_R8, Double.MAX_VALUE, codepage); + check(Variant.VT_R8, Double.MIN_VALUE, codepage); + checkString(Variant.VT_LPSTR, "\u00e4\u00f6\u00fc\u00df\u00c4\u00d6\u00dc", codepage); + checkString(Variant.VT_LPWSTR, "\u00e4\u00f6\u00fc\u00df\u00c4\u00d6\u00dc", codepage); } /** - *

Writes and reads back strings using several different codepages and + * Writes and reads back strings using several different codepages and * checks whether the stuff that has been read back equals the stuff that - * was written.

+ * was written. */ @Test - public void codepages() + public void codepages() throws ReadingNotSupportedException, UnsupportedVariantTypeException, IOException { Throwable thr = null; - final int[] validCodepages = new int[] - {CODEPAGE_DEFAULT, CODEPAGE_UTF8, CODEPAGE_UTF16, CODEPAGE_1252}; + final int[] validCodepages = {CODEPAGE_DEFAULT, CodePageUtil.CP_UTF8, CodePageUtil.CP_UNICODE, CodePageUtil.CP_WINDOWS_1252}; for (final int cp : validCodepages) { if (cp == -1 && !hasProperDefaultCharset()) { @@ -482,65 +401,22 @@ public class TestWrite continue; } - final long t = cp == CODEPAGE_UTF16 ? Variant.VT_LPWSTR - : Variant.VT_LPSTR; - try - { - check(t, "", cp); - check(t, "\u00e4", cp); - check(t, "\u00e4\u00f6", cp); - check(t, "\u00e4\u00f6\u00fc", cp); - check(t, "\u00e4\u00f6\u00fc\u00c4", cp); - check(t, "\u00e4\u00f6\u00fc\u00c4\u00d6", cp); - check(t, "\u00e4\u00f6\u00fc\u00c4\u00d6\u00dc", cp); - check(t, "\u00e4\u00f6\u00fc\u00c4\u00d6\u00dc\u00df", cp); - if (cp == CodePageUtil.CP_UTF16 || cp == CodePageUtil.CP_UTF8) - check(t, "\u79D1\u5B78", cp); + final long t = (cp == CodePageUtil.CP_UNICODE) ? Variant.VT_LPWSTR : Variant.VT_LPSTR; + checkString(t, "\u00e4\u00f6\u00fc\u00c4\u00d6\u00dc\u00df", cp); + if (cp == CodePageUtil.CP_UTF16 || cp == CodePageUtil.CP_UTF8) { + check(t, "\u79D1\u5B78", cp); } - catch (Exception ex) - { - thr = ex; - } - catch (Error ex) - { - thr = ex; - } - if (thr != null) - fail(org.apache.poi.hpsf.Util.toString(thr) + - " with codepage " + cp); } final int[] invalidCodepages = new int[] {0, 1, 2, 4711, 815}; for (int cp : invalidCodepages) { - final long type = cp == CODEPAGE_UTF16 ? Variant.VT_LPWSTR - : Variant.VT_LPSTR; - try - { - check(type, "", cp); - check(type, "\u00e4", cp); - check(type, "\u00e4\u00f6", cp); - check(type, "\u00e4\u00f6\u00fc", cp); - check(type, "\u00e4\u00f6\u00fc\u00c4", cp); - check(type, "\u00e4\u00f6\u00fc\u00c4\u00d6", cp); - check(type, "\u00e4\u00f6\u00fc\u00c4\u00d6\u00dc", cp); - check(type, "\u00e4\u00f6\u00fc\u00c4\u00d6\u00dc\u00df", cp); - fail("UnsupportedEncodingException for codepage " + cp + - " expected."); - } - catch (UnsupportedEncodingException ex) - { + final long type = (cp == CodePageUtil.CP_UNICODE) ? Variant.VT_LPWSTR : Variant.VT_LPSTR; + try { + checkString(type, "\u00e4\u00f6\u00fc\u00c4\u00d6\u00dc\u00df", cp); + fail("UnsupportedEncodingException for codepage " + cp + " expected."); + } catch (UnsupportedEncodingException ex) { /* This is the expected behaviour. */ } - catch (Exception ex) - { - thr = ex; - } - catch (Error ex) - { - thr = ex; - } - if (thr != null) - fail(org.apache.poi.hpsf.Util.toString(thr)); } } @@ -548,12 +424,10 @@ public class TestWrite /** - *

Tests whether writing 8-bit characters to a Unicode property - * succeeds.

+ * Tests whether writing 8-bit characters to a Unicode property succeeds. */ @Test - public void unicodeWrite8Bit() - { + public void unicodeWrite8Bit() throws WritingNotSupportedException, IOException, NoPropertySetStreamException { final String TITLE = "This is a sample title"; final MutablePropertySet mps = new MutablePropertySet(); final MutableSection ms = (MutableSection) mps.getSections().get(0); @@ -564,218 +438,57 @@ public class TestWrite p.setValue(TITLE); ms.setProperty(p); - Throwable t = null; - try - { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - mps.write(out); - out.close(); - byte[] bytes = out.toByteArray(); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + mps.write(out); + out.close(); + byte[] bytes = out.toByteArray(); - PropertySet psr = new PropertySet(bytes); - assertTrue(psr.isSummaryInformation()); - Section sr = psr.getSections().get(0); - String title = (String) sr.getProperty(PropertyIDMap.PID_TITLE); - assertEquals(TITLE, title); + PropertySet psr = new PropertySet(bytes); + assertTrue(psr.isSummaryInformation()); + Section sr = psr.getSections().get(0); + String title = (String) sr.getProperty(PropertyIDMap.PID_TITLE); + assertEquals(TITLE, title); + } + + private void checkString(final long variantType, final String value, final int codepage) + throws UnsupportedVariantTypeException, IOException, ReadingNotSupportedException, UnsupportedEncodingException { + for (int i=0; iWrites a property and reads it back in.

+ * Writes a property and reads it back in. * * @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) - throws UnsupportedVariantTypeException, IOException, - ReadingNotSupportedException, UnsupportedEncodingException + private void check(final long variantType, final Object value, final int codepage) + throws UnsupportedVariantTypeException, IOException, ReadingNotSupportedException, UnsupportedEncodingException { final ByteArrayOutputStream out = new ByteArrayOutputStream(); VariantSupport.write(out, variantType, value, codepage); out.close(); final byte[] b = out.toByteArray(); final Object objRead = - VariantSupport.read(b, 0, b.length + LittleEndian.INT_SIZE, - variantType, codepage); - if (objRead instanceof byte[]) - { - byte[] valueB = (byte[])value; - byte[] readB = (byte[])objRead; - if (valueB.length != readB.length) - fail("Byte arrays are different length - expected " + valueB.length + - " but found " + readB.length); - - final int diff = diff(valueB, readB); - if (diff >= 0) - fail("Byte arrays are different. First different byte is at " + - "index " + diff + "."); - } - else - if (value != null && !value.equals(objRead)) - { - fail("Expected: \"" + value + "\" but was: \"" + objRead + - "\". Codepage: " + codepage + - (codepage == -1 ? - " (" + System.getProperty("file.encoding") + ")." : ".")); - } - else - assertEquals(value, objRead); - } - - - - /** - *

Compares two byte arrays.

- * - * @param a The first byte array - * @param b The second byte array - * @return The index of the first byte that is different. If the byte arrays - * are equal, -1 is returned. - */ - private int diff(final byte[] a, final byte[] b) - { - final int min = Math.min(a.length, b.length); - for (int i = 0; i < min; i++) - if (a[i] != b[i]) - return i; - if (a.length != b.length) - return min; - return -1; - } - - - - /** - *

This test method does a write and read back test with all POI - * filesystems in the "data" directory by performing the following - * actions for each file:

- * - *
    - * - *
  • Read its property set streams.

  • - * - *
  • Create a new POI filesystem containing the origin file's - * property set streams.

  • - * - *
  • Read the property set streams from the POI filesystem just - * created.

  • - * - *
  • Compare each property set stream with the corresponding one from - * the origin file and check whether they are equal.

  • - * - *
- * @throws IOException - */ - @Test - public void recreate() throws IOException - { - final File dataDir = _samples.getFile(""); - final File[] fileList = dataDir.listFiles(new FileFilter() - { - @Override - public boolean accept(final File f) - { - return f.getName().startsWith("Test") && TestReadAllFiles.checkExclude(f); - } - }); - for (final File file : fileList) { - try { - testRecreate(file); - } catch (Exception e) { - throw new IOException("While handling file " + file, e); - } + VariantSupport.read(b, 0, b.length + LittleEndianConsts.INT_SIZE, variantType, codepage); + if (objRead instanceof byte[]) { + assertArrayEquals((byte[])value, (byte[])objRead); + } else if (value != null && !value.equals(objRead)) { + assertEquals(value, objRead); } } - - - /** - *

Performs the check described in {@link #recreate()} for a single - * POI filesystem.

- * - * @param f the POI filesystem to check - * @throws IOException - * @throws HPSFException - */ - private void testRecreate(final File f) throws IOException, HPSFException - { - /* Read the POI filesystem's property set streams: */ - final POIFile[] psf1 = Util.readPropertySets(f); - - /* Create a new POI filesystem containing the origin file's - * property set streams: */ - final File copy = TempFile.createTempFile(f.getName(), ""); - copy.deleteOnExit(); - final OutputStream out = new FileOutputStream(copy); - final POIFSFileSystem poiFs = new POIFSFileSystem(); - for (POIFile file : psf1) { - final InputStream in = - new ByteArrayInputStream(file.getBytes()); - final PropertySet psIn = PropertySetFactory.create(in); - final MutablePropertySet psOut = new MutablePropertySet(psIn); - final ByteArrayOutputStream psStream = - new ByteArrayOutputStream(); - psOut.write(psStream); - psStream.close(); - final byte[] streamData = psStream.toByteArray(); - poiFs.createDocument(new ByteArrayInputStream(streamData), - file.getName()); - poiFs.writeFilesystem(out); - } - poiFs.close(); - out.close(); - - - /* Read the property set streams from the POI filesystem just - * created. */ - final POIFile[] psf2 = Util.readPropertySets(copy); - for (int i = 0; i < psf2.length; i++) - { - final byte[] bytes1 = psf1[i].getBytes(); - final byte[] bytes2 = psf2[i].getBytes(); - final InputStream in1 = new ByteArrayInputStream(bytes1); - final InputStream in2 = new ByteArrayInputStream(bytes2); - final PropertySet ps1 = PropertySetFactory.create(in1); - final PropertySet ps2 = PropertySetFactory.create(in2); - - /* Compare the property set stream with the corresponding one - * from the origin file and check whether they are equal. */ - assertEquals("Equality for file " + f.getName(), ps1, ps2); - } - } - - - /** *

Tests writing and reading back a proper dictionary.

* @throws IOException * @throws HPSFException */ @Test - public void dictionary() throws IOException, HPSFException - { + public void dictionary() throws IOException, HPSFException { final File copy = TempFile.createTempFile("Test-HPSF", "ole2"); copy.deleteOnExit(); @@ -791,17 +504,16 @@ public class TestWrite s.setDictionary(m); s.setFormatID(SectionIDMap.DOCUMENT_SUMMARY_INFORMATION_ID[0]); int codepage = CodePageUtil.CP_UNICODE; - s.setProperty(PropertyIDMap.PID_CODEPAGE, Variant.VT_I2, - Integer.valueOf(codepage)); + s.setProperty(PropertyIDMap.PID_CODEPAGE, Variant.VT_I2, codepage); poiFs.createDocument(ps1.toInputStream(), "Test"); poiFs.writeFilesystem(out); poiFs.close(); out.close(); /* Read back: */ - final POIFile[] psf = Util.readPropertySets(copy); - assertEquals(1, psf.length); - final byte[] bytes = psf[0].getBytes(); + final List psf = Util.readPropertySets(copy); + assertEquals(1, psf.size()); + final byte[] bytes = psf.get(0).getBytes(); final InputStream in = new ByteArrayInputStream(bytes); final PropertySet ps2 = PropertySetFactory.create(in); @@ -1039,8 +751,7 @@ public class TestWrite * @throws HPSFException */ @Test(expected=IllegalPropertySetDataException.class) - public void dictionaryWithInvalidCodepage() throws IOException, HPSFException - { + public void dictionaryWithInvalidCodepage() throws IOException, HPSFException { final File copy = TempFile.createTempFile("Test-HPSF", "ole2"); copy.deleteOnExit(); @@ -1069,22 +780,17 @@ public class TestWrite } } - - /** *

Returns the display name of the default character set.

* * @return the display name of the default character set. */ - private String getDefaultCharsetName() - { + private static String getDefaultCharsetName() { final String charSetName = System.getProperty("file.encoding"); final Charset charSet = Charset.forName(charSetName); return charSet.displayName(Locale.ROOT); } - - /** *

In order to execute tests with characters beyond US-ASCII, this * method checks whether the application is runing in an environment @@ -1093,8 +799,7 @@ public class TestWrite * @return true if the default character set is 16-bit-capable, * else false. */ - private boolean hasProperDefaultCharset() - { + private boolean hasProperDefaultCharset() { final String charSetName = System.getProperty("file.encoding"); final Charset charSet = Charset.forName(charSetName); return charSet.newEncoder().canEncode('\u00e4'); diff --git a/src/testcases/org/apache/poi/hpsf/basic/TestWriteWellKnown.java b/src/testcases/org/apache/poi/hpsf/basic/TestWriteWellKnown.java index b47417ec4c..4105c223a8 100644 --- a/src/testcases/org/apache/poi/hpsf/basic/TestWriteWellKnown.java +++ b/src/testcases/org/apache/poi/hpsf/basic/TestWriteWellKnown.java @@ -24,9 +24,7 @@ import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; 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; @@ -51,7 +49,6 @@ 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.DocumentInputStream; import org.apache.poi.poifs.filesystem.NPOIFSFileSystem; import org.apache.poi.util.IOUtils; @@ -73,70 +70,6 @@ public class TestWriteWellKnown { VariantSupport.setLogUnsupportedTypes(false); } - /** - *

This test method checks whether DocumentSummary information streams - * can be read. This is done by opening all "Test*" files in the 'poifs' directrory - * pointed to by the "POI.testdata.path" system property, trying to extract - * the document summary information stream in the root directory and calling - * its get... methods.

- */ - @Test - public void testReadDocumentSummaryInformation() - throws FileNotFoundException, IOException, - NoPropertySetStreamException, MarkUnsupportedException, - UnexpectedPropertySetTypeException - { - POIDataSamples _samples = POIDataSamples.getHPSFInstance(); - final File dataDir = _samples.getFile(""); - final File[] docs = dataDir.listFiles(new FileFilter() - { - @Override - public boolean accept(final File file) - { - return file.isFile() && file.getName().startsWith("Test") && TestReadAllFiles.checkExclude(file); - } - }); - - for (final File doc : docs) { - NPOIFSFileSystem poifs = null; - try { - /* Read a test document doc into a POI filesystem. */ - poifs = new NPOIFSFileSystem(doc, true); - final DirectoryEntry dir = poifs.getRoot(); - /* - * If there is a document summry information stream, read it from - * the POI filesystem. - */ - if (dir.hasEntry(DocumentSummaryInformation.DEFAULT_STREAM_NAME)) { - final DocumentSummaryInformation dsi = getDocumentSummaryInformation(poifs); - - /* 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(); - } - } catch (Exception e) { - throw new IOException("While handling file " + doc, e); - } finally { - if (poifs != null) poifs.close(); - } - } - } - static final String P_APPLICATION_NAME = "ApplicationName"; static final String P_AUTHOR = "Author"; static final int P_CHAR_COUNT = 4712; @@ -483,15 +416,12 @@ public class TestWriteWellKnown { dsi.removeScale(); dsi.removeSlideCount(); - /* - *
  • Write the summary information stream and the document summary - * information stream to the POI filesystem. */ + // Write the summary information stream and the document summary + // information stream to the POI filesystem. si.write(poifs.getRoot(), SummaryInformation.DEFAULT_STREAM_NAME); dsi.write(poifs.getRoot(), DocumentSummaryInformation.DEFAULT_STREAM_NAME); - /* - *

  • Write the POI filesystem to a (temporary) file doc3 - * and close the latter. */ + // Write the POI filesystem to a (temporary) file doc3 and close the latter. FileOutputStream out = new FileOutputStream(fileOut); poifs.writeFilesystem(out); out.close(); @@ -501,9 +431,9 @@ public class TestWriteWellKnown { } /* - * Open doc3 for reading and check summary information + * Open {@code 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. + * be found in the property streams of {@code doc3}. */ private static CustomProperties write3rdFile(File fileIn, File fileOut) throws Exception { NPOIFSFileSystem poifs = new NPOIFSFileSystem(fileIn, false); @@ -571,7 +501,12 @@ public class TestWriteWellKnown { return si; } - private static DocumentSummaryInformation getDocumentSummaryInformation(NPOIFSFileSystem poifs) throws Exception { + static DocumentSummaryInformation getDocumentSummaryInformation(NPOIFSFileSystem poifs) + throws IOException, NoPropertySetStreamException, UnexpectedPropertySetTypeException, MarkUnsupportedException { + if (!poifs.getRoot().hasEntry(DocumentSummaryInformation.DEFAULT_STREAM_NAME)) { + return null; + } + DocumentInputStream dis = poifs.createDocumentInputStream(DocumentSummaryInformation.DEFAULT_STREAM_NAME); PropertySet ps = new PropertySet(dis); DocumentSummaryInformation dsi = new DocumentSummaryInformation(ps); @@ -579,83 +514,8 @@ public class TestWriteWellKnown { return dsi; } - - /** - *

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

    - * - * @throws Throwable if anything goes wrong. - */ - @Test - public void testReadCustomPropertiesFromFiles() throws Throwable - { - final AllDataFilesTester.TestTask task = new AllDataFilesTester.TestTask() - { - @Override - public void runTest(final File file) throws FileNotFoundException, - IOException, NoPropertySetStreamException, - MarkUnsupportedException, - UnexpectedPropertySetTypeException - { - /* Read a test document doc into a POI filesystem. */ - NPOIFSFileSystem poifs = null; - try { - poifs = new NPOIFSFileSystem(file); - final DirectoryEntry dir = poifs.getRoot(); - /* - * If there is a document summry information stream, read it from - * the POI filesystem, else create a new one. - */ - DocumentSummaryInformation dsi; - if (dir.hasEntry(DocumentSummaryInformation.DEFAULT_STREAM_NAME)) { - final DocumentInputStream dis = poifs.createDocumentInputStream(DocumentSummaryInformation.DEFAULT_STREAM_NAME); - final PropertySet ps = new PropertySet(dis); - dsi = new DocumentSummaryInformation(ps); - dis.close(); - } else { - dsi = PropertySetFactory.newDocumentSummaryInformation(); - } - final CustomProperties cps = dsi.getCustomProperties(); - - if (cps == null) - /* The document does not have custom properties. */ - return; - - for (CustomProperty cp : cps.values()) { - cp.getName(); - cp.getValue(); - } - } finally { - if (poifs != null) poifs.close(); - } - } - }; - - POIDataSamples _samples = POIDataSamples.getHPSFInstance(); - final File dataDir = _samples.getFile(""); - final File[] docs = dataDir.listFiles(new FileFilter() - { - @Override - public boolean accept(final File file) - { - return file.isFile() && file.getName().startsWith("Test") && TestReadAllFiles.checkExclude(file); - } - }); - - for (File doc : docs) { - try { - task.runTest(doc); - } catch (Exception e) { - throw new IOException("While handling file " + doc, e); - } - } - } - - - - /** - *

    Tests basic custom property features.

    + * Tests basic custom property features. */ @Test public void testCustomerProperties() @@ -693,8 +553,8 @@ public class TestWriteWellKnown { /** - *

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

    + * Tests reading custom properties from a section including reading + * custom properties which are not pure. */ @Test public void testGetCustomerProperties() diff --git a/src/testcases/org/apache/poi/hpsf/basic/Util.java b/src/testcases/org/apache/poi/hpsf/basic/Util.java index 668b1858ad..40b4e229d7 100644 --- a/src/testcases/org/apache/poi/hpsf/basic/Util.java +++ b/src/testcases/org/apache/poi/hpsf/basic/Util.java @@ -19,13 +19,11 @@ package org.apache.poi.hpsf.basic; import java.io.ByteArrayOutputStream; -import java.io.EOFException; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; -import java.io.OutputStream; import java.util.ArrayList; import java.util.Collections; import java.util.LinkedList; @@ -36,6 +34,7 @@ import org.apache.poi.hpsf.PropertySet; import org.apache.poi.poifs.eventfilesystem.POIFSReader; import org.apache.poi.poifs.eventfilesystem.POIFSReaderEvent; import org.apache.poi.poifs.eventfilesystem.POIFSReaderListener; +import org.apache.poi.util.IOUtils; @@ -44,42 +43,6 @@ import org.apache.poi.poifs.eventfilesystem.POIFSReaderListener; */ final class Util { - /** - *

    Reads bytes from an input stream and writes them to an - * output stream until end of file is encountered.

    - * - * @param in the input stream to read from - * - * @param out the output stream to write to - * - * @exception IOException if an I/O exception occurs - */ - public static void copy(final InputStream in, final OutputStream out) - throws IOException - { - final int BUF_SIZE = 1000; - byte[] b = new byte[BUF_SIZE]; - int read; - boolean eof = false; - while (!eof) - { - try - { - read = in.read(b, 0, BUF_SIZE); - if (read > 0) - out.write(b, 0, read); - else - eof = true; - } - catch (EOFException ex) - { - eof = true; - } - } - } - - - /** *

    Reads all files from a POI filesystem and returns them as an * array of {@link POIFile} instances. This method loads all files @@ -143,7 +106,7 @@ final class Util { final InputStream in = event.getStream(); final ByteArrayOutputStream out = new ByteArrayOutputStream(); - Util.copy(in, out); + IOUtils.copy(in, out); out.close(); f.setBytes(out.toByteArray()); files.add(f); @@ -192,34 +155,33 @@ final class Util { * * @exception IOException if an I/O exception occurs */ - public static POIFile[] readPropertySets(final File poiFs) - throws FileNotFoundException, IOException - { + public static List readPropertySets(final File poiFs) + throws FileNotFoundException, IOException { + FileInputStream stream = new FileInputStream(poiFs); + try { + return readPropertySets(stream); + } finally { + stream.close(); + } + } + + public static List readPropertySets(final InputStream poiFs) + throws FileNotFoundException, IOException { final List files = new ArrayList(7); final POIFSReader r = new POIFSReader(); - POIFSReaderListener pfl = new POIFSReaderListener() - { + POIFSReaderListener pfl = new POIFSReaderListener() { @Override - public void processPOIFSReaderEvent(final POIFSReaderEvent event) - { - try - { + public void processPOIFSReaderEvent(final POIFSReaderEvent event) { + try { final POIFile f = new POIFile(); f.setName(event.getName()); f.setPath(event.getPath()); final InputStream in = event.getStream(); - if (PropertySet.isPropertySetStream(in)) - { - final ByteArrayOutputStream out = - new ByteArrayOutputStream(); - Util.copy(in, out); - out.close(); - f.setBytes(out.toByteArray()); + if (PropertySet.isPropertySetStream(in)) { + f.setBytes(IOUtils.toByteArray(in)); files.add(f); } - } - catch (Exception ex) - { + } catch (Exception ex) { throw new RuntimeException(ex); } } @@ -229,17 +191,9 @@ final class Util { r.registerListener(pfl); /* Read the POI filesystem. */ - FileInputStream stream = new FileInputStream(poiFs); - try { - r.read(stream); - } finally { - stream.close(); - } + r.read(poiFs); - POIFile[] result = new POIFile[files.size()]; - for (int i = 0; i < result.length; i++) - result[i] = files.get(i); - return result; + return files; } diff --git a/test-data/hpsf/TestInvertedClassID.doc b/test-data/hpsf/TestInvertedClassID.doc new file mode 100644 index 0000000000..b9fa8f439a Binary files /dev/null and b/test-data/hpsf/TestInvertedClassID.doc differ diff --git a/test-data/hpsf/TestVisio43688.vsd b/test-data/hpsf/TestVisio43688.vsd new file mode 100644 index 0000000000..1aabaa6a80 Binary files /dev/null and b/test-data/hpsf/TestVisio43688.vsd differ