From 90dca20bd2fc9665a244a7ed189d97e0d6636412 Mon Sep 17 00:00:00 2001 From: Andreas Beeker Date: Sun, 14 May 2017 22:25:33 +0000 Subject: [PATCH] #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 --- .../apache/poi/hpsf/examples/CopyCompare.java | 7 +- .../hpsf/examples/WriteAuthorAndTitle.java | 5 +- src/java/org/apache/poi/hpsf/Filetime.java | 72 ++++- src/java/org/apache/poi/hpsf/Property.java | 88 +++--- src/java/org/apache/poi/hpsf/PropertySet.java | 2 +- src/java/org/apache/poi/hpsf/Section.java | 105 ++++--- .../apache/poi/hpsf/SummaryInformation.java | 4 +- src/java/org/apache/poi/hpsf/Util.java | 153 ---------- .../org/apache/poi/hpsf/VariantSupport.java | 11 +- .../poi/hpsf/wellknown/PropertyIDMap.java | 276 +++++++++++------- .../poi/hmef/attribute/MAPIDateAttribute.java | 4 +- .../poi/hmef/attribute/TNEFDateAttribute.java | 4 +- .../org/apache/poi/hpsf/basic/TestBasic.java | 125 ++++---- .../poi/hpsf/basic/TestEmptyProperties.java | 90 +++--- .../apache/poi/hpsf/basic/TestUnicode.java | 53 ++-- .../org/apache/poi/hpsf/basic/TestWrite.java | 4 +- .../org/apache/poi/hpsf/basic/Util.java | 108 ++----- 17 files changed, 487 insertions(+), 624 deletions(-) delete mode 100644 src/java/org/apache/poi/hpsf/Util.java 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 8282c0057..f30dc8be0 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 b5734c043..e5454aa93 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 2ed36afcc..ba5687a7b 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 60a46b804..e05a06dac 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(""); + } 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 fb8344269..db01200bc 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 59150afb5..f04198640 100644 --- a/src/java/org/apache/poi/hpsf/Section.java +++ b/src/java/org/apache/poi/hpsf/Section.java @@ -55,29 +55,19 @@ public class Section { * The section's format ID, {@link #getFormatID}. */ private ClassID formatID; - /** - * If the "dirty" flag is true, the section's size must be - * (re-)calculated before the section is written. - */ - private boolean dirty = true; /** * Contains the bytes making out the section. This byte array is * established when the section's size is calculated and can be reused - * later. It is valid only if the "dirty" flag is false. + * later. If the array is empty, the section was modified and the bytes need to be regenerated. */ - private byte[] sectionBytes; + private final ByteArrayOutputStream sectionBytes = new ByteArrayOutputStream(); /** * The offset of the section in the stream. */ private final long _offset; - /** - * The section's size in bytes. - */ - private int size; - /** * This section's properties. */ @@ -126,7 +116,6 @@ public class Section { * @exception UnsupportedEncodingException if the section's codepage is not * supported. */ - @SuppressWarnings("unchecked") public Section(final byte[] src, final int offset) throws UnsupportedEncodingException { /* * Read the format ID. @@ -154,7 +143,7 @@ public class Section { /* * Read the section length. */ - size = (int)leis.readUInt(); + int size = (int)Math.min(leis.readUInt(), src.length-_offset); /* * Read the number of properties. @@ -213,6 +202,7 @@ public class Section { /* Read the codepage number. */ codepage = leis.readUShort(); + setCodepage(codepage); } @@ -222,6 +212,10 @@ public class Section { long off = me.getKey(); long id = me.getValue(); + if (id == PropertyIDMap.PID_CODEPAGE) { + continue; + } + int pLen = propLen(offset2Id, off, size); leis.setReadIndex((int)(this._offset + off)); @@ -239,12 +233,13 @@ public class Section { LOG.log(POILogger.INFO, "Dictionary fallback failed - ignoring property"); } }; - } else if (id == PropertyIDMap.PID_CODEPAGE) { - setCodepage(codepage); } else { setProperty(new MutableProperty(id, leis, pLen, codepage)); } } + + sectionBytes.write(src, (int)_offset, size); + padSectionBytes(); } /** @@ -338,9 +333,8 @@ public class Section { public void setProperties(final Property[] properties) { this.properties.clear(); for (Property p : properties) { - this.properties.put(p.getID(), p); + setProperty(p); } - dirty = true; } /** @@ -448,7 +442,7 @@ public class Section { Property old = properties.get(p.getID()); if (old == null || !old.equals(p)) { properties.put(p.getID(), p); - dirty = true; + sectionBytes.reset(); } } @@ -543,17 +537,17 @@ public class Section { * @return the section's size in bytes. */ public int getSize() { - if (dirty) { - try { - size = calcSize(); - dirty = false; - } catch (HPSFRuntimeException ex) { - throw ex; - } catch (Exception ex) { - throw new HPSFRuntimeException(ex); - } + int size = sectionBytes.size(); + if (size > 0) { + return size; + } + try { + return calcSize(); + } catch (HPSFRuntimeException ex) { + throw ex; + } catch (Exception ex) { + throw new HPSFRuntimeException(ex); } - return size; } /** @@ -566,16 +560,19 @@ public class Section { * @throws IOException */ private int calcSize() throws WritingNotSupportedException, IOException { - final ByteArrayOutputStream out = new ByteArrayOutputStream(); - write(out); - out.close(); - /* Pad to multiple of 4 bytes so that even the Windows shell (explorer) - * shows custom properties. */ - sectionBytes = Util.pad4(out.toByteArray()); - return sectionBytes.length; + sectionBytes.reset(); + write(sectionBytes); + padSectionBytes(); + return sectionBytes.size(); } - + private void padSectionBytes() { + byte[] padArray = { 0, 0, 0 }; + /* Pad to multiple of 4 bytes so that even the Windows shell (explorer) + * shows custom properties. */ + int pad = (4 - (sectionBytes.size() & 0x3)) & 0x3; + sectionBytes.write(padArray, 0, pad); + } /** @@ -623,12 +620,8 @@ public class Section { * Removes all properties from the section including 0 (dictionary) and * 1 (codepage). */ - public void clear() - { - final Property[] properties = getProperties(); - for (int i = 0; i < properties.length; i++) - { - final Property p = properties[i]; + public void clear() { + for (Property p : getProperties()) { removeProperty(p.getID()); } } @@ -639,17 +632,17 @@ public class Section { * * * @@ -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 f59bd4872..8d9e43179 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 d18fe811b..000000000 --- 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; - -/** - *

Provides various static utility methods.

- */ -@Internal -public class Util -{ - /** - *

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? :-))

- */ - public static final long EPOCH_DIFF = 11644473600000L; - - - /** - *

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.

- * - * @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); - } - - /** - *

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 / (1000 * 10); - 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) - * @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); - } - - /** - *

Pads a byte array with 0x00 bytes so that its length is a multiple of - * 4.

- * - * @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; - } - - - /** - *

Returns a textual representation of a {@link Throwable}, including a - * stacktrace.

- * - * @param t The {@link Throwable} - * - * @return a string containing the output of a call to - * t.printStacktrace(). - */ - @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 8c351cc39..61e6bb8db 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 4a7da335d..b3aaa87a3 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 { +public class PropertyIDMap implements Map { /* * The following definitions are for property IDs in the first @@ -317,6 +322,26 @@ public class PropertyIDMap extends HashMap { * 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 { * details! */ private static PropertyIDMap documentSummaryInformationProperties; - - - + private static final Object[][] documentSummaryInformationIdValues = { + { (long)PID_DICTIONARY, "PID_DICTIONARY" }, + { (long)PID_CODEPAGE, "PID_CODEPAGE" }, + { (long)PID_CATEGORY, "PID_CATEGORY" }, + { (long)PID_PRESFORMAT, "PID_PRESFORMAT" }, + { (long)PID_BYTECOUNT, "PID_BYTECOUNT" }, + { (long)PID_LINECOUNT, "PID_LINECOUNT" }, + { (long)PID_PARCOUNT, "PID_PARCOUNT" }, + { (long)PID_SLIDECOUNT, "PID_SLIDECOUNT" }, + { (long)PID_NOTECOUNT, "PID_NOTECOUNT" }, + { (long)PID_HIDDENCOUNT, "PID_HIDDENCOUNT" }, + { (long)PID_MMCLIPCOUNT, "PID_MMCLIPCOUNT" }, + { (long)PID_SCALE, "PID_SCALE" }, + { (long)PID_HEADINGPAIR, "PID_HEADINGPAIR" }, + { (long)PID_DOCPARTS, "PID_DOCPARTS" }, + { (long)PID_MANAGER, "PID_MANAGER" }, + { (long)PID_COMPANY, "PID_COMPANY" }, + { (long)PID_LINKSDIRTY, "PID_LINKSDIRTY" }, + }; + + /** - * Creates a {@link PropertyIDMap}. - * - * @param initialCapacity The initial capacity as defined for - * {@link HashMap} - * @param loadFactor The load factor as defined for {@link HashMap} + * Contains the fallback property ID values and associated strings. + * This is only used for lookups and not for initializing a property set */ - public PropertyIDMap(final int initialCapacity, final float loadFactor) - { - super(initialCapacity, loadFactor); - } - + private static PropertyIDMap fallbackProperties; + private static final Object[][] fallbackIdValues = { + { (long)PID_DICTIONARY, "PID_DICTIONARY" }, + { (long)PID_CODEPAGE, "PID_CODEPAGE" }, + { (long)PID_CATEGORY, "PID_CATEGORY" }, + { (long)PID_PRESFORMAT, "PID_PRESFORMAT" }, + { (long)PID_BYTECOUNT, "PID_BYTECOUNT" }, + { (long)PID_LINECOUNT, "PID_LINECOUNT" }, + { (long)PID_PARCOUNT, "PID_PARCOUNT" }, + { (long)PID_SLIDECOUNT, "PID_SLIDECOUNT" }, + { (long)PID_NOTECOUNT, "PID_NOTECOUNT" }, + { (long)PID_HIDDENCOUNT, "PID_HIDDENCOUNT" }, + { (long)PID_MMCLIPCOUNT, "PID_MMCLIPCOUNT" }, + { (long)PID_SCALE, "PID_SCALE" }, + { (long)PID_HEADINGPAIR, "PID_HEADINGPAIR" }, + { (long)PID_DOCPARTS, "PID_DOCPARTS" }, + { (long)PID_MANAGER, "PID_MANAGER" }, + { (long)PID_COMPANY, "PID_COMPANY" }, + { (long)PID_LINKSDIRTY, "PID_LINKSDIRTY" }, + { (long)PID_CCHWITHSPACES, "PID_CCHWITHSPACES" }, + // 0x12 Unused + // 0x13 GKPIDDSI_SHAREDDOC - Must be False + // 0x14 GKPIDDSI_LINKBASE - Must not be written + // 0x15 GKPIDDSI_HLINKS - Must not be written + { (long)PID_HYPERLINKSCHANGED, "PID_HYPERLINKSCHANGED" }, + { (long)PID_VERSION, "PID_VERSION" }, + { (long)PID_DIGSIG, "PID_DIGSIG" }, + // 0x19 Unused + { (long)PID_CONTENTTYPE, "PID_CONTENTTYPE" }, + { (long)PID_CONTENTSTATUS, "PID_CONTENTSTATUS" }, + { (long)PID_LANGUAGE, "PID_LANGUAGE" }, + { (long)PID_DOCVERSION, "PID_DOCVERSION" }, + { (long)PID_MAX, "PID_MAX" }, + { (long)PID_LOCALE, "PID_LOCALE" }, + { (long)PID_BEHAVIOUR, "PID_BEHAVIOUR" }, + }; + private final Map 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 map) - { - super(map); + private PropertyIDMap(Object[][] idValues) { + Map m = new HashMap(idValues.length); + for (Object[] idValue : idValues) { + m.put((Long)idValue[0], (String)idValue[1]); + } + idMap = Collections.unmodifiableMap(m); } - - - /** - * Puts a ID string for an ID into the {@link - * PropertyIDMap}. - * - * @param id The ID. - * @param idString The ID string. - * @return As specified by the {@link java.util.Map} interface, this method - * returns the previous value associated with the specified - * id, or null if there was no mapping for - * key. - */ - public Object put(final long id, final String idString) - { - return put(Long.valueOf(id), idString); - } - - - - /** - * Gets the ID string for an ID from the {@link - * PropertyIDMap}. - * - * @param id The ID. - * @return The ID string associated with id. - */ - public Object get(final long id) - { - return get(Long.valueOf(id)); - } - - - /** * @return the Summary Information properties singleton */ - public static synchronized PropertyIDMap getSummaryInformationProperties() - { - if (summaryInformationProperties == null) - { - PropertyIDMap m = new PropertyIDMap(18, (float) 1.0); - m.put(PID_TITLE, "PID_TITLE"); - m.put(PID_SUBJECT, "PID_SUBJECT"); - m.put(PID_AUTHOR, "PID_AUTHOR"); - m.put(PID_KEYWORDS, "PID_KEYWORDS"); - m.put(PID_COMMENTS, "PID_COMMENTS"); - m.put(PID_TEMPLATE, "PID_TEMPLATE"); - m.put(PID_LASTAUTHOR, "PID_LASTAUTHOR"); - m.put(PID_REVNUMBER, "PID_REVNUMBER"); - m.put(PID_EDITTIME, "PID_EDITTIME"); - m.put(PID_LASTPRINTED, "PID_LASTPRINTED"); - m.put(PID_CREATE_DTM, "PID_CREATE_DTM"); - m.put(PID_LASTSAVE_DTM, "PID_LASTSAVE_DTM"); - m.put(PID_PAGECOUNT, "PID_PAGECOUNT"); - m.put(PID_WORDCOUNT, "PID_WORDCOUNT"); - m.put(PID_CHARCOUNT, "PID_CHARCOUNT"); - m.put(PID_THUMBNAIL, "PID_THUMBNAIL"); - m.put(PID_APPNAME, "PID_APPNAME"); - m.put(PID_SECURITY, "PID_SECURITY"); - summaryInformationProperties = - new PropertyIDMap(Collections.unmodifiableMap(m)); + public static synchronized PropertyIDMap getSummaryInformationProperties() { + if (summaryInformationProperties == null) { + summaryInformationProperties = new PropertyIDMap(summaryInformationIdValues); } return summaryInformationProperties; } - - /** - * Returns the Document Summary Information properties - * singleton. - * * @return The Document Summary Information properties singleton. */ - public static synchronized PropertyIDMap getDocumentSummaryInformationProperties() - { - if (documentSummaryInformationProperties == null) - { - PropertyIDMap m = new PropertyIDMap(17, (float) 1.0); - m.put(PID_DICTIONARY, "PID_DICTIONARY"); - m.put(PID_CODEPAGE, "PID_CODEPAGE"); - m.put(PID_CATEGORY, "PID_CATEGORY"); - m.put(PID_PRESFORMAT, "PID_PRESFORMAT"); - m.put(PID_BYTECOUNT, "PID_BYTECOUNT"); - m.put(PID_LINECOUNT, "PID_LINECOUNT"); - m.put(PID_PARCOUNT, "PID_PARCOUNT"); - m.put(PID_SLIDECOUNT, "PID_SLIDECOUNT"); - m.put(PID_NOTECOUNT, "PID_NOTECOUNT"); - m.put(PID_HIDDENCOUNT, "PID_HIDDENCOUNT"); - m.put(PID_MMCLIPCOUNT, "PID_MMCLIPCOUNT"); - m.put(PID_SCALE, "PID_SCALE"); - m.put(PID_HEADINGPAIR, "PID_HEADINGPAIR"); - m.put(PID_DOCPARTS, "PID_DOCPARTS"); - m.put(PID_MANAGER, "PID_MANAGER"); - m.put(PID_COMPANY, "PID_COMPANY"); - m.put(PID_LINKSDIRTY, "PID_LINKSDIRTY"); - documentSummaryInformationProperties = - new PropertyIDMap(Collections.unmodifiableMap(m)); + public static synchronized PropertyIDMap getDocumentSummaryInformationProperties() { + if (documentSummaryInformationProperties == null) { + documentSummaryInformationProperties = new PropertyIDMap(documentSummaryInformationIdValues); } return documentSummaryInformationProperties; } + /** + * Returns a property map, which is only used as a fallback, i.e. if available, the correct map + * for {@link DocumentSummaryInformation} or {@link SummaryInformation} should be used. + */ + public static synchronized PropertyIDMap getFallbackProperties() { + if (fallbackProperties == null) { + fallbackProperties = new PropertyIDMap(fallbackIdValues); + } + return fallbackProperties; + } + + @Override + public int size() { + return idMap.size(); + } + @Override + public boolean isEmpty() { + return idMap.isEmpty(); + } + + @Override + public boolean containsKey(Object key) { + return idMap.containsKey(key); + } + + @Override + public boolean containsValue(Object value) { + return idMap.containsValue(value); + } + + @Override + public String get(Object key) { + return idMap.get(key); + } + + @Override + public String put(Long key, String value) { + return idMap.put(key, value); + } + + @Override + public String remove(Object key) { + return idMap.remove(key); + } + + @Override + public void putAll(Map m) { + idMap.putAll(m); + } + + @Override + public void clear() { + idMap.clear(); + } + + @Override + public Set keySet() { + return idMap.keySet(); + } + + @Override + public Collection values() { + return idMap.values(); + } + + @Override + public Set> 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 3f1827b11..614126c0d 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 40c16cd46..eb1346b14 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 a017ecc79..e32c6b0d5 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 POIFile[] poiFiles; + private static final String[] POI_FILES = { + "\005SummaryInformation", + "\005DocumentSummaryInformation", + "WordDocument", + "\001CompObj", + "1Table" + }; + private static final int BYTE_ORDER = 0xfffe; + private static final int FORMAT = 0x0000; + private static final int OS_VERSION = 0x00020A04; + private static final ClassID CLASS_ID = new ClassID("{00000000-0000-0000-0000-000000000000}"); + private static final int[] SECTION_COUNT = {1, 2}; + private static final boolean[] IS_SUMMARY_INFORMATION = {true, false}; + private static final boolean[] IS_DOCUMENT_SUMMARY_INFORMATION = {false, true}; + + private List 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.

*/ @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
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 48103b0c9..4e9eec627 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; /** - *

Test case for OLE2 files with empty properties. An empty property's type - * is {@link Variant#VT_EMPTY}.

+ * 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(); /** - *

This test file's summary information stream contains some empty - * properties.

+ * 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 poiFiles; /** *

Read a the test file from the "data" directory.

@@ -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); } /** - *

Checks the names of the files in the POI filesystem. They - * are expected to be in a certain order.

+ * 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 6f7e6db1c..69e4367bb 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; /** - *

Tests whether Unicode string can be read from a - * DocumentSummaryInformation.

+ * 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; /** - *

Read a the test file from the "data" directory.

+ * 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 { /** - *

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}.

+ * 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 43737a3b0..4ef331f09 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 40b4e229d..cc489d853 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; @@ -43,31 +39,6 @@ import org.apache.poi.util.IOUtils; */ final class Util { - /** - *

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.

- * - * @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); - } - - - /** *

Reads a set of files from a POI filesystem and returns them * as an array of {@link POIFile} instances. This method loads all @@ -87,42 +58,34 @@ final class Util { * * @exception IOException if an I/O exception occurs */ - public static POIFile[] readPOIFiles(final File poiFs, - final String[] poiFiles) - throws FileNotFoundException, IOException - { + public static List readPOIFiles(final File poiFs, final String... poiFiles) + throws FileNotFoundException, IOException { final List files = new ArrayList(); 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 readPropertySets(final File poiFs) - throws FileNotFoundException, IOException { - FileInputStream stream = new FileInputStream(poiFs); - try { - return readPropertySets(stream); - } finally { - stream.close(); - } - } - - public static List readPropertySets(final InputStream poiFs) - throws FileNotFoundException, IOException { + public static List readPropertySets(final File poiFs) throws IOException { final List files = new ArrayList(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; } - - - - /** - *

Prints the system properties to System.out.

- */ - public static void printSystemProperties() - { - final Properties p = System.getProperties(); - final List names = new LinkedList(); - 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")); - } }