Fix hpsf javadocs and code issues

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1793596 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Andreas Beeker 2017-05-02 23:27:27 +00:00
parent 5600fa6f58
commit 456b33a837
20 changed files with 1393 additions and 1183 deletions

View File

@ -17,6 +17,10 @@
package org.apache.poi.hpsf; package org.apache.poi.hpsf;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import org.apache.poi.util.Removal; import org.apache.poi.util.Removal;
/** /**
@ -34,4 +38,9 @@ public class MutablePropertySet extends PropertySet {
public MutablePropertySet(final PropertySet ps) { public MutablePropertySet(final PropertySet ps) {
super(ps); super(ps);
} }
/* package */ MutablePropertySet(final InputStream stream)
throws NoPropertySetStreamException, MarkUnsupportedException, IOException, UnsupportedEncodingException {
super(stream);
}
} }

View File

@ -17,17 +17,20 @@
package org.apache.poi.hpsf; package org.apache.poi.hpsf;
import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.util.Arrays; 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.CodePageUtil;
import org.apache.poi.util.HexDump; import org.apache.poi.util.HexDump;
import org.apache.poi.util.LittleEndian; 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.POILogFactory;
import org.apache.poi.util.POILogger; import org.apache.poi.util.POILogger;
@ -58,6 +61,13 @@ import org.apache.poi.util.POILogger;
*/ */
public class Property { 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. */ /** The property's ID. */
private long id; private long id;
@ -120,13 +130,12 @@ public class Property {
* property IDs and property names. * property IDs and property names.
*/ */
if (id == 0) { if (id == 0) {
value = readDictionary(src, offset, length, codepage); throw new UnsupportedEncodingException("Dictionary not allowed here");
return;
} }
int o = (int) offset; int o = (int) offset;
type = LittleEndian.getUInt(src, o); type = LittleEndian.getUInt(src, o);
o += LittleEndian.INT_SIZE; o += LittleEndianConsts.INT_SIZE;
try { try {
value = VariantSupport.read(src, o, length, (int) type, codepage); 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();
}
}
/** /**
@ -196,100 +238,6 @@ 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<Object, Object> m = new LinkedHashMap<Object, Object>((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 * @exception WritingNotSupportedException if HPSF does not yet support the
* property's variant type. * property's variant type.
*/ */
protected int getSize() throws WritingNotSupportedException protected int getSize(int codepage) throws WritingNotSupportedException
{ {
int length = VariantSupport.getVariantLength(type); int length = Variant.getVariantLength(type);
if (length >= 0) { if (length >= 0 || type == Variant.VT_EMPTY) {
return length; /* Fixed length */ /* Fixed length */
return length;
} }
if (length == -2) { if (length == -2) {
/* Unknown length */ /* Unknown length */
@ -313,22 +262,19 @@ public class Property {
} }
/* Variable length: */ /* Variable length: */
final int PADDING = 4; /* Pad to multiples of 4. */ if (type == Variant.VT_LPSTR || type == Variant.VT_LPWSTR) {
switch ((int) type) { ByteArrayOutputStream bos = new ByteArrayOutputStream();
case Variant.VT_LPSTR: { try {
int l = ((String) value).length() + 1; length = write(bos, codepage) - 2*LittleEndianConsts.INT_SIZE;
int r = l % PADDING; /* Pad to multiples of 4. */
if (r > 0) length += (4 - (length & 0x3)) & 0x3;
l += PADDING - r; return length;
length += l; } catch (IOException e) {
break;
}
case Variant.VT_EMPTY:
break;
default:
throw new WritingNotSupportedException(type, value); throw new WritingNotSupportedException(type, value);
} }
return length; }
throw new WritingNotSupportedException(type, value);
} }
@ -343,6 +289,7 @@ public class Property {
* *
* @see Object#equals(java.lang.Object) * @see Object#equals(java.lang.Object)
*/ */
@Override
public boolean equals(final Object o) { public boolean equals(final Object o) {
if (!(o instanceof Property)) { if (!(o instanceof Property)) {
return false; return false;
@ -369,12 +316,35 @@ public class Property {
} }
if (value instanceof byte[]) { 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<len; i++) {
if (thisVal[i] != otherVal[i]) {
return false;
}
}
return true;
} }
return value.equals(pValue); return value.equals(pValue);
} }
/**
* Byte arrays can be 0-padded up to 3 bytes to form a full quad array.
* This returns the truncated length without the potentially 0-padded bytes
*
* @param buf the bytes
* @return the truncated size with a maximum of 4 bytes shorter (3 bytes + trailing 0 of strings)
*/
private static int unpaddedLength(byte[] buf) {
int len;
for (len = buf.length; len > 0 && len > buf.length-4 && buf[len-1] == 0; len--);
return len;
}
private boolean typesAreEqual(final long t1, final long t2) { private boolean typesAreEqual(final long t1, final long t2) {
@ -388,6 +358,7 @@ public class Property {
/** /**
* @see Object#hashCode() * @see Object#hashCode()
*/ */
@Override
public int hashCode() { public int hashCode() {
long hashCode = 0; long hashCode = 0;
hashCode += id; hashCode += id;
@ -404,47 +375,128 @@ public class Property {
/** /**
* @see Object#toString() * @see Object#toString()
*/ */
@Override
public String toString() { public String toString() {
return toString(Property.DEFAULT_CODEPAGE);
}
public String toString(int codepage) {
final StringBuffer b = new StringBuffer(); final StringBuffer b = new StringBuffer();
b.append(getClass().getName()); b.append("Property[");
b.append('[');
b.append("id: "); b.append("id: ");
b.append(getID()); b.append(getID());
String idName = getNameFromID();
if (idName != null) {
b.append(" (");
b.append(idName);
b.append(")");
}
b.append(", type: "); b.append(", type: ");
b.append(getType()); b.append(getType());
b.append(" (");
b.append(getVariantName());
b.append(") ");
final Object value = getValue(); final Object value = getValue();
b.append(", value: "); b.append(", value: ");
if (value instanceof String) { if (value instanceof String) {
b.append(value); b.append("\n");
final String s = (String) value; ByteArrayOutputStream bos = new ByteArrayOutputStream();
final int l = s.length(); try {
final byte[] bytes = new byte[l * 2]; write(bos, codepage);
for (int i = 0; i < l; i++) { } catch (Exception e) {
final char c = s.charAt(i); LOG.log(POILogger.WARN, "can't serialize string", e);
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(" ["); b.append(" [");
if(bytes.length > 0) { // skip length field
final String hex = HexDump.dump(bytes, 0L, 0); 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(hex);
} }
b.append("]"); b.append("]");
} else if (value instanceof byte[]) { } else if (value instanceof byte[]) {
b.append("\n");
byte[] bytes = (byte[])value; byte[] bytes = (byte[])value;
if(bytes.length > 0) { if(bytes.length > 0) {
String hex = HexDump.dump(bytes, 0L, 0); String hex = HexDump.dump(bytes, 0L, 0);
b.append(hex); b.append(hex);
} }
} else if (type == Variant.VT_EMPTY || type == Variant.VT_NULL) {
b.append("null");
} else { } else {
b.append(value); b.append(value.toString());
String decoded = decodeValueFromID();
if (decoded != null) {
b.append(" (");
b.append(decoded);
b.append(")");
}
} }
b.append(']'); b.append(']');
return b.toString(); 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. * Writes the property to an output stream.
* *
@ -462,11 +514,19 @@ public class Property {
long variantType = getType(); long variantType = getType();
/* Ensure that wide strings are written if the codepage is Unicode. */ /* Ensure that wide strings are written if the codepage is Unicode. */
if (codepage == CodePageUtil.CP_UNICODE && variantType == Variant.VT_LPSTR) { // 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; variantType = Variant.VT_LPWSTR;
} }
}
length += TypeWriter.writeUIntToStream(out, variantType); LittleEndian.putUInt(variantType, out);
length += LittleEndianConsts.INT_SIZE;
length += VariantSupport.write(out, variantType, getValue(), codepage); length += VariantSupport.write(out, variantType, getValue(), codepage);
return length; return length;
} }

View File

@ -22,9 +22,11 @@ import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import org.apache.poi.hpsf.wellknown.SectionIDMap;
import org.apache.poi.poifs.filesystem.DirectoryEntry; import org.apache.poi.poifs.filesystem.DirectoryEntry;
import org.apache.poi.poifs.filesystem.DocumentEntry; import org.apache.poi.poifs.filesystem.DocumentEntry;
import org.apache.poi.poifs.filesystem.DocumentInputStream; import org.apache.poi.poifs.filesystem.DocumentInputStream;
import org.apache.poi.util.LittleEndianInputStream;
/** /**
* Factory class to create instances of {@link SummaryInformation}, * Factory class to create instances of {@link SummaryInformation},
@ -87,19 +89,33 @@ public class PropertySetFactory {
*/ */
public static PropertySet create(final InputStream stream) public static PropertySet create(final InputStream stream)
throws NoPropertySetStreamException, MarkUnsupportedException, UnsupportedEncodingException, IOException { throws NoPropertySetStreamException, MarkUnsupportedException, UnsupportedEncodingException, IOException {
final PropertySet ps = new PropertySet(stream); stream.mark(PropertySet.OFFSET_HEADER+ClassID.LENGTH+1);
try { LittleEndianInputStream leis = new LittleEndianInputStream(stream);
if (ps.isSummaryInformation()) { int byteOrder = leis.readUShort();
return new SummaryInformation(ps); int format = leis.readUShort();
} else if (ps.isDocumentSummaryInformation()) { int osVersion = (int)leis.readUInt();
return new DocumentSummaryInformation(ps); byte[] clsIdBuf = new byte[ClassID.LENGTH];
} else { leis.readFully(clsIdBuf);
return ps; int sectionCount = (int)leis.readUInt();
if (byteOrder != PropertySet.BYTE_ORDER_ASSERTION ||
format != PropertySet.FORMAT_ASSERTION ||
sectionCount < 0) {
throw new NoPropertySetStreamException();
} }
} catch (UnexpectedPropertySetTypeException ex) {
/* This exception will never be throws because we already checked if (sectionCount > 0) {
* explicitly for this case above. */ leis.readFully(clsIdBuf);
throw new IllegalStateException(ex); }
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);
} }
} }

View File

@ -17,6 +17,10 @@
package org.apache.poi.hpsf; package org.apache.poi.hpsf;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import org.apache.poi.util.Removal; import org.apache.poi.util.Removal;
/** /**
@ -37,4 +41,9 @@ public class SpecialPropertySet extends MutablePropertySet {
public SpecialPropertySet(final PropertySet ps) throws UnexpectedPropertySetTypeException { public SpecialPropertySet(final PropertySet ps) throws UnexpectedPropertySetTypeException {
super(ps); super(ps);
} }
/* package */ SpecialPropertySet(final InputStream stream)
throws NoPropertySetStreamException, MarkUnsupportedException, IOException, UnsupportedEncodingException {
super(stream);
}
} }

View File

@ -17,6 +17,9 @@
package org.apache.poi.hpsf; package org.apache.poi.hpsf;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.util.Date; import java.util.Date;
import org.apache.poi.hpsf.wellknown.PropertyIDMap; import org.apache.poi.hpsf.wellknown.PropertyIDMap;
@ -62,6 +65,32 @@ public final class SummaryInformation extends SpecialPropertySet {
} }
} }
/**
* Creates a {@link SummaryInformation} instance from an {@link
* InputStream} in the Horrible Property Set Format.<p>
*
* 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);
}
/** /**

View File

@ -22,14 +22,14 @@ import java.util.HashMap;
import java.util.Map; import java.util.Map;
/** /**
* <p>This is a dictionary which maps property ID values to property * This is a dictionary which maps property ID values to property
* ID strings.</p> * ID strings.
* *
* <p>The methods {@link #getSummaryInformationProperties} and {@link * The methods {@link #getSummaryInformationProperties} and {@link
* #getDocumentSummaryInformationProperties} return singleton {@link * #getDocumentSummaryInformationProperties} return singleton {@link
* PropertyIDMap}s. An application that wants to extend these maps * PropertyIDMap}s. An application that wants to extend these maps
* should treat them as unmodifiable, copy them and modifiy the * should treat them as unmodifiable, copy them and modifiy the
* copies.</p> * copies.
*/ */
public class PropertyIDMap extends HashMap<Long,String> { public class PropertyIDMap extends HashMap<Long,String> {
@ -38,85 +38,85 @@ public class PropertyIDMap extends HashMap<Long,String> {
* (and only) section of the Summary Information property set. * (and only) section of the Summary Information property set.
*/ */
/** <p>ID of the property that denotes the document's title</p> */ /** ID of the property that denotes the document's title */
public static final int PID_TITLE = 2; public static final int PID_TITLE = 2;
/** <p>ID of the property that denotes the document's subject</p> */ /** ID of the property that denotes the document's subject */
public static final int PID_SUBJECT = 3; public static final int PID_SUBJECT = 3;
/** <p>ID of the property that denotes the document's author</p> */ /** ID of the property that denotes the document's author */
public static final int PID_AUTHOR = 4; public static final int PID_AUTHOR = 4;
/** <p>ID of the property that denotes the document's keywords</p> */ /** ID of the property that denotes the document's keywords */
public static final int PID_KEYWORDS = 5; public static final int PID_KEYWORDS = 5;
/** <p>ID of the property that denotes the document's comments</p> */ /** ID of the property that denotes the document's comments */
public static final int PID_COMMENTS = 6; public static final int PID_COMMENTS = 6;
/** <p>ID of the property that denotes the document's template</p> */ /** ID of the property that denotes the document's template */
public static final int PID_TEMPLATE = 7; public static final int PID_TEMPLATE = 7;
/** <p>ID of the property that denotes the document's last author</p> */ /** ID of the property that denotes the document's last author */
public static final int PID_LASTAUTHOR = 8; public static final int PID_LASTAUTHOR = 8;
/** <p>ID of the property that denotes the document's revision number</p> */ /** ID of the property that denotes the document's revision number */
public static final int PID_REVNUMBER = 9; public static final int PID_REVNUMBER = 9;
/** <p>ID of the property that denotes the document's edit time</p> */ /** ID of the property that denotes the document's edit time */
public static final int PID_EDITTIME = 10; public static final int PID_EDITTIME = 10;
/** <p>ID of the property that denotes the date and time the document was /** ID of the property that denotes the date and time the document was
* last printed</p> */ * last printed */
public static final int PID_LASTPRINTED = 11; public static final int PID_LASTPRINTED = 11;
/** <p>ID of the property that denotes the date and time the document was /** ID of the property that denotes the date and time the document was
* created.</p> */ * created. */
public static final int PID_CREATE_DTM = 12; public static final int PID_CREATE_DTM = 12;
/** <p>ID of the property that denotes the date and time the document was /** ID of the property that denotes the date and time the document was
* saved</p> */ * saved */
public static final int PID_LASTSAVE_DTM = 13; public static final int PID_LASTSAVE_DTM = 13;
/** <p>ID of the property that denotes the number of pages in the /** ID of the property that denotes the number of pages in the
* document</p> */ * document */
public static final int PID_PAGECOUNT = 14; public static final int PID_PAGECOUNT = 14;
/** <p>ID of the property that denotes the number of words in the /** ID of the property that denotes the number of words in the
* document</p> */ * document */
public static final int PID_WORDCOUNT = 15; public static final int PID_WORDCOUNT = 15;
/** <p>ID of the property that denotes the number of characters in the /** ID of the property that denotes the number of characters in the
* document</p> */ * document */
public static final int PID_CHARCOUNT = 16; public static final int PID_CHARCOUNT = 16;
/** <p>ID of the property that denotes the document's thumbnail</p> */ /** ID of the property that denotes the document's thumbnail */
public static final int PID_THUMBNAIL = 17; public static final int PID_THUMBNAIL = 17;
/** <p>ID of the property that denotes the application that created the /** ID of the property that denotes the application that created the
* document</p> */ * document */
public static final int PID_APPNAME = 18; public static final int PID_APPNAME = 18;
/** <p>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 * document is allowed or whether is should be opened as read-only. It can
* have the following values:</p> * have the following values:
* *
* <table summary=""> * <table summary="">
* <tbody> * <tbody>
* <tr> * <tr>
* <th><p>Value</p></th> * <th>Value</th>
* <th><p>Description</p></th> * <th>Description</th>
* </tr> * </tr>
* <tr> * <tr>
* <th><p>0</p></th> * <th>0</th>
* <th><p>No restriction</p></th> * <th>No restriction</th>
* </tr> * </tr>
* <tr> * <tr>
* <th><p>2</p></th> * <th>2</th>
* <th><p>Read-only recommended</p></th> * <th>Read-only recommended</th>
* </tr> * </tr>
* <tr> * <tr>
* <th><p>4</p></th> * <th>4</th>
* <th><p>Read-only enforced</p></th> * <th>Read-only enforced</th>
* </tr> * </tr>
* </tbody> * </tbody>
* </table> * </table>
@ -131,103 +131,103 @@ public class PropertyIDMap extends HashMap<Long,String> {
*/ */
/** /**
* <p>The entry is a dictionary.</p> * The entry is a dictionary.
*/ */
public static final int PID_DICTIONARY = 0; public static final int PID_DICTIONARY = 0;
/** /**
* <p>The entry denotes a code page.</p> * The entry denotes a code page.
*/ */
public static final int PID_CODEPAGE = 1; public static final int PID_CODEPAGE = 1;
/** /**
* <p>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 * to, e.g. review, memo, etc. This is useful to find documents of
* same type.</p> * same type.
*/ */
public static final int PID_CATEGORY = 2; public static final int PID_CATEGORY = 2;
/** /**
* <p>Target format for power point presentation, e.g. 35mm, * Target format for power point presentation, e.g. 35mm,
* printer, video etc.</p> * printer, video etc.
*/ */
public static final int PID_PRESFORMAT = 3; public static final int PID_PRESFORMAT = 3;
/** /**
* <p>Number of bytes.</p> * Number of bytes.
*/ */
public static final int PID_BYTECOUNT = 4; public static final int PID_BYTECOUNT = 4;
/** /**
* <p>Number of lines.</p> * Number of lines.
*/ */
public static final int PID_LINECOUNT = 5; public static final int PID_LINECOUNT = 5;
/** /**
* <p>Number of paragraphs.</p> * Number of paragraphs.
*/ */
public static final int PID_PARCOUNT = 6; public static final int PID_PARCOUNT = 6;
/** /**
* <p>Number of slides in a power point presentation.</p> * Number of slides in a power point presentation.
*/ */
public static final int PID_SLIDECOUNT = 7; public static final int PID_SLIDECOUNT = 7;
/** /**
* <p>Number of slides with notes.</p> * Number of slides with notes.
*/ */
public static final int PID_NOTECOUNT = 8; public static final int PID_NOTECOUNT = 8;
/** /**
* <p>Number of hidden slides.</p> * Number of hidden slides.
*/ */
public static final int PID_HIDDENCOUNT = 9; public static final int PID_HIDDENCOUNT = 9;
/** /**
* <p>Number of multimedia clips, e.g. sound or video.</p> * Number of multimedia clips, e.g. sound or video.
*/ */
public static final int PID_MMCLIPCOUNT = 10; public static final int PID_MMCLIPCOUNT = 10;
/** /**
* <p>This entry is set to -1 when scaling of the thumbnail is * This entry is set to -1 when scaling of the thumbnail is
* desired. Otherwise the thumbnail should be cropped.</p> * desired. Otherwise the thumbnail should be cropped.
*/ */
public static final int PID_SCALE = 11; public static final int PID_SCALE = 11;
/** /**
* <p>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) * vector of variants consisting of pairs of a string (VT_LPSTR)
* and a number (VT_I4). The string is a heading name, and the * and a number (VT_I4). The string is a heading name, and the
* number tells how many document parts are under that * number tells how many document parts are under that
* heading.</p> * heading.
*/ */
public static final int PID_HEADINGPAIR = 12; public static final int PID_HEADINGPAIR = 12;
/** /**
* <p>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, * of the documents in the master document, excel: sheet names,
* power point: slide titles, binder: document names).</p> * power point: slide titles, binder: document names).
*/ */
public static final int PID_DOCPARTS = 13; public static final int PID_DOCPARTS = 13;
/** /**
* <p>This entry contains the name of the project manager.</p> * This entry contains the name of the project manager.
*/ */
public static final int PID_MANAGER = 14; public static final int PID_MANAGER = 14;
/** /**
* <p>This entry contains the company name.</p> * This entry contains the company name.
*/ */
public static final int PID_COMPANY = 15; public static final int PID_COMPANY = 15;
/** /**
* <p>If this entry is -1 the links are dirty and should be * If this entry is -1 the links are dirty and should be
* re-evaluated.</p> * re-evaluated.
*/ */
public static final int PID_LINKSDIRTY = 0x10; public static final int PID_LINKSDIRTY = 0x10;
/** /**
* <p>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 * in the document, including whitespace, as an integer
*/ */
public static final int PID_CCHWITHSPACES = 0x11; public static final int PID_CCHWITHSPACES = 0x11;
@ -238,21 +238,21 @@ public class PropertyIDMap extends HashMap<Long,String> {
// 0x15 GKPIDDSI_HLINKS - Must not be written // 0x15 GKPIDDSI_HLINKS - Must not be written
/** /**
* <p>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 * Property Set has been updated outside of the Application, if so the
* hyperlinks should be updated on document load. * hyperlinks should be updated on document load.
*/ */
public static final int PID_HYPERLINKSCHANGED = 0x16; public static final int PID_HYPERLINKSCHANGED = 0x16;
/** /**
* <p>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 * Property set, stored with the two high order bytes having the major
* version number, and the two low order bytes the minor version number. * version number, and the two low order bytes the minor version number.
*/ */
public static final int PID_VERSION = 0x17; public static final int PID_VERSION = 0x17;
/** /**
* <p>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. * embedded in the document.
*/ */
public static final int PID_DIGSIG = 0x18; public static final int PID_DIGSIG = 0x18;
@ -260,54 +260,75 @@ public class PropertyIDMap extends HashMap<Long,String> {
// 0x19 Unused // 0x19 Unused
/** /**
* <p>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; public static final int PID_CONTENTTYPE = 0x1A;
/** /**
* <p>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; public static final int PID_CONTENTSTATUS = 0x1B;
/** /**
* <p>This entry contains a string of the document language, but * This entry contains a string of the document language, but
* normally should be empty. * normally should be empty.
*/ */
public static final int PID_LANGUAGE = 0x1C; public static final int PID_LANGUAGE = 0x1C;
/** /**
* <p>This entry contains a string of the document version, but * This entry contains a string of the document version, but
* normally should be empty * normally should be empty
*/ */
public static final int PID_DOCVERSION = 0x1D; public static final int PID_DOCVERSION = 0x1D;
/** /**
* <p>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, * higher values for custom purposes. (This value is based on Office 12,
* earlier versions of Office had lower values)</p> * earlier versions of Office had lower values)
*/ */
public static final int PID_MAX = 0x1F; 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;
/** /**
* <p>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.
*
* <ul>
* <li>0x00000000 = Property names are case-insensitive (default)
* <li>0x00000001 = Property names are case-sensitive.
* </ul>
*/
public static final int PID_BEHAVIOUR = 0x80000003;
/**
* Contains the summary information property ID values and
* associated strings. See the overall HPSF documentation for * associated strings. See the overall HPSF documentation for
* details!</p> * details!
*/ */
private static PropertyIDMap summaryInformationProperties; private static PropertyIDMap summaryInformationProperties;
/** /**
* <p>Contains the summary information property ID values and * Contains the summary information property ID values and
* associated strings. See the overall HPSF documentation for * associated strings. See the overall HPSF documentation for
* details!</p> * details!
*/ */
private static PropertyIDMap documentSummaryInformationProperties; private static PropertyIDMap documentSummaryInformationProperties;
/** /**
* <p>Creates a {@link PropertyIDMap}.</p> * Creates a {@link PropertyIDMap}.
* *
* @param initialCapacity The initial capacity as defined for * @param initialCapacity The initial capacity as defined for
* {@link HashMap} * {@link HashMap}
@ -321,7 +342,7 @@ public class PropertyIDMap extends HashMap<Long,String> {
/** /**
* <p>Creates a {@link PropertyIDMap} backed by another map.</p> * Creates a {@link PropertyIDMap} backed by another map.
* *
* @param map The instance to be created is backed by this map. * @param map The instance to be created is backed by this map.
*/ */
@ -333,8 +354,8 @@ public class PropertyIDMap extends HashMap<Long,String> {
/** /**
* <p>Puts a ID string for an ID into the {@link * Puts a ID string for an ID into the {@link
* PropertyIDMap}.</p> * PropertyIDMap}.
* *
* @param id The ID. * @param id The ID.
* @param idString The ID string. * @param idString The ID string.
@ -351,8 +372,8 @@ public class PropertyIDMap extends HashMap<Long,String> {
/** /**
* <p>Gets the ID string for an ID from the {@link * Gets the ID string for an ID from the {@link
* PropertyIDMap}.</p> * PropertyIDMap}.
* *
* @param id The ID. * @param id The ID.
* @return The ID string associated with <var>id</var>. * @return The ID string associated with <var>id</var>.
@ -367,7 +388,7 @@ public class PropertyIDMap extends HashMap<Long,String> {
/** /**
* @return the Summary Information properties singleton * @return the Summary Information properties singleton
*/ */
public static PropertyIDMap getSummaryInformationProperties() public static synchronized PropertyIDMap getSummaryInformationProperties()
{ {
if (summaryInformationProperties == null) if (summaryInformationProperties == null)
{ {
@ -399,12 +420,12 @@ public class PropertyIDMap extends HashMap<Long,String> {
/** /**
* <p>Returns the Document Summary Information properties * Returns the Document Summary Information properties
* singleton.</p> * singleton.
* *
* @return 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) if (documentSummaryInformationProperties == null)
{ {
@ -435,7 +456,7 @@ public class PropertyIDMap extends HashMap<Long,String> {
/** /**
* <p>For the most basic testing.</p> * For the most basic testing.
* *
* @param args The command-line arguments * @param args The command-line arguments
*/ */

View File

@ -18,8 +18,10 @@
package org.apache.poi.hpsf.wellknown; package org.apache.poi.hpsf.wellknown;
import java.util.HashMap; 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;
/** /**
* <p>Maps section format IDs to {@link PropertyIDMap}s. It is * <p>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 * as keys. A key maps to a {@link PropertyIDMap} describing the
* property IDs in sections with the specified section format ID.</p> * property IDs in sections with the specified section format ID.</p>
*/ */
@SuppressWarnings({"rawtypes","unchecked"}) // Java Generics have issues on this style of class... @Internal
public class SectionIDMap extends HashMap { public class SectionIDMap {
/**
* The default section ID map. It maps section format IDs to {@link PropertyIDMap PropertyIDMaps}
*/
private static ThreadLocal<Map<ClassID,PropertyIDMap>> defaultMap =
new ThreadLocal<Map<ClassID,PropertyIDMap>>();
/** /**
* <p>The SummaryInformation's section's format ID.</p> * <p>The SummaryInformation's section's format ID.</p>
*/ */
public static final byte[] SUMMARY_INFORMATION_ID = new byte[] public static final ClassID SUMMARY_INFORMATION_ID =
{ new ClassID("{F29F85E0-4FF9-1068-AB91-08002B27B3D9}");
(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
};
/** /**
* <p>The DocumentSummaryInformation's first and second sections' format * The DocumentSummaryInformation's first and second sections' format ID.
* ID.</p>
*/ */
public static final byte[][] DOCUMENT_SUMMARY_INFORMATION_ID = new byte[][] private static final ClassID DOC_SUMMARY_INFORMATION =
{ new ClassID("{D5CDD502-2E9C-101B-9397-08002B2CF9AE}");
{ private static final ClassID USER_DEFINED_PROPERTIES =
(byte) 0xD5, (byte) 0xCD, (byte) 0xD5, (byte) 0x02, new ClassID("{D5CDD505-2E9C-101B-9397-08002B2CF9AE}");
(byte) 0x2E, (byte) 0x9C, (byte) 0x10, (byte) 0x1B,
(byte) 0x93, (byte) 0x97, (byte) 0x08, (byte) 0x00, public static final ClassID[] DOCUMENT_SUMMARY_INFORMATION_ID = {
(byte) 0x2B, (byte) 0x2C, (byte) 0xF9, (byte) 0xAE DOC_SUMMARY_INFORMATION, USER_DEFINED_PROPERTIES
},
{
(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
}
}; };
/** /**
* <p>A property without a known name is described by this string.</p> * A property without a known name is described by this string.
*/ */
public static final String UNDEFINED = "[undefined]"; public static final String UNDEFINED = "[undefined]";
/**
* <p>The default section ID map. It maps section format IDs to
* {@link PropertyIDMap}s.</p>
*/
private static SectionIDMap defaultMap;
/** /**
* <p>Returns the singleton instance of the default {@link * <p>Returns the singleton instance of the default {@link
* SectionIDMap}.</p> * SectionIDMap}.</p>
* *
* @return The instance value * @return The instance value
*/ */
public static SectionIDMap getInstance() public static SectionIDMap getInstance() {
{ Map<ClassID,PropertyIDMap> m = defaultMap.get();
if (defaultMap == null) if (m == null) {
{ m = new HashMap<ClassID,PropertyIDMap>();
final SectionIDMap m = new SectionIDMap(); m.put(SUMMARY_INFORMATION_ID, PropertyIDMap.getSummaryInformationProperties());
m.put(SUMMARY_INFORMATION_ID, m.put(DOCUMENT_SUMMARY_INFORMATION_ID[0], PropertyIDMap.getDocumentSummaryInformationProperties());
PropertyIDMap.getSummaryInformationProperties()); defaultMap.set(m);
m.put(DOCUMENT_SUMMARY_INFORMATION_ID[0],
PropertyIDMap.getDocumentSummaryInformationProperties());
defaultMap = m;
} }
return defaultMap; return new SectionIDMap();
} }
@ -117,16 +102,15 @@ public class SectionIDMap extends HashMap {
* /<var>sectionFormatID </var> combination is not well-known, the * /<var>sectionFormatID </var> combination is not well-known, the
* string "[undefined]" is returned. * string "[undefined]" is returned.
*/ */
public static String getPIDString(final byte[] sectionFormatID, public static String getPIDString(ClassID sectionFormatID, long pid) {
final long pid)
{
final PropertyIDMap m = getInstance().get(sectionFormatID); final PropertyIDMap m = getInstance().get(sectionFormatID);
if (m == null) { if (m == null) {
return UNDEFINED; return UNDEFINED;
} }
final String s = (String) m.get(pid); final String s = (String) m.get(pid);
if (s == null) if (s == null) {
return UNDEFINED; return UNDEFINED;
}
return s; return s;
} }
@ -139,28 +123,23 @@ public class SectionIDMap extends HashMap {
* @param sectionFormatID the section format ID * @param sectionFormatID the section format ID
* @return the property ID map * @return the property ID map
*/ */
public PropertyIDMap get(final byte[] sectionFormatID) public PropertyIDMap get(final ClassID sectionFormatID) {
{ return getInstance().get(sectionFormatID);
return (PropertyIDMap)super.get(new String(sectionFormatID, StringUtil.UTF8));
} }
/** /**
* <p>Associates a section format ID with a {@link * Associates a section format ID with a {@link PropertyIDMap}.
* PropertyIDMap}.</p>
* *
* @param sectionFormatID the section format ID * @param sectionFormatID the section format ID
* @param propertyIDMap the property ID map * @param propertyIDMap the property ID map
* @return as defined by {@link java.util.Map#put} * @return as defined by {@link java.util.Map#put}
*/ */
public PropertyIDMap put(final byte[] sectionFormatID, public PropertyIDMap put(ClassID sectionFormatID, PropertyIDMap propertyIDMap) {
final PropertyIDMap propertyIDMap) return getInstance().put(sectionFormatID, propertyIDMap);
{
return (PropertyIDMap)super.put(new String(sectionFormatID, StringUtil.UTF8), propertyIDMap);
} }
/** /**
* Associates the string representation of a section * Associates the string representation of a section format ID with a {@link PropertyIDMap}
* format ID with a {@link PropertyIDMap}
* *
* @param key the key of the PropertyIDMap * @param key the key of the PropertyIDMap
* @param value the PropertyIDMap itself * @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 * @return the previous PropertyIDMap stored under this key, or {@code null} if there wasn't one
*/ */
protected PropertyIDMap put(String key, PropertyIDMap value) { protected PropertyIDMap put(String key, PropertyIDMap value) {
return (PropertyIDMap)super.put(key, value); return put(new ClassID(key), value);
} }
} }

View File

@ -19,6 +19,7 @@ package org.apache.poi.hssf.record;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import org.apache.poi.hpsf.Property;
import org.apache.poi.util.CodePageUtil; import org.apache.poi.util.CodePageUtil;
@ -73,7 +74,7 @@ public final class OldStringRecord {
} }
protected static String getString(byte[] data, CodepageRecord codepage) { protected static String getString(byte[] data, CodepageRecord codepage) {
int cp = CodePageUtil.CP_ISO_8859_1; int cp = Property.DEFAULT_CODEPAGE;
if (codepage != null) { if (codepage != null) {
cp = codepage.getCodepage() & 0xffff; cp = codepage.getCodepage() & 0xffff;
} }

View File

@ -284,7 +284,7 @@ public class CodePageUtil
switch (codepage) { switch (codepage) {
case CP_UTF16: case CP_UTF16:
return "UTF-16"; return "UTF-16LE";
case CP_UTF16_BE: case CP_UTF16_BE:
return "UTF-16BE"; return "UTF-16BE";
case CP_UTF8: case CP_UTF8:
@ -466,6 +466,7 @@ public class CodePageUtil
sb.append((char)c); sb.append((char)c);
c = reader.read(); c = reader.read();
} }
reader.close();
return sb.toString(); return sb.toString();
} }
} }

View File

@ -22,20 +22,20 @@ import java.io.ByteArrayInputStream;
/** /**
* Adapts a plain byte array to {@link LittleEndianInput} * 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 public LittleEndianByteArrayInputStream(byte[] buf, int startOffset, int maxReadLen) { // NOSONAR
super(buf, startOffset, maxReadLen); super(buf, startOffset, maxReadLen);
} }
public LittleEndianByteArrayInputStream(byte[] buf, int startOffset) { public LittleEndianByteArrayInputStream(byte[] buf, int startOffset) {
super(buf, startOffset, buf.length - startOffset); this(buf, startOffset, buf.length - startOffset);
} }
public LittleEndianByteArrayInputStream(byte[] buf) { public LittleEndianByteArrayInputStream(byte[] buf) {
super(buf); this(buf, 0);
} }
private void checkPosition(int i) { protected void checkPosition(int i) {
if (i > count - pos) { if (i > count - pos) {
throw new RuntimeException("Buffer overrun"); throw new RuntimeException("Buffer overrun");
} }
@ -45,6 +45,14 @@ public final class LittleEndianByteArrayInputStream extends ByteArrayInputStream
return pos; return pos;
} }
public void setReadIndex(int pos) {
if (pos < 0 || pos >= count) {
throw new IndexOutOfBoundsException();
}
this.pos = pos;
}
@Override @Override
public byte readByte() { public byte readByte() {
checkPosition(1); checkPosition(1);
@ -73,22 +81,26 @@ public final class LittleEndianByteArrayInputStream extends ByteArrayInputStream
@Override @Override
public short readShort() { public short readShort() {
return (short)readUShort(); final int size = LittleEndianConsts.SHORT_SIZE;
checkPosition(size);
short le = LittleEndian.getShort(buf, pos);
long skipped = super.skip(size);
assert skipped == size : "Buffer overrun";
return le;
} }
@Override @Override
public int readUByte() { public int readUByte() {
return readByte() & 0xFF; return readByte() & 0x00FF;
} }
@Override @Override
public int readUShort() { public int readUShort() {
final int size = LittleEndianConsts.SHORT_SIZE; return readShort() & 0x00FFFF;
checkPosition(size); }
int le = LittleEndian.getUShort(buf, pos);
long skipped = super.skip(size); public long readUInt() {
assert skipped == size : "Buffer overrun"; return readInt() & 0x00FFFFFFFFL;
return le;
} }
@Override @Override

View File

@ -32,7 +32,7 @@ public class LittleEndianCP950Reader extends Reader {
private static final POILogger LOGGER = POILogFactory.getLogger(LittleEndianCP950Reader.class); 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 ByteBuffer doubleByteBuffer = ByteBuffer.allocate(2);
private final CharBuffer charBuffer = CharBuffer.allocate(2); private final CharBuffer charBuffer = CharBuffer.allocate(2);
private final CharsetDecoder decoder = StringUtil.BIG5.newDecoder(); private final CharsetDecoder decoder = StringUtil.BIG5.newDecoder();
@ -131,8 +131,7 @@ public class LittleEndianCP950Reader extends Reader {
} }
@Override @Override
public void close() throws IOException { public void close() {
} }
private int handleRange1(int leading, int trailing) { private int handleRange1(int leading, int trailing) {

View File

@ -146,5 +146,475 @@ public final class LocaleUtil {
public static Calendar getLocaleCalendar(TimeZone timeZone) { public static Calendar getLocaleCalendar(TimeZone timeZone) {
return Calendar.getInstance(timeZone, getUserLocale()); 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";
}
}
} }

View File

@ -192,7 +192,7 @@ public final class TestBasic {
(poiFiles[0].getBytes())); (poiFiles[0].getBytes()));
final List<Section> sections = si.getSections(); final List<Section> sections = si.getSections();
final Section s = sections.get(0); final Section s = sections.get(0);
assertArrayEquals(s.getFormatID().getBytes(), SectionIDMap.SUMMARY_INFORMATION_ID); assertEquals(s.getFormatID(), SectionIDMap.SUMMARY_INFORMATION_ID);
assertNotNull(s.getProperties()); assertNotNull(s.getProperties());
assertEquals(17, s.getPropertyCount()); assertEquals(17, s.getPropertyCount());
assertEquals("Titel", s.getProperty(2)); assertEquals("Titel", s.getProperty(2));

View File

@ -17,135 +17,62 @@
package org.apache.poi.hpsf.basic; 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.ByteArrayInputStream;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.FileNotFoundException;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.util.Date; import java.util.Date;
import java.util.Random; import java.util.Random;
import junit.framework.TestCase; import org.apache.poi.hpsf.CustomProperties;
import org.apache.poi.hpsf.DocumentSummaryInformation;
import org.apache.poi.hpsf.*; 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.DirectoryEntry;
import org.apache.poi.poifs.filesystem.DocumentEntry;
import org.apache.poi.poifs.filesystem.DocumentInputStream;
import org.apache.poi.poifs.filesystem.POIFSFileSystem; 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 * 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 * 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 * 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 POIFSFileSystem poifs ;
private DirectoryEntry dir;
private DocumentSummaryInformation dsi; private DocumentSummaryInformation dsi;
private SummaryInformation si; private SummaryInformation si;
@After
public void tearDown() throws Exception {
poifs.close();
}
/** /**
* Setup is used to get the document ready. Gets the DocumentSummaryInformation and the * Setup is used to get the document ready. Gets the DocumentSummaryInformation and the
* SummaryInformation to reasonable values * SummaryInformation to reasonable values
*/ */
@Override @Before
public void setUp() throws Exception { public void setUp() throws Exception {
bout = new ByteArrayOutputStream();
poifs = new POIFSFileSystem(); 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(); 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(); si = PropertySetFactory.newSummaryInformation();
assertNotNull(si); dsi.write(poifs.getRoot(), DocumentSummaryInformation.DEFAULT_STREAM_NAME);
} si.write(poifs.getRoot(), SummaryInformation.DEFAULT_STREAM_NAME);
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);
}
} }
/** /**
* Sets the most important information in DocumentSummaryInformation and Summary Information and rereads it * Sets the most important information in DocumentSummaryInformation and Summary Information and rereads it
*/ */
@Test
public void testOne() throws Exception { public void testOne() throws Exception {
// DocumentSummaryInformation // DocumentSummaryInformation
@ -201,9 +128,7 @@ public final class TestMetaDataIPI extends TestCase{
* serve as a container for custom properties. * serve as a container for custom properties.
*/ */
customProperties = dsi.getCustomProperties(); customProperties = dsi.getCustomProperties();
if (customProperties == null) { assertNotNull(customProperties);
fail();
}
/* Insert some custom properties into the container. */ /* Insert some custom properties into the container. */
String a1 = (String) customProperties.get("Key1"); String a1 = (String) customProperties.get("Key1");
@ -224,23 +149,10 @@ public final class TestMetaDataIPI extends TestCase{
assertEquals("Neg", new Integer(-100000), a7); 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 very long input in each of the fields (approx 30-60KB each)
*/ */
@Test
public void testTwo() throws Exception { public void testTwo() throws Exception {
String company = elongate("company"); 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.) * Tests with strange characters in keys and data (Umlaute etc.)
*/ */
@Test
public void testThree() throws Exception { public void testThree() throws Exception {
String company = strangize("company"); String company = strangize("company");
@ -423,50 +324,18 @@ public final class TestMetaDataIPI extends TestCase{
/** /**
* Iterative testing: writing, reading etc. * Iterative testing: writing, reading etc.
*/ */
@Test
public void testFour() throws Exception { public void testFour() throws Exception {
for (int i = 1; i < 100; i++) { 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 * Unicode test
*/ */
@Test
public void testUnicode() throws Exception { public void testUnicode() throws Exception {
String company = strangizeU("company"); String company = strangizeU("company");
String manager = strangizeU("manager"); String manager = strangizeU("manager");
@ -490,11 +359,8 @@ public final class TestMetaDataIPI extends TestCase{
si.setComments(comments); si.setComments(comments);
si.setKeywords(keywords); si.setKeywords(keywords);
si.setSubject(subject); si.setSubject(subject);
CustomProperties customProperties = dsi.getCustomProperties();
if (customProperties == null) {
customProperties = new CustomProperties();
}
CustomProperties customProperties = new CustomProperties();
/* Insert some custom properties into the container. */ /* Insert some custom properties into the container. */
customProperties.put(k1, p1); customProperties.put(k1, p1);
customProperties.put(k2, p2); customProperties.put(k2, p2);
@ -529,9 +395,7 @@ public final class TestMetaDataIPI extends TestCase{
* serve as a container for custom properties. * serve as a container for custom properties.
*/ */
customProperties = dsi.getCustomProperties(); customProperties = dsi.getCustomProperties();
if (customProperties == null) { assertNotNull(customProperties);
fail();
}
/* Insert some custom properties into the container. */ /* Insert some custom properties into the container. */
// System.out.println(k1); // System.out.println(k1);
@ -552,10 +416,11 @@ public final class TestMetaDataIPI extends TestCase{
* Iterative testing of the unicode test * Iterative testing of the unicode test
* *
*/ */
@Test
public void testSix() throws Exception { public void testSix() throws Exception {
for (int i = 1; i < 100; i++) { 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 * Tests conversion in custom fields and errors
*/ */
@Test
public void testConvAndExistence() throws Exception { public void testConvAndExistence() throws Exception {
CustomProperties customProperties = dsi.getCustomProperties(); CustomProperties customProperties = dsi.getCustomProperties();
@ -656,4 +522,86 @@ public final class TestMetaDataIPI extends TestCase{
assertTrue(customProperties.get("negdouble") instanceof Double); assertTrue(customProperties.get("negdouble") instanceof Double);
assertTrue(customProperties.get("date") instanceof Date); 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;
}
} }

View File

@ -17,77 +17,214 @@
package org.apache.poi.hpsf.basic; 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.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File; import java.io.File;
import java.io.FileFilter; import java.io.FileFilter;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.util.ArrayList;
import junit.framework.TestCase; import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.poi.POIDataSamples; 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.HPSFException;
import org.apache.poi.hpsf.MarkUnsupportedException;
import org.apache.poi.hpsf.NoPropertySetStreamException;
import org.apache.poi.hpsf.PropertySet;
import org.apache.poi.hpsf.PropertySetFactory; import org.apache.poi.hpsf.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;
/** /**
* <p>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 * 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.</p> * OLE2 file, just add it to the "data" directory and run this test case.
*/ */
public class TestReadAllFiles extends TestCase { @RunWith(Parameterized.class)
private static String[] excludes = new String[] {}; public class TestReadAllFiles {
private static final POIDataSamples _samples = POIDataSamples.getHPSFInstance();
/** @Parameters(name="{index}: {0} using {1}")
* <p>This test methods reads all property set streams from all POI public static Iterable<Object[]> files() {
* filesystems in the "data" directory.</p> final List<Object[]> files = new ArrayList<Object[]>();
*
* @throws IOException _samples.getFile("").listFiles(new FileFilter() {
* @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 @Override
public boolean accept(final File f) public boolean accept(final File f) {
{ if (f.getName().startsWith("Test")) { // && f.getName().equals("TestCorel.shw")
// exclude files that we know will fail files.add(new Object[]{ f });
return f.isFile() && checkExclude(f); }
return false;
} }
}); });
for (final File f : fileList) { return files;
/* Read the POI filesystem's property set streams: */ }
final POIFile[] psf1 = Util.readPropertySets(f);
for (int j = 0; j < psf1.length; j++) @Parameter(value=0)
{ public File file;
final InputStream in =
new ByteArrayInputStream(psf1[j].getBytes()); /**
* 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 { try {
PropertySetFactory.create(in); PropertySetFactory.create(in);
} catch (Exception e) { } finally {
throw new IOException("While handling file: " + f + " at " + j, e); 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:<p>
*
* <ul>
* <li>Read its property set streams.
* <li>Create a new POI filesystem containing the origin file's property set streams.
* <li>Read the property set streams from the POI filesystem just created.
* <li>Compare each property set stream with the corresponding one from
* the origin file and check whether they are equal.
* </ul>
*/
@Test
public void recreate() throws IOException, HPSFException {
/* Read the POI filesystem's property set streams: */
Map<String,PropertySet> psMap = new HashMap<String,PropertySet>();
/* 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<String,PropertySet> 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();
}
/**
* <p>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.</p>
* @throws Exception
*/
@Test
public void readDocumentSummaryInformation() throws Exception {
/* Read a test document <em>doc</em> 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();
}
} }
/** /**
* Returns true if the file should be checked, false if it should be excluded. * <p>Tests the simplified custom properties by reading them from the
* available test files.</p>
* *
* @param f * @throws Throwable if anything goes wrong.
* @return
*/ */
public static boolean checkExclude(File f) { @Test
for(String exclude : excludes) { public void readCustomPropertiesFromFiles() throws Exception {
if(f.getAbsolutePath().endsWith(exclude)) { /* Read a test document <em>doc</em> into a POI filesystem. */
return false; 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();
} }
} }
return true;
}
} }

View File

@ -18,6 +18,7 @@
package org.apache.poi.hpsf.basic; package org.apache.poi.hpsf.basic;
import static org.hamcrest.core.IsEqual.equalTo; import static org.hamcrest.core.IsEqual.equalTo;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThat; import static org.junit.Assert.assertThat;
@ -27,7 +28,6 @@ import static org.junit.Assert.fail;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.File; import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
@ -37,6 +37,7 @@ import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.util.Date; import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Map; 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.poifs.filesystem.POIFSFileSystem;
import org.apache.poi.util.CodePageUtil; import org.apache.poi.util.CodePageUtil;
import org.apache.poi.util.IOUtils; 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.apache.poi.util.TempFile;
import org.junit.Before; import org.junit.Assume;
import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
/** /**
* <p>Tests HPSF's writing functionality.</p> * Tests HPSF's writing functionality
*/ */
public class TestWrite public class TestWrite {
{
private static final POIDataSamples _samples = POIDataSamples.getHPSFInstance(); 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; private static final int BYTE_ORDER = 0xfffe;
static final int FORMAT = 0x0000; private static final int FORMAT = 0x0000;
static final int OS_VERSION = 0x00020A04; private static final int OS_VERSION = 0x00020A04;
static final int[] SECTION_COUNT = {1, 2}; private static final int[] SECTION_COUNT = {1, 2};
static final boolean[] IS_SUMMARY_INFORMATION = {true, false}; private static final boolean[] IS_SUMMARY_INFORMATION = {true, false};
static final boolean[] IS_DOCUMENT_SUMMARY_INFORMATION = {false, true}; 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() + "Your default character set is " + getDefaultCharsetName() +
". However, this testcase must be run in an environment " + ". However, this testcase must be run in an environment " +
"with a default character set supporting at least " + "with a default character set supporting at least " +
@ -104,9 +106,8 @@ public class TestWrite
POIFile[] poiFiles; POIFile[] poiFiles;
@Before @BeforeClass
public void setUp() public static void setUp() {
{
VariantSupport.setLogUnsupportedTypes(false); VariantSupport.setLogUnsupportedTypes(false);
} }
@ -117,8 +118,7 @@ public class TestWrite
* @exception IOException if an I/O exception occurs * @exception IOException if an I/O exception occurs
*/ */
@Test(expected=NoFormatIDException.class) @Test(expected=NoFormatIDException.class)
public void withoutAFormatID() throws Exception public void withoutAFormatID() throws Exception {
{
final File filename = TempFile.createTempFile(POI_FS, ".doc"); final File filename = TempFile.createTempFile(POI_FS, ".doc");
/* Create a mutable property set with a section that does not have the /* Create a mutable property set with a section that does not have the
@ -144,8 +144,6 @@ public class TestWrite
} }
} }
/** /**
* <p>Writes an empty property set to a POIFS and reads it back * <p>Writes an empty property set to a POIFS and reads it back
* in.</p> * in.</p>
@ -156,8 +154,7 @@ public class TestWrite
*/ */
@Test @Test
public void writeEmptyPropertySet() public void writeEmptyPropertySet()
throws IOException, UnsupportedVariantTypeException throws IOException, UnsupportedVariantTypeException {
{
final File dataDir = _samples.getFile(""); final File dataDir = _samples.getFile("");
final File filename = new File(dataDir, POI_FS); final File filename = new File(dataDir, POI_FS);
filename.deleteOnExit(); filename.deleteOnExit();
@ -203,8 +200,7 @@ public class TestWrite
*/ */
@Test @Test
public void writeSimplePropertySet() public void writeSimplePropertySet()
throws IOException, UnsupportedVariantTypeException throws IOException, UnsupportedVariantTypeException {
{
final String AUTHOR = "Rainer Klute"; final String AUTHOR = "Rainer Klute";
final String TITLE = "Test Document"; final String TITLE = "Test Document";
final File dataDir = _samples.getFile(""); final File dataDir = _samples.getFile("");
@ -235,24 +231,17 @@ public class TestWrite
/* Read the POIFS: */ /* Read the POIFS: */
final PropertySet[] psa = new PropertySet[1]; final PropertySet[] psa = new PropertySet[1];
final POIFSReader r = new POIFSReader(); final POIFSReader r = new POIFSReader();
r.registerListener(new POIFSReaderListener() r.registerListener(new POIFSReaderListener() {
{
@Override @Override
public void processPOIFSReaderEvent public void processPOIFSReaderEvent(final POIFSReaderEvent event) {
(final POIFSReaderEvent event) try {
{
try
{
psa[0] = PropertySetFactory.create(event.getStream()); psa[0] = PropertySetFactory.create(event.getStream());
} } catch (Exception ex) {
catch (Exception ex)
{
fail(org.apache.poi.hpsf.Util.toString(ex)); fail(org.apache.poi.hpsf.Util.toString(ex));
} }
} }},
SummaryInformation.DEFAULT_STREAM_NAME
}, );
SummaryInformation.DEFAULT_STREAM_NAME);
InputStream stream = new FileInputStream(filename); InputStream stream = new FileInputStream(filename);
try { try {
@ -281,9 +270,7 @@ public class TestWrite
* a variant type to be written * a variant type to be written
*/ */
@Test @Test
public void writeTwoSections() public void writeTwoSections() throws WritingNotSupportedException, IOException {
throws WritingNotSupportedException, IOException
{
final String STREAM_NAME = "PropertySetStream"; final String STREAM_NAME = "PropertySetStream";
final String SECTION1 = "Section 1"; final String SECTION1 = "Section 1";
final String SECTION2 = "Section 2"; final String SECTION2 = "Section 2";
@ -318,18 +305,12 @@ public class TestWrite
/* Read the POIFS: */ /* Read the POIFS: */
final PropertySet[] psa = new PropertySet[1]; final PropertySet[] psa = new PropertySet[1];
final POIFSReader r = new POIFSReader(); final POIFSReader r = new POIFSReader();
r.registerListener(new POIFSReaderListener() r.registerListener(new POIFSReaderListener() {
{
@Override @Override
public void processPOIFSReaderEvent public void processPOIFSReaderEvent(final POIFSReaderEvent event) {
(final POIFSReaderEvent event) try {
{
try
{
psa[0] = PropertySetFactory.create(event.getStream()); psa[0] = PropertySetFactory.create(event.getStream());
} } catch (Exception ex) {
catch (Exception ex)
{
throw new RuntimeException(ex); throw new RuntimeException(ex);
} }
} }
@ -353,17 +334,12 @@ public class TestWrite
static class MyPOIFSReaderListener implements POIFSReaderListener static class MyPOIFSReaderListener implements POIFSReaderListener {
{
@Override @Override
public void processPOIFSReaderEvent(final POIFSReaderEvent event) public void processPOIFSReaderEvent(final POIFSReaderEvent event) {
{ try {
try
{
PropertySetFactory.create(event.getStream()); PropertySetFactory.create(event.getStream());
} } catch (Exception ex) {
catch (Exception ex)
{
fail(org.apache.poi.hpsf.Util.toString(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;
/** /**
* <p>Writes and reads back various variant types and checks whether the * <p>Writes and reads back various variant types and checks whether the
* stuff that has been read back equals the stuff that was written.</p> * stuff that has been read back equals the stuff that was written.</p>
* @throws IOException
* @throws UnsupportedEncodingException
* @throws UnsupportedVariantTypeException
* @throws ReadingNotSupportedException
*/ */
@Test @Test
public void variantTypes() public void variantTypes() throws Exception {
{
Throwable t = null;
final int codepage = CODEPAGE_DEFAULT; final int codepage = CODEPAGE_DEFAULT;
if (!hasProperDefaultCharset()) Assume.assumeTrue(IMPROPER_DEFAULT_CHARSET_MESSAGE, hasProperDefaultCharset());
{
System.err.println(IMPROPER_DEFAULT_CHARSET_MESSAGE +
" This testcase is skipped.");
return;
}
try
{
check(Variant.VT_EMPTY, null, codepage); check(Variant.VT_EMPTY, null, codepage);
check(Variant.VT_BOOL, Boolean.TRUE, codepage); check(Variant.VT_BOOL, Boolean.TRUE, codepage);
check(Variant.VT_BOOL, Boolean.FALSE, 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, check( Variant.VT_CF, new byte[] { 8, 0, 0, 0, 1, 0, 0, 0, 1, 2, 3, 4 }, codepage );
4 }, codepage ); check(Variant.VT_I4, 27, codepage);
check(Variant.VT_I4, Integer.valueOf(27), codepage); check(Variant.VT_I8, 28L, codepage);
check(Variant.VT_I8, Long.valueOf(28), codepage); check(Variant.VT_R8, 29.0d, codepage);
check(Variant.VT_R8, new Double(29.0), codepage); check(Variant.VT_I4, -27, codepage);
check(Variant.VT_I4, Integer.valueOf(-27), codepage); check(Variant.VT_I8, -28L, codepage);
check(Variant.VT_I8, Long.valueOf(-28), codepage); check(Variant.VT_R8, -29.0d, codepage);
check(Variant.VT_R8, new Double(-29.0), codepage);
check(Variant.VT_FILETIME, new Date(), codepage); check(Variant.VT_FILETIME, new Date(), codepage);
check(Variant.VT_I4, new Integer(Integer.MAX_VALUE), codepage); check(Variant.VT_I4, Integer.MAX_VALUE, codepage);
check(Variant.VT_I4, new Integer(Integer.MIN_VALUE), codepage); check(Variant.VT_I4, Integer.MIN_VALUE, codepage);
check(Variant.VT_I8, new Long(Long.MAX_VALUE), codepage); check(Variant.VT_I8, Long.MAX_VALUE, codepage);
check(Variant.VT_I8, new Long(Long.MIN_VALUE), codepage); check(Variant.VT_I8, Long.MIN_VALUE, codepage);
check(Variant.VT_R8, new Double(Double.MAX_VALUE), codepage); check(Variant.VT_R8, Double.MAX_VALUE, codepage);
check(Variant.VT_R8, new Double(Double.MIN_VALUE), codepage); check(Variant.VT_R8, Double.MIN_VALUE, codepage);
checkString(Variant.VT_LPSTR, "\u00e4\u00f6\u00fc\u00df\u00c4\u00d6\u00dc", codepage);
check(Variant.VT_LPSTR, checkString(Variant.VT_LPWSTR, "\u00e4\u00f6\u00fc\u00df\u00c4\u00d6\u00dc", codepage);
"", 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));
} }
/** /**
* <p>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 * checks whether the stuff that has been read back equals the stuff that
* was written.</p> * was written.
*/ */
@Test @Test
public void codepages() public void codepages() throws ReadingNotSupportedException, UnsupportedVariantTypeException, IOException
{ {
Throwable thr = null; Throwable thr = null;
final int[] validCodepages = new int[] final int[] validCodepages = {CODEPAGE_DEFAULT, CodePageUtil.CP_UTF8, CodePageUtil.CP_UNICODE, CodePageUtil.CP_WINDOWS_1252};
{CODEPAGE_DEFAULT, CODEPAGE_UTF8, CODEPAGE_UTF16, CODEPAGE_1252};
for (final int cp : validCodepages) { for (final int cp : validCodepages) {
if (cp == -1 && !hasProperDefaultCharset()) if (cp == -1 && !hasProperDefaultCharset())
{ {
@ -482,65 +401,22 @@ public class TestWrite
continue; continue;
} }
final long t = cp == CODEPAGE_UTF16 ? Variant.VT_LPWSTR final long t = (cp == CodePageUtil.CP_UNICODE) ? Variant.VT_LPWSTR : Variant.VT_LPSTR;
: Variant.VT_LPSTR; checkString(t, "\u00e4\u00f6\u00fc\u00c4\u00d6\u00dc\u00df", cp);
try if (cp == CodePageUtil.CP_UTF16 || cp == CodePageUtil.CP_UTF8) {
{
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); 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}; final int[] invalidCodepages = new int[] {0, 1, 2, 4711, 815};
for (int cp : invalidCodepages) { for (int cp : invalidCodepages) {
final long type = cp == CODEPAGE_UTF16 ? Variant.VT_LPWSTR final long type = (cp == CodePageUtil.CP_UNICODE) ? Variant.VT_LPWSTR : Variant.VT_LPSTR;
: Variant.VT_LPSTR; try {
try checkString(type, "\u00e4\u00f6\u00fc\u00c4\u00d6\u00dc\u00df", cp);
{ fail("UnsupportedEncodingException for codepage " + cp + " expected.");
check(type, "", cp); } catch (UnsupportedEncodingException ex) {
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)
{
/* This is the expected behaviour. */ /* 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
/** /**
* <p>Tests whether writing 8-bit characters to a Unicode property * Tests whether writing 8-bit characters to a Unicode property succeeds.
* succeeds.</p>
*/ */
@Test @Test
public void unicodeWrite8Bit() public void unicodeWrite8Bit() throws WritingNotSupportedException, IOException, NoPropertySetStreamException {
{
final String TITLE = "This is a sample title"; final String TITLE = "This is a sample title";
final MutablePropertySet mps = new MutablePropertySet(); final MutablePropertySet mps = new MutablePropertySet();
final MutableSection ms = (MutableSection) mps.getSections().get(0); final MutableSection ms = (MutableSection) mps.getSections().get(0);
@ -564,9 +438,6 @@ public class TestWrite
p.setValue(TITLE); p.setValue(TITLE);
ms.setProperty(p); ms.setProperty(p);
Throwable t = null;
try
{
ByteArrayOutputStream out = new ByteArrayOutputStream(); ByteArrayOutputStream out = new ByteArrayOutputStream();
mps.write(out); mps.write(out);
out.close(); out.close();
@ -578,204 +449,46 @@ public class TestWrite
String title = (String) sr.getProperty(PropertyIDMap.PID_TITLE); String title = (String) sr.getProperty(PropertyIDMap.PID_TITLE);
assertEquals(TITLE, title); assertEquals(TITLE, title);
} }
catch (WritingNotSupportedException e)
{
t = e;
}
catch (IOException e)
{
t = e;
}
catch (NoPropertySetStreamException e)
{
t = e;
}
if (t != null)
fail(t.getMessage());
}
private void checkString(final long variantType, final String value, final int codepage)
throws UnsupportedVariantTypeException, IOException, ReadingNotSupportedException, UnsupportedEncodingException {
for (int i=0; i<value.length(); i++) {
check(variantType, value.substring(0, i), codepage);
}
}
/** /**
* <p>Writes a property and reads it back in.</p> * Writes a property and reads it back in.
* *
* @param variantType The property's variant type. * @param variantType The property's variant type.
* @param value The property's value. * @param value The property's value.
* @param codepage The codepage to use for writing and reading. * @param codepage The codepage to use for writing and reading.
* @throws UnsupportedVariantTypeException if the variant is not supported. * @throws UnsupportedVariantTypeException if the variant is not supported.
* @throws IOException if an I/O exception occurs. * @throws IOException if an I/O exception occurs.
* @throws ReadingNotSupportedException
* @throws UnsupportedEncodingException
*/ */
private void check(final long variantType, final Object value, private void check(final long variantType, final Object value, final int codepage)
final int codepage) throws UnsupportedVariantTypeException, IOException, ReadingNotSupportedException, UnsupportedEncodingException
throws UnsupportedVariantTypeException, IOException,
ReadingNotSupportedException, UnsupportedEncodingException
{ {
final ByteArrayOutputStream out = new ByteArrayOutputStream(); final ByteArrayOutputStream out = new ByteArrayOutputStream();
VariantSupport.write(out, variantType, value, codepage); VariantSupport.write(out, variantType, value, codepage);
out.close(); out.close();
final byte[] b = out.toByteArray(); final byte[] b = out.toByteArray();
final Object objRead = final Object objRead =
VariantSupport.read(b, 0, b.length + LittleEndian.INT_SIZE, VariantSupport.read(b, 0, b.length + LittleEndianConsts.INT_SIZE, variantType, codepage);
variantType, codepage); if (objRead instanceof byte[]) {
if (objRead instanceof byte[]) assertArrayEquals((byte[])value, (byte[])objRead);
{ } else if (value != null && !value.equals(objRead)) {
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); assertEquals(value, objRead);
} }
/**
* <p>Compares two byte arrays.</p>
*
* @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;
} }
/**
* <p>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:</p>
*
* <ul>
*
* <li><p>Read its property set streams.</p></li>
*
* <li><p>Create a new POI filesystem containing the origin file's
* property set streams.</p></li>
*
* <li><p>Read the property set streams from the POI filesystem just
* created.</p></li>
*
* <li><p>Compare each property set stream with the corresponding one from
* the origin file and check whether they are equal.</p></li>
*
* </ul>
* @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);
}
}
}
/**
* <p>Performs the check described in {@link #recreate()} for a single
* POI filesystem.</p>
*
* @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);
}
}
/** /**
* <p>Tests writing and reading back a proper dictionary.</p> * <p>Tests writing and reading back a proper dictionary.</p>
* @throws IOException * @throws IOException
* @throws HPSFException * @throws HPSFException
*/ */
@Test @Test
public void dictionary() throws IOException, HPSFException public void dictionary() throws IOException, HPSFException {
{
final File copy = TempFile.createTempFile("Test-HPSF", "ole2"); final File copy = TempFile.createTempFile("Test-HPSF", "ole2");
copy.deleteOnExit(); copy.deleteOnExit();
@ -791,17 +504,16 @@ public class TestWrite
s.setDictionary(m); s.setDictionary(m);
s.setFormatID(SectionIDMap.DOCUMENT_SUMMARY_INFORMATION_ID[0]); s.setFormatID(SectionIDMap.DOCUMENT_SUMMARY_INFORMATION_ID[0]);
int codepage = CodePageUtil.CP_UNICODE; int codepage = CodePageUtil.CP_UNICODE;
s.setProperty(PropertyIDMap.PID_CODEPAGE, Variant.VT_I2, s.setProperty(PropertyIDMap.PID_CODEPAGE, Variant.VT_I2, codepage);
Integer.valueOf(codepage));
poiFs.createDocument(ps1.toInputStream(), "Test"); poiFs.createDocument(ps1.toInputStream(), "Test");
poiFs.writeFilesystem(out); poiFs.writeFilesystem(out);
poiFs.close(); poiFs.close();
out.close(); out.close();
/* Read back: */ /* Read back: */
final POIFile[] psf = Util.readPropertySets(copy); final List<POIFile> psf = Util.readPropertySets(copy);
assertEquals(1, psf.length); assertEquals(1, psf.size());
final byte[] bytes = psf[0].getBytes(); final byte[] bytes = psf.get(0).getBytes();
final InputStream in = new ByteArrayInputStream(bytes); final InputStream in = new ByteArrayInputStream(bytes);
final PropertySet ps2 = PropertySetFactory.create(in); final PropertySet ps2 = PropertySetFactory.create(in);
@ -1039,8 +751,7 @@ public class TestWrite
* @throws HPSFException * @throws HPSFException
*/ */
@Test(expected=IllegalPropertySetDataException.class) @Test(expected=IllegalPropertySetDataException.class)
public void dictionaryWithInvalidCodepage() throws IOException, HPSFException public void dictionaryWithInvalidCodepage() throws IOException, HPSFException {
{
final File copy = TempFile.createTempFile("Test-HPSF", "ole2"); final File copy = TempFile.createTempFile("Test-HPSF", "ole2");
copy.deleteOnExit(); copy.deleteOnExit();
@ -1069,22 +780,17 @@ public class TestWrite
} }
} }
/** /**
* <p>Returns the display name of the default character set.</p> * <p>Returns the display name of the default character set.</p>
* *
* @return 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 String charSetName = System.getProperty("file.encoding");
final Charset charSet = Charset.forName(charSetName); final Charset charSet = Charset.forName(charSetName);
return charSet.displayName(Locale.ROOT); return charSet.displayName(Locale.ROOT);
} }
/** /**
* <p>In order to execute tests with characters beyond US-ASCII, this * <p>In order to execute tests with characters beyond US-ASCII, this
* method checks whether the application is runing in an environment * method checks whether the application is runing in an environment
@ -1093,8 +799,7 @@ public class TestWrite
* @return <code>true</code> if the default character set is 16-bit-capable, * @return <code>true</code> if the default character set is 16-bit-capable,
* else <code>false</code>. * else <code>false</code>.
*/ */
private boolean hasProperDefaultCharset() private boolean hasProperDefaultCharset() {
{
final String charSetName = System.getProperty("file.encoding"); final String charSetName = System.getProperty("file.encoding");
final Charset charSet = Charset.forName(charSetName); final Charset charSet = Charset.forName(charSetName);
return charSet.newEncoder().canEncode('\u00e4'); return charSet.newEncoder().canEncode('\u00e4');

View File

@ -24,9 +24,7 @@ import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import java.io.File; import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; 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.VariantSupport;
import org.apache.poi.hpsf.WritingNotSupportedException; import org.apache.poi.hpsf.WritingNotSupportedException;
import org.apache.poi.hpsf.wellknown.SectionIDMap; 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.DocumentInputStream;
import org.apache.poi.poifs.filesystem.NPOIFSFileSystem; import org.apache.poi.poifs.filesystem.NPOIFSFileSystem;
import org.apache.poi.util.IOUtils; import org.apache.poi.util.IOUtils;
@ -73,70 +70,6 @@ public class TestWriteWellKnown {
VariantSupport.setLogUnsupportedTypes(false); VariantSupport.setLogUnsupportedTypes(false);
} }
/**
* <p>This test method checks whether DocumentSummary information streams
* can be read. This is done by opening all "Test*" files in the '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.</p>
*/
@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 <em>doc</em> 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_APPLICATION_NAME = "ApplicationName";
static final String P_AUTHOR = "Author"; static final String P_AUTHOR = "Author";
static final int P_CHAR_COUNT = 4712; static final int P_CHAR_COUNT = 4712;
@ -483,15 +416,12 @@ public class TestWriteWellKnown {
dsi.removeScale(); dsi.removeScale();
dsi.removeSlideCount(); dsi.removeSlideCount();
/* // Write the summary information stream and the document summary
* <li><p>Write the summary information stream and the document summary // information stream to the POI filesystem.
* information stream to the POI filesystem. */
si.write(poifs.getRoot(), SummaryInformation.DEFAULT_STREAM_NAME); si.write(poifs.getRoot(), SummaryInformation.DEFAULT_STREAM_NAME);
dsi.write(poifs.getRoot(), DocumentSummaryInformation.DEFAULT_STREAM_NAME); dsi.write(poifs.getRoot(), DocumentSummaryInformation.DEFAULT_STREAM_NAME);
/* // Write the POI filesystem to a (temporary) file doc3 and close the latter.
* <li><p>Write the POI filesystem to a (temporary) file <em>doc3</em>
* and close the latter. */
FileOutputStream out = new FileOutputStream(fileOut); FileOutputStream out = new FileOutputStream(fileOut);
poifs.writeFilesystem(out); poifs.writeFilesystem(out);
out.close(); out.close();
@ -501,9 +431,9 @@ public class TestWriteWellKnown {
} }
/* /*
* Open <em>doc3</em> 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 * and document summary information. All properties removed before must not
* be found in the property streams of <em>doc3</em>. * be found in the property streams of {@code doc3}.
*/ */
private static CustomProperties write3rdFile(File fileIn, File fileOut) throws Exception { private static CustomProperties write3rdFile(File fileIn, File fileOut) throws Exception {
NPOIFSFileSystem poifs = new NPOIFSFileSystem(fileIn, false); NPOIFSFileSystem poifs = new NPOIFSFileSystem(fileIn, false);
@ -571,7 +501,12 @@ public class TestWriteWellKnown {
return si; 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); DocumentInputStream dis = poifs.createDocumentInputStream(DocumentSummaryInformation.DEFAULT_STREAM_NAME);
PropertySet ps = new PropertySet(dis); PropertySet ps = new PropertySet(dis);
DocumentSummaryInformation dsi = new DocumentSummaryInformation(ps); DocumentSummaryInformation dsi = new DocumentSummaryInformation(ps);
@ -579,83 +514,8 @@ public class TestWriteWellKnown {
return dsi; return dsi;
} }
/** /**
* <p>Tests the simplified custom properties by reading them from the * Tests basic custom property features.
* available test files.</p>
*
* @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 <em>doc</em> 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);
}
}
}
/**
* <p>Tests basic custom property features.</p>
*/ */
@Test @Test
public void testCustomerProperties() public void testCustomerProperties()
@ -693,8 +553,8 @@ public class TestWriteWellKnown {
/** /**
* <p>Tests reading custom properties from a section including reading * Tests reading custom properties from a section including reading
* custom properties which are not pure.</p> * custom properties which are not pure.
*/ */
@Test @Test
public void testGetCustomerProperties() public void testGetCustomerProperties()

View File

@ -19,13 +19,11 @@
package org.apache.poi.hpsf.basic; package org.apache.poi.hpsf.basic;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.EOFException;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.LinkedList; 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.POIFSReader;
import org.apache.poi.poifs.eventfilesystem.POIFSReaderEvent; import org.apache.poi.poifs.eventfilesystem.POIFSReaderEvent;
import org.apache.poi.poifs.eventfilesystem.POIFSReaderListener; 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 { final class Util {
/**
* <p>Reads bytes from an input stream and writes them to an
* output stream until end of file is encountered.</p>
*
* @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;
}
}
}
/** /**
* <p>Reads all files from a POI filesystem and returns them as an * <p>Reads all files from a POI filesystem and returns them as an
* array of {@link POIFile} instances. This method loads all files * array of {@link POIFile} instances. This method loads all files
@ -143,7 +106,7 @@ final class Util {
final InputStream in = event.getStream(); final InputStream in = event.getStream();
final ByteArrayOutputStream out = final ByteArrayOutputStream out =
new ByteArrayOutputStream(); new ByteArrayOutputStream();
Util.copy(in, out); IOUtils.copy(in, out);
out.close(); out.close();
f.setBytes(out.toByteArray()); f.setBytes(out.toByteArray());
files.add(f); files.add(f);
@ -192,34 +155,33 @@ final class Util {
* *
* @exception IOException if an I/O exception occurs * @exception IOException if an I/O exception occurs
*/ */
public static POIFile[] readPropertySets(final File poiFs) public static List<POIFile> readPropertySets(final File poiFs)
throws FileNotFoundException, IOException throws FileNotFoundException, IOException {
{ FileInputStream stream = new FileInputStream(poiFs);
try {
return readPropertySets(stream);
} finally {
stream.close();
}
}
public static List<POIFile> readPropertySets(final InputStream poiFs)
throws FileNotFoundException, IOException {
final List<POIFile> files = new ArrayList<POIFile>(7); final List<POIFile> files = new ArrayList<POIFile>(7);
final POIFSReader r = new POIFSReader(); final POIFSReader r = new POIFSReader();
POIFSReaderListener pfl = new POIFSReaderListener() POIFSReaderListener pfl = new POIFSReaderListener() {
{
@Override @Override
public void processPOIFSReaderEvent(final POIFSReaderEvent event) public void processPOIFSReaderEvent(final POIFSReaderEvent event) {
{ try {
try
{
final POIFile f = new POIFile(); final POIFile f = new POIFile();
f.setName(event.getName()); f.setName(event.getName());
f.setPath(event.getPath()); f.setPath(event.getPath());
final InputStream in = event.getStream(); final InputStream in = event.getStream();
if (PropertySet.isPropertySetStream(in)) if (PropertySet.isPropertySetStream(in)) {
{ f.setBytes(IOUtils.toByteArray(in));
final ByteArrayOutputStream out =
new ByteArrayOutputStream();
Util.copy(in, out);
out.close();
f.setBytes(out.toByteArray());
files.add(f); files.add(f);
} }
} } catch (Exception ex) {
catch (Exception ex)
{
throw new RuntimeException(ex); throw new RuntimeException(ex);
} }
} }
@ -229,17 +191,9 @@ final class Util {
r.registerListener(pfl); r.registerListener(pfl);
/* Read the POI filesystem. */ /* Read the POI filesystem. */
FileInputStream stream = new FileInputStream(poiFs); r.read(poiFs);
try {
r.read(stream);
} finally {
stream.close();
}
POIFile[] result = new POIFile[files.size()]; return files;
for (int i = 0; i < result.length; i++)
result[i] = files.get(i);
return result;
} }

Binary file not shown.

Binary file not shown.