2006-03-03 11:57:55 -05:00
|
|
|
/* ====================================================================
|
2006-12-22 14:18:16 -05:00
|
|
|
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
|
2006-03-03 11:57:55 -05:00
|
|
|
|
|
|
|
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.util.Date;
|
|
|
|
import java.util.HashMap;
|
|
|
|
import java.util.Iterator;
|
|
|
|
import java.util.Map;
|
2008-08-12 14:44:50 -04:00
|
|
|
import java.util.Set;
|
2006-03-03 11:57:55 -05:00
|
|
|
|
|
|
|
import org.apache.poi.hpsf.wellknown.PropertyIDMap;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* <p>Maintains the instances of {@link CustomProperty} that belong to a
|
|
|
|
* {@link DocumentSummaryInformation}. The class maintains the names of the
|
|
|
|
* custom properties in a dictionary. It implements the {@link Map} interface
|
|
|
|
* and by this provides a simplified view on custom properties: A property's
|
|
|
|
* name is the key that maps to a typed value. This implementation hides
|
|
|
|
* property IDs from the developer and regards the property names as keys to
|
|
|
|
* typed values.</p>
|
2009-10-08 18:29:41 -04:00
|
|
|
*
|
2006-03-03 11:57:55 -05:00
|
|
|
* <p>While this class provides a simple API to custom properties, it ignores
|
|
|
|
* the fact that not names, but IDs are the real keys to properties. Under the
|
|
|
|
* hood this class maintains a 1:1 relationship between IDs and names. Therefore
|
|
|
|
* you should not use this class to process property sets with several IDs
|
|
|
|
* mapping to the same name or with properties without a name: the result will
|
|
|
|
* contain only a subset of the original properties. If you really need to deal
|
|
|
|
* such property sets, use HPSF's low-level access methods.</p>
|
2009-10-08 18:29:41 -04:00
|
|
|
*
|
2006-03-03 11:57:55 -05:00
|
|
|
* <p>An application can call the {@link #isPure} method to check whether a
|
|
|
|
* property set parsed by {@link CustomProperties} is still pure (i.e.
|
|
|
|
* unmodified) or whether one or more properties have been dropped.</p>
|
2009-10-08 18:29:41 -04:00
|
|
|
*
|
2006-03-03 11:57:55 -05:00
|
|
|
* <p>This class is not thread-safe; concurrent access to instances of this
|
2009-06-01 17:07:20 -04:00
|
|
|
* class must be synchronized.</p>
|
2009-10-08 18:29:41 -04:00
|
|
|
*
|
2010-01-13 10:42:53 -05:00
|
|
|
* <p>While this class is roughly HashMap<Long,CustomProperty>, that's the
|
|
|
|
* internal representation. To external calls, it should appear as
|
|
|
|
* HashMap<String,Object> mapping between Names and Custom Property Values.</p>
|
2006-03-03 11:57:55 -05:00
|
|
|
*/
|
2010-01-13 10:42:53 -05:00
|
|
|
@SuppressWarnings("serial")
|
|
|
|
public class CustomProperties extends HashMap<Object,CustomProperty>
|
2006-03-03 11:57:55 -05:00
|
|
|
{
|
|
|
|
|
|
|
|
/**
|
|
|
|
* <p>Maps property IDs to property names.</p>
|
|
|
|
*/
|
2010-01-13 10:42:53 -05:00
|
|
|
private Map<Long,String> dictionaryIDToName = new HashMap<Long,String>();
|
2006-03-03 11:57:55 -05:00
|
|
|
|
|
|
|
/**
|
|
|
|
* <p>Maps property names to property IDs.</p>
|
|
|
|
*/
|
2010-01-13 10:42:53 -05:00
|
|
|
private Map<String,Long> dictionaryNameToID = new HashMap<String,Long>();
|
2009-10-08 18:29:41 -04:00
|
|
|
|
2006-03-03 11:57:55 -05:00
|
|
|
/**
|
|
|
|
* <p>Tells whether this object is pure or not.</p>
|
|
|
|
*/
|
|
|
|
private boolean isPure = true;
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* <p>Puts a {@link CustomProperty} into this map. It is assumed that the
|
|
|
|
* {@link CustomProperty} already has a valid ID. Otherwise use
|
|
|
|
* {@link #put(CustomProperty)}.</p>
|
|
|
|
*/
|
2010-01-13 10:42:53 -05:00
|
|
|
public CustomProperty put(final String name, final CustomProperty cp)
|
2006-03-03 11:57:55 -05:00
|
|
|
{
|
|
|
|
if (name == null)
|
|
|
|
{
|
|
|
|
/* Ignoring a property without a name. */
|
|
|
|
isPure = false;
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
if (!(name.equals(cp.getName())))
|
|
|
|
throw new IllegalArgumentException("Parameter \"name\" (" + name +
|
|
|
|
") and custom property's name (" + cp.getName() +
|
|
|
|
") do not match.");
|
|
|
|
|
|
|
|
/* Register name and ID in the dictionary. Mapping in both directions is possible. If there is already a */
|
2009-10-08 18:29:41 -04:00
|
|
|
final Long idKey = Long.valueOf(cp.getID());
|
2010-01-13 10:42:53 -05:00
|
|
|
final Long oldID = dictionaryNameToID.get(name);
|
2006-03-03 11:57:55 -05:00
|
|
|
dictionaryIDToName.remove(oldID);
|
|
|
|
dictionaryNameToID.put(name, idKey);
|
|
|
|
dictionaryIDToName.put(idKey, name);
|
|
|
|
|
|
|
|
/* Put the custom property into this map. */
|
2010-01-13 10:42:53 -05:00
|
|
|
final CustomProperty oldCp = super.remove(oldID);
|
2006-03-03 11:57:55 -05:00
|
|
|
super.put(idKey, cp);
|
|
|
|
return oldCp;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* <p>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:</p>
|
2009-10-08 18:29:41 -04:00
|
|
|
*
|
2006-03-03 11:57:55 -05:00
|
|
|
* <ul>
|
2009-10-08 18:29:41 -04:00
|
|
|
*
|
2006-03-03 11:57:55 -05:00
|
|
|
* <li><p>If there is already a property with the same name, take the ID
|
|
|
|
* of that property.</p></li>
|
2009-10-08 18:29:41 -04:00
|
|
|
*
|
2006-03-03 11:57:55 -05:00
|
|
|
* <li><p>Otherwise find the highest ID and use its value plus one.</p></li>
|
2009-10-08 18:29:41 -04:00
|
|
|
*
|
2006-03-03 11:57:55 -05:00
|
|
|
* </ul>
|
2009-10-08 18:29:41 -04:00
|
|
|
*
|
2006-03-03 11:57:55 -05:00
|
|
|
* @param customProperty
|
|
|
|
* @return If the was already a property with the same name, the
|
|
|
|
* @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. */
|
2014-12-28 04:06:12 -05:00
|
|
|
final Long oldId = dictionaryNameToID.get(name);
|
2006-03-03 11:57:55 -05:00
|
|
|
if (oldId != null)
|
|
|
|
customProperty.setID(oldId.longValue());
|
|
|
|
else
|
|
|
|
{
|
|
|
|
long max = 1;
|
2010-01-13 10:42:53 -05:00
|
|
|
for (final Iterator<Long> i = dictionaryIDToName.keySet().iterator(); i.hasNext();)
|
2006-03-03 11:57:55 -05:00
|
|
|
{
|
2010-01-13 10:42:53 -05:00
|
|
|
final long id = i.next().longValue();
|
2006-03-03 11:57:55 -05:00
|
|
|
if (id > max)
|
|
|
|
max = id;
|
|
|
|
}
|
|
|
|
customProperty.setID(max + 1);
|
|
|
|
}
|
|
|
|
return this.put(name, customProperty);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* <p>Removes a custom property.</p>
|
2009-10-08 18:29:41 -04:00
|
|
|
* @param name The name of the custom property to remove
|
2006-03-03 11:57:55 -05:00
|
|
|
* @return The removed property or <code>null</code> if the specified property was not found.
|
|
|
|
*
|
|
|
|
* @see java.util.HashSet#remove(java.lang.Object)
|
|
|
|
*/
|
|
|
|
public Object remove(final String name)
|
|
|
|
{
|
2014-12-28 04:06:12 -05:00
|
|
|
final Long id = dictionaryNameToID.get(name);
|
2006-03-03 11:57:55 -05:00
|
|
|
if (id == null)
|
|
|
|
return null;
|
|
|
|
dictionaryIDToName.remove(id);
|
|
|
|
dictionaryNameToID.remove(name);
|
|
|
|
return super.remove(id);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* <p>Adds a named string property.</p>
|
2009-10-08 18:29:41 -04:00
|
|
|
*
|
2006-03-03 11:57:55 -05:00
|
|
|
* @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</code> if there was no such property before.
|
|
|
|
*/
|
|
|
|
public Object put(final String name, final String value)
|
|
|
|
{
|
|
|
|
final MutableProperty p = new MutableProperty();
|
|
|
|
p.setID(-1);
|
|
|
|
p.setType(Variant.VT_LPWSTR);
|
|
|
|
p.setValue(value);
|
|
|
|
final CustomProperty cp = new CustomProperty(p, name);
|
|
|
|
return put(cp);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* <p>Adds a named long property.</p>
|
|
|
|
*
|
|
|
|
* @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</code> if there was no such property before.
|
|
|
|
*/
|
|
|
|
public Object put(final String name, final Long value)
|
|
|
|
{
|
|
|
|
final MutableProperty p = new MutableProperty();
|
|
|
|
p.setID(-1);
|
|
|
|
p.setType(Variant.VT_I8);
|
|
|
|
p.setValue(value);
|
|
|
|
final CustomProperty cp = new CustomProperty(p, name);
|
|
|
|
return put(cp);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* <p>Adds a named double property.</p>
|
|
|
|
*
|
|
|
|
* @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</code> if there was no such property before.
|
|
|
|
*/
|
|
|
|
public Object put(final String name, final Double value)
|
|
|
|
{
|
|
|
|
final MutableProperty p = new MutableProperty();
|
|
|
|
p.setID(-1);
|
|
|
|
p.setType(Variant.VT_R8);
|
|
|
|
p.setValue(value);
|
|
|
|
final CustomProperty cp = new CustomProperty(p, name);
|
|
|
|
return put(cp);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* <p>Adds a named integer property.</p>
|
|
|
|
*
|
|
|
|
* @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</code> if there was no such property before.
|
|
|
|
*/
|
|
|
|
public Object put(final String name, final Integer value)
|
|
|
|
{
|
|
|
|
final MutableProperty p = new MutableProperty();
|
|
|
|
p.setID(-1);
|
|
|
|
p.setType(Variant.VT_I4);
|
|
|
|
p.setValue(value);
|
|
|
|
final CustomProperty cp = new CustomProperty(p, name);
|
|
|
|
return put(cp);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* <p>Adds a named boolean property.</p>
|
|
|
|
*
|
|
|
|
* @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</code> if there was no such property before.
|
|
|
|
*/
|
|
|
|
public Object put(final String name, final Boolean value)
|
|
|
|
{
|
|
|
|
final MutableProperty p = new MutableProperty();
|
|
|
|
p.setID(-1);
|
|
|
|
p.setType(Variant.VT_BOOL);
|
|
|
|
p.setValue(value);
|
|
|
|
final CustomProperty cp = new CustomProperty(p, name);
|
|
|
|
return put(cp);
|
|
|
|
}
|
|
|
|
|
2009-10-08 18:29:41 -04:00
|
|
|
|
2006-03-03 11:57:55 -05:00
|
|
|
/**
|
|
|
|
* <p>Gets a named value from the custom properties.</p>
|
2009-10-08 18:29:41 -04:00
|
|
|
*
|
2006-03-03 11:57:55 -05:00
|
|
|
* @param name the name of the value to get
|
|
|
|
* @return the value or <code>null</code> if a value with the specified
|
|
|
|
* name is not found in the custom properties.
|
|
|
|
*/
|
|
|
|
public Object get(final String name)
|
|
|
|
{
|
2014-12-28 04:06:12 -05:00
|
|
|
final Long id = dictionaryNameToID.get(name);
|
|
|
|
final CustomProperty cp = super.get(id);
|
2006-03-03 11:57:55 -05:00
|
|
|
return cp != null ? cp.getValue() : null;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* <p>Adds a named date property.</p>
|
|
|
|
*
|
|
|
|
* @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</code> if there was no such property before.
|
|
|
|
*/
|
|
|
|
public Object put(final String name, final Date value)
|
|
|
|
{
|
|
|
|
final MutableProperty p = new MutableProperty();
|
|
|
|
p.setID(-1);
|
|
|
|
p.setType(Variant.VT_FILETIME);
|
|
|
|
p.setValue(value);
|
|
|
|
final CustomProperty cp = new CustomProperty(p, name);
|
|
|
|
return put(cp);
|
|
|
|
}
|
2009-10-08 18:29:41 -04:00
|
|
|
|
2006-03-03 11:57:55 -05:00
|
|
|
/**
|
2008-08-12 14:44:50 -04:00
|
|
|
* Returns a set of all the names of our
|
2010-01-13 10:42:53 -05:00
|
|
|
* custom properties. Equivalent to
|
|
|
|
* {@link #nameSet()}
|
2008-08-12 14:44:50 -04:00
|
|
|
*/
|
|
|
|
public Set keySet() {
|
2009-10-08 18:29:41 -04:00
|
|
|
return dictionaryNameToID.keySet();
|
|
|
|
}
|
2008-08-12 14:44:50 -04:00
|
|
|
|
2010-01-13 10:42:53 -05:00
|
|
|
/**
|
|
|
|
* Returns a set of all the names of our
|
|
|
|
* custom properties
|
|
|
|
*/
|
|
|
|
public Set<String> nameSet() {
|
|
|
|
return dictionaryNameToID.keySet();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns a set of all the IDs of our
|
|
|
|
* custom properties
|
|
|
|
*/
|
|
|
|
public Set<String> idSet() {
|
|
|
|
return dictionaryNameToID.keySet();
|
|
|
|
}
|
2008-08-12 14:44:50 -04:00
|
|
|
|
|
|
|
|
2009-10-08 18:29:41 -04:00
|
|
|
/**
|
2006-03-03 11:57:55 -05:00
|
|
|
* <p>Sets the codepage.</p>
|
|
|
|
*
|
|
|
|
* @param codepage the codepage
|
|
|
|
*/
|
|
|
|
public void setCodepage(final int codepage)
|
|
|
|
{
|
|
|
|
final MutableProperty p = new MutableProperty();
|
|
|
|
p.setID(PropertyIDMap.PID_CODEPAGE);
|
|
|
|
p.setType(Variant.VT_I2);
|
2009-10-08 18:29:41 -04:00
|
|
|
p.setValue(Integer.valueOf(codepage));
|
2006-03-03 11:57:55 -05:00
|
|
|
put(new CustomProperty(p));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* <p>Gets the dictionary which contains IDs and names of the named custom
|
|
|
|
* properties.
|
2009-10-08 18:29:41 -04:00
|
|
|
*
|
2006-03-03 11:57:55 -05:00
|
|
|
* @return the dictionary.
|
|
|
|
*/
|
2010-01-13 10:42:53 -05:00
|
|
|
Map<Long,String> getDictionary()
|
2006-03-03 11:57:55 -05:00
|
|
|
{
|
|
|
|
return dictionaryIDToName;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2010-01-13 10:42:53 -05:00
|
|
|
* Checks against both String Name and Long ID
|
|
|
|
*/
|
|
|
|
public boolean containsKey(Object key) {
|
|
|
|
if(key instanceof Long) {
|
2014-12-28 04:06:12 -05:00
|
|
|
return super.containsKey(key);
|
2010-01-13 10:42:53 -05:00
|
|
|
}
|
|
|
|
if(key instanceof String) {
|
2014-12-28 04:06:12 -05:00
|
|
|
return super.containsKey(dictionaryNameToID.get(key));
|
2010-01-13 10:42:53 -05:00
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Checks against both the property, and its values.
|
|
|
|
*/
|
|
|
|
public boolean containsValue(Object value) {
|
|
|
|
if(value instanceof CustomProperty) {
|
2014-12-28 04:06:12 -05:00
|
|
|
return super.containsValue(value);
|
2010-01-13 10:42:53 -05:00
|
|
|
} else {
|
|
|
|
for(CustomProperty cp : super.values()) {
|
|
|
|
if(cp.getValue() == value) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2006-03-03 11:57:55 -05:00
|
|
|
* <p>Gets the codepage.</p>
|
|
|
|
*
|
|
|
|
* @return the codepage or -1 if the codepage is undefined.
|
|
|
|
*/
|
|
|
|
public int getCodepage()
|
|
|
|
{
|
|
|
|
int codepage = -1;
|
2010-01-13 10:42:53 -05:00
|
|
|
for (final Iterator<CustomProperty> i = this.values().iterator(); codepage == -1 && i.hasNext();)
|
2006-03-03 11:57:55 -05:00
|
|
|
{
|
2010-01-13 10:42:53 -05:00
|
|
|
final CustomProperty cp = i.next();
|
2006-03-03 11:57:55 -05:00
|
|
|
if (cp.getID() == PropertyIDMap.PID_CODEPAGE)
|
|
|
|
codepage = ((Integer) cp.getValue()).intValue();
|
|
|
|
}
|
|
|
|
return codepage;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* <p>Tells whether this {@link CustomProperties} instance is pure or one or
|
|
|
|
* more properties of the underlying low-level property set has been
|
|
|
|
* dropped.</p>
|
2009-10-08 18:29:41 -04:00
|
|
|
*
|
2006-03-03 11:57:55 -05:00
|
|
|
* @return <code>true</code> if the {@link CustomProperties} is pure, else
|
|
|
|
* <code>false</code>.
|
|
|
|
*/
|
|
|
|
public boolean isPure()
|
|
|
|
{
|
|
|
|
return isPure;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* <p>Sets the purity of the custom property set.</p>
|
|
|
|
*
|
|
|
|
* @param isPure the purity
|
|
|
|
*/
|
|
|
|
public void setPure(final boolean isPure)
|
|
|
|
{
|
|
|
|
this.isPure = isPure;
|
|
|
|
}
|
|
|
|
}
|