HPSF: Change CustomProperties to delegate

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1793699 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Andreas Beeker 2017-05-03 19:18:45 +00:00
parent d0630390d2
commit 76dbb5a1eb
3 changed files with 166 additions and 136 deletions

View File

@ -18,9 +18,15 @@
package org.apache.poi.hpsf; package org.apache.poi.hpsf;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date; import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
@ -58,10 +64,14 @@ import org.apache.poi.util.POILogger;
* internal representation. To external calls, it should appear as * internal representation. To external calls, it should appear as
* HashMap<String,Object> mapping between Names and Custom Property Values. * HashMap<String,Object> mapping between Names and Custom Property Values.
*/ */
@SuppressWarnings("serial") public class CustomProperties implements Map<String,Object> {
public class CustomProperties extends HashMap<Long,CustomProperty> {
private static final POILogger LOG = POILogFactory.getLogger(CustomProperties.class); private static final POILogger LOG = POILogFactory.getLogger(CustomProperties.class);
/**
* The custom properties
*/
private final HashMap<Long,CustomProperty> props = new HashMap<Long,CustomProperty>();
/** /**
* Maps property IDs to property names and vice versa. * Maps property IDs to property names and vice versa.
*/ */
@ -100,151 +110,141 @@ public class CustomProperties extends HashMap<Long,CustomProperty> {
checkCodePage(name); checkCodePage(name);
/* Register name and ID in the dictionary. Mapping in both directions is possible. If there is already a */ /* 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); dictionary.put(cp.getID(), name);
/* Put the custom property into this map. */ /* 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 * Adds a named property.
* map. The method will allocate a suitable ID for the custom property:
* *
* <ul> * @param key The property's name.
* <li>If there is already a property with the same name, take the ID * @param value The property's value.
* of that property. * @return the property that was stored under the specified name before, or
* * {@code null} if there was no such property before.
* <li>Otherwise find the highest ID and use its value plus one.
* </ul>
*
* @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 { @Override
final String name = customProperty.getName(); public Object put(String key, Object value) {
int variantType;
/* Check whether a property with this name is in the map already. */ if (value instanceof String) {
final Long oldId = (name == null) ? null : dictionary.getKey(name); variantType = Variant.VT_LPSTR;
if (oldId != null) { } else if (value instanceof Short) {
customProperty.setID(oldId); 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 { } else {
long lastKey = (dictionary.isEmpty()) ? 0 : dictionary.lastKey(); throw new IllegalStateException("unsupported datatype - currently String,Short,Integer,Long,Float,Double,Boolean,BigInteger(unsigned long),Date can be processed.");
long nextKey = Math.max(lastKey,PropertyIDMap.PID_MAX)+1;
customProperty.setID(nextKey);
} }
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.
*
* @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. * Gets a named value from the custom properties - only works for keys of type String
* *
* @param name The property's name. * @param key the name of the value to get
* @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
* @return the value or {@code null} if a value with the specified * @return the value or {@code null} if a value with the specified
* name is not found in the custom properties. * name is not found in the custom properties.
*/ */
public Object get(final String name) { @Override
final Long id = dictionary.getKey(name); public Object get(final Object key) {
final CustomProperty cp = super.get(id); final Long id = dictionary.getKey(key);
final CustomProperty cp = props.get(id);
return cp != null ? cp.getValue() : null; 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<? extends String, ? extends Object> m) {
for (Map.Entry<? extends String, ? extends Object> me : m.entrySet()) {
put(me.getKey(), me.getValue());
}
}
/** /**
* Adds a named date property. * @return the list of properties
*
* @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 Date value) { public List<CustomProperty> properties() {
final Property p = new MutableProperty(-1, Variant.VT_FILETIME, value); List<CustomProperty> list = new ArrayList<CustomProperty>(props.size());
return put(new CustomProperty(p, name)); 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<Object> values() {
List<Object> list = new ArrayList<Object>(props.size());
for (Long l : dictionary.keySet()) {
list.add(props.get(l).getValue());
}
return Collections.unmodifiableCollection(list);
}
@Override
public Set<Entry<String, Object>> entrySet() {
Map<String,Object> set = new LinkedHashMap<String,Object>(props.size());
for (Entry<Long,String> 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<Long,CustomProperty> {
@Override @Override
@SuppressWarnings({ "rawtypes", "unchecked" }) @SuppressWarnings({ "rawtypes", "unchecked" })
public Set keySet() { public Set keySet() {
return dictionary.values(); return Collections.unmodifiableSet(dictionary.values());
} }
/** /**
@ -265,7 +265,7 @@ public class CustomProperties extends HashMap<Long,CustomProperty> {
* @return a set of all the names of our custom properties * @return a set of all the names of our custom properties
*/ */
public Set<String> nameSet() { public Set<String> nameSet() {
return dictionary.values(); return Collections.unmodifiableSet(dictionary.values());
} }
/** /**
@ -273,8 +273,8 @@ public class CustomProperties extends HashMap<Long,CustomProperty> {
* *
* @return a set of all the IDs of our custom properties * @return a set of all the IDs of our custom properties
*/ */
public Set<String> idSet() { public Set<Long> idSet() {
return dictionary.values(); return Collections.unmodifiableSet(dictionary.keySet());
} }
@ -321,10 +321,10 @@ public class CustomProperties extends HashMap<Long,CustomProperty> {
@Override @Override
public boolean containsValue(Object value) { public boolean containsValue(Object value) {
if(value instanceof CustomProperty) { 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) { if(cp.getValue() == value) {
return true; return true;
} }
@ -354,6 +354,36 @@ public class CustomProperties extends HashMap<Long,CustomProperty> {
this.isPure = 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:
*
* <ul>
* <li>If there is already a property with the same name, take the ID
* of that property.
*
* <li>Otherwise find the highest ID and use its value plus one.
* </ul>
*
* @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) { private void checkCodePage(String value) {
int cp = getCodepage(); int cp = getCodepage();
if (cp == -1) { if (cp == -1) {

View File

@ -801,7 +801,7 @@ public class DocumentSummaryInformation extends SpecialPropertySet {
customProperties.setCodepage(cpCodepage); customProperties.setCodepage(cpCodepage);
section.setCodepage(cpCodepage); section.setCodepage(cpCodepage);
section.setDictionary(dictionary); section.setDictionary(dictionary);
for (CustomProperty p : customProperties.values()) { for (CustomProperty p : customProperties.properties()) {
section.setProperty(p); section.setProperty(p);
} }
} }

View File

@ -218,9 +218,9 @@ public class TestReadAllFiles {
return; return;
} }
for (CustomProperty cp : cps.values()) { for (CustomProperty cp : cps.properties()) {
cp.getName(); assertNotNull(cp.getName());
cp.getValue(); assertNotNull(cp.getValue());
} }
} finally { } finally {
poifs.close(); poifs.close();