diff --git a/src/java/org/apache/poi/hpsf/CustomProperties.java b/src/java/org/apache/poi/hpsf/CustomProperties.java index a279e2321..da4e9277b 100644 --- a/src/java/org/apache/poi/hpsf/CustomProperties.java +++ b/src/java/org/apache/poi/hpsf/CustomProperties.java @@ -18,9 +18,15 @@ package org.apache.poi.hpsf; import java.io.UnsupportedEncodingException; +import java.math.BigInteger; import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; import java.util.Date; import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; import java.util.Map; import java.util.Set; @@ -58,10 +64,14 @@ import org.apache.poi.util.POILogger; * internal representation. To external calls, it should appear as * HashMap<String,Object> mapping between Names and Custom Property Values. */ -@SuppressWarnings("serial") -public class CustomProperties extends HashMap { +public class CustomProperties implements Map { private static final POILogger LOG = POILogFactory.getLogger(CustomProperties.class); - + + /** + * The custom properties + */ + private final HashMap props = new HashMap(); + /** * Maps property IDs to property names and vice versa. */ @@ -100,151 +110,141 @@ public class CustomProperties extends HashMap { checkCodePage(name); /* Register name and ID in the dictionary. Mapping in both directions is possible. If there is already a */ - super.remove(dictionary.getKey(name)); + props.remove(dictionary.getKey(name)); dictionary.put(cp.getID(), name); /* Put the custom property into this map. */ - return super.put(cp.getID(), cp); + return props.put(cp.getID(), cp); } - - /** - * Puts a {@link CustomProperty} that has not yet a valid ID into this - * map. The method will allocate a suitable ID for the custom property: + * Adds a named property. * - *
    - *
  • If there is already a property with the same name, take the ID - * of that property. - * - *
  • Otherwise find the highest ID and use its value plus one. - *
- * - * @param customProperty - * @return If there was already a property with the same name, the old property - * @throws ClassCastException + * @param key The property's name. + * @param value The property's value. + * @return the property that was stored under the specified name before, or + * {@code null} if there was no such property before. */ - private Object put(final CustomProperty customProperty) throws ClassCastException { - final String name = customProperty.getName(); - - /* Check whether a property with this name is in the map already. */ - final Long oldId = (name == null) ? null : dictionary.getKey(name); - if (oldId != null) { - customProperty.setID(oldId); + @Override + public Object put(String key, Object value) { + int variantType; + if (value instanceof String) { + variantType = Variant.VT_LPSTR; + } else if (value instanceof Short) { + variantType = Variant.VT_I2; + } else if (value instanceof Integer) { + variantType = Variant.VT_I4; + } else if (value instanceof Long) { + variantType = Variant.VT_I8; + } else if (value instanceof Float) { + variantType = Variant.VT_R4; + } else if (value instanceof Double) { + variantType = Variant.VT_R8; + } else if (value instanceof Boolean) { + variantType = Variant.VT_BOOL; + } else if (value instanceof BigInteger + && ((BigInteger)value).bitLength() <= 64 + && ((BigInteger)value).compareTo(BigInteger.ZERO) >= 0) { + variantType = Variant.VT_UI8; + } else if (value instanceof Date) { + variantType = Variant.VT_FILETIME; } else { - long lastKey = (dictionary.isEmpty()) ? 0 : dictionary.lastKey(); - long nextKey = Math.max(lastKey,PropertyIDMap.PID_MAX)+1; - customProperty.setID(nextKey); + throw new IllegalStateException("unsupported datatype - currently String,Short,Integer,Long,Float,Double,Boolean,BigInteger(unsigned long),Date can be processed."); } - return this.put(name, customProperty); + final Property p = new MutableProperty(-1, variantType, value); + return put(new CustomProperty(p, key)); } - - - + /** - * Removes a custom property. - * @param name The name of the custom property to remove - * @return The removed property or {@code null} if the specified property was not found. + * Gets a named value from the custom properties - only works for keys of type String * - * @see java.util.HashSet#remove(java.lang.Object) - */ - public Object remove(final String name) { - final Long id = dictionary.removeValue(name); - return super.remove(id); - } - - /** - * Adds a named string property. - * - * @param name The property's name. - * @param value The property's value. - * @return the property that was stored under the specified name before, or - * {@code null} if there was no such property before. - */ - public Object put(final String name, final String value) { - final Property p = new MutableProperty(-1, Variant.VT_LPSTR, value); - return put(new CustomProperty(p, name)); - } - - /** - * Adds a named long property. - * - * @param name The property's name. - * @param value The property's value. - * @return the property that was stored under the specified name before, or - * {@code null} if there was no such property before. - */ - public Object put(final String name, final Long value) { - final Property p = new MutableProperty(-1, Variant.VT_I8, value); - return put(new CustomProperty(p, name)); - } - - /** - * Adds a named double property. - * - * @param name The property's name. - * @param value The property's value. - * @return the property that was stored under the specified name before, or - * {@code null} if there was no such property before. - */ - public Object put(final String name, final Double value) { - final Property p = new MutableProperty(-1, Variant.VT_R8, value); - return put(new CustomProperty(p, name)); - } - - /** - * Adds a named integer property. - * - * @param name The property's name. - * @param value The property's value. - * @return the property that was stored under the specified name before, or - * {@code null} if there was no such property before. - */ - public Object put(final String name, final Integer value) { - final Property p = new MutableProperty(-1, Variant.VT_I4, value); - return put(new CustomProperty(p, name)); - } - - /** - * Adds a named boolean property. - * - * @param name The property's name. - * @param value The property's value. - * @return the property that was stored under the specified name before, or - * {@code null} if there was no such property before. - */ - public Object put(final String name, final Boolean value) { - final Property p = new MutableProperty(-1, Variant.VT_BOOL, value); - return put(new CustomProperty(p, name)); - } - - - /** - * Gets a named value from the custom properties. - * - * @param name the name of the value to get + * @param key the name of the value to get * @return the value or {@code null} if a value with the specified * name is not found in the custom properties. */ - public Object get(final String name) { - final Long id = dictionary.getKey(name); - final CustomProperty cp = super.get(id); + @Override + public Object get(final Object key) { + final Long id = dictionary.getKey(key); + final CustomProperty cp = props.get(id); return cp != null ? cp.getValue() : null; } + /** + * Removes a custom property - only works for keys of type String + * @param key The name of the custom property to remove + * @return The removed property or {@code null} if the specified property was not found. + */ + @Override + public CustomProperty remove(Object key) { + final Long id = dictionary.removeValue(key); + return props.remove(id); + } + @Override + public int size() { + return props.size(); + } + + @Override + public boolean isEmpty() { + return props.isEmpty(); + } + + @Override + public void clear() { + props.clear(); + } + + @Override + public int hashCode() { + return props.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof CustomProperties)) { + return false; + } + return props.equals(((CustomProperties)obj).props); + } + + @Override + public void putAll(Map m) { + for (Map.Entry me : m.entrySet()) { + put(me.getKey(), me.getValue()); + } + } /** - * Adds a named date property. - * - * @param name The property's name. - * @param value The property's value. - * @return the property that was stored under the specified name before, or - * {@code null} if there was no such property before. + * @return the list of properties */ - public Object put(final String name, final Date value) { - final Property p = new MutableProperty(-1, Variant.VT_FILETIME, value); - return put(new CustomProperty(p, name)); + public List properties() { + List list = new ArrayList(props.size()); + for (Long l : dictionary.keySet()) { + list.add(props.get(l)); + } + return Collections.unmodifiableList(list); + } + + /** + * @return the list of property values - use {@link #properties()} for the wrapped values + */ + @Override + public Collection values() { + List list = new ArrayList(props.size()); + for (Long l : dictionary.keySet()) { + list.add(props.get(l).getValue()); + } + return Collections.unmodifiableCollection(list); + } + + @Override + public Set> entrySet() { + Map set = new LinkedHashMap(props.size()); + for (Entry se : dictionary.entrySet()) { + set.put(se.getValue(), props.get(se.getKey()).getValue()); + } + return Collections.unmodifiableSet(set.entrySet()); } /** @@ -256,7 +256,7 @@ public class CustomProperties extends HashMap { @Override @SuppressWarnings({ "rawtypes", "unchecked" }) public Set keySet() { - return dictionary.values(); + return Collections.unmodifiableSet(dictionary.values()); } /** @@ -265,7 +265,7 @@ public class CustomProperties extends HashMap { * @return a set of all the names of our custom properties */ public Set nameSet() { - return dictionary.values(); + return Collections.unmodifiableSet(dictionary.values()); } /** @@ -273,8 +273,8 @@ public class CustomProperties extends HashMap { * * @return a set of all the IDs of our custom properties */ - public Set idSet() { - return dictionary.values(); + public Set idSet() { + return Collections.unmodifiableSet(dictionary.keySet()); } @@ -321,10 +321,10 @@ public class CustomProperties extends HashMap { @Override public boolean containsValue(Object value) { if(value instanceof CustomProperty) { - return super.containsValue(value); + return props.containsValue(value); } - for(CustomProperty cp : super.values()) { + for(CustomProperty cp : props.values()) { if(cp.getValue() == value) { return true; } @@ -353,7 +353,37 @@ public class CustomProperties extends HashMap { public void setPure(final boolean isPure) { this.isPure = isPure; } - + + /** + * Puts a {@link CustomProperty} that has not yet a valid ID into this + * map. The method will allocate a suitable ID for the custom property: + * + *
    + *
  • If there is already a property with the same name, take the ID + * of that property. + * + *
  • Otherwise find the highest ID and use its value plus one. + *
+ * + * @param customProperty + * @return If there was already a property with the same name, the old property + * @throws ClassCastException + */ + private Object put(final CustomProperty customProperty) throws ClassCastException { + final String name = customProperty.getName(); + + /* Check whether a property with this name is in the map already. */ + final Long oldId = (name == null) ? null : dictionary.getKey(name); + if (oldId != null) { + customProperty.setID(oldId); + } else { + long lastKey = (dictionary.isEmpty()) ? 0 : dictionary.lastKey(); + long nextKey = Math.max(lastKey,PropertyIDMap.PID_MAX)+1; + customProperty.setID(nextKey); + } + return this.put(name, customProperty); + } + private void checkCodePage(String value) { int cp = getCodepage(); if (cp == -1) { diff --git a/src/java/org/apache/poi/hpsf/DocumentSummaryInformation.java b/src/java/org/apache/poi/hpsf/DocumentSummaryInformation.java index 7dd41a0cc..cea4d5dd9 100644 --- a/src/java/org/apache/poi/hpsf/DocumentSummaryInformation.java +++ b/src/java/org/apache/poi/hpsf/DocumentSummaryInformation.java @@ -801,7 +801,7 @@ public class DocumentSummaryInformation extends SpecialPropertySet { customProperties.setCodepage(cpCodepage); section.setCodepage(cpCodepage); section.setDictionary(dictionary); - for (CustomProperty p : customProperties.values()) { + for (CustomProperty p : customProperties.properties()) { section.setProperty(p); } } diff --git a/src/testcases/org/apache/poi/hpsf/basic/TestReadAllFiles.java b/src/testcases/org/apache/poi/hpsf/basic/TestReadAllFiles.java index 0df2f56cb..ce3cf6d34 100644 --- a/src/testcases/org/apache/poi/hpsf/basic/TestReadAllFiles.java +++ b/src/testcases/org/apache/poi/hpsf/basic/TestReadAllFiles.java @@ -218,9 +218,9 @@ public class TestReadAllFiles { return; } - for (CustomProperty cp : cps.values()) { - cp.getName(); - cp.getValue(); + for (CustomProperty cp : cps.properties()) { + assertNotNull(cp.getName()); + assertNotNull(cp.getValue()); } } finally { poifs.close();