#60331 - Remove deprecated classes - deprecate Mutable* property classes
sonarcube fix - make protected attributes private git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1771640 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
8968b6d6b6
commit
6ea58e94bc
@ -19,132 +19,112 @@ package org.apache.poi.hpsf;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.commons.collections4.bidimap.TreeBidiMap;
|
||||
import org.apache.poi.hpsf.wellknown.PropertyIDMap;
|
||||
|
||||
/**
|
||||
* <p>Maintains the instances of {@link CustomProperty} that belong to a
|
||||
* 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>
|
||||
* typed values.<p>
|
||||
*
|
||||
* <p>While this class provides a simple API to custom properties, it ignores
|
||||
* 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>
|
||||
* such property sets, use HPSF's low-level access methods.<p>
|
||||
*
|
||||
* <p>An application can call the {@link #isPure} method to check whether a
|
||||
* 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>
|
||||
* unmodified) or whether one or more properties have been dropped.<p>
|
||||
*
|
||||
* <p>This class is not thread-safe; concurrent access to instances of this
|
||||
* class must be synchronized.</p>
|
||||
* This class is not thread-safe; concurrent access to instances of this
|
||||
* class must be synchronized.<p>
|
||||
*
|
||||
* <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>
|
||||
* 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.
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class CustomProperties extends HashMap<Object,CustomProperty>
|
||||
{
|
||||
public class CustomProperties extends HashMap<Long,CustomProperty> {
|
||||
|
||||
/**
|
||||
* <p>Maps property IDs to property names.</p>
|
||||
* Maps property IDs to property names and vice versa.
|
||||
*/
|
||||
private final Map<Long,String> dictionaryIDToName = new HashMap<Long,String>();
|
||||
private final TreeBidiMap<Long,String> dictionary = new TreeBidiMap<Long,String>();
|
||||
|
||||
/**
|
||||
* <p>Maps property names to property IDs.</p>
|
||||
*/
|
||||
private final Map<String,Long> dictionaryNameToID = new HashMap<String,Long>();
|
||||
|
||||
/**
|
||||
* <p>Tells whether this object is pure or not.</p>
|
||||
* Tells whether this object is pure or not.
|
||||
*/
|
||||
private boolean isPure = true;
|
||||
|
||||
|
||||
/**
|
||||
* <p>Puts a {@link CustomProperty} into this map. It is assumed that the
|
||||
* 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>
|
||||
* {@link #put(CustomProperty)}.
|
||||
*
|
||||
* @param name the property name
|
||||
* @param cp the property
|
||||
*
|
||||
* @return the previous property stored under this name
|
||||
*/
|
||||
public CustomProperty put(final String name, final CustomProperty cp)
|
||||
{
|
||||
if (name == null)
|
||||
{
|
||||
public CustomProperty put(final String name, final CustomProperty cp) {
|
||||
if (name == null) {
|
||||
/* Ignoring a property without a name. */
|
||||
isPure = false;
|
||||
return null;
|
||||
}
|
||||
if (!(name.equals(cp.getName())))
|
||||
|
||||
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 */
|
||||
final Long idKey = Long.valueOf(cp.getID());
|
||||
final Long oldID = dictionaryNameToID.get(name);
|
||||
dictionaryIDToName.remove(oldID);
|
||||
dictionaryNameToID.put(name, idKey);
|
||||
dictionaryIDToName.put(idKey, name);
|
||||
super.remove(dictionary.getKey(name));
|
||||
dictionary.put(cp.getID(), name);
|
||||
|
||||
/* Put the custom property into this map. */
|
||||
final CustomProperty oldCp = super.remove(oldID);
|
||||
super.put(idKey, cp);
|
||||
return oldCp;
|
||||
return super.put(cp.getID(), cp);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <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>
|
||||
* 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><p>If there is already a property with the same name, take the ID
|
||||
* of that property.</p></li>
|
||||
*
|
||||
* <li><p>Otherwise find the highest ID and use its value plus one.</p></li>
|
||||
*
|
||||
* <li>Otherwise find the highest ID and use its value plus one.
|
||||
* </ul>
|
||||
*
|
||||
* @param customProperty
|
||||
* @return If the was already a property with the same name, the
|
||||
* @return If there was already a property with the same name, the old property
|
||||
* @throws ClassCastException
|
||||
*/
|
||||
private Object put(final CustomProperty customProperty) 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 = dictionaryNameToID.get(name);
|
||||
if (oldId != null)
|
||||
customProperty.setID(oldId.longValue());
|
||||
else
|
||||
{
|
||||
long max = 1;
|
||||
for (Long long1 : dictionaryIDToName.keySet()) {
|
||||
final long id = long1.longValue();
|
||||
if (id > max)
|
||||
max = id;
|
||||
}
|
||||
customProperty.setID(max + 1);
|
||||
final Long oldId = (name == null) ? null : dictionary.getKey(name);
|
||||
if (oldId != null) {
|
||||
customProperty.setID(oldId);
|
||||
} else {
|
||||
long lastKey = (dictionary.isEmpty()) ? 0 : dictionary.lastKey();
|
||||
customProperty.setID(Math.max(lastKey,PropertyIDMap.PID_MAX) + 1);
|
||||
}
|
||||
return this.put(name, customProperty);
|
||||
}
|
||||
@ -152,123 +132,92 @@ public class CustomProperties extends HashMap<Object,CustomProperty>
|
||||
|
||||
|
||||
/**
|
||||
* <p>Removes a custom property.</p>
|
||||
* Removes a custom property.
|
||||
* @param name The name of the custom property to remove
|
||||
* @return The removed property or <code>null</code> if the specified property was not found.
|
||||
* @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 = dictionaryNameToID.get(name);
|
||||
if (id == null)
|
||||
return null;
|
||||
dictionaryIDToName.remove(id);
|
||||
dictionaryNameToID.remove(name);
|
||||
public Object remove(final String name) {
|
||||
final Long id = dictionary.removeValue(name);
|
||||
return super.remove(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Adds a named string property.</p>
|
||||
* 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</code> if there was no such property before.
|
||||
* {@code null} 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);
|
||||
public Object put(final String name, final String value) {
|
||||
final Property p = new Property(-1, Variant.VT_LPWSTR, value);
|
||||
return put(new CustomProperty(p, name));
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Adds a named long property.</p>
|
||||
* 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</code> if there was no such property before.
|
||||
* {@code null} 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);
|
||||
public Object put(final String name, final Long value) {
|
||||
final Property p = new Property(-1, Variant.VT_I8, value);
|
||||
return put(new CustomProperty(p, name));
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Adds a named double property.</p>
|
||||
* 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</code> if there was no such property before.
|
||||
* {@code null} 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);
|
||||
public Object put(final String name, final Double value) {
|
||||
final Property p = new Property(-1, Variant.VT_R8, value);
|
||||
return put(new CustomProperty(p, name));
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Adds a named integer property.</p>
|
||||
* 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</code> if there was no such property before.
|
||||
* {@code null} 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);
|
||||
public Object put(final String name, final Integer value) {
|
||||
final Property p = new Property(-1, Variant.VT_I4, value);
|
||||
return put(new CustomProperty(p, name));
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Adds a named boolean property.</p>
|
||||
* 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</code> if there was no such property before.
|
||||
* {@code null} 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);
|
||||
public Object put(final String name, final Boolean value) {
|
||||
final Property p = new Property(-1, Variant.VT_BOOL, value);
|
||||
return put(new CustomProperty(p, name));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* <p>Gets a named value from the custom properties.</p>
|
||||
* Gets a named value from the custom properties.
|
||||
*
|
||||
* @param name the name of the value to get
|
||||
* @return the value or <code>null</code> 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.
|
||||
*/
|
||||
public Object get(final String name)
|
||||
{
|
||||
final Long id = dictionaryNameToID.get(name);
|
||||
public Object get(final String name) {
|
||||
final Long id = dictionary.getKey(name);
|
||||
final CustomProperty cp = super.get(id);
|
||||
return cp != null ? cp.getValue() : null;
|
||||
}
|
||||
@ -276,21 +225,16 @@ public class CustomProperties extends HashMap<Object,CustomProperty>
|
||||
|
||||
|
||||
/**
|
||||
* <p>Adds a named date property.</p>
|
||||
* 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</code> if there was no such property before.
|
||||
* {@code null} 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);
|
||||
public Object put(final String name, final Date value) {
|
||||
final Property p = new Property(-1, Variant.VT_FILETIME, value);
|
||||
return put(new CustomProperty(p, name));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -302,7 +246,7 @@ public class CustomProperties extends HashMap<Object,CustomProperty>
|
||||
@Override
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
public Set keySet() {
|
||||
return dictionaryNameToID.keySet();
|
||||
return dictionary.values();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -311,7 +255,7 @@ public class CustomProperties extends HashMap<Object,CustomProperty>
|
||||
* @return a set of all the names of our custom properties
|
||||
*/
|
||||
public Set<String> nameSet() {
|
||||
return dictionaryNameToID.keySet();
|
||||
return dictionary.values();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -320,21 +264,17 @@ public class CustomProperties extends HashMap<Object,CustomProperty>
|
||||
* @return a set of all the IDs of our custom properties
|
||||
*/
|
||||
public Set<String> idSet() {
|
||||
return dictionaryNameToID.keySet();
|
||||
return dictionary.values();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* <p>Sets the codepage.</p>
|
||||
* Sets the codepage.
|
||||
*
|
||||
* @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);
|
||||
p.setValue(Integer.valueOf(codepage));
|
||||
public void setCodepage(final int codepage) {
|
||||
Property p = new Property(PropertyIDMap.PID_CODEPAGE, Variant.VT_I2, codepage);
|
||||
put(new CustomProperty(p));
|
||||
}
|
||||
|
||||
@ -346,84 +286,65 @@ public class CustomProperties extends HashMap<Object,CustomProperty>
|
||||
*
|
||||
* @return the dictionary.
|
||||
*/
|
||||
Map<Long,String> getDictionary()
|
||||
{
|
||||
return dictionaryIDToName;
|
||||
Map<Long,String> getDictionary() {
|
||||
return dictionary;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks against both String Name and Long ID
|
||||
*/
|
||||
@Override
|
||||
public boolean containsKey(Object key) {
|
||||
if(key instanceof Long) {
|
||||
return super.containsKey(key);
|
||||
}
|
||||
if(key instanceof String) {
|
||||
return super.containsKey(dictionaryNameToID.get(key));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@Override
|
||||
public boolean containsKey(Object key) {
|
||||
return ((key instanceof Long && dictionary.containsKey(key)) || dictionary.containsValue(key));
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks against both the property, and its values.
|
||||
*/
|
||||
@Override
|
||||
public boolean containsValue(Object value) {
|
||||
if(value instanceof CustomProperty) {
|
||||
return super.containsValue(value);
|
||||
} else {
|
||||
for(CustomProperty cp : super.values()) {
|
||||
/**
|
||||
* Checks against both the property, and its values.
|
||||
*/
|
||||
@Override
|
||||
public boolean containsValue(Object value) {
|
||||
if(value instanceof CustomProperty) {
|
||||
return super.containsValue(value);
|
||||
}
|
||||
|
||||
for(CustomProperty cp : super.values()) {
|
||||
if(cp.getValue() == value) {
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* <p>Gets the codepage.</p>
|
||||
/**
|
||||
* Gets the codepage.
|
||||
*
|
||||
* @return the codepage or -1 if the codepage is undefined.
|
||||
*/
|
||||
public int getCodepage()
|
||||
{
|
||||
int codepage = -1;
|
||||
for (final Iterator<CustomProperty> i = this.values().iterator(); codepage == -1 && i.hasNext();)
|
||||
{
|
||||
final CustomProperty cp = i.next();
|
||||
if (cp.getID() == PropertyIDMap.PID_CODEPAGE)
|
||||
codepage = ((Integer) cp.getValue()).intValue();
|
||||
}
|
||||
return codepage;
|
||||
public int getCodepage() {
|
||||
CustomProperty cp = get(PropertyIDMap.PID_CODEPAGE);
|
||||
return (cp == null) ? -1 : (Integer)cp.getValue();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Tells whether this {@link CustomProperties} instance is pure or one or
|
||||
* Tells whether this {@link CustomProperties} instance is pure or one or
|
||||
* more properties of the underlying low-level property set has been
|
||||
* dropped.</p>
|
||||
* dropped.
|
||||
*
|
||||
* @return <code>true</code> if the {@link CustomProperties} is pure, else
|
||||
* <code>false</code>.
|
||||
* @return {@code true} if the {@link CustomProperties} is pure, else
|
||||
* {@code false}.
|
||||
*/
|
||||
public boolean isPure()
|
||||
{
|
||||
public boolean isPure() {
|
||||
return isPure;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Sets the purity of the custom property set.</p>
|
||||
* Sets the purity of the custom property set.
|
||||
*
|
||||
* @param isPure the purity
|
||||
*/
|
||||
public void setPure(final boolean isPure)
|
||||
{
|
||||
public void setPure(final boolean isPure) {
|
||||
this.isPure = isPure;
|
||||
}
|
||||
}
|
||||
|
@ -18,10 +18,10 @@
|
||||
package org.apache.poi.hpsf;
|
||||
|
||||
/**
|
||||
* <p>This class represents custom properties in the document summary
|
||||
* This class represents custom properties in the document summary
|
||||
* information stream. The difference to normal properties is that custom
|
||||
* properties have an optional name. If the name is not <code>null</code> it
|
||||
* will be maintained in the section's dictionary.</p>
|
||||
* properties have an optional name. If the name is not {@code null} it
|
||||
* will be maintained in the section's dictionary.
|
||||
*/
|
||||
public class CustomProperty extends MutableProperty
|
||||
{
|
||||
@ -29,80 +29,75 @@ public class CustomProperty extends MutableProperty
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* <p>Creates an empty {@link CustomProperty}. The set methods must be
|
||||
* called to make it usable.</p>
|
||||
* Creates an empty {@link CustomProperty}. The set methods must be
|
||||
* called to make it usable.
|
||||
*/
|
||||
public CustomProperty()
|
||||
{
|
||||
public CustomProperty() {
|
||||
this.name = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Creates a {@link CustomProperty} without a name by copying the
|
||||
* underlying {@link Property}' attributes.</p>
|
||||
* Creates a {@link CustomProperty} without a name by copying the
|
||||
* underlying {@link Property}' attributes.
|
||||
*
|
||||
* @param property the property to copy
|
||||
*/
|
||||
public CustomProperty(final Property property)
|
||||
{
|
||||
public CustomProperty(final Property property) {
|
||||
this(property, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Creates a {@link CustomProperty} with a name.</p>
|
||||
* Creates a {@link CustomProperty} with a name.
|
||||
*
|
||||
* @param property This property's attributes are copied to the new custom
|
||||
* property.
|
||||
* @param name The new custom property's name.
|
||||
*/
|
||||
public CustomProperty(final Property property, final String name)
|
||||
{
|
||||
public CustomProperty(final Property property, final String name) {
|
||||
super(property);
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Gets the property's name.</p>
|
||||
* Gets the property's name.
|
||||
*
|
||||
* @return the property's name.
|
||||
*/
|
||||
public String getName()
|
||||
{
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Sets the property's name.</p>
|
||||
* Sets the property's name.
|
||||
*
|
||||
* @param name The name to set.
|
||||
*/
|
||||
public void setName(final String name)
|
||||
{
|
||||
public void setName(final String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* <p>Compares two custom properties for equality. The method returns
|
||||
* <code>true</code> if all attributes of the two custom properties are
|
||||
* equal.</p>
|
||||
* Compares two custom properties for equality. The method returns
|
||||
* {@code true} if all attributes of the two custom properties are
|
||||
* equal.
|
||||
*
|
||||
* @param o The custom property to compare with.
|
||||
* @return <code>true</code> if both custom properties are equal, else
|
||||
* <code>false</code>.
|
||||
* @return {@code true} if both custom properties are equal, else
|
||||
* {@code false}.
|
||||
*
|
||||
* @see java.util.AbstractSet#equals(java.lang.Object)
|
||||
*/
|
||||
public boolean equalsContents(final Object o)
|
||||
{
|
||||
public boolean equalsContents(final Object o) {
|
||||
final CustomProperty c = (CustomProperty) o;
|
||||
final String name1 = c.getName();
|
||||
final String name2 = this.getName();
|
||||
boolean equalNames = true;
|
||||
if (name1 == null)
|
||||
if (name1 == null) {
|
||||
equalNames = name2 == null;
|
||||
else
|
||||
} else {
|
||||
equalNames = name1.equals(name2);
|
||||
}
|
||||
return equalNames && c.getID() == this.getID()
|
||||
&& c.getType() == this.getType()
|
||||
&& c.getValue().equals(this.getValue());
|
||||
@ -112,8 +107,7 @@ public class CustomProperty extends MutableProperty
|
||||
* @see java.util.AbstractSet#hashCode()
|
||||
*/
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
public int hashCode() {
|
||||
return (int) this.getID();
|
||||
}
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -17,103 +17,22 @@
|
||||
|
||||
package org.apache.poi.hpsf;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import org.apache.poi.util.CodePageUtil;
|
||||
import org.apache.poi.util.Removal;
|
||||
|
||||
/**
|
||||
* <p>Adds writing capability to the {@link Property} class.</p>
|
||||
*
|
||||
* <p>Please be aware that this class' functionality will be merged into the
|
||||
* {@link Property} class at a later time, so the API will change.</p>
|
||||
*
|
||||
* @deprecated POI 3.16 - use Property as base class instead
|
||||
*/
|
||||
public class MutableProperty extends Property
|
||||
{
|
||||
@Removal(version="3.18")
|
||||
public class MutableProperty extends Property {
|
||||
public MutableProperty() {}
|
||||
|
||||
/**
|
||||
* <p>Creates an empty property. It must be filled using the set method to
|
||||
* be usable.</p>
|
||||
*/
|
||||
public MutableProperty()
|
||||
{ }
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Creates a <code>MutableProperty</code> as a copy of an existing
|
||||
* <code>Property</code>.</p>
|
||||
*
|
||||
* @param p The property to copy.
|
||||
*/
|
||||
public MutableProperty(final Property p)
|
||||
{
|
||||
setID(p.getID());
|
||||
setType(p.getType());
|
||||
setValue(p.getValue());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* <p>Sets the property's ID.</p>
|
||||
*
|
||||
* @param id the ID
|
||||
*/
|
||||
public void setID(final long id)
|
||||
{
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Sets the property's type.</p>
|
||||
*
|
||||
* @param type the property's type
|
||||
*/
|
||||
public void setType(final long type)
|
||||
{
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Sets the property's value.</p>
|
||||
*
|
||||
* @param value the property's value
|
||||
*/
|
||||
public void setValue(final Object value)
|
||||
{
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Writes the property to an output stream.</p>
|
||||
*
|
||||
* @param out The output stream to write to.
|
||||
* @param codepage The codepage to use for writing non-wide strings
|
||||
* @return the number of bytes written to the stream
|
||||
*
|
||||
* @exception IOException if an I/O error occurs
|
||||
* @exception WritingNotSupportedException if a variant type is to be
|
||||
* written that is not yet supported
|
||||
*/
|
||||
public int write(final OutputStream out, final int codepage)
|
||||
throws IOException, WritingNotSupportedException
|
||||
{
|
||||
int length = 0;
|
||||
long variantType = getType();
|
||||
|
||||
/* Ensure that wide strings are written if the codepage is Unicode. */
|
||||
if (codepage == CodePageUtil.CP_UNICODE && variantType == Variant.VT_LPSTR)
|
||||
variantType = Variant.VT_LPWSTR;
|
||||
|
||||
length += TypeWriter.writeUIntToStream(out, variantType);
|
||||
length += VariantSupport.write(out, variantType, getValue(), codepage);
|
||||
return length;
|
||||
public MutableProperty(final Property p) {
|
||||
super(p);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -17,288 +17,21 @@
|
||||
|
||||
package org.apache.poi.hpsf;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.LinkedList;
|
||||
|
||||
import org.apache.poi.poifs.filesystem.DirectoryEntry;
|
||||
import org.apache.poi.poifs.filesystem.Entry;
|
||||
import org.apache.poi.util.LittleEndian;
|
||||
import org.apache.poi.util.LittleEndianConsts;
|
||||
import org.apache.poi.util.Removal;
|
||||
|
||||
/**
|
||||
* <p>Adds writing support to the {@link PropertySet} class.</p>
|
||||
* dds writing support to the {@link PropertySet} class.<p>
|
||||
*
|
||||
* <p>Please be aware that this class' functionality will be merged into the
|
||||
* {@link PropertySet} class at a later time, so the API will change.</p>
|
||||
* Please be aware that this class' functionality will be merged into the
|
||||
* {@link PropertySet} class at a later time, so the API will change.
|
||||
*
|
||||
* @deprecated POI 3.16 - use PropertySet as base class instead
|
||||
*/
|
||||
public class MutablePropertySet extends PropertySet
|
||||
{
|
||||
@Removal(version="3.18")
|
||||
public class MutablePropertySet extends PropertySet {
|
||||
public MutablePropertySet() {}
|
||||
|
||||
/**
|
||||
* <p>Constructs a <code>MutablePropertySet</code> instance. Its
|
||||
* primary task is to initialize the immutable field with their proper
|
||||
* values. It also sets fields that might change to reasonable defaults.</p>
|
||||
*/
|
||||
public MutablePropertySet()
|
||||
{
|
||||
/* Initialize the "byteOrder" field. */
|
||||
byteOrder = LittleEndian.getUShort(BYTE_ORDER_ASSERTION);
|
||||
|
||||
/* Initialize the "format" field. */
|
||||
format = LittleEndian.getUShort(FORMAT_ASSERTION);
|
||||
|
||||
/* Initialize "osVersion" field as if the property has been created on
|
||||
* a Win32 platform, whether this is the case or not. */
|
||||
osVersion = (OS_WIN32 << 16) | 0x0A04;
|
||||
|
||||
/* Initailize the "classID" field. */
|
||||
classID = new ClassID();
|
||||
|
||||
/* Initialize the sections. Since property set must have at least
|
||||
* one section it is added right here. */
|
||||
sections = new LinkedList<Section>();
|
||||
sections.add(new MutableSection());
|
||||
public MutablePropertySet(final PropertySet ps) {
|
||||
super(ps);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Constructs a <code>MutablePropertySet</code> by doing a deep copy of
|
||||
* an existing <code>PropertySet</code>. All nested elements, i.e.
|
||||
* <code>Section</code>s and <code>Property</code> instances, will be their
|
||||
* mutable counterparts in the new <code>MutablePropertySet</code>.</p>
|
||||
*
|
||||
* @param ps The property set to copy
|
||||
*/
|
||||
public MutablePropertySet(final PropertySet ps)
|
||||
{
|
||||
byteOrder = ps.getByteOrder();
|
||||
format = ps.getFormat();
|
||||
osVersion = ps.getOSVersion();
|
||||
setClassID(ps.getClassID());
|
||||
clearSections();
|
||||
if (sections == null)
|
||||
sections = new LinkedList<Section>();
|
||||
for (final Section section : ps.getSections())
|
||||
{
|
||||
final MutableSection s = new MutableSection(section);
|
||||
addSection(s);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>The length of the property set stream header.</p>
|
||||
*/
|
||||
private final static int OFFSET_HEADER =
|
||||
BYTE_ORDER_ASSERTION.length + /* Byte order */
|
||||
FORMAT_ASSERTION.length + /* Format */
|
||||
LittleEndianConsts.INT_SIZE + /* OS version */
|
||||
ClassID.LENGTH + /* Class ID */
|
||||
LittleEndianConsts.INT_SIZE; /* Section count */
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Sets the "byteOrder" property.</p>
|
||||
*
|
||||
* @param byteOrder the byteOrder value to set
|
||||
*/
|
||||
public void setByteOrder(final int byteOrder)
|
||||
{
|
||||
this.byteOrder = byteOrder;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Sets the "format" property.</p>
|
||||
*
|
||||
* @param format the format value to set
|
||||
*/
|
||||
public void setFormat(final int format)
|
||||
{
|
||||
this.format = format;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Sets the "osVersion" property.</p>
|
||||
*
|
||||
* @param osVersion the osVersion value to set
|
||||
*/
|
||||
public void setOSVersion(final int osVersion)
|
||||
{
|
||||
this.osVersion = osVersion;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Sets the property set stream's low-level "class ID"
|
||||
* field.</p>
|
||||
*
|
||||
* @param classID The property set stream's low-level "class ID" field.
|
||||
*
|
||||
* @see PropertySet#getClassID()
|
||||
*/
|
||||
public void setClassID(final ClassID classID)
|
||||
{
|
||||
this.classID = classID;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Removes all sections from this property set.</p>
|
||||
*/
|
||||
public void clearSections()
|
||||
{
|
||||
sections = null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Adds a section to this property set.</p>
|
||||
*
|
||||
* @param section The {@link Section} to add. It will be appended
|
||||
* after any sections that are already present in the property set
|
||||
* and thus become the last section.
|
||||
*/
|
||||
public void addSection(final Section section)
|
||||
{
|
||||
if (sections == null)
|
||||
sections = new LinkedList<Section>();
|
||||
sections.add(section);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Writes the property set to an output stream.</p>
|
||||
*
|
||||
* @param out the output stream to write the section to
|
||||
* @exception IOException if an error when writing to the output stream
|
||||
* occurs
|
||||
* @exception WritingNotSupportedException if HPSF does not yet support
|
||||
* writing a property's variant type.
|
||||
*/
|
||||
public void write(final OutputStream out)
|
||||
throws WritingNotSupportedException, IOException
|
||||
{
|
||||
/* Write the number of sections in this property set stream. */
|
||||
final int nrSections = sections.size();
|
||||
|
||||
/* Write the property set's header. */
|
||||
TypeWriter.writeToStream(out, (short) getByteOrder());
|
||||
TypeWriter.writeToStream(out, (short) getFormat());
|
||||
TypeWriter.writeToStream(out, getOSVersion());
|
||||
TypeWriter.writeToStream(out, getClassID());
|
||||
TypeWriter.writeToStream(out, nrSections);
|
||||
int offset = OFFSET_HEADER;
|
||||
|
||||
/* Write the section list, i.e. the references to the sections. Each
|
||||
* entry in the section list consist of the section's class ID and the
|
||||
* section's offset relative to the beginning of the stream. */
|
||||
offset += nrSections * (ClassID.LENGTH + LittleEndianConsts.INT_SIZE);
|
||||
final int sectionsBegin = offset;
|
||||
for (final Section section : sections)
|
||||
{
|
||||
final MutableSection s = (MutableSection)section;
|
||||
final ClassID formatID = s.getFormatID();
|
||||
if (formatID == null)
|
||||
throw new NoFormatIDException();
|
||||
TypeWriter.writeToStream(out, s.getFormatID());
|
||||
TypeWriter.writeUIntToStream(out, offset);
|
||||
try
|
||||
{
|
||||
offset += s.getSize();
|
||||
}
|
||||
catch (HPSFRuntimeException ex)
|
||||
{
|
||||
final Throwable cause = ex.getReason();
|
||||
if (cause instanceof UnsupportedEncodingException) {
|
||||
throw new IllegalPropertySetDataException(cause);
|
||||
}
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
/* Write the sections themselves. */
|
||||
offset = sectionsBegin;
|
||||
for (final Section section : sections)
|
||||
{
|
||||
final MutableSection s = (MutableSection)section;
|
||||
offset += s.write(out);
|
||||
}
|
||||
|
||||
/* Indicate that we're done */
|
||||
out.close();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Returns the contents of this property set stream as an input stream.
|
||||
* The latter can be used for example to write the property set into a POIFS
|
||||
* document. The input stream represents a snapshot of the property set.
|
||||
* If the latter is modified while the input stream is still being
|
||||
* read, the modifications will not be reflected in the input stream but in
|
||||
* the {@link MutablePropertySet} only.</p>
|
||||
*
|
||||
* @return the contents of this property set stream
|
||||
*
|
||||
* @throws WritingNotSupportedException if HPSF does not yet support writing
|
||||
* of a property's variant type.
|
||||
* @throws IOException if an I/O exception occurs.
|
||||
*/
|
||||
public InputStream toInputStream()
|
||||
throws IOException, WritingNotSupportedException
|
||||
{
|
||||
final ByteArrayOutputStream psStream = new ByteArrayOutputStream();
|
||||
try {
|
||||
write(psStream);
|
||||
} finally {
|
||||
psStream.close();
|
||||
}
|
||||
final byte[] streamData = psStream.toByteArray();
|
||||
return new ByteArrayInputStream(streamData);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Writes a property set to a document in a POI filesystem directory.</p>
|
||||
*
|
||||
* @param dir The directory in the POI filesystem to write the document to.
|
||||
* @param name The document's name. If there is already a document with the
|
||||
* same name in the directory the latter will be overwritten.
|
||||
*
|
||||
* @throws WritingNotSupportedException if the filesystem doesn't support writing
|
||||
* @throws IOException if the old entry can't be deleted or the new entry be written
|
||||
*/
|
||||
public void write(final DirectoryEntry dir, final String name)
|
||||
throws WritingNotSupportedException, IOException
|
||||
{
|
||||
/* If there is already an entry with the same name, remove it. */
|
||||
try
|
||||
{
|
||||
final Entry e = dir.getEntry(name);
|
||||
e.delete();
|
||||
}
|
||||
catch (FileNotFoundException ex)
|
||||
{
|
||||
/* Entry not found, no need to remove it. */
|
||||
}
|
||||
/* Create the new entry. */
|
||||
dir.createDocument(name, toInputStream());
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -17,683 +17,27 @@
|
||||
|
||||
package org.apache.poi.hpsf;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Date;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
import java.util.Map;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
|
||||
import org.apache.poi.hpsf.wellknown.PropertyIDMap;
|
||||
import org.apache.poi.util.CodePageUtil;
|
||||
import org.apache.poi.util.LittleEndian;
|
||||
import org.apache.poi.util.Removal;
|
||||
|
||||
/**
|
||||
* <p>Adds writing capability to the {@link Section} class.</p>
|
||||
*
|
||||
* <p>Please be aware that this class' functionality will be merged into the
|
||||
* {@link Section} class at a later time, so the API will change.</p>
|
||||
*
|
||||
* @deprecated POI 3.16 - use Section as base class instead
|
||||
*/
|
||||
public class MutableSection extends Section
|
||||
{
|
||||
/**
|
||||
* <p>If the "dirty" flag is true, the section's size must be
|
||||
* (re-)calculated before the section is written.</p>
|
||||
*/
|
||||
private boolean dirty = true;
|
||||
@Removal(version="3.18")
|
||||
public class MutableSection extends Section {
|
||||
public MutableSection() {}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>List to assemble the properties. Unfortunately a wrong
|
||||
* decision has been taken when specifying the "properties" field
|
||||
* as an Property[]. It should have been a {@link java.util.List}.</p>
|
||||
*/
|
||||
private List<Property> preprops;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>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.</p>
|
||||
*/
|
||||
private byte[] sectionBytes;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Creates an empty mutable section.</p>
|
||||
*/
|
||||
public MutableSection()
|
||||
{
|
||||
dirty = true;
|
||||
formatID = null;
|
||||
offset = -1;
|
||||
preprops = new LinkedList<Property>();
|
||||
public MutableSection(final Section s) {
|
||||
super(s);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Constructs a <code>MutableSection</code> by doing a deep copy of an
|
||||
* existing <code>Section</code>. All nested <code>Property</code>
|
||||
* instances, will be their mutable counterparts in the new
|
||||
* <code>MutableSection</code>.</p>
|
||||
*
|
||||
* @param s The section set to copy
|
||||
*/
|
||||
public MutableSection(final Section s)
|
||||
{
|
||||
setFormatID(s.getFormatID());
|
||||
final Property[] pa = s.getProperties();
|
||||
final MutableProperty[] mpa = new MutableProperty[pa.length];
|
||||
for (int i = 0; i < pa.length; i++)
|
||||
mpa[i] = new MutableProperty(pa[i]);
|
||||
setProperties(mpa);
|
||||
setDictionary(s.getDictionary());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Sets the section's format ID.</p>
|
||||
*
|
||||
* @param formatID The section's format ID
|
||||
*
|
||||
* @see #setFormatID(byte[])
|
||||
* @see Section#getFormatID
|
||||
*/
|
||||
public void setFormatID(final ClassID formatID)
|
||||
{
|
||||
this.formatID = formatID;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Sets the section's format ID.</p>
|
||||
*
|
||||
* @param formatID The section's format ID as a byte array. It components
|
||||
* are in big-endian format.
|
||||
*
|
||||
* @see #setFormatID(ClassID)
|
||||
* @see Section#getFormatID
|
||||
*/
|
||||
public void setFormatID(final byte[] formatID)
|
||||
{
|
||||
ClassID fid = getFormatID();
|
||||
if (fid == null)
|
||||
{
|
||||
fid = new ClassID();
|
||||
setFormatID(fid);
|
||||
}
|
||||
fid.setBytes(formatID);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Sets this section's properties. Any former values are overwritten.</p>
|
||||
*
|
||||
* @param properties This section's new properties.
|
||||
*/
|
||||
public void setProperties(final Property[] properties)
|
||||
{
|
||||
this.properties = properties;
|
||||
preprops = new LinkedList<Property>();
|
||||
for (int i = 0; i < properties.length; i++)
|
||||
preprops.add(properties[i]);
|
||||
dirty = true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Sets the string value of the property with the specified ID.</p>
|
||||
*
|
||||
* @param id The property's ID
|
||||
* @param value The property's value. It will be written as a Unicode
|
||||
* string.
|
||||
*
|
||||
* @see #setProperty(int, long, Object)
|
||||
* @see #getProperty
|
||||
*/
|
||||
public void setProperty(final int id, final String value)
|
||||
{
|
||||
setProperty(id, Variant.VT_LPWSTR, value);
|
||||
dirty = true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Sets the int value of the property with the specified ID.</p>
|
||||
*
|
||||
* @param id The property's ID
|
||||
* @param value The property's value.
|
||||
*
|
||||
* @see #setProperty(int, long, Object)
|
||||
* @see #getProperty
|
||||
*/
|
||||
public void setProperty(final int id, final int value)
|
||||
{
|
||||
setProperty(id, Variant.VT_I4, Integer.valueOf(value));
|
||||
dirty = true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Sets the long value of the property with the specified ID.</p>
|
||||
*
|
||||
* @param id The property's ID
|
||||
* @param value The property's value.
|
||||
*
|
||||
* @see #setProperty(int, long, Object)
|
||||
* @see #getProperty
|
||||
*/
|
||||
public void setProperty(final int id, final long value)
|
||||
{
|
||||
setProperty(id, Variant.VT_I8, Long.valueOf(value));
|
||||
dirty = true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Sets the boolean value of the property with the specified ID.</p>
|
||||
*
|
||||
* @param id The property's ID
|
||||
* @param value The property's value.
|
||||
*
|
||||
* @see #setProperty(int, long, Object)
|
||||
* @see #getProperty
|
||||
*/
|
||||
public void setProperty(final int id, final boolean value)
|
||||
{
|
||||
setProperty(id, Variant.VT_BOOL, Boolean.valueOf(value));
|
||||
dirty = true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Sets the value and the variant type of the property with the
|
||||
* specified ID. If a property with this ID is not yet present in
|
||||
* the section, it will be added. An already present property with
|
||||
* the specified ID will be overwritten. A default mapping will be
|
||||
* used to choose the property's type.</p>
|
||||
*
|
||||
* @param id The property's ID.
|
||||
* @param variantType The property's variant type.
|
||||
* @param value The property's value.
|
||||
*
|
||||
* @see #setProperty(int, String)
|
||||
* @see #getProperty
|
||||
* @see Variant
|
||||
*/
|
||||
public void setProperty(final int id, final long variantType,
|
||||
final Object value)
|
||||
{
|
||||
final MutableProperty p = new MutableProperty();
|
||||
p.setID(id);
|
||||
p.setType(variantType);
|
||||
p.setValue(value);
|
||||
setProperty(p);
|
||||
dirty = true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Sets a property.</p>
|
||||
*
|
||||
* @param p The property to be set.
|
||||
*
|
||||
* @see #setProperty(int, long, Object)
|
||||
* @see #getProperty
|
||||
* @see Variant
|
||||
*/
|
||||
public void setProperty(final Property p)
|
||||
{
|
||||
final long id = p.getID();
|
||||
removeProperty(id);
|
||||
preprops.add(p);
|
||||
dirty = true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Removes a property.</p>
|
||||
*
|
||||
* @param id The ID of the property to be removed
|
||||
*/
|
||||
public void removeProperty(final long id)
|
||||
{
|
||||
for (final Iterator<Property> i = preprops.iterator(); i.hasNext();)
|
||||
if (i.next().getID() == id)
|
||||
{
|
||||
i.remove();
|
||||
break;
|
||||
}
|
||||
dirty = true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Sets the value of the boolean property with the specified
|
||||
* ID.</p>
|
||||
*
|
||||
* @param id The property's ID
|
||||
* @param value The property's value
|
||||
*
|
||||
* @see #setProperty(int, long, Object)
|
||||
* @see #getProperty
|
||||
* @see Variant
|
||||
*/
|
||||
protected void setPropertyBooleanValue(final int id, final boolean value)
|
||||
{
|
||||
setProperty(id, Variant.VT_BOOL, Boolean.valueOf(value));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Returns the section's size.</p>
|
||||
*
|
||||
* @return the section's size.
|
||||
*/
|
||||
public int getSize()
|
||||
{
|
||||
if (dirty)
|
||||
{
|
||||
try
|
||||
{
|
||||
size = calcSize();
|
||||
dirty = false;
|
||||
}
|
||||
catch (HPSFRuntimeException ex)
|
||||
{
|
||||
throw ex;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new HPSFRuntimeException(ex);
|
||||
}
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Calculates the section's size. It is the sum of the lengths of the
|
||||
* section's header (8), the properties list (16 times the number of
|
||||
* properties) and the properties themselves.</p>
|
||||
*
|
||||
* @return the section's length in bytes.
|
||||
* @throws WritingNotSupportedException
|
||||
* @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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Writes this section into an output stream.</p>
|
||||
*
|
||||
* <p>Internally this is done by writing into three byte array output
|
||||
* streams: one for the properties, one for the property list and one for
|
||||
* the section as such. The two former are appended to the latter when they
|
||||
* have received all their data.</p>
|
||||
*
|
||||
* @param out The stream to write into.
|
||||
*
|
||||
* @return The number of bytes written, i.e. the section's size.
|
||||
* @exception IOException if an I/O error occurs
|
||||
* @exception WritingNotSupportedException if HPSF does not yet support
|
||||
* writing a property's variant type.
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
/* The properties are written to this stream. */
|
||||
final ByteArrayOutputStream propertyStream =
|
||||
new ByteArrayOutputStream();
|
||||
|
||||
/* The property list is established here. After each property that has
|
||||
* been written to "propertyStream", a property list entry is written to
|
||||
* "propertyListStream". */
|
||||
final ByteArrayOutputStream propertyListStream =
|
||||
new ByteArrayOutputStream();
|
||||
|
||||
/* Maintain the current position in the list. */
|
||||
int position = 0;
|
||||
|
||||
/* Increase the position variable by the size of the property list so
|
||||
* that it points behind the property list and to the beginning of the
|
||||
* properties themselves. */
|
||||
position += 2 * LittleEndian.INT_SIZE +
|
||||
getPropertyCount() * 2 * LittleEndian.INT_SIZE;
|
||||
|
||||
/* Writing the section's dictionary it tricky. If there is a dictionary
|
||||
* (property 0) the codepage property (property 1) must be set, too. */
|
||||
int codepage = -1;
|
||||
if (getProperty(PropertyIDMap.PID_DICTIONARY) != null)
|
||||
{
|
||||
final Object p1 = getProperty(PropertyIDMap.PID_CODEPAGE);
|
||||
if (p1 != null)
|
||||
{
|
||||
if (!(p1 instanceof Integer))
|
||||
throw new IllegalPropertySetDataException
|
||||
("The codepage property (ID = 1) must be an " +
|
||||
"Integer object.");
|
||||
}
|
||||
else
|
||||
/* Warning: The codepage property is not set although a
|
||||
* dictionary is present. In order to cope with this problem we
|
||||
* add the codepage property and set it to Unicode. */
|
||||
setProperty(PropertyIDMap.PID_CODEPAGE, Variant.VT_I2,
|
||||
Integer.valueOf(CodePageUtil.CP_UNICODE));
|
||||
codepage = getCodepage();
|
||||
}
|
||||
|
||||
/* Sort the property list by their property IDs: */
|
||||
Collections.sort(preprops, new Comparator<Property>()
|
||||
{
|
||||
public int compare(final Property p1, final Property p2)
|
||||
{
|
||||
if (p1.getID() < p2.getID())
|
||||
return -1;
|
||||
else if (p1.getID() == p2.getID())
|
||||
return 0;
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
});
|
||||
|
||||
/* Write the properties and the property list into their respective
|
||||
* streams: */
|
||||
for (final ListIterator<Property> i = preprops.listIterator(); i.hasNext();)
|
||||
{
|
||||
final MutableProperty p = (MutableProperty) i.next();
|
||||
final long id = p.getID();
|
||||
|
||||
/* Write the property list entry. */
|
||||
TypeWriter.writeUIntToStream(propertyListStream, p.getID());
|
||||
TypeWriter.writeUIntToStream(propertyListStream, position);
|
||||
|
||||
/* If the property ID is not equal 0 we write the property and all
|
||||
* is fine. However, if it equals 0 we have to write the section's
|
||||
* dictionary which has an implicit type only and an explicit
|
||||
* value. */
|
||||
if (id != 0)
|
||||
/* Write the property and update the position to the next
|
||||
* property. */
|
||||
position += p.write(propertyStream, getCodepage());
|
||||
else
|
||||
{
|
||||
if (codepage == -1)
|
||||
throw new IllegalPropertySetDataException
|
||||
("Codepage (property 1) is undefined.");
|
||||
position += writeDictionary(propertyStream, dictionary,
|
||||
codepage);
|
||||
}
|
||||
}
|
||||
propertyStream.close();
|
||||
propertyListStream.close();
|
||||
|
||||
/* Write the section: */
|
||||
byte[] pb1 = propertyListStream.toByteArray();
|
||||
byte[] pb2 = propertyStream.toByteArray();
|
||||
|
||||
/* Write the section's length: */
|
||||
TypeWriter.writeToStream(out, LittleEndian.INT_SIZE * 2 +
|
||||
pb1.length + pb2.length);
|
||||
|
||||
/* Write the section's number of properties: */
|
||||
TypeWriter.writeToStream(out, getPropertyCount());
|
||||
|
||||
/* Write the property list: */
|
||||
out.write(pb1);
|
||||
|
||||
/* Write the properties: */
|
||||
out.write(pb2);
|
||||
|
||||
int streamLength = LittleEndian.INT_SIZE * 2 + pb1.length + pb2.length;
|
||||
return streamLength;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Writes the section's dictionary.</p>
|
||||
*
|
||||
* @param out The output stream to write to.
|
||||
* @param dictionary The dictionary.
|
||||
* @param codepage The codepage to be used to write the dictionary items.
|
||||
* @return The number of bytes written
|
||||
* @exception IOException if an I/O exception occurs.
|
||||
*/
|
||||
private static int writeDictionary(final OutputStream out,
|
||||
final Map<Long,String> dictionary, final int codepage)
|
||||
throws IOException
|
||||
{
|
||||
int length = TypeWriter.writeUIntToStream(out, dictionary.size());
|
||||
for (Map.Entry<Long,String> ls : dictionary.entrySet()) {
|
||||
final Long key = ls.getKey();
|
||||
final String value = ls.getValue();
|
||||
|
||||
if (codepage == CodePageUtil.CP_UNICODE)
|
||||
{
|
||||
/* Write the dictionary item in Unicode. */
|
||||
int sLength = value.length() + 1;
|
||||
if ((sLength & 1) == 1) {
|
||||
sLength++;
|
||||
}
|
||||
length += TypeWriter.writeUIntToStream(out, key.longValue());
|
||||
length += TypeWriter.writeUIntToStream(out, sLength);
|
||||
final byte[] ca = CodePageUtil.getBytesInCodePage(value, codepage);
|
||||
for (int j = 2; j < ca.length; j += 2)
|
||||
{
|
||||
out.write(ca[j+1]);
|
||||
out.write(ca[j]);
|
||||
length += 2;
|
||||
}
|
||||
sLength -= value.length();
|
||||
while (sLength > 0)
|
||||
{
|
||||
out.write(0x00);
|
||||
out.write(0x00);
|
||||
length += 2;
|
||||
sLength--;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Write the dictionary item in another codepage than
|
||||
* Unicode. */
|
||||
length += TypeWriter.writeUIntToStream(out, key.longValue());
|
||||
length += TypeWriter.writeUIntToStream(out, value.length() + 1);
|
||||
final byte[] ba = CodePageUtil.getBytesInCodePage(value, codepage);
|
||||
for (int j = 0; j < ba.length; j++)
|
||||
{
|
||||
out.write(ba[j]);
|
||||
length++;
|
||||
}
|
||||
out.write(0x00);
|
||||
length++;
|
||||
}
|
||||
}
|
||||
return length;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Overwrites the super class' method to cope with a redundancy:
|
||||
* the property count is maintained in a separate member variable, but
|
||||
* shouldn't.</p>
|
||||
*
|
||||
* @return The number of properties in this section
|
||||
*/
|
||||
public int getPropertyCount()
|
||||
{
|
||||
return preprops.size();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Gets this section's properties.</p>
|
||||
*
|
||||
* @return this section's properties.
|
||||
*/
|
||||
public Property[] getProperties()
|
||||
{
|
||||
properties = preprops.toArray(new Property[0]);
|
||||
return properties;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Gets a property.</p>
|
||||
*
|
||||
* @param id The ID of the property to get
|
||||
* @return The property or <code>null</code> if there is no such property
|
||||
*/
|
||||
public Object getProperty(final long id)
|
||||
{
|
||||
/* Calling getProperties() ensures that properties and preprops are in
|
||||
* sync.</p> */
|
||||
getProperties();
|
||||
return super.getProperty(id);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Sets the section's dictionary. All keys in the dictionary must be
|
||||
* {@link java.lang.Long} instances, all values must be
|
||||
* {@link java.lang.String}s. This method overwrites the properties with IDs
|
||||
* 0 and 1 since they are reserved for the dictionary and the dictionary's
|
||||
* codepage. Setting these properties explicitly might have surprising
|
||||
* effects. An application should never do this but always use this
|
||||
* method.</p>
|
||||
*
|
||||
* @param dictionary The dictionary
|
||||
*
|
||||
* @exception IllegalPropertySetDataException if the dictionary's key and
|
||||
* value types are not correct.
|
||||
*
|
||||
* @see Section#getDictionary()
|
||||
*/
|
||||
public void setDictionary(final Map<Long,String> dictionary)
|
||||
throws IllegalPropertySetDataException
|
||||
{
|
||||
if (dictionary != null)
|
||||
{
|
||||
this.dictionary = dictionary;
|
||||
|
||||
/* Set the dictionary property (ID 0). Please note that the second
|
||||
* parameter in the method call below is unused because dictionaries
|
||||
* don't have a type. */
|
||||
setProperty(PropertyIDMap.PID_DICTIONARY, -1, dictionary);
|
||||
|
||||
/* If the codepage property (ID 1) for the strings (keys and
|
||||
* values) used in the dictionary is not yet defined, set it to
|
||||
* Unicode. */
|
||||
final Integer codepage =
|
||||
(Integer) getProperty(PropertyIDMap.PID_CODEPAGE);
|
||||
if (codepage == null)
|
||||
setProperty(PropertyIDMap.PID_CODEPAGE, Variant.VT_I2,
|
||||
Integer.valueOf(CodePageUtil.CP_UNICODE));
|
||||
}
|
||||
else
|
||||
/* Setting the dictionary to null means to remove property 0.
|
||||
* However, it does not mean to remove property 1 (codepage). */
|
||||
removeProperty(PropertyIDMap.PID_DICTIONARY);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Sets a property.</p>
|
||||
*
|
||||
* @param id The property ID.
|
||||
* @param value The property's value. The value's class must be one of those
|
||||
* supported by HPSF.
|
||||
*/
|
||||
public void setProperty(final int id, final Object value)
|
||||
{
|
||||
if (value instanceof String)
|
||||
setProperty(id, (String) value);
|
||||
else if (value instanceof Long)
|
||||
setProperty(id, ((Long) value).longValue());
|
||||
else if (value instanceof Integer)
|
||||
setProperty(id, ((Integer) value).intValue());
|
||||
else if (value instanceof Short)
|
||||
setProperty(id, ((Short) value).intValue());
|
||||
else if (value instanceof Boolean)
|
||||
setProperty(id, ((Boolean) value).booleanValue());
|
||||
else if (value instanceof Date)
|
||||
setProperty(id, Variant.VT_FILETIME, value);
|
||||
else
|
||||
throw new HPSFRuntimeException(
|
||||
"HPSF does not support properties of type " +
|
||||
value.getClass().getName() + ".");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Removes all properties from the section including 0 (dictionary) and
|
||||
* 1 (codepage).</p>
|
||||
*/
|
||||
public void clear()
|
||||
{
|
||||
final Property[] properties = getProperties();
|
||||
for (int i = 0; i < properties.length; i++)
|
||||
{
|
||||
final Property p = properties[i];
|
||||
removeProperty(p.getID());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Sets the codepage.</p>
|
||||
*
|
||||
* @param codepage the codepage
|
||||
*/
|
||||
public void setCodepage(final int codepage)
|
||||
{
|
||||
setProperty(PropertyIDMap.PID_CODEPAGE, Variant.VT_I2,
|
||||
Integer.valueOf(codepage));
|
||||
public MutableSection(final byte[] src, final int offset) throws UnsupportedEncodingException {
|
||||
super(src,offset);
|
||||
}
|
||||
}
|
||||
|
@ -18,53 +18,47 @@
|
||||
package org.apache.poi.hpsf;
|
||||
|
||||
/**
|
||||
* <p>This exception is thrown if a {@link MutablePropertySet} is to be written
|
||||
* but does not have a formatID set (see {@link
|
||||
* MutableSection#setFormatID(ClassID)} or
|
||||
* {@link org.apache.poi.hpsf.MutableSection#setFormatID(byte[])}.
|
||||
* This exception is thrown if a {@link PropertySet} is to be written
|
||||
* but does not have a formatID set (see {@link Section#setFormatID(ClassID)} or
|
||||
* {@link org.apache.poi.hpsf.Section#setFormatID(byte[])}.
|
||||
*/
|
||||
public class NoFormatIDException extends HPSFRuntimeException
|
||||
{
|
||||
public class NoFormatIDException extends HPSFRuntimeException {
|
||||
|
||||
/**
|
||||
* <p>Constructor</p>
|
||||
* Constructor
|
||||
*/
|
||||
public NoFormatIDException()
|
||||
{
|
||||
public NoFormatIDException() {
|
||||
super();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* <p>Constructor</p>
|
||||
* Constructor
|
||||
*
|
||||
* @param msg The exception's message string
|
||||
*/
|
||||
public NoFormatIDException(final String msg)
|
||||
{
|
||||
public NoFormatIDException(final String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* <p>Constructor</p>
|
||||
* Constructor
|
||||
*
|
||||
* @param reason This exception's underlying reason
|
||||
*/
|
||||
public NoFormatIDException(final Throwable reason)
|
||||
{
|
||||
public NoFormatIDException(final Throwable reason) {
|
||||
super(reason);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* <p>Constructor</p>
|
||||
* Constructor
|
||||
*
|
||||
* @param msg The exception's message string
|
||||
* @param reason This exception's underlying reason
|
||||
*/
|
||||
public NoFormatIDException(final String msg, final Throwable reason)
|
||||
{
|
||||
public NoFormatIDException(final String msg, final Throwable reason) {
|
||||
super(msg, reason);
|
||||
}
|
||||
|
||||
|
@ -17,8 +17,11 @@
|
||||
|
||||
package org.apache.poi.hpsf;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@ -29,104 +32,74 @@ import org.apache.poi.util.POILogFactory;
|
||||
import org.apache.poi.util.POILogger;
|
||||
|
||||
/**
|
||||
* <p>A property in a {@link Section} of a {@link PropertySet}.</p>
|
||||
* A property in a {@link Section} of a {@link PropertySet}.<p>
|
||||
*
|
||||
* <p>The property's <strong>ID</strong> gives the property a meaning
|
||||
* The property's {@code ID} gives the property a meaning
|
||||
* in the context of its {@link Section}. Each {@link Section} spans
|
||||
* its own name space of property IDs.</p>
|
||||
* its own name space of property IDs.<p>
|
||||
*
|
||||
* <p>The property's <strong>type</strong> determines how its
|
||||
* <strong>value </strong> is interpreted. For example, if the type is
|
||||
* The property's {@code type} determines how its
|
||||
* {@code value} is interpreted. For example, if the type is
|
||||
* {@link Variant#VT_LPSTR} (byte string), the value consists of a
|
||||
* DWord telling how many bytes the string contains. The bytes follow
|
||||
* immediately, including any null bytes that terminate the
|
||||
* string. The type {@link Variant#VT_I4} denotes a four-byte integer
|
||||
* value, {@link Variant#VT_FILETIME} some date and time (of a
|
||||
* file).</p>
|
||||
* value, {@link Variant#VT_FILETIME} some date and time (of a file).<p>
|
||||
*
|
||||
* <p>Please note that not all {@link Variant} types yet. This might change
|
||||
* Please note that not all {@link Variant} types yet. This might change
|
||||
* over time but largely depends on your feedback so that the POI team knows
|
||||
* which variant types are really needed. So please feel free to submit error
|
||||
* reports or patches for the types you need.</p>
|
||||
*
|
||||
* <p>Microsoft documentation: <a
|
||||
* href="http://msdn.microsoft.com/library/en-us/stg/stg/property_set_display_name_dictionary.asp?frame=true">
|
||||
* Property Set Display Name Dictionary</a>.
|
||||
* reports or patches for the types you need.
|
||||
*
|
||||
* @see Section
|
||||
* @see Variant
|
||||
* @see <a href="https://msdn.microsoft.com/en-us/library/dd942421.aspx">
|
||||
* [MS-OLEPS]: Object Linking and Embedding (OLE) Property Set Data Structures</a>
|
||||
*/
|
||||
public class Property
|
||||
{
|
||||
public class Property {
|
||||
|
||||
/** <p>The property's ID.</p> */
|
||||
protected long id;
|
||||
/** The property's ID. */
|
||||
private long id;
|
||||
|
||||
/** The property's type. */
|
||||
private long type;
|
||||
|
||||
/**
|
||||
* <p>Returns the property's ID.</p>
|
||||
*
|
||||
* @return The ID value
|
||||
*/
|
||||
public long getID()
|
||||
{
|
||||
return id;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** <p>The property's type.</p> */
|
||||
protected long type;
|
||||
|
||||
|
||||
/**
|
||||
* <p>Returns the property's type.</p>
|
||||
*
|
||||
* @return The type value
|
||||
*/
|
||||
public long getType()
|
||||
{
|
||||
return type;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** <p>The property's value.</p> */
|
||||
/** The property's value. */
|
||||
protected Object value;
|
||||
|
||||
|
||||
/**
|
||||
* <p>Returns the property's value.</p>
|
||||
*
|
||||
* @return The property's value
|
||||
* Creates an empty property. It must be filled using the set method to be usable.
|
||||
*/
|
||||
public Object getValue()
|
||||
{
|
||||
return value;
|
||||
public Property() {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a {@code Property} as a copy of an existing {@code Property}.
|
||||
*
|
||||
* @param p The property to copy.
|
||||
*/
|
||||
public Property(Property p) {
|
||||
this(p.id, p.type, p.value);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Creates a property.</p>
|
||||
* Creates a property.
|
||||
*
|
||||
* @param id the property's ID.
|
||||
* @param type the property's type, see {@link Variant}.
|
||||
* @param value the property's value. Only certain types are allowed, see
|
||||
* {@link Variant}.
|
||||
*/
|
||||
public Property(final long id, final long type, final Object value)
|
||||
{
|
||||
public Property(final long id, final long type, final Object value) {
|
||||
this.id = id;
|
||||
this.type = type;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Creates a {@link Property} instance by reading its bytes
|
||||
* from the property set stream.</p>
|
||||
* Creates a {@link Property} instance by reading its bytes
|
||||
* from the property set stream.
|
||||
*
|
||||
* @param id The property's ID.
|
||||
* @param src The bytes the property set stream consists of.
|
||||
@ -138,18 +111,15 @@ public class Property
|
||||
* @exception UnsupportedEncodingException if the specified codepage is not
|
||||
* supported.
|
||||
*/
|
||||
public Property(final long id, final byte[] src, final long offset,
|
||||
final int length, final int codepage)
|
||||
throws UnsupportedEncodingException
|
||||
{
|
||||
public Property(final long id, final byte[] src, final long offset, final int length, final int codepage)
|
||||
throws UnsupportedEncodingException {
|
||||
this.id = id;
|
||||
|
||||
/*
|
||||
* ID 0 is a special case since it specifies a dictionary of
|
||||
* property IDs and property names.
|
||||
*/
|
||||
if (id == 0)
|
||||
{
|
||||
if (id == 0) {
|
||||
value = readDictionary(src, offset, length, codepage);
|
||||
return;
|
||||
}
|
||||
@ -158,12 +128,9 @@ public class Property
|
||||
type = LittleEndian.getUInt(src, o);
|
||||
o += LittleEndian.INT_SIZE;
|
||||
|
||||
try
|
||||
{
|
||||
try {
|
||||
value = VariantSupport.read(src, o, length, (int) type, codepage);
|
||||
}
|
||||
catch (UnsupportedVariantTypeException ex)
|
||||
{
|
||||
} catch (UnsupportedVariantTypeException ex) {
|
||||
VariantSupport.writeUnsupportedTypeMessage(ex);
|
||||
value = ex.getValue();
|
||||
}
|
||||
@ -172,19 +139,68 @@ public class Property
|
||||
|
||||
|
||||
/**
|
||||
* <p>Creates an empty property. It must be filled using the set method to
|
||||
* be usable.</p>
|
||||
* Returns the property's ID.
|
||||
*
|
||||
* @return The ID value
|
||||
*/
|
||||
protected Property()
|
||||
{ }
|
||||
public long getID() {
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the property's ID.
|
||||
*
|
||||
* @param id the ID
|
||||
*/
|
||||
public void setID(final long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the property's type.
|
||||
*
|
||||
* @return The type value
|
||||
*/
|
||||
public long getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the property's type.
|
||||
*
|
||||
* @param type the property's type
|
||||
*/
|
||||
public void setType(final long type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the property's value.
|
||||
*
|
||||
* @return The property's value
|
||||
*/
|
||||
public Object getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the property's value.
|
||||
*
|
||||
* @param value the property's value
|
||||
*/
|
||||
public void setValue(final Object value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Reads a dictionary.</p>
|
||||
* Reads a dictionary.
|
||||
*
|
||||
* @param src The byte array containing the bytes making out the dictionary.
|
||||
* @param offset At this offset within <var>src </var> the dictionary
|
||||
* @param offset At this offset within {@code src} the dictionary
|
||||
* starts.
|
||||
* @param length The dictionary contains at most this many bytes.
|
||||
* @param codepage The codepage of the string values.
|
||||
@ -192,15 +208,14 @@ public class Property
|
||||
* @throws UnsupportedEncodingException if the dictionary's codepage is not
|
||||
* (yet) supported.
|
||||
*/
|
||||
protected Map<?, ?> readDictionary(final byte[] src, final long offset,
|
||||
final int length, final int codepage)
|
||||
throws UnsupportedEncodingException
|
||||
{
|
||||
protected Map<?, ?> readDictionary(final byte[] src, final long offset, final int length, final int codepage)
|
||||
throws UnsupportedEncodingException {
|
||||
/* Check whether "offset" points into the "src" array". */
|
||||
if (offset < 0 || offset > src.length)
|
||||
if (offset < 0 || offset > src.length) {
|
||||
throw new HPSFRuntimeException
|
||||
("Illegal offset " + offset + " while HPSF stream contains " +
|
||||
length + " bytes.");
|
||||
}
|
||||
int o = (int) offset;
|
||||
|
||||
/*
|
||||
@ -209,13 +224,10 @@ public class Property
|
||||
final long nrEntries = LittleEndian.getUInt(src, o);
|
||||
o += LittleEndian.INT_SIZE;
|
||||
|
||||
final Map<Object, Object> m = new LinkedHashMap<Object, Object>(
|
||||
(int) nrEntries, (float) 1.0 );
|
||||
final Map<Object, Object> m = new LinkedHashMap<Object, Object>((int) nrEntries, (float) 1.0 );
|
||||
|
||||
try
|
||||
{
|
||||
for (int i = 0; i < nrEntries; i++)
|
||||
{
|
||||
try {
|
||||
for (int i = 0; i < nrEntries; i++) {
|
||||
/* The key. */
|
||||
final Long id = Long.valueOf(LittleEndian.getUInt(src, o));
|
||||
o += LittleEndian.INT_SIZE;
|
||||
@ -230,17 +242,13 @@ public class Property
|
||||
|
||||
/* Read the string. */
|
||||
final StringBuffer b = new StringBuffer();
|
||||
switch (codepage)
|
||||
{
|
||||
switch (codepage) {
|
||||
case -1:
|
||||
{
|
||||
/* Without a codepage the length is equal to the number of
|
||||
* bytes. */
|
||||
b.append(new String(src, o, (int) sLength, Charset.forName("ASCII")));
|
||||
break;
|
||||
}
|
||||
case CodePageUtil.CP_UNICODE:
|
||||
{
|
||||
/* The length is the number of characters, i.e. the number
|
||||
* of bytes is twice the number of the characters. */
|
||||
final int nrBytes = (int) (sLength * 2);
|
||||
@ -250,36 +258,30 @@ public class Property
|
||||
h[i2] = src[o + i2 + 1];
|
||||
h[i2 + 1] = src[o + i2];
|
||||
}
|
||||
b.append(new String(h, 0, nrBytes,
|
||||
CodePageUtil.codepageToEncoding(codepage)));
|
||||
b.append(new String(h, 0, nrBytes, CodePageUtil.codepageToEncoding(codepage)));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
/* For encodings other than Unicode the length is the number
|
||||
* of bytes. */
|
||||
b.append(new String(src, o, (int) sLength,
|
||||
VariantSupport.codepageToEncoding(codepage)));
|
||||
b.append(new String(src, o, (int) sLength, CodePageUtil.codepageToEncoding(codepage)));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Strip 0x00 characters from the end of the string: */
|
||||
while (b.length() > 0 && b.charAt(b.length() - 1) == 0x00)
|
||||
while (b.length() > 0 && b.charAt(b.length() - 1) == 0x00) {
|
||||
b.setLength(b.length() - 1);
|
||||
if (codepage == CodePageUtil.CP_UNICODE)
|
||||
{
|
||||
if (sLength % 2 == 1)
|
||||
sLength++;
|
||||
o += (sLength + sLength);
|
||||
}
|
||||
else
|
||||
if (codepage == CodePageUtil.CP_UNICODE) {
|
||||
if (sLength % 2 == 1) {
|
||||
sLength++;
|
||||
}
|
||||
o += (sLength + sLength);
|
||||
} else {
|
||||
o += sLength;
|
||||
}
|
||||
m.put(id, b.toString());
|
||||
}
|
||||
}
|
||||
catch (RuntimeException ex)
|
||||
{
|
||||
} catch (RuntimeException ex) {
|
||||
final POILogger l = POILogFactory.getLogger(getClass());
|
||||
l.log(POILogger.WARN,
|
||||
"The property set's dictionary contains bogus data. "
|
||||
@ -292,8 +294,7 @@ public class Property
|
||||
|
||||
|
||||
/**
|
||||
* <p>Returns the property's size in bytes. This is always a multiple of
|
||||
* 4.</p>
|
||||
* Returns the property's size in bytes. This is always a multiple of 4.
|
||||
*
|
||||
* @return the property's size in bytes
|
||||
*
|
||||
@ -303,18 +304,18 @@ public class Property
|
||||
protected int getSize() throws WritingNotSupportedException
|
||||
{
|
||||
int length = VariantSupport.getVariantLength(type);
|
||||
if (length >= 0)
|
||||
if (length >= 0) {
|
||||
return length; /* Fixed length */
|
||||
if (length == -2)
|
||||
}
|
||||
if (length == -2) {
|
||||
/* Unknown length */
|
||||
throw new WritingNotSupportedException(type, null);
|
||||
}
|
||||
|
||||
/* Variable length: */
|
||||
final int PADDING = 4; /* Pad to multiples of 4. */
|
||||
switch ((int) type)
|
||||
{
|
||||
case Variant.VT_LPSTR:
|
||||
{
|
||||
switch ((int) type) {
|
||||
case Variant.VT_LPSTR: {
|
||||
int l = ((String) value).length() + 1;
|
||||
int r = l % PADDING;
|
||||
if (r > 0)
|
||||
@ -333,51 +334,53 @@ public class Property
|
||||
|
||||
|
||||
/**
|
||||
* <p>Compares two properties.</p> <p>Please beware that a property with
|
||||
* Compares two properties.<p>
|
||||
*
|
||||
* Please beware that a property with
|
||||
* ID == 0 is a special case: It does not have a type, and its value is the
|
||||
* section's dictionary. Another special case are strings: Two properties
|
||||
* may have the different types Variant.VT_LPSTR and Variant.VT_LPWSTR;</p>
|
||||
* may have the different types Variant.VT_LPSTR and Variant.VT_LPWSTR;
|
||||
*
|
||||
* @see Object#equals(java.lang.Object)
|
||||
*/
|
||||
public boolean equals(final Object o)
|
||||
{
|
||||
public boolean equals(final Object o) {
|
||||
if (!(o instanceof Property)) {
|
||||
return false;
|
||||
}
|
||||
final Property p = (Property) o;
|
||||
final Object pValue = p.getValue();
|
||||
final long pId = p.getID();
|
||||
if (id != pId || (id != 0 && !typesAreEqual(type, p.getType())))
|
||||
if (id != pId || (id != 0 && !typesAreEqual(type, p.getType()))) {
|
||||
return false;
|
||||
if (value == null && pValue == null)
|
||||
}
|
||||
if (value == null && pValue == null) {
|
||||
return true;
|
||||
if (value == null || pValue == null)
|
||||
}
|
||||
if (value == null || pValue == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* It's clear now that both values are non-null. */
|
||||
final Class<?> valueClass = value.getClass();
|
||||
final Class<?> pValueClass = pValue.getClass();
|
||||
if (!(valueClass.isAssignableFrom(pValueClass)) &&
|
||||
!(pValueClass.isAssignableFrom(valueClass)))
|
||||
!(pValueClass.isAssignableFrom(valueClass))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (value instanceof byte[])
|
||||
return Util.equal((byte[]) value, (byte[]) pValue);
|
||||
if (value instanceof byte[]) {
|
||||
return Arrays.equals((byte[]) value, (byte[]) pValue);
|
||||
}
|
||||
|
||||
return value.equals(pValue);
|
||||
}
|
||||
|
||||
|
||||
|
||||
private boolean typesAreEqual(final long t1, final long t2)
|
||||
{
|
||||
if (t1 == t2 ||
|
||||
private boolean typesAreEqual(final long t1, final long t2) {
|
||||
return (t1 == t2 ||
|
||||
(t1 == Variant.VT_LPSTR && t2 == Variant.VT_LPWSTR) ||
|
||||
(t2 == Variant.VT_LPSTR && t1 == Variant.VT_LPWSTR)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
(t2 == Variant.VT_LPSTR && t1 == Variant.VT_LPWSTR));
|
||||
}
|
||||
|
||||
|
||||
@ -385,15 +388,14 @@ public class Property
|
||||
/**
|
||||
* @see Object#hashCode()
|
||||
*/
|
||||
public int hashCode()
|
||||
{
|
||||
public int hashCode() {
|
||||
long hashCode = 0;
|
||||
hashCode += id;
|
||||
hashCode += type;
|
||||
if (value != null)
|
||||
if (value != null) {
|
||||
hashCode += value.hashCode();
|
||||
final int returnHashCode = (int) (hashCode & 0x0ffffffffL );
|
||||
return returnHashCode;
|
||||
}
|
||||
return (int) (hashCode & 0x0ffffffffL );
|
||||
|
||||
}
|
||||
|
||||
@ -402,8 +404,7 @@ public class Property
|
||||
/**
|
||||
* @see Object#toString()
|
||||
*/
|
||||
public String toString()
|
||||
{
|
||||
public String toString() {
|
||||
final StringBuffer b = new StringBuffer();
|
||||
b.append(getClass().getName());
|
||||
b.append('[');
|
||||
@ -413,14 +414,12 @@ public class Property
|
||||
b.append(getType());
|
||||
final Object value = getValue();
|
||||
b.append(", value: ");
|
||||
if (value instanceof String)
|
||||
{
|
||||
if (value instanceof String) {
|
||||
b.append(value.toString());
|
||||
final String s = (String) value;
|
||||
final int l = s.length();
|
||||
final byte[] bytes = new byte[l * 2];
|
||||
for (int i = 0; i < l; i++)
|
||||
{
|
||||
for (int i = 0; i < l; i++) {
|
||||
final char c = s.charAt(i);
|
||||
final byte high = (byte) ((c & 0x00ff00) >> 8);
|
||||
final byte low = (byte) ((c & 0x0000ff) >> 0);
|
||||
@ -433,21 +432,43 @@ public class Property
|
||||
b.append(hex);
|
||||
}
|
||||
b.append("]");
|
||||
}
|
||||
else if (value instanceof byte[])
|
||||
{
|
||||
} else if (value instanceof byte[]) {
|
||||
byte[] bytes = (byte[])value;
|
||||
if(bytes.length > 0) {
|
||||
String hex = HexDump.dump(bytes, 0L, 0);
|
||||
b.append(hex);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
b.append(value.toString());
|
||||
}
|
||||
b.append(']');
|
||||
return b.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the property to an output stream.
|
||||
*
|
||||
* @param out The output stream to write to.
|
||||
* @param codepage The codepage to use for writing non-wide strings
|
||||
* @return the number of bytes written to the stream
|
||||
*
|
||||
* @exception IOException if an I/O error occurs
|
||||
* @exception WritingNotSupportedException if a variant type is to be
|
||||
* written that is not yet supported
|
||||
*/
|
||||
public int write(final OutputStream out, final int codepage)
|
||||
throws IOException, WritingNotSupportedException {
|
||||
int length = 0;
|
||||
long variantType = getType();
|
||||
|
||||
/* Ensure that wide strings are written if the codepage is Unicode. */
|
||||
if (codepage == CodePageUtil.CP_UNICODE && variantType == Variant.VT_LPSTR) {
|
||||
variantType = Variant.VT_LPWSTR;
|
||||
}
|
||||
|
||||
length += TypeWriter.writeUIntToStream(out, variantType);
|
||||
length += VariantSupport.write(out, variantType, getValue(), codepage);
|
||||
return length;
|
||||
}
|
||||
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -22,24 +22,22 @@ import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
|
||||
import org.apache.poi.hpsf.wellknown.SectionIDMap;
|
||||
import org.apache.poi.poifs.filesystem.DirectoryEntry;
|
||||
import org.apache.poi.poifs.filesystem.DocumentEntry;
|
||||
import org.apache.poi.poifs.filesystem.DocumentInputStream;
|
||||
|
||||
/**
|
||||
* <p>Factory class to create instances of {@link SummaryInformation},
|
||||
* {@link DocumentSummaryInformation} and {@link PropertySet}.</p>
|
||||
* Factory class to create instances of {@link SummaryInformation},
|
||||
* {@link DocumentSummaryInformation} and {@link PropertySet}.
|
||||
*/
|
||||
public class PropertySetFactory
|
||||
{
|
||||
public class PropertySetFactory {
|
||||
/**
|
||||
* <p>Creates the most specific {@link PropertySet} from an entry
|
||||
* Creates the most specific {@link PropertySet} from an entry
|
||||
* in the specified POIFS Directory. This is preferrably a {@link
|
||||
* DocumentSummaryInformation} or a {@link SummaryInformation}. If
|
||||
* the specified entry does not contain a property set stream, an
|
||||
* exception is thrown. If no entry is found with the given name,
|
||||
* an exception is thrown.</p>
|
||||
* an exception is thrown.
|
||||
*
|
||||
* @param dir The directory to find the PropertySet in
|
||||
* @param name The name of the entry containing the PropertySet
|
||||
@ -52,55 +50,53 @@ public class PropertySetFactory
|
||||
* supported.
|
||||
*/
|
||||
public static PropertySet create(final DirectoryEntry dir, final String name)
|
||||
throws FileNotFoundException, NoPropertySetStreamException,
|
||||
IOException, UnsupportedEncodingException
|
||||
{
|
||||
throws FileNotFoundException, NoPropertySetStreamException, IOException, UnsupportedEncodingException {
|
||||
InputStream inp = null;
|
||||
try {
|
||||
DocumentEntry entry = (DocumentEntry)dir.getEntry(name);
|
||||
inp = new DocumentInputStream(entry);
|
||||
try {
|
||||
return create(inp);
|
||||
} catch (MarkUnsupportedException e) { return null; }
|
||||
} catch (MarkUnsupportedException e) {
|
||||
return null;
|
||||
}
|
||||
} finally {
|
||||
if (inp != null) inp.close();
|
||||
if (inp != null) {
|
||||
inp.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Creates the most specific {@link PropertySet} from an {@link
|
||||
* Creates the most specific {@link PropertySet} from an {@link
|
||||
* InputStream}. This is preferrably a {@link
|
||||
* DocumentSummaryInformation} or a {@link SummaryInformation}. If
|
||||
* the specified {@link InputStream} does not contain a property
|
||||
* set stream, an exception is thrown and the {@link InputStream}
|
||||
* is repositioned at its beginning.</p>
|
||||
* is repositioned at its beginning.
|
||||
*
|
||||
* @param stream Contains the property set stream's data.
|
||||
* @return The created {@link PropertySet}.
|
||||
* @throws NoPropertySetStreamException if the stream does not
|
||||
* contain a property set.
|
||||
* @throws MarkUnsupportedException if the stream does not support
|
||||
* the <code>mark</code> operation.
|
||||
* the {@code mark} operation.
|
||||
* @throws IOException if some I/O problem occurs.
|
||||
* @exception UnsupportedEncodingException if the specified codepage is not
|
||||
* supported.
|
||||
*/
|
||||
public static PropertySet create(final InputStream stream)
|
||||
throws NoPropertySetStreamException, MarkUnsupportedException,
|
||||
UnsupportedEncodingException, IOException
|
||||
{
|
||||
throws NoPropertySetStreamException, MarkUnsupportedException, UnsupportedEncodingException, IOException {
|
||||
final PropertySet ps = new PropertySet(stream);
|
||||
try
|
||||
{
|
||||
if (ps.isSummaryInformation())
|
||||
try {
|
||||
if (ps.isSummaryInformation()) {
|
||||
return new SummaryInformation(ps);
|
||||
else if (ps.isDocumentSummaryInformation())
|
||||
} else if (ps.isDocumentSummaryInformation()) {
|
||||
return new DocumentSummaryInformation(ps);
|
||||
else
|
||||
} else {
|
||||
return ps;
|
||||
}
|
||||
catch (UnexpectedPropertySetTypeException ex)
|
||||
{
|
||||
}
|
||||
} catch (UnexpectedPropertySetTypeException ex) {
|
||||
/* This exception will never be throws because we already checked
|
||||
* explicitly for this case above. */
|
||||
throw new IllegalStateException(ex);
|
||||
@ -108,44 +104,20 @@ public class PropertySetFactory
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Creates a new summary information.</p>
|
||||
* Creates a new summary information.
|
||||
*
|
||||
* @return the new summary information.
|
||||
*/
|
||||
public static SummaryInformation newSummaryInformation()
|
||||
{
|
||||
final MutablePropertySet ps = new MutablePropertySet();
|
||||
final MutableSection s = (MutableSection) ps.getFirstSection();
|
||||
s.setFormatID(SectionIDMap.SUMMARY_INFORMATION_ID);
|
||||
try
|
||||
{
|
||||
return new SummaryInformation(ps);
|
||||
}
|
||||
catch (UnexpectedPropertySetTypeException ex)
|
||||
{
|
||||
/* This should never happen. */
|
||||
throw new HPSFRuntimeException(ex);
|
||||
}
|
||||
public static SummaryInformation newSummaryInformation() {
|
||||
return new SummaryInformation();
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Creates a new document summary information.</p>
|
||||
* Creates a new document summary information.
|
||||
*
|
||||
* @return the new document summary information.
|
||||
*/
|
||||
public static DocumentSummaryInformation newDocumentSummaryInformation()
|
||||
{
|
||||
final MutablePropertySet ps = new MutablePropertySet();
|
||||
final MutableSection s = (MutableSection) ps.getFirstSection();
|
||||
s.setFormatID(SectionIDMap.DOCUMENT_SUMMARY_INFORMATION_ID[0]);
|
||||
try
|
||||
{
|
||||
return new DocumentSummaryInformation(ps);
|
||||
}
|
||||
catch (UnexpectedPropertySetTypeException ex)
|
||||
{
|
||||
/* This should never happen. */
|
||||
throw new HPSFRuntimeException(ex);
|
||||
}
|
||||
public static DocumentSummaryInformation newDocumentSummaryInformation() {
|
||||
return new DocumentSummaryInformation();
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -17,397 +17,24 @@
|
||||
|
||||
package org.apache.poi.hpsf;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.poi.hpsf.wellknown.PropertyIDMap;
|
||||
import org.apache.poi.poifs.filesystem.DirectoryEntry;
|
||||
import org.apache.poi.util.LittleEndian;
|
||||
import org.apache.poi.util.Removal;
|
||||
|
||||
/**
|
||||
* <p>Abstract superclass for the convenience classes {@link
|
||||
* SummaryInformation} and {@link DocumentSummaryInformation}.</p>
|
||||
* Interface for the convenience classes {@link SummaryInformation}
|
||||
* and {@link DocumentSummaryInformation}.<p>
|
||||
*
|
||||
* <p>The motivation behind this class is quite nasty if you look
|
||||
* behind the scenes, but it serves the application programmer well by
|
||||
* providing him with the easy-to-use {@link SummaryInformation} and
|
||||
* {@link DocumentSummaryInformation} classes. When parsing the data a
|
||||
* property set stream consists of (possibly coming from an {@link
|
||||
* java.io.InputStream}) we want to read and process each byte only
|
||||
* once. Since we don't know in advance which kind of property set we
|
||||
* have, we can expect only the most general {@link
|
||||
* PropertySet}. Creating a special subclass should be as easy as
|
||||
* calling the special subclass' constructor and pass the general
|
||||
* {@link PropertySet} in. To make things easy internally, the special
|
||||
* class just holds a reference to the general {@link PropertySet} and
|
||||
* delegates all method calls to it.</p>
|
||||
* This used to be an abstract class to support late loading
|
||||
* of the SummaryInformation classes, as their concrete instance can
|
||||
* only be determined after the PropertySet has been loaded.
|
||||
*
|
||||
* <p>A cleaner implementation would have been like this: The {@link
|
||||
* PropertySetFactory} parses the stream data into some internal
|
||||
* object first. Then it finds out whether the stream is a {@link
|
||||
* SummaryInformation}, a {@link DocumentSummaryInformation} or a
|
||||
* general {@link PropertySet}. However, the current implementation
|
||||
* went the other way round historically: the convenience classes came
|
||||
* only late to my mind.</p>
|
||||
* @deprecated POI 3.16 - use PropertySet as base class instead
|
||||
*/
|
||||
public abstract class SpecialPropertySet extends MutablePropertySet
|
||||
{
|
||||
/**
|
||||
* The id to name mapping of the properties in this set.
|
||||
*
|
||||
* @return the id to name mapping of the properties in this set
|
||||
*/
|
||||
public abstract PropertyIDMap getPropertySetIDMap();
|
||||
|
||||
/**
|
||||
* <p>The "real" property set <code>SpecialPropertySet</code>
|
||||
* delegates to.</p>
|
||||
*/
|
||||
private final MutablePropertySet delegate;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Creates a <code>SpecialPropertySet</code>.
|
||||
*
|
||||
* @param ps The property set to be encapsulated by the
|
||||
* <code>SpecialPropertySet</code>
|
||||
*/
|
||||
public SpecialPropertySet(final PropertySet ps)
|
||||
{
|
||||
delegate = new MutablePropertySet(ps);
|
||||
@Removal(version="3.18")
|
||||
public class SpecialPropertySet extends MutablePropertySet {
|
||||
public SpecialPropertySet() {
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Creates a <code>SpecialPropertySet</code>.
|
||||
*
|
||||
* @param ps The mutable property set to be encapsulated by the
|
||||
* <code>SpecialPropertySet</code>
|
||||
*/
|
||||
public SpecialPropertySet(final MutablePropertySet ps)
|
||||
{
|
||||
delegate = ps;
|
||||
public SpecialPropertySet(final PropertySet ps) throws UnexpectedPropertySetTypeException {
|
||||
super(ps);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @see PropertySet#getByteOrder
|
||||
*/
|
||||
@Override
|
||||
public int getByteOrder()
|
||||
{
|
||||
return delegate.getByteOrder();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @see PropertySet#getFormat
|
||||
*/
|
||||
@Override
|
||||
public int getFormat()
|
||||
{
|
||||
return delegate.getFormat();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @see PropertySet#getOSVersion
|
||||
*/
|
||||
@Override
|
||||
public int getOSVersion()
|
||||
{
|
||||
return delegate.getOSVersion();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @see PropertySet#getClassID
|
||||
*/
|
||||
@Override
|
||||
public ClassID getClassID()
|
||||
{
|
||||
return delegate.getClassID();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @see PropertySet#getSectionCount
|
||||
*/
|
||||
@Override
|
||||
public int getSectionCount()
|
||||
{
|
||||
return delegate.getSectionCount();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @see PropertySet#getSections
|
||||
*/
|
||||
@Override
|
||||
public List<Section> getSections()
|
||||
{
|
||||
return delegate.getSections();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @see PropertySet#isSummaryInformation
|
||||
*/
|
||||
@Override
|
||||
public boolean isSummaryInformation()
|
||||
{
|
||||
return delegate.isSummaryInformation();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @see PropertySet#isDocumentSummaryInformation
|
||||
*/
|
||||
@Override
|
||||
public boolean isDocumentSummaryInformation()
|
||||
{
|
||||
return delegate.isDocumentSummaryInformation();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @see PropertySet#getSingleSection
|
||||
*/
|
||||
@Override
|
||||
public Section getFirstSection()
|
||||
{
|
||||
return delegate.getFirstSection();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @see org.apache.poi.hpsf.MutablePropertySet#addSection(org.apache.poi.hpsf.Section)
|
||||
*/
|
||||
@Override
|
||||
public void addSection(final Section section)
|
||||
{
|
||||
delegate.addSection(section);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @see org.apache.poi.hpsf.MutablePropertySet#clearSections()
|
||||
*/
|
||||
@Override
|
||||
public void clearSections()
|
||||
{
|
||||
delegate.clearSections();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @see org.apache.poi.hpsf.MutablePropertySet#setByteOrder(int)
|
||||
*/
|
||||
@Override
|
||||
public void setByteOrder(final int byteOrder)
|
||||
{
|
||||
delegate.setByteOrder(byteOrder);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @see org.apache.poi.hpsf.MutablePropertySet#setClassID(org.apache.poi.hpsf.ClassID)
|
||||
*/
|
||||
@Override
|
||||
public void setClassID(final ClassID classID)
|
||||
{
|
||||
delegate.setClassID(classID);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @see org.apache.poi.hpsf.MutablePropertySet#setFormat(int)
|
||||
*/
|
||||
@Override
|
||||
public void setFormat(final int format)
|
||||
{
|
||||
delegate.setFormat(format);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @see org.apache.poi.hpsf.MutablePropertySet#setOSVersion(int)
|
||||
*/
|
||||
@Override
|
||||
public void setOSVersion(final int osVersion)
|
||||
{
|
||||
delegate.setOSVersion(osVersion);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @see org.apache.poi.hpsf.MutablePropertySet#toInputStream()
|
||||
*/
|
||||
@Override
|
||||
public InputStream toInputStream() throws IOException, WritingNotSupportedException
|
||||
{
|
||||
return delegate.toInputStream();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @see org.apache.poi.hpsf.MutablePropertySet#write(org.apache.poi.poifs.filesystem.DirectoryEntry, java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public void write(final DirectoryEntry dir, final String name) throws WritingNotSupportedException, IOException
|
||||
{
|
||||
delegate.write(dir, name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.apache.poi.hpsf.MutablePropertySet#write(java.io.OutputStream)
|
||||
*/
|
||||
@Override
|
||||
public void write(final OutputStream out) throws WritingNotSupportedException, IOException
|
||||
{
|
||||
delegate.write(out);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.apache.poi.hpsf.PropertySet#equals(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(final Object o)
|
||||
{
|
||||
return delegate.equals(o);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.apache.poi.hpsf.PropertySet#getProperties()
|
||||
*/
|
||||
@Override
|
||||
public Property[] getProperties() throws NoSingleSectionException
|
||||
{
|
||||
return delegate.getProperties();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.apache.poi.hpsf.PropertySet#getProperty(int)
|
||||
*/
|
||||
@Override
|
||||
protected Object getProperty(final int id) throws NoSingleSectionException
|
||||
{
|
||||
return delegate.getProperty(id);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @see org.apache.poi.hpsf.PropertySet#getPropertyBooleanValue(int)
|
||||
*/
|
||||
@Override
|
||||
protected boolean getPropertyBooleanValue(final int id) throws NoSingleSectionException
|
||||
{
|
||||
return delegate.getPropertyBooleanValue(id);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @see org.apache.poi.hpsf.PropertySet#getPropertyIntValue(int)
|
||||
*/
|
||||
@Override
|
||||
protected int getPropertyIntValue(final int id) throws NoSingleSectionException
|
||||
{
|
||||
return delegate.getPropertyIntValue(id);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Fetches the property with the given ID, then does its
|
||||
* best to return it as a String
|
||||
*
|
||||
* @param propertyId the property id
|
||||
*
|
||||
* @return The property as a String, or null if unavailable
|
||||
*/
|
||||
protected String getPropertyStringValue(final int propertyId) {
|
||||
Object propertyValue = getProperty(propertyId);
|
||||
return getPropertyStringValue(propertyValue);
|
||||
}
|
||||
protected static String getPropertyStringValue(final Object propertyValue) {
|
||||
// Normal cases
|
||||
if (propertyValue == null) return null;
|
||||
if (propertyValue instanceof String) return (String)propertyValue;
|
||||
|
||||
// Do our best with some edge cases
|
||||
if (propertyValue instanceof byte[]) {
|
||||
byte[] b = (byte[])propertyValue;
|
||||
if (b.length == 0) {
|
||||
return "";
|
||||
}
|
||||
if (b.length == 1) {
|
||||
return Byte.toString(b[0]);
|
||||
}
|
||||
if (b.length == 2) {
|
||||
return Integer.toString( LittleEndian.getUShort(b) );
|
||||
}
|
||||
if (b.length == 4) {
|
||||
return Long.toString( LittleEndian.getUInt(b) );
|
||||
}
|
||||
// Maybe it's a string? who knows!
|
||||
return new String(b, Charset.forName("ASCII"));
|
||||
}
|
||||
return propertyValue.toString();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @see org.apache.poi.hpsf.PropertySet#hashCode()
|
||||
*/
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
return delegate.hashCode();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @see org.apache.poi.hpsf.PropertySet#toString()
|
||||
*/
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return delegate.toString();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @see org.apache.poi.hpsf.PropertySet#wasNull()
|
||||
*/
|
||||
@Override
|
||||
public boolean wasNull() throws NoSingleSectionException
|
||||
{
|
||||
return delegate.wasNull();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -20,18 +20,18 @@ package org.apache.poi.hpsf;
|
||||
import java.util.Date;
|
||||
|
||||
import org.apache.poi.hpsf.wellknown.PropertyIDMap;
|
||||
import org.apache.poi.hpsf.wellknown.SectionIDMap;
|
||||
|
||||
/**
|
||||
* <p>Convenience class representing a Summary Information stream in a
|
||||
* Microsoft Office document.</p>
|
||||
* Convenience class representing a Summary Information stream in a
|
||||
* Microsoft Office document.
|
||||
*
|
||||
* @see DocumentSummaryInformation
|
||||
*/
|
||||
public final class SummaryInformation extends SpecialPropertySet {
|
||||
|
||||
/**
|
||||
* <p>The document name a summary information stream usually has in a POIFS
|
||||
* filesystem.</p>
|
||||
* The document name a summary information stream usually has in a POIFS filesystem.
|
||||
*/
|
||||
public static final String DEFAULT_STREAM_NAME = "\005SummaryInformation";
|
||||
|
||||
@ -39,324 +39,291 @@ public final class SummaryInformation extends SpecialPropertySet {
|
||||
return PropertyIDMap.getSummaryInformationProperties();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* <p>Creates a {@link SummaryInformation} from a given {@link
|
||||
* PropertySet}.</p>
|
||||
* Creates a {@link SummaryInformation} from a given {@link
|
||||
* PropertySet}.
|
||||
*
|
||||
* @param ps A property set which should be created from a summary
|
||||
* information stream.
|
||||
* @throws UnexpectedPropertySetTypeException if <var>ps</var> does not
|
||||
* @throws UnexpectedPropertySetTypeException if {@code ps} does not
|
||||
* contain a summary information stream.
|
||||
*/
|
||||
public SummaryInformation(final PropertySet ps)
|
||||
throws UnexpectedPropertySetTypeException
|
||||
{
|
||||
public SummaryInformation() {
|
||||
getFirstSection().setFormatID(SectionIDMap.SUMMARY_INFORMATION_ID);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link SummaryInformation} from a given {@link
|
||||
* PropertySet}.
|
||||
*
|
||||
* @param ps A property set which should be created from a summary
|
||||
* information stream.
|
||||
* @throws UnexpectedPropertySetTypeException if {@code ps} does not
|
||||
* contain a summary information stream.
|
||||
*/
|
||||
public SummaryInformation(final PropertySet ps) throws UnexpectedPropertySetTypeException {
|
||||
super(ps);
|
||||
if (!isSummaryInformation())
|
||||
throw new UnexpectedPropertySetTypeException("Not a "
|
||||
+ getClass().getName());
|
||||
if (!isSummaryInformation()) {
|
||||
throw new UnexpectedPropertySetTypeException("Not a " + getClass().getName());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Returns the title (or <code>null</code>).</p>
|
||||
*
|
||||
* @return The title or <code>null</code>
|
||||
* @return The title or {@code null}
|
||||
*/
|
||||
public String getTitle()
|
||||
{
|
||||
public String getTitle() {
|
||||
return getPropertyStringValue(PropertyIDMap.PID_TITLE);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Sets the title.</p>
|
||||
* Sets the title.
|
||||
*
|
||||
* @param title The title to set.
|
||||
*/
|
||||
public void setTitle(final String title)
|
||||
{
|
||||
final MutableSection s = (MutableSection) getFirstSection();
|
||||
s.setProperty(PropertyIDMap.PID_TITLE, title);
|
||||
public void setTitle(final String title) {
|
||||
set1stProperty(PropertyIDMap.PID_TITLE, title);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Removes the title.</p>
|
||||
* Removes the title.
|
||||
*/
|
||||
public void removeTitle()
|
||||
{
|
||||
final MutableSection s = (MutableSection) getFirstSection();
|
||||
s.removeProperty(PropertyIDMap.PID_TITLE);
|
||||
public void removeTitle() {
|
||||
remove1stProperty(PropertyIDMap.PID_TITLE);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Returns the subject (or <code>null</code>).</p>
|
||||
* Returns the subject (or {@code null}).
|
||||
*
|
||||
* @return The subject or <code>null</code>
|
||||
* @return The subject or {@code null}
|
||||
*/
|
||||
public String getSubject()
|
||||
{
|
||||
public String getSubject() {
|
||||
return getPropertyStringValue(PropertyIDMap.PID_SUBJECT);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Sets the subject.</p>
|
||||
* Sets the subject.
|
||||
*
|
||||
* @param subject The subject to set.
|
||||
*/
|
||||
public void setSubject(final String subject)
|
||||
{
|
||||
final MutableSection s = (MutableSection) getFirstSection();
|
||||
s.setProperty(PropertyIDMap.PID_SUBJECT, subject);
|
||||
public void setSubject(final String subject) {
|
||||
set1stProperty(PropertyIDMap.PID_SUBJECT, subject);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Removes the subject.</p>
|
||||
* Removes the subject.
|
||||
*/
|
||||
public void removeSubject()
|
||||
{
|
||||
final MutableSection s = (MutableSection) getFirstSection();
|
||||
s.removeProperty(PropertyIDMap.PID_SUBJECT);
|
||||
public void removeSubject() {
|
||||
remove1stProperty(PropertyIDMap.PID_SUBJECT);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Returns the author (or <code>null</code>).</p>
|
||||
* Returns the author (or {@code null}).
|
||||
*
|
||||
* @return The author or <code>null</code>
|
||||
* @return The author or {@code null}
|
||||
*/
|
||||
public String getAuthor()
|
||||
{
|
||||
public String getAuthor() {
|
||||
return getPropertyStringValue(PropertyIDMap.PID_AUTHOR);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Sets the author.</p>
|
||||
* Sets the author.
|
||||
*
|
||||
* @param author The author to set.
|
||||
*/
|
||||
public void setAuthor(final String author)
|
||||
{
|
||||
final MutableSection s = (MutableSection) getFirstSection();
|
||||
s.setProperty(PropertyIDMap.PID_AUTHOR, author);
|
||||
public void setAuthor(final String author) {
|
||||
set1stProperty(PropertyIDMap.PID_AUTHOR, author);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Removes the author.</p>
|
||||
* Removes the author.
|
||||
*/
|
||||
public void removeAuthor()
|
||||
{
|
||||
final MutableSection s = (MutableSection) getFirstSection();
|
||||
s.removeProperty(PropertyIDMap.PID_AUTHOR);
|
||||
public void removeAuthor() {
|
||||
remove1stProperty(PropertyIDMap.PID_AUTHOR);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Returns the keywords (or <code>null</code>).</p>
|
||||
* Returns the keywords (or {@code null}).
|
||||
*
|
||||
* @return The keywords or <code>null</code>
|
||||
* @return The keywords or {@code null}
|
||||
*/
|
||||
public String getKeywords()
|
||||
{
|
||||
public String getKeywords() {
|
||||
return getPropertyStringValue(PropertyIDMap.PID_KEYWORDS);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Sets the keywords.</p>
|
||||
* Sets the keywords.
|
||||
*
|
||||
* @param keywords The keywords to set.
|
||||
*/
|
||||
public void setKeywords(final String keywords)
|
||||
{
|
||||
final MutableSection s = (MutableSection) getFirstSection();
|
||||
s.setProperty(PropertyIDMap.PID_KEYWORDS, keywords);
|
||||
public void setKeywords(final String keywords) {
|
||||
set1stProperty(PropertyIDMap.PID_KEYWORDS, keywords);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Removes the keywords.</p>
|
||||
* Removes the keywords.
|
||||
*/
|
||||
public void removeKeywords()
|
||||
{
|
||||
final MutableSection s = (MutableSection) getFirstSection();
|
||||
s.removeProperty(PropertyIDMap.PID_KEYWORDS);
|
||||
public void removeKeywords() {
|
||||
remove1stProperty(PropertyIDMap.PID_KEYWORDS);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Returns the comments (or <code>null</code>).</p>
|
||||
* Returns the comments (or {@code null}).
|
||||
*
|
||||
* @return The comments or <code>null</code>
|
||||
* @return The comments or {@code null}
|
||||
*/
|
||||
public String getComments()
|
||||
{
|
||||
public String getComments() {
|
||||
return getPropertyStringValue(PropertyIDMap.PID_COMMENTS);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Sets the comments.</p>
|
||||
* Sets the comments.
|
||||
*
|
||||
* @param comments The comments to set.
|
||||
*/
|
||||
public void setComments(final String comments)
|
||||
{
|
||||
final MutableSection s = (MutableSection) getFirstSection();
|
||||
s.setProperty(PropertyIDMap.PID_COMMENTS, comments);
|
||||
public void setComments(final String comments) {
|
||||
set1stProperty(PropertyIDMap.PID_COMMENTS, comments);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Removes the comments.</p>
|
||||
* Removes the comments.
|
||||
*/
|
||||
public void removeComments()
|
||||
{
|
||||
final MutableSection s = (MutableSection) getFirstSection();
|
||||
s.removeProperty(PropertyIDMap.PID_COMMENTS);
|
||||
public void removeComments() {
|
||||
remove1stProperty(PropertyIDMap.PID_COMMENTS);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Returns the template (or <code>null</code>).</p>
|
||||
* Returns the template (or {@code null}).
|
||||
*
|
||||
* @return The template or <code>null</code>
|
||||
* @return The template or {@code null}
|
||||
*/
|
||||
public String getTemplate()
|
||||
{
|
||||
public String getTemplate() {
|
||||
return getPropertyStringValue(PropertyIDMap.PID_TEMPLATE);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Sets the template.</p>
|
||||
* Sets the template.
|
||||
*
|
||||
* @param template The template to set.
|
||||
*/
|
||||
public void setTemplate(final String template)
|
||||
{
|
||||
final MutableSection s = (MutableSection) getFirstSection();
|
||||
s.setProperty(PropertyIDMap.PID_TEMPLATE, template);
|
||||
public void setTemplate(final String template) {
|
||||
set1stProperty(PropertyIDMap.PID_TEMPLATE, template);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Removes the template.</p>
|
||||
* Removes the template.
|
||||
*/
|
||||
public void removeTemplate()
|
||||
{
|
||||
final MutableSection s = (MutableSection) getFirstSection();
|
||||
s.removeProperty(PropertyIDMap.PID_TEMPLATE);
|
||||
public void removeTemplate() {
|
||||
remove1stProperty(PropertyIDMap.PID_TEMPLATE);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Returns the last author (or <code>null</code>).</p>
|
||||
* Returns the last author (or {@code null}).
|
||||
*
|
||||
* @return The last author or <code>null</code>
|
||||
* @return The last author or {@code null}
|
||||
*/
|
||||
public String getLastAuthor()
|
||||
{
|
||||
public String getLastAuthor() {
|
||||
return getPropertyStringValue(PropertyIDMap.PID_LASTAUTHOR);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Sets the last author.</p>
|
||||
* Sets the last author.
|
||||
*
|
||||
* @param lastAuthor The last author to set.
|
||||
*/
|
||||
public void setLastAuthor(final String lastAuthor)
|
||||
{
|
||||
final MutableSection s = (MutableSection) getFirstSection();
|
||||
s.setProperty(PropertyIDMap.PID_LASTAUTHOR, lastAuthor);
|
||||
public void setLastAuthor(final String lastAuthor) {
|
||||
set1stProperty(PropertyIDMap.PID_LASTAUTHOR, lastAuthor);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Removes the last author.</p>
|
||||
* Removes the last author.
|
||||
*/
|
||||
public void removeLastAuthor()
|
||||
{
|
||||
final MutableSection s = (MutableSection) getFirstSection();
|
||||
s.removeProperty(PropertyIDMap.PID_LASTAUTHOR);
|
||||
public void removeLastAuthor() {
|
||||
remove1stProperty(PropertyIDMap.PID_LASTAUTHOR);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Returns the revision number (or <code>null</code>). </p>
|
||||
* Returns the revision number (or {@code null}).
|
||||
*
|
||||
* @return The revision number or <code>null</code>
|
||||
* @return The revision number or {@code null}
|
||||
*/
|
||||
public String getRevNumber()
|
||||
{
|
||||
public String getRevNumber() {
|
||||
return getPropertyStringValue(PropertyIDMap.PID_REVNUMBER);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Sets the revision number.</p>
|
||||
* Sets the revision number.
|
||||
*
|
||||
* @param revNumber The revision number to set.
|
||||
*/
|
||||
public void setRevNumber(final String revNumber)
|
||||
{
|
||||
final MutableSection s = (MutableSection) getFirstSection();
|
||||
s.setProperty(PropertyIDMap.PID_REVNUMBER, revNumber);
|
||||
public void setRevNumber(final String revNumber) {
|
||||
set1stProperty(PropertyIDMap.PID_REVNUMBER, revNumber);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Removes the revision number.</p>
|
||||
* Removes the revision number.
|
||||
*/
|
||||
public void removeRevNumber()
|
||||
{
|
||||
final MutableSection s = (MutableSection) getFirstSection();
|
||||
s.removeProperty(PropertyIDMap.PID_REVNUMBER);
|
||||
public void removeRevNumber() {
|
||||
remove1stProperty(PropertyIDMap.PID_REVNUMBER);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Returns the total time spent in editing the document (or
|
||||
* <code>0</code>).</p>
|
||||
* Returns the total time spent in editing the document (or
|
||||
* {@code 0}).
|
||||
*
|
||||
* @return The total time spent in editing the document or 0 if the {@link
|
||||
* SummaryInformation} does not contain this information.
|
||||
*/
|
||||
public long getEditTime()
|
||||
{
|
||||
public long getEditTime() {
|
||||
final Date d = (Date) getProperty(PropertyIDMap.PID_EDITTIME);
|
||||
if (d == null) {
|
||||
return 0;
|
||||
@ -367,124 +334,106 @@ public final class SummaryInformation extends SpecialPropertySet {
|
||||
|
||||
|
||||
/**
|
||||
* <p>Sets the total time spent in editing the document.</p>
|
||||
* Sets the total time spent in editing the document.
|
||||
*
|
||||
* @param time The time to set.
|
||||
*/
|
||||
public void setEditTime(final long time)
|
||||
{
|
||||
public void setEditTime(final long time) {
|
||||
final Date d = Util.filetimeToDate(time);
|
||||
final MutableSection s = (MutableSection) getFirstSection();
|
||||
s.setProperty(PropertyIDMap.PID_EDITTIME, Variant.VT_FILETIME, d);
|
||||
getFirstSection().setProperty(PropertyIDMap.PID_EDITTIME, Variant.VT_FILETIME, d);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Remove the total time spent in editing the document.</p>
|
||||
* Remove the total time spent in editing the document.
|
||||
*/
|
||||
public void removeEditTime()
|
||||
{
|
||||
final MutableSection s = (MutableSection) getFirstSection();
|
||||
s.removeProperty(PropertyIDMap.PID_EDITTIME);
|
||||
public void removeEditTime() {
|
||||
remove1stProperty(PropertyIDMap.PID_EDITTIME);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Returns the last printed time (or <code>null</code>).</p>
|
||||
* Returns the last printed time (or {@code null}).
|
||||
*
|
||||
* @return The last printed time or <code>null</code>
|
||||
* @return The last printed time or {@code null}
|
||||
*/
|
||||
public Date getLastPrinted()
|
||||
{
|
||||
public Date getLastPrinted() {
|
||||
return (Date) getProperty(PropertyIDMap.PID_LASTPRINTED);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Sets the lastPrinted.</p>
|
||||
* Sets the lastPrinted.
|
||||
*
|
||||
* @param lastPrinted The lastPrinted to set.
|
||||
*/
|
||||
public void setLastPrinted(final Date lastPrinted)
|
||||
{
|
||||
final MutableSection s = (MutableSection) getFirstSection();
|
||||
s.setProperty(PropertyIDMap.PID_LASTPRINTED, Variant.VT_FILETIME,
|
||||
lastPrinted);
|
||||
public void setLastPrinted(final Date lastPrinted) {
|
||||
getFirstSection().setProperty(PropertyIDMap.PID_LASTPRINTED, Variant.VT_FILETIME, lastPrinted);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Removes the lastPrinted.</p>
|
||||
* Removes the lastPrinted.
|
||||
*/
|
||||
public void removeLastPrinted()
|
||||
{
|
||||
final MutableSection s = (MutableSection) getFirstSection();
|
||||
s.removeProperty(PropertyIDMap.PID_LASTPRINTED);
|
||||
public void removeLastPrinted() {
|
||||
remove1stProperty(PropertyIDMap.PID_LASTPRINTED);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Returns the creation time (or <code>null</code>).</p>
|
||||
* Returns the creation time (or {@code null}).
|
||||
*
|
||||
* @return The creation time or <code>null</code>
|
||||
* @return The creation time or {@code null}
|
||||
*/
|
||||
public Date getCreateDateTime()
|
||||
{
|
||||
public Date getCreateDateTime() {
|
||||
return (Date) getProperty(PropertyIDMap.PID_CREATE_DTM);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Sets the creation time.</p>
|
||||
* Sets the creation time.
|
||||
*
|
||||
* @param createDateTime The creation time to set.
|
||||
*/
|
||||
public void setCreateDateTime(final Date createDateTime)
|
||||
{
|
||||
final MutableSection s = (MutableSection) getFirstSection();
|
||||
s.setProperty(PropertyIDMap.PID_CREATE_DTM, Variant.VT_FILETIME,
|
||||
createDateTime);
|
||||
public void setCreateDateTime(final Date createDateTime) {
|
||||
getFirstSection().setProperty(PropertyIDMap.PID_CREATE_DTM, Variant.VT_FILETIME, createDateTime);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Removes the creation time.</p>
|
||||
* Removes the creation time.
|
||||
*/
|
||||
public void removeCreateDateTime()
|
||||
{
|
||||
final MutableSection s = (MutableSection) getFirstSection();
|
||||
s.removeProperty(PropertyIDMap.PID_CREATE_DTM);
|
||||
public void removeCreateDateTime() {
|
||||
remove1stProperty(PropertyIDMap.PID_CREATE_DTM);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Returns the last save time (or <code>null</code>).</p>
|
||||
* Returns the last save time (or {@code null}).
|
||||
*
|
||||
* @return The last save time or <code>null</code>
|
||||
* @return The last save time or {@code null}
|
||||
*/
|
||||
public Date getLastSaveDateTime()
|
||||
{
|
||||
public Date getLastSaveDateTime() {
|
||||
return (Date) getProperty(PropertyIDMap.PID_LASTSAVE_DTM);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Sets the total time spent in editing the document.</p>
|
||||
* Sets the total time spent in editing the document.
|
||||
*
|
||||
* @param time The time to set.
|
||||
*/
|
||||
public void setLastSaveDateTime(final Date time)
|
||||
{
|
||||
final MutableSection s = (MutableSection) getFirstSection();
|
||||
public void setLastSaveDateTime(final Date time) {
|
||||
final Section s = getFirstSection();
|
||||
s
|
||||
.setProperty(PropertyIDMap.PID_LASTSAVE_DTM,
|
||||
Variant.VT_FILETIME, time);
|
||||
@ -493,153 +442,134 @@ public final class SummaryInformation extends SpecialPropertySet {
|
||||
|
||||
|
||||
/**
|
||||
* <p>Remove the total time spent in editing the document.</p>
|
||||
* Remove the total time spent in editing the document.
|
||||
*/
|
||||
public void removeLastSaveDateTime()
|
||||
{
|
||||
final MutableSection s = (MutableSection) getFirstSection();
|
||||
s.removeProperty(PropertyIDMap.PID_LASTSAVE_DTM);
|
||||
public void removeLastSaveDateTime() {
|
||||
remove1stProperty(PropertyIDMap.PID_LASTSAVE_DTM);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Returns the page count or 0 if the {@link SummaryInformation} does
|
||||
* not contain a page count.</p>
|
||||
* Returns the page count or 0 if the {@link SummaryInformation} does
|
||||
* not contain a page count.
|
||||
*
|
||||
* @return The page count or 0 if the {@link SummaryInformation} does not
|
||||
* contain a page count.
|
||||
*/
|
||||
public int getPageCount()
|
||||
{
|
||||
public int getPageCount() {
|
||||
return getPropertyIntValue(PropertyIDMap.PID_PAGECOUNT);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Sets the page count.</p>
|
||||
* Sets the page count.
|
||||
*
|
||||
* @param pageCount The page count to set.
|
||||
*/
|
||||
public void setPageCount(final int pageCount)
|
||||
{
|
||||
final MutableSection s = (MutableSection) getFirstSection();
|
||||
s.setProperty(PropertyIDMap.PID_PAGECOUNT, pageCount);
|
||||
public void setPageCount(final int pageCount) {
|
||||
set1stProperty(PropertyIDMap.PID_PAGECOUNT, pageCount);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Removes the page count.</p>
|
||||
* Removes the page count.
|
||||
*/
|
||||
public void removePageCount()
|
||||
{
|
||||
final MutableSection s = (MutableSection) getFirstSection();
|
||||
s.removeProperty(PropertyIDMap.PID_PAGECOUNT);
|
||||
public void removePageCount() {
|
||||
remove1stProperty(PropertyIDMap.PID_PAGECOUNT);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Returns the word count or 0 if the {@link SummaryInformation} does
|
||||
* not contain a word count.</p>
|
||||
* Returns the word count or 0 if the {@link SummaryInformation} does
|
||||
* not contain a word count.
|
||||
*
|
||||
* @return The word count or <code>null</code>
|
||||
* @return The word count or {@code null}
|
||||
*/
|
||||
public int getWordCount()
|
||||
{
|
||||
public int getWordCount() {
|
||||
return getPropertyIntValue(PropertyIDMap.PID_WORDCOUNT);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Sets the word count.</p>
|
||||
* Sets the word count.
|
||||
*
|
||||
* @param wordCount The word count to set.
|
||||
*/
|
||||
public void setWordCount(final int wordCount)
|
||||
{
|
||||
final MutableSection s = (MutableSection) getFirstSection();
|
||||
s.setProperty(PropertyIDMap.PID_WORDCOUNT, wordCount);
|
||||
public void setWordCount(final int wordCount) {
|
||||
set1stProperty(PropertyIDMap.PID_WORDCOUNT, wordCount);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Removes the word count.</p>
|
||||
* Removes the word count.
|
||||
*/
|
||||
public void removeWordCount()
|
||||
{
|
||||
final MutableSection s = (MutableSection) getFirstSection();
|
||||
s.removeProperty(PropertyIDMap.PID_WORDCOUNT);
|
||||
public void removeWordCount() {
|
||||
remove1stProperty(PropertyIDMap.PID_WORDCOUNT);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Returns the character count or 0 if the {@link SummaryInformation}
|
||||
* does not contain a char count.</p>
|
||||
* Returns the character count or 0 if the {@link SummaryInformation}
|
||||
* does not contain a char count.
|
||||
*
|
||||
* @return The character count or <code>null</code>
|
||||
* @return The character count or {@code null}
|
||||
*/
|
||||
public int getCharCount()
|
||||
{
|
||||
public int getCharCount() {
|
||||
return getPropertyIntValue(PropertyIDMap.PID_CHARCOUNT);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Sets the character count.</p>
|
||||
* Sets the character count.
|
||||
*
|
||||
* @param charCount The character count to set.
|
||||
*/
|
||||
public void setCharCount(final int charCount)
|
||||
{
|
||||
final MutableSection s = (MutableSection) getFirstSection();
|
||||
s.setProperty(PropertyIDMap.PID_CHARCOUNT, charCount);
|
||||
public void setCharCount(final int charCount) {
|
||||
set1stProperty(PropertyIDMap.PID_CHARCOUNT, charCount);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Removes the character count.</p>
|
||||
* Removes the character count.
|
||||
*/
|
||||
public void removeCharCount()
|
||||
{
|
||||
final MutableSection s = (MutableSection) getFirstSection();
|
||||
s.removeProperty(PropertyIDMap.PID_CHARCOUNT);
|
||||
public void removeCharCount() {
|
||||
remove1stProperty(PropertyIDMap.PID_CHARCOUNT);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Returns the thumbnail (or <code>null</code>) <strong>when this
|
||||
* Returns the thumbnail (or {@code null}) <strong>when this
|
||||
* method is implemented. Please note that the return type is likely to
|
||||
* change!</strong></p>
|
||||
* change!</strong><p>
|
||||
*
|
||||
* <p>To process this data, you may wish to make use of the
|
||||
* To process this data, you may wish to make use of the
|
||||
* {@link Thumbnail} class. The raw data is generally
|
||||
* an image in WMF or Clipboard (BMP?) format</p>
|
||||
* an image in WMF or Clipboard (BMP?) format
|
||||
*
|
||||
* @return The thumbnail or <code>null</code>
|
||||
* @return The thumbnail or {@code null}
|
||||
*/
|
||||
public byte[] getThumbnail()
|
||||
{
|
||||
public byte[] getThumbnail() {
|
||||
return (byte[]) getProperty(PropertyIDMap.PID_THUMBNAIL);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Returns the thumbnail (or <code>null</code>), processed
|
||||
* Returns the thumbnail (or {@code null}), processed
|
||||
* as an object which is (largely) able to unpack the thumbnail
|
||||
* image data.</p>
|
||||
* image data.
|
||||
*
|
||||
* @return The thumbnail or <code>null</code>
|
||||
* @return The thumbnail or {@code null}
|
||||
*/
|
||||
public Thumbnail getThumbnailThumbnail()
|
||||
{
|
||||
public Thumbnail getThumbnailThumbnail() {
|
||||
byte[] data = getThumbnail();
|
||||
if (data == null) return null;
|
||||
return new Thumbnail(data);
|
||||
@ -648,115 +578,100 @@ public final class SummaryInformation extends SpecialPropertySet {
|
||||
|
||||
|
||||
/**
|
||||
* <p>Sets the thumbnail.</p>
|
||||
* Sets the thumbnail.
|
||||
*
|
||||
* @param thumbnail The thumbnail to set.
|
||||
*/
|
||||
public void setThumbnail(final byte[] thumbnail)
|
||||
{
|
||||
final MutableSection s = (MutableSection) getFirstSection();
|
||||
s.setProperty(PropertyIDMap.PID_THUMBNAIL, /* FIXME: */
|
||||
Variant.VT_LPSTR, thumbnail);
|
||||
public void setThumbnail(final byte[] thumbnail) {
|
||||
getFirstSection().setProperty(PropertyIDMap.PID_THUMBNAIL, /* FIXME: */ Variant.VT_LPSTR, thumbnail);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Removes the thumbnail.</p>
|
||||
* Removes the thumbnail.
|
||||
*/
|
||||
public void removeThumbnail()
|
||||
{
|
||||
final MutableSection s = (MutableSection) getFirstSection();
|
||||
s.removeProperty(PropertyIDMap.PID_THUMBNAIL);
|
||||
public void removeThumbnail() {
|
||||
remove1stProperty(PropertyIDMap.PID_THUMBNAIL);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Returns the application name (or <code>null</code>).</p>
|
||||
* Returns the application name (or {@code null}).
|
||||
*
|
||||
* @return The application name or <code>null</code>
|
||||
* @return The application name or {@code null}
|
||||
*/
|
||||
public String getApplicationName()
|
||||
{
|
||||
public String getApplicationName() {
|
||||
return getPropertyStringValue(PropertyIDMap.PID_APPNAME);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Sets the application name.</p>
|
||||
* Sets the application name.
|
||||
*
|
||||
* @param applicationName The application name to set.
|
||||
*/
|
||||
public void setApplicationName(final String applicationName)
|
||||
{
|
||||
final MutableSection s = (MutableSection) getFirstSection();
|
||||
s.setProperty(PropertyIDMap.PID_APPNAME, applicationName);
|
||||
public void setApplicationName(final String applicationName) {
|
||||
set1stProperty(PropertyIDMap.PID_APPNAME, applicationName);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Removes the application name.</p>
|
||||
* Removes the application name.
|
||||
*/
|
||||
public void removeApplicationName()
|
||||
{
|
||||
final MutableSection s = (MutableSection) getFirstSection();
|
||||
s.removeProperty(PropertyIDMap.PID_APPNAME);
|
||||
public void removeApplicationName() {
|
||||
remove1stProperty(PropertyIDMap.PID_APPNAME);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Returns a security code which is one of the following values:</p>
|
||||
* Returns a security code which is one of the following values:
|
||||
*
|
||||
* <ul>
|
||||
*
|
||||
* <li><p>0 if the {@link SummaryInformation} does not contain a
|
||||
* <li>0 if the {@link SummaryInformation} does not contain a
|
||||
* security field or if there is no security on the document. Use
|
||||
* {@link PropertySet#wasNull()} to distinguish between the two
|
||||
* cases!</p></li>
|
||||
* cases!
|
||||
*
|
||||
* <li><p>1 if the document is password protected</p></li>
|
||||
* <li>1 if the document is password protected
|
||||
*
|
||||
* <li><p>2 if the document is read-only recommended</p></li>
|
||||
* <li>2 if the document is read-only recommended
|
||||
*
|
||||
* <li><p>4 if the document is read-only enforced</p></li>
|
||||
* <li>4 if the document is read-only enforced
|
||||
*
|
||||
* <li><p>8 if the document is locked for annotations</p></li>
|
||||
* <li>8 if the document is locked for annotations
|
||||
*
|
||||
* </ul>
|
||||
*
|
||||
* @return The security code or <code>null</code>
|
||||
* @return The security code or {@code null}
|
||||
*/
|
||||
public int getSecurity()
|
||||
{
|
||||
public int getSecurity() {
|
||||
return getPropertyIntValue(PropertyIDMap.PID_SECURITY);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Sets the security code.</p>
|
||||
* Sets the security code.
|
||||
*
|
||||
* @param security The security code to set.
|
||||
*/
|
||||
public void setSecurity(final int security)
|
||||
{
|
||||
final MutableSection s = (MutableSection) getFirstSection();
|
||||
s.setProperty(PropertyIDMap.PID_SECURITY, security);
|
||||
public void setSecurity(final int security) {
|
||||
set1stProperty(PropertyIDMap.PID_SECURITY, security);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Removes the security code.</p>
|
||||
* Removes the security code.
|
||||
*/
|
||||
public void removeSecurity()
|
||||
{
|
||||
final MutableSection s = (MutableSection) getFirstSection();
|
||||
s.removeProperty(PropertyIDMap.PID_SECURITY);
|
||||
public void removeSecurity() {
|
||||
remove1stProperty(PropertyIDMap.PID_SECURITY);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -20,110 +20,17 @@ package org.apache.poi.hpsf;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
|
||||
import org.apache.poi.util.Internal;
|
||||
import org.apache.poi.util.SuppressForbidden;
|
||||
|
||||
/**
|
||||
* <p>Provides various static utility methods.</p>
|
||||
*/
|
||||
@Internal
|
||||
public class Util
|
||||
{
|
||||
|
||||
/**
|
||||
* <p>Checks whether two byte arrays <var>a</var> and <var>b</var>
|
||||
* are equal. They are equal</p>
|
||||
*
|
||||
* <ul>
|
||||
*
|
||||
* <li><p>if they have the same length and</p></li>
|
||||
*
|
||||
* <li><p>if for each <var>i</var> with
|
||||
* <var>i</var> >= 0 and
|
||||
* <var>i</var> < <var>a.length</var> holds
|
||||
* <var>a</var>[<var>i</var>] == <var>b</var>[<var>i</var>].</p></li>
|
||||
*
|
||||
* </ul>
|
||||
*
|
||||
* @param a The first byte array
|
||||
* @param b The first byte array
|
||||
* @return <code>true</code> if the byte arrays are equal, else
|
||||
* <code>false</code>
|
||||
*/
|
||||
public static boolean equal(final byte[] a, final byte[] b)
|
||||
{
|
||||
if (a.length != b.length)
|
||||
return false;
|
||||
for (int i = 0; i < a.length; i++)
|
||||
if (a[i] != b[i])
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Copies a part of a byte array into another byte array.</p>
|
||||
*
|
||||
* @param src The source byte array.
|
||||
* @param srcOffset Offset in the source byte array.
|
||||
* @param length The number of bytes to copy.
|
||||
* @param dst The destination byte array.
|
||||
* @param dstOffset Offset in the destination byte array.
|
||||
*/
|
||||
public static void copy(final byte[] src, final int srcOffset,
|
||||
final int length, final byte[] dst,
|
||||
final int dstOffset)
|
||||
{
|
||||
for (int i = 0; i < length; i++)
|
||||
dst[dstOffset + i] = src[srcOffset + i];
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Concatenates the contents of several byte arrays into a
|
||||
* single one.</p>
|
||||
*
|
||||
* @param byteArrays The byte arrays to be concatened.
|
||||
* @return A new byte array containing the concatenated byte
|
||||
* arrays.
|
||||
*/
|
||||
public static byte[] cat(final byte[][] byteArrays)
|
||||
{
|
||||
int capacity = 0;
|
||||
for (int i = 0; i < byteArrays.length; i++)
|
||||
capacity += byteArrays[i].length;
|
||||
final byte[] result = new byte[capacity];
|
||||
int r = 0;
|
||||
for (int i = 0; i < byteArrays.length; i++)
|
||||
for (int j = 0; j < byteArrays[i].length; j++)
|
||||
result[r++] = byteArrays[i][j];
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Copies bytes from a source byte array into a new byte
|
||||
* array.</p>
|
||||
*
|
||||
* @param src Copy from this byte array.
|
||||
* @param offset Start copying here.
|
||||
* @param length Copy this many bytes.
|
||||
* @return The new byte array. Its length is number of copied bytes.
|
||||
*/
|
||||
public static byte[] copy(final byte[] src, final int offset,
|
||||
final int length)
|
||||
{
|
||||
final byte[] result = new byte[length];
|
||||
copy(src, offset, length, result, 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>The difference between the Windows epoch (1601-01-01
|
||||
* 00:00:00) and the Unix epoch (1970-01-01 00:00:00) in
|
||||
@ -189,36 +96,6 @@ public class Util
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* <p>Checks whether two collections are equal. Two collections
|
||||
* C<sub>1</sub> and C<sub>2</sub> are equal, if the following conditions
|
||||
* are true:</p>
|
||||
*
|
||||
* <ul>
|
||||
*
|
||||
* <li><p>For each c<sub>1<em>i</em></sub> (element of C<sub>1</sub>) there
|
||||
* is a c<sub>2<em>j</em></sub> (element of C<sub>2</sub>), and
|
||||
* c<sub>1<em>i</em></sub> equals c<sub>2<em>j</em></sub>.</p></li>
|
||||
*
|
||||
* <li><p>For each c<sub>2<em>i</em></sub> (element of C<sub>2</sub>) there
|
||||
* is a c<sub>1<em>j</em></sub> (element of C<sub>1</sub>) and
|
||||
* c<sub>2<em>i</em></sub> equals c<sub>1<em>j</em></sub>.</p></li>
|
||||
*
|
||||
* </ul>
|
||||
*
|
||||
* @param c1 the first collection
|
||||
* @param c2 the second collection
|
||||
* @return <code>true</code> if the collections are equal, else
|
||||
* <code>false</code>.
|
||||
*/
|
||||
public static boolean equals(Collection<?> c1, Collection<?> c2)
|
||||
{
|
||||
Object[] o1 = c1.toArray();
|
||||
Object[] o2 = c2.toArray();
|
||||
return internalEquals(o1, o2);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Compares to object arrays with regarding the objects' order. For
|
||||
@ -231,24 +108,17 @@ public class Util
|
||||
*/
|
||||
public static boolean equals(Object[] c1, Object[] c2)
|
||||
{
|
||||
final Object[] o1 = c1.clone();
|
||||
final Object[] o2 = c2.clone();
|
||||
return internalEquals(o1, o2);
|
||||
}
|
||||
|
||||
private static boolean internalEquals(Object[] o1, Object[] o2)
|
||||
{
|
||||
for (int i1 = 0; i1 < o1.length; i1++)
|
||||
for (int i1 = 0; i1 < c1.length; i1++)
|
||||
{
|
||||
final Object obj1 = o1[i1];
|
||||
final Object obj1 = c1[i1];
|
||||
boolean matchFound = false;
|
||||
for (int i2 = 0; !matchFound && i2 < o1.length; i2++)
|
||||
for (int i2 = 0; !matchFound && i2 < c1.length; i2++)
|
||||
{
|
||||
final Object obj2 = o2[i2];
|
||||
final Object obj2 = c2[i2];
|
||||
if (obj1.equals(obj2))
|
||||
{
|
||||
matchFound = true;
|
||||
o2[i2] = null;
|
||||
c2[i2] = null;
|
||||
}
|
||||
}
|
||||
if (!matchFound)
|
||||
@ -257,8 +127,6 @@ public class Util
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Pads a byte array with 0x00 bytes so that its length is a multiple of
|
||||
* 4.</p>
|
||||
@ -283,46 +151,6 @@ public class Util
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Pads a character array with 0x0000 characters so that its length is a
|
||||
* multiple of 4.</p>
|
||||
*
|
||||
* @param ca The character array to pad.
|
||||
* @return The padded character array.
|
||||
*/
|
||||
public static char[] pad4(final char[] ca)
|
||||
{
|
||||
final int PAD = 4;
|
||||
final char[] result;
|
||||
int l = ca.length % PAD;
|
||||
if (l == 0)
|
||||
result = ca;
|
||||
else
|
||||
{
|
||||
l = PAD - l;
|
||||
result = new char[ca.length + l];
|
||||
System.arraycopy(ca, 0, result, 0, ca.length);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Pads a string with 0x0000 characters so that its length is a
|
||||
* multiple of 4.</p>
|
||||
*
|
||||
* @param s The string to pad.
|
||||
* @return The padded string as a character array.
|
||||
*/
|
||||
public static char[] pad4(final String s)
|
||||
{
|
||||
return pad4(s.toCharArray());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Returns a textual representation of a {@link Throwable}, including a
|
||||
* stacktrace.</p>
|
||||
|
@ -27,70 +27,77 @@ import java.util.List;
|
||||
import org.apache.poi.util.CodePageUtil;
|
||||
import org.apache.poi.util.POILogFactory;
|
||||
import org.apache.poi.util.POILogger;
|
||||
import org.apache.poi.util.Removal;
|
||||
|
||||
/**
|
||||
* <p>Supports reading and writing of variant data.</p>
|
||||
* Supports reading and writing of variant data.<p>
|
||||
*
|
||||
* <p><strong>FIXME (3):</strong> Reading and writing should be made more
|
||||
* uniform than it is now. The following items should be resolved:
|
||||
* <strong>FIXME (3):</strong> Reading and writing should be made more
|
||||
* uniform than it is now. The following items should be resolved:<p>
|
||||
*
|
||||
* <ul>
|
||||
*
|
||||
* <li><p>Reading requires a length parameter that is 4 byte greater than the
|
||||
* actual data, because the variant type field is included. </p></li>
|
||||
* <li>Reading requires a length parameter that is 4 byte greater than the
|
||||
* actual data, because the variant type field is included.
|
||||
*
|
||||
* <li><p>Reading reads from a byte array while writing writes to an byte array
|
||||
* output stream.</p></li>
|
||||
* <li>Reading reads from a byte array while writing writes to an byte array
|
||||
* output stream.
|
||||
*
|
||||
* </ul>
|
||||
*/
|
||||
public class VariantSupport extends Variant
|
||||
{
|
||||
private static final POILogger logger = POILogFactory.getLogger(VariantSupport.class);
|
||||
public class VariantSupport extends Variant {
|
||||
/**
|
||||
* HPSF is able to read these {@link Variant} types.
|
||||
*/
|
||||
public static final int[] SUPPORTED_TYPES = { Variant.VT_EMPTY,
|
||||
Variant.VT_I2, Variant.VT_I4, Variant.VT_I8, Variant.VT_R8,
|
||||
Variant.VT_FILETIME, Variant.VT_LPSTR, Variant.VT_LPWSTR,
|
||||
Variant.VT_CF, Variant.VT_BOOL };
|
||||
|
||||
|
||||
private static final POILogger logger = POILogFactory.getLogger(VariantSupport.class);
|
||||
private static boolean logUnsupportedTypes = false;
|
||||
|
||||
/**
|
||||
* <p>Specifies whether warnings about unsupported variant types are to be
|
||||
* written to <code>System.err</code> or not.</p>
|
||||
*
|
||||
* @param logUnsupportedTypes If <code>true</code> warnings will be written,
|
||||
* if <code>false</code> they won't.
|
||||
* Keeps a list of the variant types an "unsupported" message has already
|
||||
* been issued for.
|
||||
*/
|
||||
public static void setLogUnsupportedTypes(final boolean logUnsupportedTypes)
|
||||
{
|
||||
protected static List<Long> unsupportedMessage;
|
||||
|
||||
|
||||
/**
|
||||
* Specifies whether warnings about unsupported variant types are to be
|
||||
* written to {@code System.err} or not.
|
||||
*
|
||||
* @param logUnsupportedTypes If {@code true} warnings will be written,
|
||||
* if {@code false} they won't.
|
||||
*/
|
||||
public static void setLogUnsupportedTypes(final boolean logUnsupportedTypes) {
|
||||
VariantSupport.logUnsupportedTypes = logUnsupportedTypes;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Checks whether logging of unsupported variant types warning is turned
|
||||
* on or off.</p>
|
||||
* Checks whether logging of unsupported variant types warning is turned
|
||||
* on or off.
|
||||
*
|
||||
* @return <code>true</code> if logging is turned on, else
|
||||
* <code>false</code>.
|
||||
* @return {@code true} if logging is turned on, else
|
||||
* {@code false}.
|
||||
*/
|
||||
public static boolean isLogUnsupportedTypes()
|
||||
{
|
||||
public static boolean isLogUnsupportedTypes() {
|
||||
return logUnsupportedTypes;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Keeps a list of the variant types an "unsupported" message has already
|
||||
* been issued for.</p>
|
||||
*/
|
||||
protected static List<Long> unsupportedMessage;
|
||||
|
||||
/**
|
||||
* <p>Writes a warning to <code>System.err</code> that a variant type is
|
||||
* Writes a warning to {@code System.err} that a variant type is
|
||||
* unsupported by HPSF. Such a warning is written only once for each variant
|
||||
* type. Log messages can be turned on or off by </p>
|
||||
* type. Log messages can be turned on or off by
|
||||
*
|
||||
* @param ex The exception to log
|
||||
*/
|
||||
protected static void writeUnsupportedTypeMessage
|
||||
(final UnsupportedVariantTypeException ex)
|
||||
{
|
||||
(final UnsupportedVariantTypeException ex) {
|
||||
if (isLogUnsupportedTypes())
|
||||
{
|
||||
if (unsupportedMessage == null)
|
||||
@ -105,28 +112,18 @@ public class VariantSupport extends Variant
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* <p>HPSF is able to read these {@link Variant} types.</p>
|
||||
*/
|
||||
final static public int[] SUPPORTED_TYPES = { Variant.VT_EMPTY,
|
||||
Variant.VT_I2, Variant.VT_I4, Variant.VT_I8, Variant.VT_R8,
|
||||
Variant.VT_FILETIME, Variant.VT_LPSTR, Variant.VT_LPWSTR,
|
||||
Variant.VT_CF, Variant.VT_BOOL };
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Checks whether HPSF supports the specified variant type. Unsupported
|
||||
* Checks whether HPSF supports the specified variant type. Unsupported
|
||||
* types should be implemented included in the {@link #SUPPORTED_TYPES}
|
||||
* array.</p>
|
||||
* array.
|
||||
*
|
||||
* @see Variant
|
||||
* @param variantType the variant type to check
|
||||
* @return <code>true</code> if HPFS supports this type, else
|
||||
* <code>false</code>
|
||||
* @return {@code true} if HPFS supports this type, else
|
||||
* {@code false}
|
||||
*/
|
||||
public boolean isSupportedType(final int variantType)
|
||||
{
|
||||
public boolean isSupportedType(final int variantType) {
|
||||
for (int i = 0; i < SUPPORTED_TYPES.length; i++)
|
||||
if (variantType == SUPPORTED_TYPES[i])
|
||||
return true;
|
||||
@ -136,7 +133,7 @@ public class VariantSupport extends Variant
|
||||
|
||||
|
||||
/**
|
||||
* <p>Reads a variant type from a byte array.</p>
|
||||
* Reads a variant type from a byte array.
|
||||
*
|
||||
* @param src The byte array
|
||||
* @param offset The offset in the byte array where the variant starts
|
||||
@ -154,66 +151,50 @@ public class VariantSupport extends Variant
|
||||
*/
|
||||
public static Object read( final byte[] src, final int offset,
|
||||
final int length, final long type, final int codepage )
|
||||
throws ReadingNotSupportedException, UnsupportedEncodingException
|
||||
{
|
||||
TypedPropertyValue typedPropertyValue = new TypedPropertyValue(
|
||||
(int) type, null );
|
||||
throws ReadingNotSupportedException, UnsupportedEncodingException {
|
||||
TypedPropertyValue typedPropertyValue = new TypedPropertyValue( (int) type, null );
|
||||
int unpadded;
|
||||
try
|
||||
{
|
||||
try {
|
||||
unpadded = typedPropertyValue.readValue( src, offset );
|
||||
}
|
||||
catch ( UnsupportedOperationException exc )
|
||||
{
|
||||
} catch ( UnsupportedOperationException exc ) {
|
||||
int propLength = Math.min( length, src.length - offset );
|
||||
final byte[] v = new byte[propLength];
|
||||
System.arraycopy( src, offset, v, 0, propLength );
|
||||
throw new ReadingNotSupportedException( type, v );
|
||||
}
|
||||
|
||||
switch ( (int) type )
|
||||
{
|
||||
case Variant.VT_EMPTY:
|
||||
case Variant.VT_I4:
|
||||
case Variant.VT_I8:
|
||||
case Variant.VT_R8:
|
||||
switch ( (int) type ) {
|
||||
/*
|
||||
* we have more property types that can be converted into Java
|
||||
* objects, but current API need to be preserved, and it returns
|
||||
* other types as byte arrays. In future major versions it shall be
|
||||
* changed -- sergey
|
||||
*/
|
||||
return typedPropertyValue.getValue();
|
||||
case Variant.VT_EMPTY:
|
||||
case Variant.VT_I4:
|
||||
case Variant.VT_I8:
|
||||
case Variant.VT_R8:
|
||||
return typedPropertyValue.getValue();
|
||||
|
||||
case Variant.VT_I2:
|
||||
{
|
||||
/*
|
||||
* also for backward-compatibility with prev. versions of POI
|
||||
* --sergey
|
||||
*/
|
||||
return Integer.valueOf( ( (Short) typedPropertyValue.getValue() )
|
||||
.intValue() );
|
||||
}
|
||||
case Variant.VT_FILETIME:
|
||||
{
|
||||
Filetime filetime = (Filetime) typedPropertyValue.getValue();
|
||||
return Util.filetimeToDate( (int) filetime.getHigh(),
|
||||
(int) filetime.getLow() );
|
||||
}
|
||||
case Variant.VT_LPSTR:
|
||||
{
|
||||
CodePageString string = (CodePageString) typedPropertyValue
|
||||
.getValue();
|
||||
return string.getJavaValue( codepage );
|
||||
}
|
||||
case Variant.VT_LPWSTR:
|
||||
{
|
||||
UnicodeString string = (UnicodeString) typedPropertyValue
|
||||
.getValue();
|
||||
return string.toJavaString();
|
||||
}
|
||||
case Variant.VT_CF:
|
||||
{
|
||||
case Variant.VT_I2:
|
||||
return ( (Short) typedPropertyValue.getValue() ).intValue();
|
||||
|
||||
case Variant.VT_FILETIME:
|
||||
Filetime filetime = (Filetime) typedPropertyValue.getValue();
|
||||
return Util.filetimeToDate( (int) filetime.getHigh(), (int) filetime.getLow() );
|
||||
|
||||
case Variant.VT_LPSTR:
|
||||
CodePageString cpString = (CodePageString) typedPropertyValue.getValue();
|
||||
return cpString.getJavaValue( codepage );
|
||||
|
||||
case Variant.VT_LPWSTR:
|
||||
UnicodeString uniString = (UnicodeString) typedPropertyValue.getValue();
|
||||
return uniString.toJavaString();
|
||||
|
||||
// if(l1 < 0) {
|
||||
/**
|
||||
* YK: reading the ClipboardData packet (VT_CF) is not quite
|
||||
@ -232,33 +213,28 @@ public class VariantSupport extends Variant
|
||||
// System.arraycopy(src, o1, v, 0, v.length);
|
||||
// value = v;
|
||||
// break;
|
||||
ClipboardData clipboardData = (ClipboardData) typedPropertyValue
|
||||
.getValue();
|
||||
return clipboardData.toByteArray();
|
||||
}
|
||||
case Variant.VT_CF:
|
||||
ClipboardData clipboardData = (ClipboardData) typedPropertyValue.getValue();
|
||||
return clipboardData.toByteArray();
|
||||
|
||||
case Variant.VT_BOOL:
|
||||
{
|
||||
VariantBool bool = (VariantBool) typedPropertyValue.getValue();
|
||||
return Boolean.valueOf( bool.getValue() );
|
||||
}
|
||||
case Variant.VT_BOOL:
|
||||
VariantBool bool = (VariantBool) typedPropertyValue.getValue();
|
||||
return bool.getValue();
|
||||
|
||||
default:
|
||||
{
|
||||
/*
|
||||
* it is not very good, but what can do without breaking current
|
||||
* API? --sergey
|
||||
*/
|
||||
final byte[] v = new byte[unpadded];
|
||||
System.arraycopy( src, offset, v, 0, unpadded );
|
||||
throw new ReadingNotSupportedException( type, v );
|
||||
}
|
||||
default:
|
||||
final byte[] v = new byte[unpadded];
|
||||
System.arraycopy( src, offset, v, 0, unpadded );
|
||||
throw new ReadingNotSupportedException( type, v );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Turns a codepage number into the equivalent character encoding's
|
||||
* name.</p>
|
||||
* Turns a codepage number into the equivalent character encoding's
|
||||
* name.
|
||||
*
|
||||
* @param codepage The codepage number
|
||||
*
|
||||
@ -269,7 +245,10 @@ public class VariantSupport extends Variant
|
||||
*
|
||||
* @exception UnsupportedEncodingException if the specified codepage is
|
||||
* less than zero.
|
||||
*
|
||||
* @deprecated POI 3.16 - use {@link CodePageUtil#codepageToEncoding(int)}
|
||||
*/
|
||||
@Removal(version="3.18")
|
||||
public static String codepageToEncoding(final int codepage)
|
||||
throws UnsupportedEncodingException
|
||||
{
|
||||
@ -278,13 +257,13 @@ public class VariantSupport extends Variant
|
||||
|
||||
|
||||
/**
|
||||
* <p>Writes a variant value to an output stream. This method ensures that
|
||||
* always a multiple of 4 bytes is written.</p>
|
||||
* Writes a variant value to an output stream. This method ensures that
|
||||
* always a multiple of 4 bytes is written.<p>
|
||||
*
|
||||
* <p>If the codepage is UTF-16, which is encouraged, strings
|
||||
* If the codepage is UTF-16, which is encouraged, strings
|
||||
* <strong>must</strong> always be written as {@link Variant#VT_LPWSTR}
|
||||
* strings, not as {@link Variant#VT_LPSTR} strings. This method ensure this
|
||||
* by converting strings appropriately, if needed.</p>
|
||||
* by converting strings appropriately, if needed.
|
||||
*
|
||||
* @param out The stream to write the value to.
|
||||
* @param type The variant's type.
|
||||
@ -298,42 +277,31 @@ public class VariantSupport extends Variant
|
||||
*/
|
||||
public static int write(final OutputStream out, final long type,
|
||||
final Object value, final int codepage)
|
||||
throws IOException, WritingNotSupportedException
|
||||
{
|
||||
throws IOException, WritingNotSupportedException {
|
||||
int length = 0;
|
||||
switch ((int) type)
|
||||
{
|
||||
switch ((int) type) {
|
||||
case Variant.VT_BOOL:
|
||||
{
|
||||
if ( ( (Boolean) value ).booleanValue() )
|
||||
{
|
||||
if ( ( (Boolean) value ).booleanValue() ) {
|
||||
out.write( 0xff );
|
||||
out.write( 0xff );
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
out.write( 0x00 );
|
||||
out.write( 0x00 );
|
||||
}
|
||||
length += 2;
|
||||
break;
|
||||
}
|
||||
|
||||
case Variant.VT_LPSTR:
|
||||
{
|
||||
CodePageString codePageString = new CodePageString( (String) value,
|
||||
codepage );
|
||||
CodePageString codePageString = new CodePageString( (String) value, codepage );
|
||||
length += codePageString.write( out );
|
||||
break;
|
||||
}
|
||||
|
||||
case Variant.VT_LPWSTR:
|
||||
{
|
||||
final int nrOfChars = ( (String) value ).length() + 1;
|
||||
length += TypeWriter.writeUIntToStream( out, nrOfChars );
|
||||
char[] s = ( (String) value ).toCharArray();
|
||||
for ( int i = 0; i < s.length; i++ )
|
||||
{
|
||||
final int high = ( ( s[i] & 0x0000ff00 ) >> 8 );
|
||||
final int low = ( s[i] & 0x000000ff );
|
||||
for ( char s : ( (String) value ).toCharArray() ) {
|
||||
final int high = ( ( s & 0x0000ff00 ) >> 8 );
|
||||
final int low = ( s & 0x000000ff );
|
||||
final byte highb = (byte) high;
|
||||
final byte lowb = (byte) low;
|
||||
out.write( lowb );
|
||||
@ -345,79 +313,63 @@ public class VariantSupport extends Variant
|
||||
out.write( 0x00 );
|
||||
length += 2;
|
||||
break;
|
||||
}
|
||||
|
||||
case Variant.VT_CF:
|
||||
{
|
||||
final byte[] b = (byte[]) value;
|
||||
out.write(b);
|
||||
length = b.length;
|
||||
final byte[] cf = (byte[]) value;
|
||||
out.write(cf);
|
||||
length = cf.length;
|
||||
break;
|
||||
}
|
||||
|
||||
case Variant.VT_EMPTY:
|
||||
{
|
||||
length += TypeWriter.writeUIntToStream( out, Variant.VT_EMPTY );
|
||||
break;
|
||||
}
|
||||
|
||||
case Variant.VT_I2:
|
||||
{
|
||||
length += TypeWriter.writeToStream( out,
|
||||
( (Integer) value ).shortValue() );
|
||||
length += TypeWriter.writeToStream( out, ( (Integer) value ).shortValue() );
|
||||
break;
|
||||
}
|
||||
|
||||
case Variant.VT_I4:
|
||||
{
|
||||
if (!(value instanceof Integer))
|
||||
{
|
||||
if (!(value instanceof Integer)) {
|
||||
throw new ClassCastException("Could not cast an object to "
|
||||
+ Integer.class.toString() + ": "
|
||||
+ value.getClass().toString() + ", "
|
||||
+ value.toString());
|
||||
}
|
||||
length += TypeWriter.writeToStream(out,
|
||||
((Integer) value).intValue());
|
||||
length += TypeWriter.writeToStream(out, ((Integer) value).intValue());
|
||||
break;
|
||||
}
|
||||
|
||||
case Variant.VT_I8:
|
||||
{
|
||||
length += TypeWriter.writeToStream(out, ((Long) value).longValue());
|
||||
break;
|
||||
}
|
||||
|
||||
case Variant.VT_R8:
|
||||
{
|
||||
length += TypeWriter.writeToStream(out,
|
||||
((Double) value).doubleValue());
|
||||
length += TypeWriter.writeToStream(out, ((Double) value).doubleValue());
|
||||
break;
|
||||
}
|
||||
|
||||
case Variant.VT_FILETIME:
|
||||
{
|
||||
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 );
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
/* The variant type is not supported yet. However, if the value
|
||||
* is a byte array we can write it nevertheless. */
|
||||
if (value instanceof byte[])
|
||||
{
|
||||
if (value instanceof byte[]) {
|
||||
final byte[] b = (byte[]) value;
|
||||
out.write(b);
|
||||
length = b.length;
|
||||
writeUnsupportedTypeMessage
|
||||
(new WritingNotSupportedException(type, value));
|
||||
}
|
||||
else
|
||||
writeUnsupportedTypeMessage(new WritingNotSupportedException(type, value));
|
||||
} else {
|
||||
throw new WritingNotSupportedException(type, value);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* pad values to 4-bytes */
|
||||
while ( ( length & 0x3 ) != 0 )
|
||||
{
|
||||
while ( ( length & 0x3 ) != 0 ) {
|
||||
out.write( 0x00 );
|
||||
length++;
|
||||
}
|
||||
|
@ -27,6 +27,7 @@ import org.apache.poi.hpsf.CustomProperties;
|
||||
import org.apache.poi.hpsf.DocumentSummaryInformation;
|
||||
import org.apache.poi.hpsf.HPSFPropertiesOnlyDocument;
|
||||
import org.apache.poi.hpsf.Property;
|
||||
import org.apache.poi.hpsf.PropertySet;
|
||||
import org.apache.poi.hpsf.SpecialPropertySet;
|
||||
import org.apache.poi.hpsf.SummaryInformation;
|
||||
import org.apache.poi.hpsf.wellknown.PropertyIDMap;
|
||||
@ -67,7 +68,7 @@ public class HPSFPropertiesExtractor extends POIOLE2TextExtractor {
|
||||
CustomProperties cps = dsi == null ? null : dsi.getCustomProperties();
|
||||
if (cps != null) {
|
||||
for (String key : cps.nameSet()) {
|
||||
String val = HelperPropertySet.getPropertyValueText(cps.get(key));
|
||||
String val = getPropertyValueText(cps.get(key));
|
||||
text.append(key).append(" = ").append(val).append("\n");
|
||||
}
|
||||
}
|
||||
@ -86,7 +87,7 @@ public class HPSFPropertiesExtractor extends POIOLE2TextExtractor {
|
||||
return getPropertiesText(si);
|
||||
}
|
||||
|
||||
private static String getPropertiesText(SpecialPropertySet ps) {
|
||||
private static String getPropertiesText(PropertySet ps) {
|
||||
if (ps == null) {
|
||||
// Not defined, oh well
|
||||
return "";
|
||||
@ -98,12 +99,12 @@ public class HPSFPropertiesExtractor extends POIOLE2TextExtractor {
|
||||
Property[] props = ps.getProperties();
|
||||
for (Property prop : props) {
|
||||
String type = Long.toString(prop.getID());
|
||||
Object typeObj = idMap.get(prop.getID());
|
||||
Object typeObj = (idMap == null) ? null : idMap.get(prop.getID());
|
||||
if (typeObj != null) {
|
||||
type = typeObj.toString();
|
||||
}
|
||||
|
||||
String val = HelperPropertySet.getPropertyValueText(prop.getValue());
|
||||
String val = getPropertyValueText(prop.getValue());
|
||||
text.append(type).append(" = ").append(val).append("\n");
|
||||
}
|
||||
|
||||
@ -125,16 +126,10 @@ public class HPSFPropertiesExtractor extends POIOLE2TextExtractor {
|
||||
throw new IllegalStateException("You already have the Metadata Text Extractor, not recursing!");
|
||||
}
|
||||
|
||||
private static abstract class HelperPropertySet extends SpecialPropertySet {
|
||||
public HelperPropertySet() {
|
||||
super(null);
|
||||
}
|
||||
public static String getPropertyValueText(Object val) {
|
||||
if (val == null) {
|
||||
return "(not set)";
|
||||
}
|
||||
return SpecialPropertySet.getPropertyStringValue(val);
|
||||
}
|
||||
private static String getPropertyValueText(Object val) {
|
||||
return (val == null)
|
||||
? "(not set)"
|
||||
: PropertySet.getPropertyStringValue(val);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -45,26 +45,42 @@ public final class IOUtils {
|
||||
* @throws EmptyFileException if the stream is empty
|
||||
*/
|
||||
public static byte[] peekFirst8Bytes(InputStream stream) throws IOException, EmptyFileException {
|
||||
// We want to peek at the first 8 bytes
|
||||
stream.mark(8);
|
||||
return peekFirstNBytes(stream, 8);
|
||||
}
|
||||
|
||||
byte[] header = new byte[8];
|
||||
int read = IOUtils.readFully(stream, header);
|
||||
/**
|
||||
* Peeks at the first N bytes of the stream. Returns those bytes, but
|
||||
* with the stream unaffected. Requires a stream that supports mark/reset,
|
||||
* or a PushbackInputStream. If the stream has >0 but <N bytes,
|
||||
* remaining bytes will be zero.
|
||||
* @throws EmptyFileException if the stream is empty
|
||||
*/
|
||||
public static byte[] peekFirstNBytes(InputStream stream, int limit) throws IOException, EmptyFileException {
|
||||
stream.mark(limit);
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream(limit);
|
||||
copy(new BoundedInputStream(stream, limit), bos);
|
||||
|
||||
if (read < 1)
|
||||
int readBytes = bos.size();
|
||||
if (readBytes == 0) {
|
||||
throw new EmptyFileException();
|
||||
}
|
||||
|
||||
// Wind back those 8 bytes
|
||||
if (readBytes < limit) {
|
||||
bos.write(new byte[limit-readBytes]);
|
||||
}
|
||||
byte peekedBytes[] = bos.toByteArray();
|
||||
if(stream instanceof PushbackInputStream) {
|
||||
PushbackInputStream pin = (PushbackInputStream)stream;
|
||||
pin.unread(header, 0, read);
|
||||
pin.unread(peekedBytes, 0, readBytes);
|
||||
} else {
|
||||
stream.reset();
|
||||
}
|
||||
|
||||
return header;
|
||||
return peekedBytes;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Reads all the data from the input stream, and returns the bytes read.
|
||||
*/
|
||||
|
@ -192,8 +192,7 @@ public final class TestBasic {
|
||||
(poiFiles[0].getBytes()));
|
||||
final List<Section> sections = si.getSections();
|
||||
final Section s = sections.get(0);
|
||||
assertTrue(org.apache.poi.hpsf.Util.equal
|
||||
(s.getFormatID().getBytes(), SectionIDMap.SUMMARY_INFORMATION_ID));
|
||||
assertArrayEquals(s.getFormatID().getBytes(), SectionIDMap.SUMMARY_INFORMATION_ID);
|
||||
assertNotNull(s.getProperties());
|
||||
assertEquals(17, s.getPropertyCount());
|
||||
assertEquals("Titel", s.getProperty(2));
|
||||
|
@ -216,7 +216,8 @@ public class TestWrite
|
||||
final MutablePropertySet ps = new MutablePropertySet();
|
||||
final MutableSection si = new MutableSection();
|
||||
si.setFormatID(SectionIDMap.SUMMARY_INFORMATION_ID);
|
||||
ps.getSections().set(0, si);
|
||||
ps.clearSections();
|
||||
ps.addSection(si);
|
||||
|
||||
final MutableProperty p = new MutableProperty();
|
||||
p.setID(PropertyIDMap.PID_AUTHOR);
|
||||
|
Loading…
Reference in New Issue
Block a user