aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorAndreas Beeker <kiwiwings@apache.org>2017-05-14 22:25:33 +0000
committerAndreas Beeker <kiwiwings@apache.org>2017-05-14 22:25:33 +0000
commit98b10cf68454fef15de8632b472c38442aaf95d5 (patch)
tree38c5b401f837ec45ae4ba572e863e46f3a8d3013 /src
parent965860b11607a2d610b941cdd648cc9bb7017a58 (diff)
downloadpoi-98b10cf68454fef15de8632b472c38442aaf95d5.tar.gz
poi-98b10cf68454fef15de8632b472c38442aaf95d5.zip
#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
Diffstat (limited to 'src')
-rw-r--r--src/examples/src/org/apache/poi/hpsf/examples/CopyCompare.java7
-rw-r--r--src/examples/src/org/apache/poi/hpsf/examples/WriteAuthorAndTitle.java5
-rw-r--r--src/java/org/apache/poi/hpsf/Filetime.java72
-rw-r--r--src/java/org/apache/poi/hpsf/Property.java88
-rw-r--r--src/java/org/apache/poi/hpsf/PropertySet.java2
-rw-r--r--src/java/org/apache/poi/hpsf/Section.java103
-rw-r--r--src/java/org/apache/poi/hpsf/SummaryInformation.java4
-rw-r--r--src/java/org/apache/poi/hpsf/Util.java153
-rw-r--r--src/java/org/apache/poi/hpsf/VariantSupport.java11
-rw-r--r--src/java/org/apache/poi/hpsf/wellknown/PropertyIDMap.java274
-rw-r--r--src/scratchpad/src/org/apache/poi/hmef/attribute/MAPIDateAttribute.java4
-rw-r--r--src/scratchpad/src/org/apache/poi/hmef/attribute/TNEFDateAttribute.java4
-rw-r--r--src/testcases/org/apache/poi/hpsf/basic/TestBasic.java125
-rw-r--r--src/testcases/org/apache/poi/hpsf/basic/TestEmptyProperties.java90
-rw-r--r--src/testcases/org/apache/poi/hpsf/basic/TestUnicode.java53
-rw-r--r--src/testcases/org/apache/poi/hpsf/basic/TestWrite.java4
-rw-r--r--src/testcases/org/apache/poi/hpsf/basic/Util.java108
17 files changed, 485 insertions, 622 deletions
diff --git a/src/examples/src/org/apache/poi/hpsf/examples/CopyCompare.java b/src/examples/src/org/apache/poi/hpsf/examples/CopyCompare.java
index 8282c00576..f30dc8be0e 100644
--- a/src/examples/src/org/apache/poi/hpsf/examples/CopyCompare.java
+++ b/src/examples/src/org/apache/poi/hpsf/examples/CopyCompare.java
@@ -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);
}
}
diff --git a/src/examples/src/org/apache/poi/hpsf/examples/WriteAuthorAndTitle.java b/src/examples/src/org/apache/poi/hpsf/examples/WriteAuthorAndTitle.java
index b5734c0439..e5454aa93c 100644
--- a/src/examples/src/org/apache/poi/hpsf/examples/WriteAuthorAndTitle.java
+++ b/src/examples/src/org/apache/poi/hpsf/examples/WriteAuthorAndTitle.java
@@ -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);
}
}
diff --git a/src/java/org/apache/poi/hpsf/Filetime.java b/src/java/org/apache/poi/hpsf/Filetime.java
index 2ed36afccc..ba5687a7b3 100644
--- a/src/java/org/apache/poi/hpsf/Filetime.java
+++ b/src/java/org/apache/poi/hpsf/Filetime.java
@@ -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);
+ }
}
diff --git a/src/java/org/apache/poi/hpsf/Property.java b/src/java/org/apache/poi/hpsf/Property.java
index 60a46b804d..e05a06dacd 100644
--- a/src/java/org/apache/poi/hpsf/Property.java
+++ b/src/java/org/apache/poi/hpsf/Property.java
@@ -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.
*
diff --git a/src/java/org/apache/poi/hpsf/PropertySet.java b/src/java/org/apache/poi/hpsf/PropertySet.java
index fb83442693..db01200bc7 100644
--- a/src/java/org/apache/poi/hpsf/PropertySet.java
+++ b/src/java/org/apache/poi/hpsf/PropertySet.java
@@ -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(']');
diff --git a/src/java/org/apache/poi/hpsf/Section.java b/src/java/org/apache/poi/hpsf/Section.java
index 59150afb53..f041986402 100644
--- a/src/java/org/apache/poi/hpsf/Section.java
+++ b/src/java/org/apache/poi/hpsf/Section.java
@@ -55,18 +55,13 @@ 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.
@@ -74,11 +69,6 @@ public class Section {
private final long _offset;
/**
- * The section's size in bytes.
- */
- private int size;
-
- /**
* This section's properties.
*/
private final Map<Long,Property> properties = new LinkedHashMap<Long,Property>();
@@ -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,18 +560,21 @@ public class Section {
* @throws IOException
*/
private int calcSize() throws WritingNotSupportedException, IOException {
- final ByteArrayOutputStream out = new ByteArrayOutputStream();
- write(out);
- out.close();
+ 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. */
- sectionBytes = Util.pad4(out.toByteArray());
- return sectionBytes.length;
+ int pad = (4 - (sectionBytes.size() & 0x3)) & 0x3;
+ sectionBytes.write(padArray, 0, pad);
}
-
-
/**
* Checks whether the property which the last call to {@link
* #getPropertyIntValue} or {@link #getProperty} tried to access
@@ -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(']');
diff --git a/src/java/org/apache/poi/hpsf/SummaryInformation.java b/src/java/org/apache/poi/hpsf/SummaryInformation.java
index f59bd4872a..8d9e431794 100644
--- a/src/java/org/apache/poi/hpsf/SummaryInformation.java
+++ b/src/java/org/apache/poi/hpsf/SummaryInformation.java
@@ -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);
}
diff --git a/src/java/org/apache/poi/hpsf/Util.java b/src/java/org/apache/poi/hpsf/Util.java
deleted file mode 100644
index d18fe811be..0000000000
--- a/src/java/org/apache/poi/hpsf/Util.java
+++ /dev/null
@@ -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();
- }
- }
-
-}
diff --git a/src/java/org/apache/poi/hpsf/VariantSupport.java b/src/java/org/apache/poi/hpsf/VariantSupport.java
index 8c351cc39a..61e6bb8db9 100644
--- a/src/java/org/apache/poi/hpsf/VariantSupport.java
+++ b/src/java/org/apache/poi/hpsf/VariantSupport.java
@@ -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:
diff --git a/src/java/org/apache/poi/hpsf/wellknown/PropertyIDMap.java b/src/java/org/apache/poi/hpsf/wellknown/PropertyIDMap.java
index 4a7da335d0..b3aaa87a3c 100644
--- a/src/java/org/apache/poi/hpsf/wellknown/PropertyIDMap.java
+++ b/src/java/org/apache/poi/hpsf/wellknown/PropertyIDMap.java
@@ -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}
- */
- public PropertyIDMap(final int initialCapacity, final float loadFactor)
- {
- super(initialCapacity, loadFactor);
- }
-
-
+ * Contains the fallback property ID values and associated strings.
+ * This is only used for lookups and not for initializing a property set
+ */
+ 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);
}
+ /**
+ * @return the Summary Information properties singleton
+ */
+ public static synchronized PropertyIDMap getSummaryInformationProperties() {
+ if (summaryInformationProperties == null) {
+ summaryInformationProperties = new PropertyIDMap(summaryInformationIdValues);
+ }
+ return summaryInformationProperties;
+ }
+ /**
+ * @return The Document Summary Information properties singleton.
+ */
+ public static synchronized PropertyIDMap getDocumentSummaryInformationProperties() {
+ if (documentSummaryInformationProperties == null) {
+ documentSummaryInformationProperties = new PropertyIDMap(documentSummaryInformationIdValues);
+ }
+ return documentSummaryInformationProperties;
+ }
/**
- * 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);
+ * 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);
+ }
- /**
- * 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));
+ @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);
+ }
- /**
- * @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));
- }
- return summaryInformationProperties;
+ @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();
+ }
- /**
- * 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));
- }
- return documentSummaryInformationProperties;
+ @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);
diff --git a/src/scratchpad/src/org/apache/poi/hmef/attribute/MAPIDateAttribute.java b/src/scratchpad/src/org/apache/poi/hmef/attribute/MAPIDateAttribute.java
index 3f1827b116..614126c0da 100644
--- a/src/scratchpad/src/org/apache/poi/hmef/attribute/MAPIDateAttribute.java
+++ b/src/scratchpad/src/org/apache/poi/hmef/attribute/MAPIDateAttribute.java
@@ -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)
);
}
diff --git a/src/scratchpad/src/org/apache/poi/hmef/attribute/TNEFDateAttribute.java b/src/scratchpad/src/org/apache/poi/hmef/attribute/TNEFDateAttribute.java
index 40c16cd46e..eb1346b143 100644
--- a/src/scratchpad/src/org/apache/poi/hmef/attribute/TNEFDateAttribute.java
+++ b/src/scratchpad/src/org/apache/poi/hmef/attribute/TNEFDateAttribute.java
@@ -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) {
diff --git a/src/testcases/org/apache/poi/hpsf/basic/TestBasic.java b/src/testcases/org/apache/poi/hpsf/basic/TestBasic.java
index a017ecc797..e32c6b0d5b 100644
--- a/src/testcases/org/apache/poi/hpsf/basic/TestBasic.java
+++ b/src/testcases/org/apache/poi/hpsf/basic/TestBasic.java
@@ -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 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 POIFile[] poiFiles;
+ 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);
+ }
}
diff --git a/src/testcases/org/apache/poi/hpsf/basic/TestEmptyProperties.java b/src/testcases/org/apache/poi/hpsf/basic/TestEmptyProperties.java
index 48103b0c9c..4e9eec6271 100644
--- a/src/testcases/org/apache/poi/hpsf/basic/TestEmptyProperties.java
+++ b/src/testcases/org/apache/poi/hpsf/basic/TestEmptyProperties.java
@@ -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());
diff --git a/src/testcases/org/apache/poi/hpsf/basic/TestUnicode.java b/src/testcases/org/apache/poi/hpsf/basic/TestUnicode.java
index 6f7e6db1c5..69e4367bbf 100644
--- a/src/testcases/org/apache/poi/hpsf/basic/TestUnicode.java
+++ b/src/testcases/org/apache/poi/hpsf/basic/TestUnicode.java
@@ -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");
}
}
diff --git a/src/testcases/org/apache/poi/hpsf/basic/TestWrite.java b/src/testcases/org/apache/poi/hpsf/basic/TestWrite.java
index 43737a3b04..4ef331f09e 100644
--- a/src/testcases/org/apache/poi/hpsf/basic/TestWrite.java
+++ b/src/testcases/org/apache/poi/hpsf/basic/TestWrite.java
@@ -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());
}
}
}
diff --git a/src/testcases/org/apache/poi/hpsf/basic/Util.java b/src/testcases/org/apache/poi/hpsf/basic/Util.java
index 40b4e229d7..cc489d8533 100644
--- a/src/testcases/org/apache/poi/hpsf/basic/Util.java
+++ b/src/testcases/org/apache/poi/hpsf/basic/Util.java
@@ -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;
@@ -44,31 +40,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
* files into memory and thus does not cope well with large POI
@@ -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"));
- }
}