diff --git a/src/scratchpad/src/org/apache/poi/hsmf/datatypes/Chunks.java b/src/scratchpad/src/org/apache/poi/hsmf/datatypes/Chunks.java index d7ad2f009..19165a013 100644 --- a/src/scratchpad/src/org/apache/poi/hsmf/datatypes/Chunks.java +++ b/src/scratchpad/src/org/apache/poi/hsmf/datatypes/Chunks.java @@ -90,6 +90,13 @@ public final class Chunks implements ChunkGroupWithProperties { } else return Collections.emptyMap(); } + public Map getRawProperties() { + if (messageProperties != null) { + return messageProperties.getRawProperties(); + } + else return Collections.emptyMap(); + } + public Map> getAll() { return allChunks; } diff --git a/src/scratchpad/src/org/apache/poi/hsmf/datatypes/PropertiesChunk.java b/src/scratchpad/src/org/apache/poi/hsmf/datatypes/PropertiesChunk.java index c48b757dc..8669878ff 100644 --- a/src/scratchpad/src/org/apache/poi/hsmf/datatypes/PropertiesChunk.java +++ b/src/scratchpad/src/org/apache/poi/hsmf/datatypes/PropertiesChunk.java @@ -20,7 +20,7 @@ package org.apache.poi.hsmf.datatypes; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -42,9 +42,10 @@ import org.apache.poi.util.POILogFactory; import org.apache.poi.util.POILogger; /** - * A Chunk which holds fixed-length properties, and pointer - * to the variable length ones (which get their own chunk). - * There are two kinds of PropertiesChunks, which differ only in + *

A Chunk which holds (single) fixed-length properties, and pointer + * to the variable length ones / multi-valued ones (which get their + * own chunk). + *

There are two kinds of PropertiesChunks, which differ only in * their headers. */ public abstract class PropertiesChunk extends Chunk { @@ -53,16 +54,16 @@ public abstract class PropertiesChunk extends Chunk { /** For logging problems we spot with the file */ private POILogger logger = POILogFactory.getLogger(PropertiesChunk.class); - /** - * Holds properties, indexed by type. Properties can be multi-valued + * Holds properties, indexed by type. If a property is multi-valued, + * or variable length, it will be held via a {@link ChunkBasedPropertyValue}. */ - private Map> properties = - new HashMap>(); + private Map properties = + new HashMap(); /** * The ChunkGroup that these properties apply to. Used when - * matching chunks to variable sized properties + * matching chunks to variable sized and multi-valued properties */ private ChunkGroup parentGroup; @@ -80,29 +81,51 @@ public abstract class PropertiesChunk extends Chunk { } /** - * Returns all the properties in the chunk + * Returns all the properties in the chunk, without + * looking up any chunk-based values */ - public Map> getProperties() { + public Map getRawProperties() { return properties; } /** - * Returns all values for the given property, of null if none exist + *

Returns all the properties in the chunk, along with their + * values. + *

Any chunk-based values will be looked up and returned as such */ - public List getValues(MAPIProperty property) { - return properties.get(property); + public Map> getProperties() { + Map> props = + new HashMap>(properties.size()); + for (MAPIProperty prop : properties.keySet()) { + props.put(prop, getValues(prop)); + } + return props; } /** - * Returns the (first/only) value for the given property, or - * null if none exist + * Returns all values for the given property, looking up chunk based + * ones as required, of null if none exist */ - public PropertyValue getValue(MAPIProperty property) { - List values = properties.get(property); - if (values != null && values.size() > 0) { - return values.get(0); - } - return null; + public List getValues(MAPIProperty property) { + PropertyValue val = properties.get(property); + if (val == null) { + return null; + } + if (val instanceof ChunkBasedPropertyValue) { + ChunkBasedPropertyValue cval = (ChunkBasedPropertyValue)val; + // TODO Lookup + return Collections.emptyList(); + } else { + return Collections.singletonList(val); + } + } + + /** + * Returns the value / pointer to the value chunk of + * the property, or null if none exists + */ + public PropertyValue getRawValue(MAPIProperty property) { + return properties.get(property); } /** @@ -118,23 +141,19 @@ public abstract class PropertiesChunk extends Chunk { } // Loop over our values, looking for chunk based ones - for (List vals : properties.values()) { - if (vals != null) { - for (PropertyValue val : vals) { - if (val instanceof ChunkBasedPropertyValue) { - ChunkBasedPropertyValue cVal = (ChunkBasedPropertyValue)val; - Chunk chunk = chunks.get(cVal.getProperty().id); -//System.err.println(cVal + " -> " + HexDump.toHex(cVal.data)); + for (PropertyValue val : properties.values()) { + if (val instanceof ChunkBasedPropertyValue) { + ChunkBasedPropertyValue cVal = (ChunkBasedPropertyValue)val; + Chunk chunk = chunks.get(cVal.getProperty().id); +//System.err.println(cVal.getProperty() + " = " + cVal + " -> " + HexDump.toHex(cVal.data)); - // TODO Make sense of the raw offset value - - if (chunk != null) { - cVal.setValue(chunk); - } else { - logger.log(POILogger.WARN, "No chunk found matching Property " + cVal); - } - } - } + // TODO Make sense of the raw offset value + + if (chunk != null) { + cVal.setValue(chunk); + } else { + logger.log(POILogger.WARN, "No chunk found matching Property " + cVal); + } } } } @@ -183,6 +202,10 @@ public abstract class PropertiesChunk extends Chunk { } } + // TODO Detect if it is multi-valued, since if it is + // then even fixed-length strings store their multiple + // values in another chunk (much as variable length ones) + // Work out how long the "data" is // This might be the actual data, or just a pointer // to another chunk which holds the data itself @@ -240,11 +263,11 @@ public abstract class PropertiesChunk extends Chunk { else { propVal = new PropertyValue(prop, flags, data); } - - if (properties.get(prop) == null) { - properties.put(prop, new ArrayList()); + + if (properties.get(prop) != null) { + logger.log(POILogger.WARN, "Duplicate values found for " + prop); } - properties.get(prop).add(propVal); + properties.put(prop, propVal); } catch (BufferUnderrunException e) { // Invalid property, ended short going = false; diff --git a/src/scratchpad/testcases/org/apache/poi/hsmf/TestFixedSizedProperties.java b/src/scratchpad/testcases/org/apache/poi/hsmf/TestFixedSizedProperties.java index cf4687857..4108ec0ec 100644 --- a/src/scratchpad/testcases/org/apache/poi/hsmf/TestFixedSizedProperties.java +++ b/src/scratchpad/testcases/org/apache/poi/hsmf/TestFixedSizedProperties.java @@ -31,6 +31,7 @@ import java.util.TimeZone; import org.apache.poi.POIDataSamples; import org.apache.poi.POITestCase; import org.apache.poi.hsmf.datatypes.ChunkBasedPropertyValue; +import org.apache.poi.hsmf.datatypes.Chunks; import org.apache.poi.hsmf.datatypes.MAPIProperty; import org.apache.poi.hsmf.datatypes.PropertyValue; import org.apache.poi.hsmf.datatypes.PropertyValue.LongPropertyValue; @@ -86,9 +87,12 @@ public final class TestFixedSizedProperties extends POITestCase { * Check we find properties of a variety of different types */ public void testPropertyValueTypes() throws Exception { - Map> props = - mapiMessageSucceeds.getMainChunks().getProperties(); - HashSet> seenTypes = new HashSet>(); + Chunks mainChunks = mapiMessageSucceeds.getMainChunks(); + + // Ask to have the values looked up + Map> props = mainChunks.getProperties(); + HashSet> seenTypes = + new HashSet>(); for (List pvs : props.values()) { for (PropertyValue pv : pvs) { seenTypes.add(pv.getClass()); @@ -97,6 +101,16 @@ public final class TestFixedSizedProperties extends POITestCase { assertTrue(seenTypes.toString(), seenTypes.size() > 3); assertTrue(seenTypes.toString(), seenTypes.contains(LongPropertyValue.class)); assertTrue(seenTypes.toString(), seenTypes.contains(TimePropertyValue.class)); + assertFalse(seenTypes.toString(), seenTypes.contains(ChunkBasedPropertyValue.class)); + + // Ask for the raw values + seenTypes.clear(); + for (PropertyValue pv : mainChunks.getRawProperties().values()) { + seenTypes.add(pv.getClass()); + } + assertTrue(seenTypes.toString(), seenTypes.size() > 3); + assertTrue(seenTypes.toString(), seenTypes.contains(LongPropertyValue.class)); + assertTrue(seenTypes.toString(), seenTypes.contains(TimePropertyValue.class)); assertTrue(seenTypes.toString(), seenTypes.contains(ChunkBasedPropertyValue.class)); }