aboutsummaryrefslogtreecommitdiffstats
path: root/src/java
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/java
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/java')
-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
8 files changed, 331 insertions, 376 deletions
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);