#52117 - Invalid "last printed" summary field value - added helper method to identify undefined dates

HPSF: fixed uid listing in Section.toString()
HPSF: moved timestamp based "utility" methods to Filetime class
HPSF: preserve original datastream for unchanged property sets

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1795123 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Andreas Beeker 2017-05-14 22:25:33 +00:00
parent 965860b116
commit 98b10cf684
17 changed files with 487 additions and 624 deletions

View File

@ -36,7 +36,6 @@ import org.apache.poi.hpsf.MutablePropertySet;
import org.apache.poi.hpsf.NoPropertySetStreamException;
import org.apache.poi.hpsf.PropertySet;
import org.apache.poi.hpsf.PropertySetFactory;
import org.apache.poi.hpsf.Util;
import org.apache.poi.hpsf.WritingNotSupportedException;
import org.apache.poi.poifs.eventfilesystem.POIFSReader;
import org.apache.poi.poifs.eventfilesystem.POIFSReaderEvent;
@ -352,13 +351,11 @@ public class CopyCompare
/* According to the definition of the processPOIFSReaderEvent method
* we cannot pass checked exceptions to the caller. The following
* lines check whether a checked exception occured and throws an
* lines check whether a checked exception occurred and throws an
* unchecked exception. The message of that exception is that of
* the underlying checked exception. */
if (t != null) {
throw new HPSFRuntimeException
("Could not read file \"" + path + "/" + name +
"\". Reason: " + Util.toString(t));
throw new HPSFRuntimeException("Could not read file \"" + path + "/" + name, t);
}
}

View File

@ -36,7 +36,6 @@ import org.apache.poi.hpsf.NoPropertySetStreamException;
import org.apache.poi.hpsf.PropertySet;
import org.apache.poi.hpsf.PropertySetFactory;
import org.apache.poi.hpsf.SummaryInformation;
import org.apache.poi.hpsf.Util;
import org.apache.poi.hpsf.Variant;
import org.apache.poi.hpsf.WritingNotSupportedException;
import org.apache.poi.hpsf.wellknown.PropertyIDMap;
@ -211,9 +210,7 @@ public class WriteAuthorAndTitle
* unchecked exception. The message of that exception is that of
* the underlying checked exception. */
if (t != null) {
throw new HPSFRuntimeException
("Could not read file \"" + path + "/" + name +
"\". Reason: " + Util.toString(t));
throw new HPSFRuntimeException("Could not read file \"" + path + "/" + name, t);
}
}

View File

@ -18,24 +18,40 @@ package org.apache.poi.hpsf;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Date;
import org.apache.poi.util.LittleEndian;
import org.apache.poi.util.LittleEndianByteArrayInputStream;
import org.apache.poi.util.LittleEndianConsts;
class Filetime {
private static final int SIZE = LittleEndian.INT_SIZE * 2;
public class Filetime {
/**
* The difference between the Windows epoch (1601-01-01
* 00:00:00) and the Unix epoch (1970-01-01 00:00:00) in
* milliseconds.
*/
private static final long EPOCH_DIFF = -11644473600000L;
private static final int SIZE = LittleEndian.INT_SIZE * 2;
private static final long UINT_MASK = 0x00000000FFFFFFFFL;
private static final long NANO_100 = 1000L * 10L;
private int _dwHighDateTime;
private int _dwLowDateTime;
Filetime() {}
Filetime() {}
Filetime( int low, int high ) {
_dwLowDateTime = low;
_dwHighDateTime = high;
}
Filetime( Date date ) {
long filetime = Filetime.dateToFileTime(date);
_dwHighDateTime = (int) ((filetime >>> 32) & UINT_MASK);
_dwLowDateTime = (int) (filetime & UINT_MASK);
}
void read( LittleEndianByteArrayInputStream lei ) {
_dwLowDateTime = lei.readInt();
@ -53,8 +69,7 @@ class Filetime {
byte[] toByteArray() {
byte[] result = new byte[SIZE];
LittleEndian.putInt( result, 0 * LittleEndianConsts.INT_SIZE, _dwLowDateTime );
LittleEndian
.putInt( result, 1 * LittleEndianConsts.INT_SIZE, _dwHighDateTime );
LittleEndian.putInt( result, 1 * LittleEndianConsts.INT_SIZE, _dwHighDateTime );
return result;
}
@ -63,4 +78,49 @@ class Filetime {
LittleEndian.putInt( _dwHighDateTime, out );
return SIZE;
}
Date getJavaValue() {
long l = (((long)_dwHighDateTime) << 32) | (_dwLowDateTime & UINT_MASK);
return filetimeToDate( l );
}
/**
* Converts a Windows FILETIME into a {@link Date}. The Windows
* FILETIME structure holds a date and time associated with a
* file. The structure identifies a 64-bit integer specifying the
* number of 100-nanosecond intervals which have passed since
* January 1, 1601.
*
* @param filetime The filetime to convert.
* @return The Windows FILETIME as a {@link Date}.
*/
public static Date filetimeToDate(final long filetime) {
final long ms_since_16010101 = filetime / NANO_100;
final long ms_since_19700101 = ms_since_16010101 + EPOCH_DIFF;
return new Date(ms_since_19700101);
}
/**
* Converts a {@link Date} into a filetime.
*
* @param date The date to be converted
* @return The filetime
*
* @see #filetimeToDate(long)
*/
public static long dateToFileTime(final Date date) {
long ms_since_19700101 = date.getTime();
long ms_since_16010101 = ms_since_19700101 - EPOCH_DIFF;
return ms_since_16010101 * NANO_100;
}
/**
* Return {@code true} if the date is undefined
*
* @param date the date
* @return {@code true} if the date is undefined
*/
public static boolean isUndefined(Date date) {
return (date == null || dateToFileTime(date) == 0);
}
}

View File

@ -22,7 +22,11 @@ import java.io.IOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Locale;
import java.util.concurrent.TimeUnit;
import javax.xml.bind.DatatypeConverter;
import org.apache.poi.hpsf.wellknown.PropertyIDMap;
import org.apache.poi.util.CodePageUtil;
@ -377,15 +381,18 @@ public class Property {
*/
@Override
public String toString() {
return toString(Property.DEFAULT_CODEPAGE);
return toString(Property.DEFAULT_CODEPAGE, null);
}
public String toString(int codepage) {
final StringBuffer b = new StringBuffer();
public String toString(int codepage, PropertyIDMap idMap) {
final StringBuilder b = new StringBuilder();
b.append("Property[");
b.append("id: ");
b.append(getID());
String idName = getNameFromID();
b.append(id);
String idName = (idMap == null) ? null : idMap.get(id);
if (idName == null) {
idName = PropertyIDMap.getFallbackProperties().get(id);
}
if (idName != null) {
b.append(" (");
b.append(idName);
@ -399,6 +406,7 @@ public class Property {
final Object value = getValue();
b.append(", value: ");
if (value instanceof String) {
b.append((String)value);
b.append("\n");
ByteArrayOutputStream bos = new ByteArrayOutputStream();
try {
@ -407,13 +415,11 @@ public class Property {
LOG.log(POILogger.WARN, "can't serialize string", e);
}
b.append(" [");
// skip length field
if(bos.size() > 2*LittleEndianConsts.INT_SIZE) {
final String hex = HexDump.dump(bos.toByteArray(), -2*LittleEndianConsts.INT_SIZE, 2*LittleEndianConsts.INT_SIZE);
b.append(hex);
}
b.append("]");
} else if (value instanceof byte[]) {
b.append("\n");
byte[] bytes = (byte[])value;
@ -421,7 +427,32 @@ public class Property {
String hex = HexDump.dump(bytes, 0L, 0);
b.append(hex);
}
} else if (type == Variant.VT_EMPTY || type == Variant.VT_NULL) {
} else if (value instanceof java.util.Date) {
java.util.Date d = (java.util.Date)value;
long filetime = Filetime.dateToFileTime(d);
if (Filetime.isUndefined(d)) {
b.append("<undefined>");
} else if ((filetime >>> 32) == 0) {
// if the upper dword isn't set, we deal with time intervals
long l = filetime*100;
TimeUnit tu = TimeUnit.NANOSECONDS;
final long hr = tu.toHours(l);
l -= TimeUnit.HOURS.toNanos(hr);
final long min = tu.toMinutes(l);
l -= TimeUnit.MINUTES.toNanos(min);
final long sec = tu.toSeconds(l);
l -= TimeUnit.SECONDS.toNanos(sec);
final long ms = tu.toMillis(l);
String str = String.format(Locale.ROOT, "%02d:%02d:%02d.%03d",hr,min,sec,ms);
b.append(str);
} else {
Calendar cal = Calendar.getInstance(LocaleUtil.TIMEZONE_UTC, Locale.ROOT);
cal.setTime(d);
// use ISO-8601 timestamp format
b.append(DatatypeConverter.printDateTime(cal));
}
} else if (type == Variant.VT_EMPTY || type == Variant.VT_NULL || value == null) {
b.append("null");
} else {
b.append(value.toString());
@ -458,45 +489,6 @@ public class Property {
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.
*

View File

@ -871,7 +871,7 @@ public class PropertySet {
b.append(sectionCount);
b.append(", sections: [\n");
for (Section section: getSections()) {
b.append(section);
b.append(section.toString(getPropertySetIDMap()));
}
b.append(']');
b.append(']');

View File

@ -55,29 +55,19 @@ public class Section {
* The section's format ID, {@link #getFormatID}.
*/
private ClassID formatID;
/**
* If the "dirty" flag is true, the section's size must be
* (re-)calculated before the section is written.
*/
private boolean dirty = true;
/**
* Contains the bytes making out the section. This byte array is
* established when the section's size is calculated and can be reused
* later. It is valid only if the "dirty" flag is false.
* later. If the array is empty, the section was modified and the bytes need to be regenerated.
*/
private byte[] sectionBytes;
private final ByteArrayOutputStream sectionBytes = new ByteArrayOutputStream();
/**
* The offset of the section in the stream.
*/
private final long _offset;
/**
* The section's size in bytes.
*/
private int size;
/**
* This section's properties.
*/
@ -126,7 +116,6 @@ public class Section {
* @exception UnsupportedEncodingException if the section's codepage is not
* supported.
*/
@SuppressWarnings("unchecked")
public Section(final byte[] src, final int offset) throws UnsupportedEncodingException {
/*
* Read the format ID.
@ -154,7 +143,7 @@ public class Section {
/*
* Read the section length.
*/
size = (int)leis.readUInt();
int size = (int)Math.min(leis.readUInt(), src.length-_offset);
/*
* Read the number of properties.
@ -213,6 +202,7 @@ public class Section {
/* Read the codepage number. */
codepage = leis.readUShort();
setCodepage(codepage);
}
@ -222,6 +212,10 @@ public class Section {
long off = me.getKey();
long id = me.getValue();
if (id == PropertyIDMap.PID_CODEPAGE) {
continue;
}
int pLen = propLen(offset2Id, off, size);
leis.setReadIndex((int)(this._offset + off));
@ -239,12 +233,13 @@ public class Section {
LOG.log(POILogger.INFO, "Dictionary fallback failed - ignoring property");
}
};
} else if (id == PropertyIDMap.PID_CODEPAGE) {
setCodepage(codepage);
} else {
setProperty(new MutableProperty(id, leis, pLen, codepage));
}
}
sectionBytes.write(src, (int)_offset, size);
padSectionBytes();
}
/**
@ -338,9 +333,8 @@ public class Section {
public void setProperties(final Property[] properties) {
this.properties.clear();
for (Property p : properties) {
this.properties.put(p.getID(), p);
setProperty(p);
}
dirty = true;
}
/**
@ -448,7 +442,7 @@ public class Section {
Property old = properties.get(p.getID());
if (old == null || !old.equals(p)) {
properties.put(p.getID(), p);
dirty = true;
sectionBytes.reset();
}
}
@ -543,17 +537,17 @@ public class Section {
* @return the section's size in bytes.
*/
public int getSize() {
if (dirty) {
try {
size = calcSize();
dirty = false;
} catch (HPSFRuntimeException ex) {
throw ex;
} catch (Exception ex) {
throw new HPSFRuntimeException(ex);
}
int size = sectionBytes.size();
if (size > 0) {
return size;
}
try {
return calcSize();
} catch (HPSFRuntimeException ex) {
throw ex;
} catch (Exception ex) {
throw new HPSFRuntimeException(ex);
}
return size;
}
/**
@ -566,16 +560,19 @@ public class Section {
* @throws IOException
*/
private int calcSize() throws WritingNotSupportedException, IOException {
final ByteArrayOutputStream out = new ByteArrayOutputStream();
write(out);
out.close();
/* Pad to multiple of 4 bytes so that even the Windows shell (explorer)
* shows custom properties. */
sectionBytes = Util.pad4(out.toByteArray());
return sectionBytes.length;
sectionBytes.reset();
write(sectionBytes);
padSectionBytes();
return sectionBytes.size();
}
private void padSectionBytes() {
byte[] padArray = { 0, 0, 0 };
/* Pad to multiple of 4 bytes so that even the Windows shell (explorer)
* shows custom properties. */
int pad = (4 - (sectionBytes.size() & 0x3)) & 0x3;
sectionBytes.write(padArray, 0, pad);
}
/**
@ -623,12 +620,8 @@ public class Section {
* Removes all properties from the section including 0 (dictionary) and
* 1 (codepage).
*/
public void clear()
{
final Property[] properties = getProperties();
for (int i = 0; i < properties.length; i++)
{
final Property p = properties[i];
public void clear() {
for (Property p : getProperties()) {
removeProperty(p.getID());
}
}
@ -639,17 +632,17 @@ public class Section {
*
* <ul>
*
* <li>The other object is not a {@link Section}.</li>
* <li>The other object is not a {@link Section}.
*
* <li>The format IDs of the two sections are not equal.</li>
* <li>The format IDs of the two sections are not equal.
*
* <li>The sections have a different number of properties. However,
* properties with ID 1 (codepage) are not counted.</li>
* properties with ID 1 (codepage) are not counted.
*
* <li>The other object is not a {@link Section}.</li>
* <li>The other object is not a {@link Section}.
*
* <li>The properties have different values. The order of the properties
* is irrelevant.</li>
* is irrelevant.
*
* </ul>
*
@ -695,7 +688,9 @@ public class Section {
* @param id The ID of the property to be removed
*/
public void removeProperty(final long id) {
dirty |= (properties.remove(id) != null);
if (properties.remove(id) != null) {
sectionBytes.reset();
}
}
/**
@ -716,9 +711,9 @@ public class Section {
public int write(final OutputStream out) throws WritingNotSupportedException, IOException {
/* Check whether we have already generated the bytes making out the
* section. */
if (!dirty && sectionBytes != null) {
out.write(sectionBytes);
return sectionBytes.length;
if (sectionBytes.size() > 0) {
sectionBytes.writeTo(out);
return sectionBytes.size();
}
/* Writing the section's dictionary it tricky. If there is a dictionary
@ -971,6 +966,10 @@ public class Section {
*/
@Override
public String toString() {
return toString(null);
}
public String toString(PropertyIDMap idMap) {
final StringBuffer b = new StringBuffer();
final Property[] pa = getProperties();
b.append("\n\n\n");
@ -990,7 +989,7 @@ public class Section {
codepage = Property.DEFAULT_CODEPAGE;
}
for (Property p : pa) {
b.append(p.toString(codepage));
b.append(p.toString(codepage, idMap));
b.append(",\n");
}
b.append(']');

View File

@ -351,7 +351,7 @@ public final class SummaryInformation extends SpecialPropertySet {
if (d == null) {
return 0;
}
return Util.dateToFileTime(d);
return Filetime.dateToFileTime(d);
}
@ -362,7 +362,7 @@ public final class SummaryInformation extends SpecialPropertySet {
* @param time The time to set.
*/
public void setEditTime(final long time) {
final Date d = Util.filetimeToDate(time);
final Date d = Filetime.filetimeToDate(time);
getFirstSection().setProperty(PropertyIDMap.PID_EDITTIME, Variant.VT_FILETIME, d);
}

View File

@ -1,153 +0,0 @@
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
package org.apache.poi.hpsf;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Date;
import org.apache.poi.util.Internal;
import org.apache.poi.util.SuppressForbidden;
/**
* <p>Provides various static utility methods.</p>
*/
@Internal
public class Util
{
/**
* <p>The difference between the Windows epoch (1601-01-01
* 00:00:00) and the Unix epoch (1970-01-01 00:00:00) in
* milliseconds: 11644473600000L. (Use your favorite spreadsheet
* program to verify the correctness of this value. By the way,
* did you notice that you can tell from the epochs which
* operating system is the modern one? :-))</p>
*/
public static final long EPOCH_DIFF = 11644473600000L;
/**
* <p>Converts a Windows FILETIME into a {@link Date}. The Windows
* FILETIME structure holds a date and time associated with a
* file. The structure identifies a 64-bit integer specifying the
* number of 100-nanosecond intervals which have passed since
* January 1, 1601. This 64-bit value is split into the two double
* words stored in the structure.</p>
*
* @param high The higher double word of the FILETIME structure.
* @param low The lower double word of the FILETIME structure.
* @return The Windows FILETIME as a {@link Date}.
*/
public static Date filetimeToDate(final int high, final int low)
{
final long filetime = ((long) high) << 32 | (low & 0xffffffffL);
return filetimeToDate(filetime);
}
/**
* <p>Converts a Windows FILETIME into a {@link Date}. The Windows
* FILETIME structure holds a date and time associated with a
* file. The structure identifies a 64-bit integer specifying the
* number of 100-nanosecond intervals which have passed since
* January 1, 1601.</p>
*
* @param filetime The filetime to convert.
* @return The Windows FILETIME as a {@link Date}.
*/
public static Date filetimeToDate(final long filetime)
{
final long ms_since_16010101 = filetime / (1000 * 10);
final long ms_since_19700101 = ms_since_16010101 - EPOCH_DIFF;
return new Date(ms_since_19700101);
}
/**
* <p>Converts a {@link Date} into a filetime.</p>
*
* @param date The date to be converted
* @return The filetime
*
* @see #filetimeToDate(long)
* @see #filetimeToDate(int, int)
*/
public static long dateToFileTime(final Date date)
{
long ms_since_19700101 = date.getTime();
long ms_since_16010101 = ms_since_19700101 + EPOCH_DIFF;
return ms_since_16010101 * (1000 * 10);
}
/**
* <p>Pads a byte array with 0x00 bytes so that its length is a multiple of
* 4.</p>
*
* @param ba The byte array to pad.
* @return The padded byte array.
*/
public static byte[] pad4(final byte[] ba)
{
final int PAD = 4;
final byte[] result;
int l = ba.length % PAD;
if (l == 0)
result = ba;
else
{
l = PAD - l;
result = new byte[ba.length + l];
System.arraycopy(ba, 0, result, 0, ba.length);
}
return result;
}
/**
* <p>Returns a textual representation of a {@link Throwable}, including a
* stacktrace.</p>
*
* @param t The {@link Throwable}
*
* @return a string containing the output of a call to
* <code>t.printStacktrace()</code>.
*/
@SuppressForbidden("uses printStackTrace")
public static String toString(final Throwable t)
{
final StringWriter sw = new StringWriter();
final PrintWriter pw = new PrintWriter(sw);
t.printStackTrace(pw);
pw.close();
try
{
sw.close();
return sw.toString();
}
catch (IOException e)
{
final StringBuffer b = new StringBuffer(t.getMessage());
b.append("\n");
b.append("Could not create a stacktrace. Reason: ");
b.append(e.getMessage());
return b.toString();
}
}
}

View File

@ -207,7 +207,7 @@ public class VariantSupport extends Variant {
case Variant.VT_FILETIME:
Filetime filetime = (Filetime) typedPropertyValue.getValue();
return Util.filetimeToDate( (int) filetime.getHigh(), (int) filetime.getLow() );
return filetime.getJavaValue();
case Variant.VT_LPSTR:
CodePageString cpString = (CodePageString) typedPropertyValue.getValue();
@ -413,13 +413,8 @@ public class VariantSupport extends Variant {
break;
case Variant.VT_FILETIME:
if (value instanceof Date) {
long filetime = Util.dateToFileTime((Date) value);
int high = (int) ((filetime >> 32) & 0x00000000FFFFFFFFL);
int low = (int) (filetime & 0x00000000FFFFFFFFL);
Filetime filetimeValue = new Filetime( low, high);
length = filetimeValue.write( out );
}
Filetime filetimeValue = (value instanceof Date) ? new Filetime((Date)value) : new Filetime();
length = filetimeValue.write( out );
break;
default:

View File

@ -17,9 +17,14 @@
package org.apache.poi.hpsf.wellknown;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import org.apache.poi.hpsf.DocumentSummaryInformation;
import org.apache.poi.hpsf.SummaryInformation;
/**
* This is a dictionary which maps property ID values to property
@ -31,7 +36,7 @@ import java.util.Map;
* should treat them as unmodifiable, copy them and modifiy the
* copies.
*/
public class PropertyIDMap extends HashMap<Long,String> {
public class PropertyIDMap implements Map<Long,String> {
/*
* The following definitions are for property IDs in the first
@ -317,6 +322,26 @@ public class PropertyIDMap extends HashMap<Long,String> {
* details!
*/
private static PropertyIDMap summaryInformationProperties;
private static final Object[][] summaryInformationIdValues = {
{ (long)PID_TITLE, "PID_TITLE" },
{ (long)PID_SUBJECT, "PID_SUBJECT" },
{ (long)PID_AUTHOR, "PID_AUTHOR" },
{ (long)PID_KEYWORDS, "PID_KEYWORDS" },
{ (long)PID_COMMENTS, "PID_COMMENTS" },
{ (long)PID_TEMPLATE, "PID_TEMPLATE" },
{ (long)PID_LASTAUTHOR, "PID_LASTAUTHOR" },
{ (long)PID_REVNUMBER, "PID_REVNUMBER" },
{ (long)PID_EDITTIME, "PID_EDITTIME" },
{ (long)PID_LASTPRINTED, "PID_LASTPRINTED" },
{ (long)PID_CREATE_DTM, "PID_CREATE_DTM" },
{ (long)PID_LASTSAVE_DTM, "PID_LASTSAVE_DTM" },
{ (long)PID_PAGECOUNT, "PID_PAGECOUNT" },
{ (long)PID_WORDCOUNT, "PID_WORDCOUNT" },
{ (long)PID_CHARCOUNT, "PID_CHARCOUNT" },
{ (long)PID_THUMBNAIL, "PID_THUMBNAIL" },
{ (long)PID_APPNAME, "PID_APPNAME" },
{ (long)PID_SECURITY, "PID_SECURITY" },
};
/**
* Contains the summary information property ID values and
@ -324,144 +349,181 @@ public class PropertyIDMap extends HashMap<Long,String> {
* details!
*/
private static PropertyIDMap documentSummaryInformationProperties;
private static final Object[][] documentSummaryInformationIdValues = {
{ (long)PID_DICTIONARY, "PID_DICTIONARY" },
{ (long)PID_CODEPAGE, "PID_CODEPAGE" },
{ (long)PID_CATEGORY, "PID_CATEGORY" },
{ (long)PID_PRESFORMAT, "PID_PRESFORMAT" },
{ (long)PID_BYTECOUNT, "PID_BYTECOUNT" },
{ (long)PID_LINECOUNT, "PID_LINECOUNT" },
{ (long)PID_PARCOUNT, "PID_PARCOUNT" },
{ (long)PID_SLIDECOUNT, "PID_SLIDECOUNT" },
{ (long)PID_NOTECOUNT, "PID_NOTECOUNT" },
{ (long)PID_HIDDENCOUNT, "PID_HIDDENCOUNT" },
{ (long)PID_MMCLIPCOUNT, "PID_MMCLIPCOUNT" },
{ (long)PID_SCALE, "PID_SCALE" },
{ (long)PID_HEADINGPAIR, "PID_HEADINGPAIR" },
{ (long)PID_DOCPARTS, "PID_DOCPARTS" },
{ (long)PID_MANAGER, "PID_MANAGER" },
{ (long)PID_COMPANY, "PID_COMPANY" },
{ (long)PID_LINKSDIRTY, "PID_LINKSDIRTY" },
};
/**
* Creates a {@link PropertyIDMap}.
*
* @param initialCapacity The initial capacity as defined for
* {@link HashMap}
* @param loadFactor The load factor as defined for {@link HashMap}
* Contains the fallback property ID values and associated strings.
* This is only used for lookups and not for initializing a property set
*/
public PropertyIDMap(final int initialCapacity, final float loadFactor)
{
super(initialCapacity, loadFactor);
}
private static PropertyIDMap fallbackProperties;
private static final Object[][] fallbackIdValues = {
{ (long)PID_DICTIONARY, "PID_DICTIONARY" },
{ (long)PID_CODEPAGE, "PID_CODEPAGE" },
{ (long)PID_CATEGORY, "PID_CATEGORY" },
{ (long)PID_PRESFORMAT, "PID_PRESFORMAT" },
{ (long)PID_BYTECOUNT, "PID_BYTECOUNT" },
{ (long)PID_LINECOUNT, "PID_LINECOUNT" },
{ (long)PID_PARCOUNT, "PID_PARCOUNT" },
{ (long)PID_SLIDECOUNT, "PID_SLIDECOUNT" },
{ (long)PID_NOTECOUNT, "PID_NOTECOUNT" },
{ (long)PID_HIDDENCOUNT, "PID_HIDDENCOUNT" },
{ (long)PID_MMCLIPCOUNT, "PID_MMCLIPCOUNT" },
{ (long)PID_SCALE, "PID_SCALE" },
{ (long)PID_HEADINGPAIR, "PID_HEADINGPAIR" },
{ (long)PID_DOCPARTS, "PID_DOCPARTS" },
{ (long)PID_MANAGER, "PID_MANAGER" },
{ (long)PID_COMPANY, "PID_COMPANY" },
{ (long)PID_LINKSDIRTY, "PID_LINKSDIRTY" },
{ (long)PID_CCHWITHSPACES, "PID_CCHWITHSPACES" },
// 0x12 Unused
// 0x13 GKPIDDSI_SHAREDDOC - Must be False
// 0x14 GKPIDDSI_LINKBASE - Must not be written
// 0x15 GKPIDDSI_HLINKS - Must not be written
{ (long)PID_HYPERLINKSCHANGED, "PID_HYPERLINKSCHANGED" },
{ (long)PID_VERSION, "PID_VERSION" },
{ (long)PID_DIGSIG, "PID_DIGSIG" },
// 0x19 Unused
{ (long)PID_CONTENTTYPE, "PID_CONTENTTYPE" },
{ (long)PID_CONTENTSTATUS, "PID_CONTENTSTATUS" },
{ (long)PID_LANGUAGE, "PID_LANGUAGE" },
{ (long)PID_DOCVERSION, "PID_DOCVERSION" },
{ (long)PID_MAX, "PID_MAX" },
{ (long)PID_LOCALE, "PID_LOCALE" },
{ (long)PID_BEHAVIOUR, "PID_BEHAVIOUR" },
};
private final Map<Long,String> idMap;
/**
* Creates a {@link PropertyIDMap} backed by another map.
*
* @param map The instance to be created is backed by this map.
*/
public PropertyIDMap(final Map<Long,String> map)
{
super(map);
private PropertyIDMap(Object[][] idValues) {
Map<Long,String> m = new HashMap<Long,String>(idValues.length);
for (Object[] idValue : idValues) {
m.put((Long)idValue[0], (String)idValue[1]);
}
idMap = Collections.unmodifiableMap(m);
}
/**
* Puts a ID string for an ID into the {@link
* PropertyIDMap}.
*
* @param id The ID.
* @param idString The ID string.
* @return As specified by the {@link java.util.Map} interface, this method
* returns the previous value associated with the specified
* <var>id</var>, or <code>null</code> if there was no mapping for
* key.
*/
public Object put(final long id, final String idString)
{
return put(Long.valueOf(id), idString);
}
/**
* Gets the ID string for an ID from the {@link
* PropertyIDMap}.
*
* @param id The ID.
* @return The ID string associated with <var>id</var>.
*/
public Object get(final long id)
{
return get(Long.valueOf(id));
}
/**
* @return the Summary Information properties singleton
*/
public static synchronized PropertyIDMap getSummaryInformationProperties()
{
if (summaryInformationProperties == null)
{
PropertyIDMap m = new PropertyIDMap(18, (float) 1.0);
m.put(PID_TITLE, "PID_TITLE");
m.put(PID_SUBJECT, "PID_SUBJECT");
m.put(PID_AUTHOR, "PID_AUTHOR");
m.put(PID_KEYWORDS, "PID_KEYWORDS");
m.put(PID_COMMENTS, "PID_COMMENTS");
m.put(PID_TEMPLATE, "PID_TEMPLATE");
m.put(PID_LASTAUTHOR, "PID_LASTAUTHOR");
m.put(PID_REVNUMBER, "PID_REVNUMBER");
m.put(PID_EDITTIME, "PID_EDITTIME");
m.put(PID_LASTPRINTED, "PID_LASTPRINTED");
m.put(PID_CREATE_DTM, "PID_CREATE_DTM");
m.put(PID_LASTSAVE_DTM, "PID_LASTSAVE_DTM");
m.put(PID_PAGECOUNT, "PID_PAGECOUNT");
m.put(PID_WORDCOUNT, "PID_WORDCOUNT");
m.put(PID_CHARCOUNT, "PID_CHARCOUNT");
m.put(PID_THUMBNAIL, "PID_THUMBNAIL");
m.put(PID_APPNAME, "PID_APPNAME");
m.put(PID_SECURITY, "PID_SECURITY");
summaryInformationProperties =
new PropertyIDMap(Collections.unmodifiableMap(m));
public static synchronized PropertyIDMap getSummaryInformationProperties() {
if (summaryInformationProperties == null) {
summaryInformationProperties = new PropertyIDMap(summaryInformationIdValues);
}
return summaryInformationProperties;
}
/**
* Returns the Document Summary Information properties
* singleton.
*
* @return The Document Summary Information properties singleton.
*/
public static synchronized PropertyIDMap getDocumentSummaryInformationProperties()
{
if (documentSummaryInformationProperties == null)
{
PropertyIDMap m = new PropertyIDMap(17, (float) 1.0);
m.put(PID_DICTIONARY, "PID_DICTIONARY");
m.put(PID_CODEPAGE, "PID_CODEPAGE");
m.put(PID_CATEGORY, "PID_CATEGORY");
m.put(PID_PRESFORMAT, "PID_PRESFORMAT");
m.put(PID_BYTECOUNT, "PID_BYTECOUNT");
m.put(PID_LINECOUNT, "PID_LINECOUNT");
m.put(PID_PARCOUNT, "PID_PARCOUNT");
m.put(PID_SLIDECOUNT, "PID_SLIDECOUNT");
m.put(PID_NOTECOUNT, "PID_NOTECOUNT");
m.put(PID_HIDDENCOUNT, "PID_HIDDENCOUNT");
m.put(PID_MMCLIPCOUNT, "PID_MMCLIPCOUNT");
m.put(PID_SCALE, "PID_SCALE");
m.put(PID_HEADINGPAIR, "PID_HEADINGPAIR");
m.put(PID_DOCPARTS, "PID_DOCPARTS");
m.put(PID_MANAGER, "PID_MANAGER");
m.put(PID_COMPANY, "PID_COMPANY");
m.put(PID_LINKSDIRTY, "PID_LINKSDIRTY");
documentSummaryInformationProperties =
new PropertyIDMap(Collections.unmodifiableMap(m));
public static synchronized PropertyIDMap getDocumentSummaryInformationProperties() {
if (documentSummaryInformationProperties == null) {
documentSummaryInformationProperties = new PropertyIDMap(documentSummaryInformationIdValues);
}
return documentSummaryInformationProperties;
}
/**
* Returns a property map, which is only used as a fallback, i.e. if available, the correct map
* for {@link DocumentSummaryInformation} or {@link SummaryInformation} should be used.
*/
public static synchronized PropertyIDMap getFallbackProperties() {
if (fallbackProperties == null) {
fallbackProperties = new PropertyIDMap(fallbackIdValues);
}
return fallbackProperties;
}
@Override
public int size() {
return idMap.size();
}
@Override
public boolean isEmpty() {
return idMap.isEmpty();
}
@Override
public boolean containsKey(Object key) {
return idMap.containsKey(key);
}
@Override
public boolean containsValue(Object value) {
return idMap.containsValue(value);
}
@Override
public String get(Object key) {
return idMap.get(key);
}
@Override
public String put(Long key, String value) {
return idMap.put(key, value);
}
@Override
public String remove(Object key) {
return idMap.remove(key);
}
@Override
public void putAll(Map<? extends Long, ? extends String> m) {
idMap.putAll(m);
}
@Override
public void clear() {
idMap.clear();
}
@Override
public Set<Long> keySet() {
return idMap.keySet();
}
@Override
public Collection<String> values() {
return idMap.values();
}
@Override
public Set<Entry<Long, String>> entrySet() {
return idMap.entrySet();
}
/**
* For the most basic testing.
*
* @param args The command-line arguments
*/
public static void main(final String[] args)
{
public static void main(final String[] args) {
PropertyIDMap s1 = getSummaryInformationProperties();
PropertyIDMap s2 = getDocumentSummaryInformationProperties();
System.out.println("s1: " + s1);

View File

@ -25,7 +25,7 @@ import java.util.Locale;
import org.apache.poi.hmef.Attachment;
import org.apache.poi.hmef.HMEFMessage;
import org.apache.poi.hpsf.Util;
import org.apache.poi.hpsf.Filetime;
import org.apache.poi.hsmf.datatypes.MAPIProperty;
import org.apache.poi.util.LittleEndian;
import org.apache.poi.util.LocaleUtil;
@ -53,7 +53,7 @@ public final class MAPIDateAttribute extends MAPIAttribute {
super(property, type, data);
// The value is a 64 bit Windows Filetime
this.data = Util.filetimeToDate(
this.data = Filetime.filetimeToDate(
LittleEndian.getLong(data, 0)
);
}

View File

@ -28,7 +28,7 @@ import java.util.Locale;
import org.apache.poi.hmef.Attachment;
import org.apache.poi.hmef.HMEFMessage;
import org.apache.poi.hpsf.Util;
import org.apache.poi.hpsf.Filetime;
import org.apache.poi.util.LittleEndian;
import org.apache.poi.util.LocaleUtil;
import org.apache.poi.util.POILogFactory;
@ -52,7 +52,7 @@ public final class TNEFDateAttribute extends TNEFAttribute {
byte[] binData = getData();
if(binData.length == 8) {
// The value is a 64 bit Windows Filetime
this.data = Util.filetimeToDate(
this.data = Filetime.filetimeToDate(
LittleEndian.getLong(getData(), 0)
);
} else if(binData.length == 14) {

View File

@ -17,7 +17,6 @@
package org.apache.poi.hpsf.basic;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
@ -28,10 +27,13 @@ import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.util.Date;
import java.util.List;
import org.apache.poi.POIDataSamples;
import org.apache.poi.hpsf.ClassID;
import org.apache.poi.hpsf.DocumentSummaryInformation;
import org.apache.poi.hpsf.Filetime;
import org.apache.poi.hpsf.HPSFException;
import org.apache.poi.hpsf.MarkUnsupportedException;
import org.apache.poi.hpsf.NoPropertySetStreamException;
@ -48,33 +50,24 @@ import org.junit.Test;
*/
public final class TestBasic {
private static final String POI_FS = "TestGermanWord90.doc";
private static final String[] POI_FILES = new String[]
{
"\005SummaryInformation",
"\005DocumentSummaryInformation",
"WordDocument",
"\001CompObj",
"1Table"
};
private static final int BYTE_ORDER = 0xfffe;
private static final int FORMAT = 0x0000;
private static final int OS_VERSION = 0x00020A04;
private static final byte[] CLASS_ID =
{
(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00
};
private static final int[] SECTION_COUNT =
{1, 2};
private static final boolean[] IS_SUMMARY_INFORMATION =
{true, false};
private static final boolean[] IS_DOCUMENT_SUMMARY_INFORMATION =
{false, true};
private static final POIDataSamples samples = POIDataSamples.getHPSFInstance();
private POIFile[] poiFiles;
private static final String[] POI_FILES = {
"\005SummaryInformation",
"\005DocumentSummaryInformation",
"WordDocument",
"\001CompObj",
"1Table"
};
private static final int BYTE_ORDER = 0xfffe;
private static final int FORMAT = 0x0000;
private static final int OS_VERSION = 0x00020A04;
private static final ClassID CLASS_ID = new ClassID("{00000000-0000-0000-0000-000000000000}");
private static final int[] SECTION_COUNT = {1, 2};
private static final boolean[] IS_SUMMARY_INFORMATION = {true, false};
private static final boolean[] IS_DOCUMENT_SUMMARY_INFORMATION = {false, true};
private List<POIFile> poiFiles;
/**
@ -84,10 +77,8 @@ public final class TestBasic {
* @exception IOException if any other I/O exception occurs.
*/
@Before
public void setUp() throws IOException
{
POIDataSamples samples = POIDataSamples.getHPSFInstance();
final File data = samples.getFile(POI_FS);
public void setUp() throws IOException {
final File data = samples.getFile("TestGermanWord90.doc");
poiFiles = Util.readPOIFiles(data);
}
@ -96,11 +87,11 @@ public final class TestBasic {
* are expected to be in a certain order.</p>
*/
@Test
public void testReadFiles()
{
public void testReadFiles() {
String[] expected = POI_FILES;
for (int i = 0; i < expected.length; i++)
assertEquals(poiFiles[i].getName(), expected[i]);
for (int i = 0; i < expected.length; i++) {
assertEquals(poiFiles.get(i).getName(), expected[i]);
}
}
/**
@ -119,30 +110,22 @@ public final class TestBasic {
*/
@Test
public void testCreatePropertySets()
throws UnsupportedEncodingException, IOException
{
Class<?>[] expected = new Class[]
{
SummaryInformation.class,
DocumentSummaryInformation.class,
NoPropertySetStreamException.class,
NoPropertySetStreamException.class,
NoPropertySetStreamException.class
};
for (int i = 0; i < expected.length; i++)
{
InputStream in = new ByteArrayInputStream(poiFiles[i].getBytes());
throws UnsupportedEncodingException, IOException {
Class<?>[] expected = {
SummaryInformation.class,
DocumentSummaryInformation.class,
NoPropertySetStreamException.class,
NoPropertySetStreamException.class,
NoPropertySetStreamException.class
};
for (int i = 0; i < expected.length; i++) {
InputStream in = new ByteArrayInputStream(poiFiles.get(i).getBytes());
Object o;
try
{
try {
o = PropertySetFactory.create(in);
}
catch (NoPropertySetStreamException ex)
{
} catch (NoPropertySetStreamException ex) {
o = ex;
}
catch (MarkUnsupportedException ex)
{
} catch (MarkUnsupportedException ex) {
o = ex;
}
in.close();
@ -159,17 +142,15 @@ public final class TestBasic {
* @exception HPSFException if any HPSF exception occurs
*/
@Test
public void testPropertySetMethods() throws IOException, HPSFException
{
public void testPropertySetMethods() throws IOException, HPSFException {
/* Loop over the two property sets. */
for (int i = 0; i < 2; i++)
{
byte[] b = poiFiles[i].getBytes();
for (int i = 0; i < 2; i++) {
byte[] b = poiFiles.get(i).getBytes();
PropertySet ps = PropertySetFactory.create(new ByteArrayInputStream(b));
assertEquals(BYTE_ORDER, ps.getByteOrder());
assertEquals(FORMAT, ps.getFormat());
assertEquals(OS_VERSION, ps.getOSVersion());
assertArrayEquals(CLASS_ID, ps.getClassID().getBytes());
assertEquals(CLASS_ID, ps.getClassID());
assertEquals(SECTION_COUNT[i], ps.getSectionCount());
assertEquals(IS_SUMMARY_INFORMATION[i], ps.isSummaryInformation());
assertEquals(IS_DOCUMENT_SUMMARY_INFORMATION[i], ps.isDocumentSummaryInformation());
@ -185,11 +166,9 @@ public final class TestBasic {
* @exception HPSFException if any HPSF exception occurs
*/
@Test
public void testSectionMethods() throws IOException, HPSFException
{
final SummaryInformation si = (SummaryInformation)
PropertySetFactory.create(new ByteArrayInputStream
(poiFiles[0].getBytes()));
public void testSectionMethods() throws IOException, HPSFException {
InputStream is = new ByteArrayInputStream(poiFiles.get(0).getBytes());
final SummaryInformation si = (SummaryInformation)PropertySetFactory.create(is);
final List<Section> sections = si.getSections();
final Section s = sections.get(0);
assertEquals(s.getFormatID(), SectionIDMap.SUMMARY_INFORMATION_ID);
@ -198,4 +177,16 @@ public final class TestBasic {
assertEquals("Titel", s.getProperty(2));
assertEquals(1764, s.getSize());
}
@Test
public void bug52117LastPrinted() throws IOException, HPSFException {
File f = samples.getFile("TestBug52117.doc");
POIFile poiFile = Util.readPOIFiles(f, new String[]{POI_FILES[0]}).get(0);
InputStream in = new ByteArrayInputStream(poiFile.getBytes());
SummaryInformation si = (SummaryInformation)PropertySetFactory.create(in);
Date lastPrinted = si.getLastPrinted();
long editTime = si.getEditTime();
assertTrue(Filetime.isUndefined(lastPrinted));
assertEquals(1800000000L, editTime);
}
}

View File

@ -17,14 +17,17 @@
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 java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import junit.framework.TestCase;
import java.util.List;
import org.apache.poi.POIDataSamples;
import org.apache.poi.hpsf.DocumentSummaryInformation;
@ -35,27 +38,29 @@ import org.apache.poi.hpsf.PropertySet;
import org.apache.poi.hpsf.PropertySetFactory;
import org.apache.poi.hpsf.SummaryInformation;
import org.apache.poi.hpsf.Variant;
import org.junit.Before;
import org.junit.Test;
/**
* <p>Test case for OLE2 files with empty properties. An empty property's type
* is {@link Variant#VT_EMPTY}.</p>
* Test case for OLE2 files with empty properties.
* An empty property's type is {@link Variant#VT_EMPTY}.
*/
public final class TestEmptyProperties extends TestCase {
public final class TestEmptyProperties {
private static final POIDataSamples samples = POIDataSamples.getHPSFInstance();
/**
* <p>This test file's summary information stream contains some empty
* properties.</p>
* This test file's summary information stream contains some empty properties.
*/
private static final String POI_FS = "TestCorel.shw";
private static final String[] POI_FILES = new String[]
{
"PerfectOffice_MAIN",
"\005SummaryInformation",
"Main"
};
private static final String[] POI_FILES = {
"PerfectOffice_MAIN",
"\005SummaryInformation",
"Main"
};
private POIFile[] poiFiles;
private List<POIFile> poiFiles;
/**
* <p>Read a the test file from the "data" directory.</p>
@ -64,24 +69,21 @@ public final class TestEmptyProperties extends TestCase {
* does not exist
* @exception IOException if an I/O exception occurs
*/
@Override
public void setUp() throws FileNotFoundException, IOException
{
POIDataSamples samples = POIDataSamples.getHPSFInstance();
@Before
public void setUp() throws IOException {
final File data = samples.getFile(POI_FS);
poiFiles = Util.readPOIFiles(data);
}
/**
* <p>Checks the names of the files in the POI filesystem. They
* are expected to be in a certain order.</p>
* Checks the names of the files in the POI filesystem. They
* are expected to be in a certain order.
*/
public void testReadFiles()
{
@Test
public void testReadFiles() {
String[] expected = POI_FILES;
for (int i = 0; i < expected.length; i++)
assertEquals(poiFiles[i].getName(), expected[i]);
assertEquals(poiFiles.get(i).getName(), expected[i]);
}
/**
@ -98,29 +100,22 @@ public final class TestEmptyProperties extends TestCase {
* @exception UnsupportedEncodingException if a character encoding is not
* supported.
*/
@Test
public void testCreatePropertySets()
throws UnsupportedEncodingException, IOException
{
Class<?>[] expected = new Class[]
{
NoPropertySetStreamException.class,
SummaryInformation.class,
NoPropertySetStreamException.class
};
for (int i = 0; i < expected.length; i++)
{
InputStream in = new ByteArrayInputStream(poiFiles[i].getBytes());
throws UnsupportedEncodingException, IOException {
Class<?>[] expected = {
NoPropertySetStreamException.class,
SummaryInformation.class,
NoPropertySetStreamException.class
};
for (int i = 0; i < expected.length; i++) {
InputStream in = new ByteArrayInputStream(poiFiles.get(i).getBytes());
Object o;
try
{
try {
o = PropertySetFactory.create(in);
}
catch (NoPropertySetStreamException ex)
{
} catch (NoPropertySetStreamException ex) {
o = ex;
}
catch (MarkUnsupportedException ex)
{
} catch (MarkUnsupportedException ex) {
o = ex;
}
in.close();
@ -136,11 +131,10 @@ public final class TestEmptyProperties extends TestCase {
* @exception IOException if an I/O exception occurs
* @exception HPSFException if an HPSF operation fails
*/
public void testPropertySetMethods() throws IOException, HPSFException
{
byte[] b = poiFiles[1].getBytes();
PropertySet ps =
PropertySetFactory.create(new ByteArrayInputStream(b));
@Test
public void testPropertySetMethods() throws IOException, HPSFException {
byte[] b = poiFiles.get(1).getBytes();
PropertySet ps = PropertySetFactory.create(new ByteArrayInputStream(b));
SummaryInformation s = (SummaryInformation) ps;
assertNull(s.getTitle());
assertNull(s.getSubject());

View File

@ -17,13 +17,14 @@
package org.apache.poi.hpsf.basic;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import junit.framework.TestCase;
import org.apache.poi.POIDataSamples;
import org.apache.poi.hpsf.DocumentSummaryInformation;
import org.apache.poi.hpsf.HPSFException;
@ -32,30 +33,30 @@ import org.apache.poi.hpsf.PropertySetFactory;
import org.apache.poi.hpsf.Section;
import org.apache.poi.hpsf.SummaryInformation;
import org.apache.poi.util.CodePageUtil;
import org.junit.Before;
import org.junit.Test;
/**
* <p>Tests whether Unicode string can be read from a
* DocumentSummaryInformation.</p>
* Tests whether Unicode string can be read from a DocumentSummaryInformation.
*/
public class TestUnicode extends TestCase {
public class TestUnicode {
static final String POI_FS = "TestUnicode.xls";
static final String[] POI_FILES = new String[]
{
"\005DocumentSummaryInformation",
};
static final String[] POI_FILES = {
"\005DocumentSummaryInformation",
};
File data;
POIFile[] poiFiles;
/**
* <p>Read a the test file from the "data" directory.</p>
* Read a the test file from the "data" directory.
*
* @exception FileNotFoundException if the file to be read does not exist.
* @exception IOException if any other I/O exception occurs
*/
@Override
protected void setUp() {
@Before
public void setUp() {
POIDataSamples samples = POIDataSamples.getHPSFInstance();
data = samples.getFile(POI_FS);
}
@ -63,31 +64,25 @@ public class TestUnicode extends TestCase {
/**
* <p>Tests the {@link PropertySet} methods. The test file has two
* Tests the {@link PropertySet} methods. The test file has two
* property set: the first one is a {@link SummaryInformation},
* the second one is a {@link DocumentSummaryInformation}.</p>
* the second one is a {@link DocumentSummaryInformation}.
*
* @exception IOException if an I/O exception occurs
* @exception HPSFException if an HPSF exception occurs
*/
public void testPropertySetMethods() throws IOException, HPSFException
{
POIFile poiFile = Util.readPOIFiles(data, POI_FILES)[0];
@Test
public void testPropertySetMethods() throws IOException, HPSFException {
POIFile poiFile = Util.readPOIFiles(data, POI_FILES).get(0);
byte[] b = poiFile.getBytes();
PropertySet ps =
PropertySetFactory.create(new ByteArrayInputStream(b));
PropertySet ps = PropertySetFactory.create(new ByteArrayInputStream(b));
assertTrue(ps.isDocumentSummaryInformation());
assertEquals(ps.getSectionCount(), 2);
Section s = ps.getSections().get(1);
assertEquals(s.getProperty(1),
Integer.valueOf(CodePageUtil.CP_UTF16));
assertEquals(s.getProperty(2),
Integer.valueOf(-96070278));
assertEquals(s.getProperty(3),
"MCon_Info zu Office bei Schreiner");
assertEquals(s.getProperty(4),
"petrovitsch@schreiner-online.de");
assertEquals(s.getProperty(5),
"Petrovitsch, Wilhelm");
assertEquals(s.getProperty(1), CodePageUtil.CP_UTF16);
assertEquals(s.getProperty(2), -96070278);
assertEquals(s.getProperty(3), "MCon_Info zu Office bei Schreiner");
assertEquals(s.getProperty(4), "petrovitsch@schreiner-online.de");
assertEquals(s.getProperty(5), "Petrovitsch, Wilhelm");
}
}

View File

@ -237,7 +237,7 @@ public class TestWrite {
try {
psa[0] = PropertySetFactory.create(event.getStream());
} catch (Exception ex) {
fail(org.apache.poi.hpsf.Util.toString(ex));
fail(ex.getMessage());
}
}},
SummaryInformation.DEFAULT_STREAM_NAME
@ -340,7 +340,7 @@ public class TestWrite {
try {
PropertySetFactory.create(event.getStream());
} catch (Exception ex) {
fail(org.apache.poi.hpsf.Util.toString(ex));
fail(ex.getMessage());
}
}
}

View File

@ -18,17 +18,13 @@
package org.apache.poi.hpsf.basic;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Properties;
import org.apache.poi.hpsf.PropertySet;
import org.apache.poi.poifs.eventfilesystem.POIFSReader;
@ -43,31 +39,6 @@ import org.apache.poi.util.IOUtils;
*/
final class Util {
/**
* <p>Reads all files from a POI filesystem and returns them as an
* array of {@link POIFile} instances. This method loads all files
* into memory and thus does not cope well with large POI
* filessystems.</p>
*
* @param poiFs The name of the POI filesystem as seen by the
* operating system. (This is the "filename".)
*
* @return The POI files. The elements are ordered in the same way
* as the files in the POI filesystem.
*
* @exception FileNotFoundException if the file containing the POI
* filesystem does not exist
*
* @exception IOException if an I/O exception occurs
*/
public static POIFile[] readPOIFiles(final File poiFs)
throws FileNotFoundException, IOException
{
return readPOIFiles(poiFs, null);
}
/**
* <p>Reads a set of files from a POI filesystem and returns them
* as an array of {@link POIFile} instances. This method loads all
@ -87,42 +58,34 @@ final class Util {
*
* @exception IOException if an I/O exception occurs
*/
public static POIFile[] readPOIFiles(final File poiFs,
final String[] poiFiles)
throws FileNotFoundException, IOException
{
public static List<POIFile> readPOIFiles(final File poiFs, final String... poiFiles)
throws FileNotFoundException, IOException {
final List<POIFile> files = new ArrayList<POIFile>();
POIFSReader r = new POIFSReader();
POIFSReaderListener pfl = new POIFSReaderListener()
{
POIFSReaderListener pfl = new POIFSReaderListener() {
@Override
public void processPOIFSReaderEvent(final POIFSReaderEvent event)
{
try
{
public void processPOIFSReaderEvent(final POIFSReaderEvent event) {
try {
final POIFile f = new POIFile();
f.setName(event.getName());
f.setPath(event.getPath());
final InputStream in = event.getStream();
final ByteArrayOutputStream out =
new ByteArrayOutputStream();
IOUtils.copy(in, out);
out.close();
f.setBytes(out.toByteArray());
f.setBytes(IOUtils.toByteArray(in));
in.close();
files.add(f);
}
catch (IOException ex)
{
} catch (IOException ex) {
throw new RuntimeException(ex);
}
}
};
if (poiFiles == null)
if (poiFiles.length == 0) {
/* Register the listener for all POI files. */
r.registerListener(pfl);
else
for (String poiFile : poiFiles)
} else {
for (String poiFile : poiFiles) {
r.registerListener(pfl, poiFile);
}
}
/* Read the POI filesystem. */
FileInputStream stream = new FileInputStream(poiFs);
@ -131,10 +94,7 @@ final class Util {
} finally {
stream.close();
}
POIFile[] result = new POIFile[files.size()];
for (int i = 0; i < result.length; i++)
result[i] = files.get(i);
return result;
return files;
}
@ -155,18 +115,7 @@ final class Util {
*
* @exception IOException if an I/O exception occurs
*/
public static List<POIFile> readPropertySets(final File poiFs)
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 {
public static List<POIFile> readPropertySets(final File poiFs) throws IOException {
final List<POIFile> files = new ArrayList<POIFile>(7);
final POIFSReader r = new POIFSReader();
POIFSReaderListener pfl = new POIFSReaderListener() {
@ -191,28 +140,13 @@ final class Util {
r.registerListener(pfl);
/* Read the POI filesystem. */
r.read(poiFs);
InputStream is = new FileInputStream(poiFs);
try {
r.read(is);
} finally {
is.close();
}
return files;
}
/**
* <p>Prints the system properties to System.out.</p>
*/
public static void printSystemProperties()
{
final Properties p = System.getProperties();
final List<String> names = new LinkedList<String>();
for (String name : p.stringPropertyNames())
names.add(name);
Collections.sort(names);
for (String name : names) {
String value = p.getProperty(name);
System.out.println(name + ": " + value);
}
System.out.println("Current directory: " +
System.getProperty("user.dir"));
}
}