#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.Date;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.apache.commons.collections4.bidimap.TreeBidiMap;
|
||||||
import org.apache.poi.hpsf.wellknown.PropertyIDMap;
|
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
|
* {@link DocumentSummaryInformation}. The class maintains the names of the
|
||||||
* custom properties in a dictionary. It implements the {@link Map} interface
|
* custom properties in a dictionary. It implements the {@link Map} interface
|
||||||
* and by this provides a simplified view on custom properties: A property's
|
* 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
|
* 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
|
* 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
|
* 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
|
* 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
|
* 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
|
* 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
|
* 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.
|
* 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
|
* This class is not thread-safe; concurrent access to instances of this
|
||||||
* class must be synchronized.</p>
|
* class must be synchronized.<p>
|
||||||
*
|
*
|
||||||
* <p>While this class is roughly HashMap<Long,CustomProperty>, that's the
|
* While this class is roughly HashMap<Long,CustomProperty>, that's the
|
||||||
* internal representation. To external calls, it should appear as
|
* internal representation. To external calls, it should appear as
|
||||||
* HashMap<String,Object> mapping between Names and Custom Property Values.</p>
|
* HashMap<String,Object> mapping between Names and Custom Property Values.
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("serial")
|
@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>
|
* Tells whether this object is pure or not.
|
||||||
*/
|
|
||||||
private final Map<String,Long> dictionaryNameToID = new HashMap<String,Long>();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <p>Tells whether this object is pure or not.</p>
|
|
||||||
*/
|
*/
|
||||||
private boolean isPure = true;
|
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 CustomProperty} already has a valid ID. Otherwise use
|
||||||
* {@link #put(CustomProperty)}.</p>
|
* {@link #put(CustomProperty)}.
|
||||||
*
|
*
|
||||||
* @param name the property name
|
* @param name the property name
|
||||||
* @param cp the property
|
* @param cp the property
|
||||||
*
|
*
|
||||||
* @return the previous property stored under this name
|
* @return the previous property stored under this name
|
||||||
*/
|
*/
|
||||||
public CustomProperty put(final String name, final CustomProperty cp)
|
public CustomProperty put(final String name, final CustomProperty cp) {
|
||||||
{
|
if (name == null) {
|
||||||
if (name == null)
|
|
||||||
{
|
|
||||||
/* Ignoring a property without a name. */
|
/* Ignoring a property without a name. */
|
||||||
isPure = false;
|
isPure = false;
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (!(name.equals(cp.getName())))
|
|
||||||
|
if (!name.equals(cp.getName())) {
|
||||||
throw new IllegalArgumentException("Parameter \"name\" (" + name +
|
throw new IllegalArgumentException("Parameter \"name\" (" + name +
|
||||||
") and custom property's name (" + cp.getName() +
|
") and custom property's name (" + cp.getName() +
|
||||||
") do not match.");
|
") do not match.");
|
||||||
|
}
|
||||||
|
|
||||||
/* Register name and ID in the dictionary. Mapping in both directions is possible. If there is already a */
|
/* Register name and ID in the dictionary. Mapping in both directions is possible. If there is already a */
|
||||||
final Long idKey = Long.valueOf(cp.getID());
|
super.remove(dictionary.getKey(name));
|
||||||
final Long oldID = dictionaryNameToID.get(name);
|
dictionary.put(cp.getID(), name);
|
||||||
dictionaryIDToName.remove(oldID);
|
|
||||||
dictionaryNameToID.put(name, idKey);
|
|
||||||
dictionaryIDToName.put(idKey, name);
|
|
||||||
|
|
||||||
/* Put the custom property into this map. */
|
/* Put the custom property into this map. */
|
||||||
final CustomProperty oldCp = super.remove(oldID);
|
return super.put(cp.getID(), cp);
|
||||||
super.put(idKey, cp);
|
|
||||||
return oldCp;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Puts a {@link CustomProperty} that has not yet a valid ID into this
|
* 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>
|
* map. The method will allocate a suitable ID for the custom property:
|
||||||
*
|
*
|
||||||
* <ul>
|
* <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
|
* <li>Otherwise find the highest ID and use its value plus one.
|
||||||
* of that property.</p></li>
|
|
||||||
*
|
|
||||||
* <li><p>Otherwise find the highest ID and use its value plus one.</p></li>
|
|
||||||
*
|
|
||||||
* </ul>
|
* </ul>
|
||||||
*
|
*
|
||||||
* @param customProperty
|
* @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
|
* @throws ClassCastException
|
||||||
*/
|
*/
|
||||||
private Object put(final CustomProperty customProperty) throws ClassCastException
|
private Object put(final CustomProperty customProperty) throws ClassCastException {
|
||||||
{
|
|
||||||
final String name = customProperty.getName();
|
final String name = customProperty.getName();
|
||||||
|
|
||||||
/* Check whether a property with this name is in the map already. */
|
/* Check whether a property with this name is in the map already. */
|
||||||
final Long oldId = dictionaryNameToID.get(name);
|
final Long oldId = (name == null) ? null : dictionary.getKey(name);
|
||||||
if (oldId != null)
|
if (oldId != null) {
|
||||||
customProperty.setID(oldId.longValue());
|
customProperty.setID(oldId);
|
||||||
else
|
} else {
|
||||||
{
|
long lastKey = (dictionary.isEmpty()) ? 0 : dictionary.lastKey();
|
||||||
long max = 1;
|
customProperty.setID(Math.max(lastKey,PropertyIDMap.PID_MAX) + 1);
|
||||||
for (Long long1 : dictionaryIDToName.keySet()) {
|
|
||||||
final long id = long1.longValue();
|
|
||||||
if (id > max)
|
|
||||||
max = id;
|
|
||||||
}
|
|
||||||
customProperty.setID(max + 1);
|
|
||||||
}
|
}
|
||||||
return this.put(name, customProperty);
|
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
|
* @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)
|
* @see java.util.HashSet#remove(java.lang.Object)
|
||||||
*/
|
*/
|
||||||
public Object remove(final String name)
|
public Object remove(final String name) {
|
||||||
{
|
final Long id = dictionary.removeValue(name);
|
||||||
final Long id = dictionaryNameToID.get(name);
|
|
||||||
if (id == null)
|
|
||||||
return null;
|
|
||||||
dictionaryIDToName.remove(id);
|
|
||||||
dictionaryNameToID.remove(name);
|
|
||||||
return super.remove(id);
|
return super.remove(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Adds a named string property.</p>
|
* Adds a named string property.
|
||||||
*
|
*
|
||||||
* @param name The property's name.
|
* @param name The property's name.
|
||||||
* @param value The property's value.
|
* @param value The property's value.
|
||||||
* @return the property that was stored under the specified name before, or
|
* @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)
|
public Object put(final String name, final String value) {
|
||||||
{
|
final Property p = new Property(-1, Variant.VT_LPWSTR, value);
|
||||||
final MutableProperty p = new MutableProperty();
|
return put(new CustomProperty(p, name));
|
||||||
p.setID(-1);
|
|
||||||
p.setType(Variant.VT_LPWSTR);
|
|
||||||
p.setValue(value);
|
|
||||||
final CustomProperty cp = new CustomProperty(p, name);
|
|
||||||
return put(cp);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Adds a named long property.</p>
|
* Adds a named long property.
|
||||||
*
|
*
|
||||||
* @param name The property's name.
|
* @param name The property's name.
|
||||||
* @param value The property's value.
|
* @param value The property's value.
|
||||||
* @return the property that was stored under the specified name before, or
|
* @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)
|
public Object put(final String name, final Long value) {
|
||||||
{
|
final Property p = new Property(-1, Variant.VT_I8, value);
|
||||||
final MutableProperty p = new MutableProperty();
|
return put(new CustomProperty(p, name));
|
||||||
p.setID(-1);
|
|
||||||
p.setType(Variant.VT_I8);
|
|
||||||
p.setValue(value);
|
|
||||||
final CustomProperty cp = new CustomProperty(p, name);
|
|
||||||
return put(cp);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Adds a named double property.</p>
|
* Adds a named double property.
|
||||||
*
|
*
|
||||||
* @param name The property's name.
|
* @param name The property's name.
|
||||||
* @param value The property's value.
|
* @param value The property's value.
|
||||||
* @return the property that was stored under the specified name before, or
|
* @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)
|
public Object put(final String name, final Double value) {
|
||||||
{
|
final Property p = new Property(-1, Variant.VT_R8, value);
|
||||||
final MutableProperty p = new MutableProperty();
|
return put(new CustomProperty(p, name));
|
||||||
p.setID(-1);
|
|
||||||
p.setType(Variant.VT_R8);
|
|
||||||
p.setValue(value);
|
|
||||||
final CustomProperty cp = new CustomProperty(p, name);
|
|
||||||
return put(cp);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Adds a named integer property.</p>
|
* Adds a named integer property.
|
||||||
*
|
*
|
||||||
* @param name The property's name.
|
* @param name The property's name.
|
||||||
* @param value The property's value.
|
* @param value The property's value.
|
||||||
* @return the property that was stored under the specified name before, or
|
* @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)
|
public Object put(final String name, final Integer value) {
|
||||||
{
|
final Property p = new Property(-1, Variant.VT_I4, value);
|
||||||
final MutableProperty p = new MutableProperty();
|
return put(new CustomProperty(p, name));
|
||||||
p.setID(-1);
|
|
||||||
p.setType(Variant.VT_I4);
|
|
||||||
p.setValue(value);
|
|
||||||
final CustomProperty cp = new CustomProperty(p, name);
|
|
||||||
return put(cp);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Adds a named boolean property.</p>
|
* Adds a named boolean property.
|
||||||
*
|
*
|
||||||
* @param name The property's name.
|
* @param name The property's name.
|
||||||
* @param value The property's value.
|
* @param value The property's value.
|
||||||
* @return the property that was stored under the specified name before, or
|
* @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)
|
public Object put(final String name, final Boolean value) {
|
||||||
{
|
final Property p = new Property(-1, Variant.VT_BOOL, value);
|
||||||
final MutableProperty p = new MutableProperty();
|
return put(new CustomProperty(p, name));
|
||||||
p.setID(-1);
|
|
||||||
p.setType(Variant.VT_BOOL);
|
|
||||||
p.setValue(value);
|
|
||||||
final CustomProperty cp = new CustomProperty(p, name);
|
|
||||||
return put(cp);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <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
|
* @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.
|
* name is not found in the custom properties.
|
||||||
*/
|
*/
|
||||||
public Object get(final String name)
|
public Object get(final String name) {
|
||||||
{
|
final Long id = dictionary.getKey(name);
|
||||||
final Long id = dictionaryNameToID.get(name);
|
|
||||||
final CustomProperty cp = super.get(id);
|
final CustomProperty cp = super.get(id);
|
||||||
return cp != null ? cp.getValue() : null;
|
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 name The property's name.
|
||||||
* @param value The property's value.
|
* @param value The property's value.
|
||||||
* @return the property that was stored under the specified name before, or
|
* @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)
|
public Object put(final String name, final Date value) {
|
||||||
{
|
final Property p = new Property(-1, Variant.VT_FILETIME, value);
|
||||||
final MutableProperty p = new MutableProperty();
|
return put(new CustomProperty(p, name));
|
||||||
p.setID(-1);
|
|
||||||
p.setType(Variant.VT_FILETIME);
|
|
||||||
p.setValue(value);
|
|
||||||
final CustomProperty cp = new CustomProperty(p, name);
|
|
||||||
return put(cp);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -302,7 +246,7 @@ public class CustomProperties extends HashMap<Object,CustomProperty>
|
|||||||
@Override
|
@Override
|
||||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||||
public Set keySet() {
|
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
|
* @return a set of all the names of our custom properties
|
||||||
*/
|
*/
|
||||||
public Set<String> nameSet() {
|
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
|
* @return a set of all the IDs of our custom properties
|
||||||
*/
|
*/
|
||||||
public Set<String> idSet() {
|
public Set<String> idSet() {
|
||||||
return dictionaryNameToID.keySet();
|
return dictionary.values();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Sets the codepage.</p>
|
* Sets the codepage.
|
||||||
*
|
*
|
||||||
* @param codepage the codepage
|
* @param codepage the codepage
|
||||||
*/
|
*/
|
||||||
public void setCodepage(final int codepage)
|
public void setCodepage(final int codepage) {
|
||||||
{
|
Property p = new Property(PropertyIDMap.PID_CODEPAGE, Variant.VT_I2, codepage);
|
||||||
final MutableProperty p = new MutableProperty();
|
|
||||||
p.setID(PropertyIDMap.PID_CODEPAGE);
|
|
||||||
p.setType(Variant.VT_I2);
|
|
||||||
p.setValue(Integer.valueOf(codepage));
|
|
||||||
put(new CustomProperty(p));
|
put(new CustomProperty(p));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -346,84 +286,65 @@ public class CustomProperties extends HashMap<Object,CustomProperty>
|
|||||||
*
|
*
|
||||||
* @return the dictionary.
|
* @return the dictionary.
|
||||||
*/
|
*/
|
||||||
Map<Long,String> getDictionary()
|
Map<Long,String> getDictionary() {
|
||||||
{
|
return dictionary;
|
||||||
return dictionaryIDToName;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks against both String Name and Long ID
|
* Checks against both String Name and Long ID
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public boolean containsKey(Object key) {
|
public boolean containsKey(Object key) {
|
||||||
if(key instanceof Long) {
|
return ((key instanceof Long && dictionary.containsKey(key)) || dictionary.containsValue(key));
|
||||||
return super.containsKey(key);
|
}
|
||||||
}
|
|
||||||
if(key instanceof String) {
|
|
||||||
return super.containsKey(dictionaryNameToID.get(key));
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks against both the property, and its values.
|
* Checks against both the property, and its values.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public boolean containsValue(Object value) {
|
public boolean containsValue(Object value) {
|
||||||
if(value instanceof CustomProperty) {
|
if(value instanceof CustomProperty) {
|
||||||
return super.containsValue(value);
|
return super.containsValue(value);
|
||||||
} else {
|
}
|
||||||
for(CustomProperty cp : super.values()) {
|
|
||||||
|
for(CustomProperty cp : super.values()) {
|
||||||
if(cp.getValue() == value) {
|
if(cp.getValue() == value) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
/**
|
* Gets the codepage.
|
||||||
* <p>Gets the codepage.</p>
|
|
||||||
*
|
*
|
||||||
* @return the codepage or -1 if the codepage is undefined.
|
* @return the codepage or -1 if the codepage is undefined.
|
||||||
*/
|
*/
|
||||||
public int getCodepage()
|
public int getCodepage() {
|
||||||
{
|
CustomProperty cp = get(PropertyIDMap.PID_CODEPAGE);
|
||||||
int codepage = -1;
|
return (cp == null) ? -1 : (Integer)cp.getValue();
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <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
|
* 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
|
* @return {@code true} if the {@link CustomProperties} is pure, else
|
||||||
* <code>false</code>.
|
* {@code false}.
|
||||||
*/
|
*/
|
||||||
public boolean isPure()
|
public boolean isPure() {
|
||||||
{
|
|
||||||
return 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
|
* @param isPure the purity
|
||||||
*/
|
*/
|
||||||
public void setPure(final boolean isPure)
|
public void setPure(final boolean isPure) {
|
||||||
{
|
|
||||||
this.isPure = isPure;
|
this.isPure = isPure;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,10 +18,10 @@
|
|||||||
package org.apache.poi.hpsf;
|
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
|
* information stream. The difference to normal properties is that custom
|
||||||
* properties have an optional name. If the name is not <code>null</code> it
|
* properties have an optional name. If the name is not {@code null} it
|
||||||
* will be maintained in the section's dictionary.</p>
|
* will be maintained in the section's dictionary.
|
||||||
*/
|
*/
|
||||||
public class CustomProperty extends MutableProperty
|
public class CustomProperty extends MutableProperty
|
||||||
{
|
{
|
||||||
@ -29,80 +29,75 @@ public class CustomProperty extends MutableProperty
|
|||||||
private String name;
|
private String name;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Creates an empty {@link CustomProperty}. The set methods must be
|
* Creates an empty {@link CustomProperty}. The set methods must be
|
||||||
* called to make it usable.</p>
|
* called to make it usable.
|
||||||
*/
|
*/
|
||||||
public CustomProperty()
|
public CustomProperty() {
|
||||||
{
|
|
||||||
this.name = null;
|
this.name = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Creates a {@link CustomProperty} without a name by copying the
|
* Creates a {@link CustomProperty} without a name by copying the
|
||||||
* underlying {@link Property}' attributes.</p>
|
* underlying {@link Property}' attributes.
|
||||||
*
|
*
|
||||||
* @param property the property to copy
|
* @param property the property to copy
|
||||||
*/
|
*/
|
||||||
public CustomProperty(final Property property)
|
public CustomProperty(final Property property) {
|
||||||
{
|
|
||||||
this(property, null);
|
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
|
* @param property This property's attributes are copied to the new custom
|
||||||
* property.
|
* property.
|
||||||
* @param name The new custom property's name.
|
* @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);
|
super(property);
|
||||||
this.name = name;
|
this.name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Gets the property's name.</p>
|
* Gets the property's name.
|
||||||
*
|
*
|
||||||
* @return the property's name.
|
* @return the property's name.
|
||||||
*/
|
*/
|
||||||
public String getName()
|
public String getName() {
|
||||||
{
|
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Sets the property's name.</p>
|
* Sets the property's name.
|
||||||
*
|
*
|
||||||
* @param name The name to set.
|
* @param name The name to set.
|
||||||
*/
|
*/
|
||||||
public void setName(final String name)
|
public void setName(final String name) {
|
||||||
{
|
|
||||||
this.name = name;
|
this.name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Compares two custom properties for equality. The method returns
|
* Compares two custom properties for equality. The method returns
|
||||||
* <code>true</code> if all attributes of the two custom properties are
|
* {@code true} if all attributes of the two custom properties are
|
||||||
* equal.</p>
|
* equal.
|
||||||
*
|
*
|
||||||
* @param o The custom property to compare with.
|
* @param o The custom property to compare with.
|
||||||
* @return <code>true</code> if both custom properties are equal, else
|
* @return {@code true} if both custom properties are equal, else
|
||||||
* <code>false</code>.
|
* {@code false}.
|
||||||
*
|
*
|
||||||
* @see java.util.AbstractSet#equals(java.lang.Object)
|
* @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 CustomProperty c = (CustomProperty) o;
|
||||||
final String name1 = c.getName();
|
final String name1 = c.getName();
|
||||||
final String name2 = this.getName();
|
final String name2 = this.getName();
|
||||||
boolean equalNames = true;
|
boolean equalNames = true;
|
||||||
if (name1 == null)
|
if (name1 == null) {
|
||||||
equalNames = name2 == null;
|
equalNames = name2 == null;
|
||||||
else
|
} else {
|
||||||
equalNames = name1.equals(name2);
|
equalNames = name1.equals(name2);
|
||||||
|
}
|
||||||
return equalNames && c.getID() == this.getID()
|
return equalNames && c.getID() == this.getID()
|
||||||
&& c.getType() == this.getType()
|
&& c.getType() == this.getType()
|
||||||
&& c.getValue().equals(this.getValue());
|
&& c.getValue().equals(this.getValue());
|
||||||
@ -112,8 +107,7 @@ public class CustomProperty extends MutableProperty
|
|||||||
* @see java.util.AbstractSet#hashCode()
|
* @see java.util.AbstractSet#hashCode()
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public int hashCode()
|
public int hashCode() {
|
||||||
{
|
|
||||||
return (int) this.getID();
|
return (int) this.getID();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -17,103 +17,22 @@
|
|||||||
|
|
||||||
package org.apache.poi.hpsf;
|
package org.apache.poi.hpsf;
|
||||||
|
|
||||||
import java.io.IOException;
|
import org.apache.poi.util.Removal;
|
||||||
import java.io.OutputStream;
|
|
||||||
|
|
||||||
import org.apache.poi.util.CodePageUtil;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Adds writing capability to the {@link Property} class.</p>
|
* <p>Adds writing capability to the {@link Property} class.</p>
|
||||||
*
|
*
|
||||||
* <p>Please be aware that this class' functionality will be merged into the
|
* <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>
|
* {@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() {}
|
||||||
|
|
||||||
/**
|
public MutableProperty(final Property p) {
|
||||||
* <p>Creates an empty property. It must be filled using the set method to
|
super(p);
|
||||||
* 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -17,288 +17,21 @@
|
|||||||
|
|
||||||
package org.apache.poi.hpsf;
|
package org.apache.poi.hpsf;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
import org.apache.poi.util.Removal;
|
||||||
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;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <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
|
* 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>
|
* {@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
|
public MutablePropertySet(final PropertySet ps) {
|
||||||
* primary task is to initialize the immutable field with their proper
|
super(ps);
|
||||||
* 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());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <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;
|
package org.apache.poi.hpsf;
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.UnsupportedEncodingException;
|
||||||
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 org.apache.poi.hpsf.wellknown.PropertyIDMap;
|
import org.apache.poi.util.Removal;
|
||||||
import org.apache.poi.util.CodePageUtil;
|
|
||||||
import org.apache.poi.util.LittleEndian;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Adds writing capability to the {@link Section} class.</p>
|
* <p>Adds writing capability to the {@link Section} class.</p>
|
||||||
*
|
*
|
||||||
* <p>Please be aware that this class' functionality will be merged into the
|
* <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>
|
* {@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
|
@Removal(version="3.18")
|
||||||
{
|
public class MutableSection extends Section {
|
||||||
/**
|
public MutableSection() {}
|
||||||
* <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;
|
|
||||||
|
|
||||||
|
public MutableSection(final Section s) {
|
||||||
|
super(s);
|
||||||
/**
|
|
||||||
* <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 byte[] src, final int offset) throws UnsupportedEncodingException {
|
||||||
|
super(src,offset);
|
||||||
/**
|
|
||||||
* <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));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,53 +18,47 @@
|
|||||||
package org.apache.poi.hpsf;
|
package org.apache.poi.hpsf;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>This exception is thrown if a {@link MutablePropertySet} is to be written
|
* This exception is thrown if a {@link PropertySet} is to be written
|
||||||
* but does not have a formatID set (see {@link
|
* but does not have a formatID set (see {@link Section#setFormatID(ClassID)} or
|
||||||
* MutableSection#setFormatID(ClassID)} or
|
* {@link org.apache.poi.hpsf.Section#setFormatID(byte[])}.
|
||||||
* {@link org.apache.poi.hpsf.MutableSection#setFormatID(byte[])}.
|
|
||||||
*/
|
*/
|
||||||
public class NoFormatIDException extends HPSFRuntimeException
|
public class NoFormatIDException extends HPSFRuntimeException {
|
||||||
{
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Constructor</p>
|
* Constructor
|
||||||
*/
|
*/
|
||||||
public NoFormatIDException()
|
public NoFormatIDException() {
|
||||||
{
|
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Constructor</p>
|
* Constructor
|
||||||
*
|
*
|
||||||
* @param msg The exception's message string
|
* @param msg The exception's message string
|
||||||
*/
|
*/
|
||||||
public NoFormatIDException(final String msg)
|
public NoFormatIDException(final String msg) {
|
||||||
{
|
|
||||||
super(msg);
|
super(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Constructor</p>
|
* Constructor
|
||||||
*
|
*
|
||||||
* @param reason This exception's underlying reason
|
* @param reason This exception's underlying reason
|
||||||
*/
|
*/
|
||||||
public NoFormatIDException(final Throwable reason)
|
public NoFormatIDException(final Throwable reason) {
|
||||||
{
|
|
||||||
super(reason);
|
super(reason);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Constructor</p>
|
* Constructor
|
||||||
*
|
*
|
||||||
* @param msg The exception's message string
|
* @param msg The exception's message string
|
||||||
* @param reason This exception's underlying reason
|
* @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);
|
super(msg, reason);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,8 +17,11 @@
|
|||||||
|
|
||||||
package org.apache.poi.hpsf;
|
package org.apache.poi.hpsf;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStream;
|
||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
@ -29,104 +32,74 @@ import org.apache.poi.util.POILogFactory;
|
|||||||
import org.apache.poi.util.POILogger;
|
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
|
* 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
|
* The property's {@code type} determines how its
|
||||||
* <strong>value </strong> is interpreted. For example, if the type is
|
* {@code value} is interpreted. For example, if the type is
|
||||||
* {@link Variant#VT_LPSTR} (byte string), the value consists of a
|
* {@link Variant#VT_LPSTR} (byte string), the value consists of a
|
||||||
* DWord telling how many bytes the string contains. The bytes follow
|
* DWord telling how many bytes the string contains. The bytes follow
|
||||||
* immediately, including any null bytes that terminate the
|
* immediately, including any null bytes that terminate the
|
||||||
* string. The type {@link Variant#VT_I4} denotes a four-byte integer
|
* string. The type {@link Variant#VT_I4} denotes a four-byte integer
|
||||||
* value, {@link Variant#VT_FILETIME} some date and time (of a
|
* value, {@link Variant#VT_FILETIME} some date and time (of a file).<p>
|
||||||
* 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
|
* 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
|
* which variant types are really needed. So please feel free to submit error
|
||||||
* reports or patches for the types you need.</p>
|
* reports or patches for the types you need.
|
||||||
*
|
|
||||||
* <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>.
|
|
||||||
*
|
*
|
||||||
* @see Section
|
* @see Section
|
||||||
* @see Variant
|
* @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> */
|
/** The property's ID. */
|
||||||
protected long id;
|
private long id;
|
||||||
|
|
||||||
|
/** The property's type. */
|
||||||
|
private long type;
|
||||||
|
|
||||||
/**
|
/** The property's value. */
|
||||||
* <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> */
|
|
||||||
protected Object value;
|
protected Object value;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Returns the property's value.</p>
|
* Creates an empty property. It must be filled using the set method to be usable.
|
||||||
*
|
|
||||||
* @return The property's value
|
|
||||||
*/
|
*/
|
||||||
public Object getValue()
|
public Property() {
|
||||||
{
|
|
||||||
return value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Creates a property.</p>
|
* 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a property.
|
||||||
*
|
*
|
||||||
* @param id the property's ID.
|
* @param id the property's ID.
|
||||||
* @param type the property's type, see {@link Variant}.
|
* @param type the property's type, see {@link Variant}.
|
||||||
* @param value the property's value. Only certain types are allowed, see
|
* @param value the property's value. Only certain types are allowed, see
|
||||||
* {@link Variant}.
|
* {@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.id = id;
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.value = value;
|
this.value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Creates a {@link Property} instance by reading its bytes
|
* Creates a {@link Property} instance by reading its bytes
|
||||||
* from the property set stream.</p>
|
* from the property set stream.
|
||||||
*
|
*
|
||||||
* @param id The property's ID.
|
* @param id The property's ID.
|
||||||
* @param src The bytes the property set stream consists of.
|
* @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
|
* @exception UnsupportedEncodingException if the specified codepage is not
|
||||||
* supported.
|
* supported.
|
||||||
*/
|
*/
|
||||||
public Property(final long id, final byte[] src, final long offset,
|
public Property(final long id, final byte[] src, final long offset, final int length, final int codepage)
|
||||||
final int length, final int codepage)
|
throws UnsupportedEncodingException {
|
||||||
throws UnsupportedEncodingException
|
|
||||||
{
|
|
||||||
this.id = id;
|
this.id = id;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ID 0 is a special case since it specifies a dictionary of
|
* ID 0 is a special case since it specifies a dictionary of
|
||||||
* property IDs and property names.
|
* property IDs and property names.
|
||||||
*/
|
*/
|
||||||
if (id == 0)
|
if (id == 0) {
|
||||||
{
|
|
||||||
value = readDictionary(src, offset, length, codepage);
|
value = readDictionary(src, offset, length, codepage);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -158,12 +128,9 @@ public class Property
|
|||||||
type = LittleEndian.getUInt(src, o);
|
type = LittleEndian.getUInt(src, o);
|
||||||
o += LittleEndian.INT_SIZE;
|
o += LittleEndian.INT_SIZE;
|
||||||
|
|
||||||
try
|
try {
|
||||||
{
|
|
||||||
value = VariantSupport.read(src, o, length, (int) type, codepage);
|
value = VariantSupport.read(src, o, length, (int) type, codepage);
|
||||||
}
|
} catch (UnsupportedVariantTypeException ex) {
|
||||||
catch (UnsupportedVariantTypeException ex)
|
|
||||||
{
|
|
||||||
VariantSupport.writeUnsupportedTypeMessage(ex);
|
VariantSupport.writeUnsupportedTypeMessage(ex);
|
||||||
value = ex.getValue();
|
value = ex.getValue();
|
||||||
}
|
}
|
||||||
@ -172,19 +139,68 @@ public class Property
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Creates an empty property. It must be filled using the set method to
|
* Returns the property's ID.
|
||||||
* be usable.</p>
|
*
|
||||||
|
* @return The ID value
|
||||||
*/
|
*/
|
||||||
protected Property()
|
public long getID() {
|
||||||
{ }
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Reads a dictionary.</p>
|
* 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads a dictionary.
|
||||||
*
|
*
|
||||||
* @param src The byte array containing the bytes making out the 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.
|
* starts.
|
||||||
* @param length The dictionary contains at most this many bytes.
|
* @param length The dictionary contains at most this many bytes.
|
||||||
* @param codepage The codepage of the string values.
|
* @param codepage The codepage of the string values.
|
||||||
@ -192,15 +208,14 @@ public class Property
|
|||||||
* @throws UnsupportedEncodingException if the dictionary's codepage is not
|
* @throws UnsupportedEncodingException if the dictionary's codepage is not
|
||||||
* (yet) supported.
|
* (yet) supported.
|
||||||
*/
|
*/
|
||||||
protected Map<?, ?> readDictionary(final byte[] src, final long offset,
|
protected Map<?, ?> readDictionary(final byte[] src, final long offset, final int length, final int codepage)
|
||||||
final int length, final int codepage)
|
throws UnsupportedEncodingException {
|
||||||
throws UnsupportedEncodingException
|
|
||||||
{
|
|
||||||
/* Check whether "offset" points into the "src" array". */
|
/* Check whether "offset" points into the "src" array". */
|
||||||
if (offset < 0 || offset > src.length)
|
if (offset < 0 || offset > src.length) {
|
||||||
throw new HPSFRuntimeException
|
throw new HPSFRuntimeException
|
||||||
("Illegal offset " + offset + " while HPSF stream contains " +
|
("Illegal offset " + offset + " while HPSF stream contains " +
|
||||||
length + " bytes.");
|
length + " bytes.");
|
||||||
|
}
|
||||||
int o = (int) offset;
|
int o = (int) offset;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -209,13 +224,10 @@ public class Property
|
|||||||
final long nrEntries = LittleEndian.getUInt(src, o);
|
final long nrEntries = LittleEndian.getUInt(src, o);
|
||||||
o += LittleEndian.INT_SIZE;
|
o += LittleEndian.INT_SIZE;
|
||||||
|
|
||||||
final Map<Object, Object> m = new LinkedHashMap<Object, Object>(
|
final Map<Object, Object> m = new LinkedHashMap<Object, Object>((int) nrEntries, (float) 1.0 );
|
||||||
(int) nrEntries, (float) 1.0 );
|
|
||||||
|
|
||||||
try
|
try {
|
||||||
{
|
for (int i = 0; i < nrEntries; i++) {
|
||||||
for (int i = 0; i < nrEntries; i++)
|
|
||||||
{
|
|
||||||
/* The key. */
|
/* The key. */
|
||||||
final Long id = Long.valueOf(LittleEndian.getUInt(src, o));
|
final Long id = Long.valueOf(LittleEndian.getUInt(src, o));
|
||||||
o += LittleEndian.INT_SIZE;
|
o += LittleEndian.INT_SIZE;
|
||||||
@ -230,17 +242,13 @@ public class Property
|
|||||||
|
|
||||||
/* Read the string. */
|
/* Read the string. */
|
||||||
final StringBuffer b = new StringBuffer();
|
final StringBuffer b = new StringBuffer();
|
||||||
switch (codepage)
|
switch (codepage) {
|
||||||
{
|
|
||||||
case -1:
|
case -1:
|
||||||
{
|
|
||||||
/* Without a codepage the length is equal to the number of
|
/* Without a codepage the length is equal to the number of
|
||||||
* bytes. */
|
* bytes. */
|
||||||
b.append(new String(src, o, (int) sLength, Charset.forName("ASCII")));
|
b.append(new String(src, o, (int) sLength, Charset.forName("ASCII")));
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
case CodePageUtil.CP_UNICODE:
|
case CodePageUtil.CP_UNICODE:
|
||||||
{
|
|
||||||
/* The length is the number of characters, i.e. the number
|
/* The length is the number of characters, i.e. the number
|
||||||
* of bytes is twice the number of the characters. */
|
* of bytes is twice the number of the characters. */
|
||||||
final int nrBytes = (int) (sLength * 2);
|
final int nrBytes = (int) (sLength * 2);
|
||||||
@ -250,36 +258,30 @@ public class Property
|
|||||||
h[i2] = src[o + i2 + 1];
|
h[i2] = src[o + i2 + 1];
|
||||||
h[i2 + 1] = src[o + i2];
|
h[i2 + 1] = src[o + i2];
|
||||||
}
|
}
|
||||||
b.append(new String(h, 0, nrBytes,
|
b.append(new String(h, 0, nrBytes, CodePageUtil.codepageToEncoding(codepage)));
|
||||||
CodePageUtil.codepageToEncoding(codepage)));
|
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
default:
|
default:
|
||||||
{
|
|
||||||
/* For encodings other than Unicode the length is the number
|
/* For encodings other than Unicode the length is the number
|
||||||
* of bytes. */
|
* of bytes. */
|
||||||
b.append(new String(src, o, (int) sLength,
|
b.append(new String(src, o, (int) sLength, CodePageUtil.codepageToEncoding(codepage)));
|
||||||
VariantSupport.codepageToEncoding(codepage)));
|
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Strip 0x00 characters from the end of the string: */
|
/* 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);
|
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;
|
o += sLength;
|
||||||
|
}
|
||||||
m.put(id, b.toString());
|
m.put(id, b.toString());
|
||||||
}
|
}
|
||||||
}
|
} catch (RuntimeException ex) {
|
||||||
catch (RuntimeException ex)
|
|
||||||
{
|
|
||||||
final POILogger l = POILogFactory.getLogger(getClass());
|
final POILogger l = POILogFactory.getLogger(getClass());
|
||||||
l.log(POILogger.WARN,
|
l.log(POILogger.WARN,
|
||||||
"The property set's dictionary contains bogus data. "
|
"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
|
* Returns the property's size in bytes. This is always a multiple of 4.
|
||||||
* 4.</p>
|
|
||||||
*
|
*
|
||||||
* @return the property's size in bytes
|
* @return the property's size in bytes
|
||||||
*
|
*
|
||||||
@ -303,18 +304,18 @@ public class Property
|
|||||||
protected int getSize() throws WritingNotSupportedException
|
protected int getSize() throws WritingNotSupportedException
|
||||||
{
|
{
|
||||||
int length = VariantSupport.getVariantLength(type);
|
int length = VariantSupport.getVariantLength(type);
|
||||||
if (length >= 0)
|
if (length >= 0) {
|
||||||
return length; /* Fixed length */
|
return length; /* Fixed length */
|
||||||
if (length == -2)
|
}
|
||||||
|
if (length == -2) {
|
||||||
/* Unknown length */
|
/* Unknown length */
|
||||||
throw new WritingNotSupportedException(type, null);
|
throw new WritingNotSupportedException(type, null);
|
||||||
|
}
|
||||||
|
|
||||||
/* Variable length: */
|
/* Variable length: */
|
||||||
final int PADDING = 4; /* Pad to multiples of 4. */
|
final int PADDING = 4; /* Pad to multiples of 4. */
|
||||||
switch ((int) type)
|
switch ((int) type) {
|
||||||
{
|
case Variant.VT_LPSTR: {
|
||||||
case Variant.VT_LPSTR:
|
|
||||||
{
|
|
||||||
int l = ((String) value).length() + 1;
|
int l = ((String) value).length() + 1;
|
||||||
int r = l % PADDING;
|
int r = l % PADDING;
|
||||||
if (r > 0)
|
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
|
* 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
|
* 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)
|
* @see Object#equals(java.lang.Object)
|
||||||
*/
|
*/
|
||||||
public boolean equals(final Object o)
|
public boolean equals(final Object o) {
|
||||||
{
|
|
||||||
if (!(o instanceof Property)) {
|
if (!(o instanceof Property)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
final Property p = (Property) o;
|
final Property p = (Property) o;
|
||||||
final Object pValue = p.getValue();
|
final Object pValue = p.getValue();
|
||||||
final long pId = p.getID();
|
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;
|
return false;
|
||||||
if (value == null && pValue == null)
|
}
|
||||||
|
if (value == null && pValue == null) {
|
||||||
return true;
|
return true;
|
||||||
if (value == null || pValue == null)
|
}
|
||||||
|
if (value == null || pValue == null) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/* It's clear now that both values are non-null. */
|
/* It's clear now that both values are non-null. */
|
||||||
final Class<?> valueClass = value.getClass();
|
final Class<?> valueClass = value.getClass();
|
||||||
final Class<?> pValueClass = pValue.getClass();
|
final Class<?> pValueClass = pValue.getClass();
|
||||||
if (!(valueClass.isAssignableFrom(pValueClass)) &&
|
if (!(valueClass.isAssignableFrom(pValueClass)) &&
|
||||||
!(pValueClass.isAssignableFrom(valueClass)))
|
!(pValueClass.isAssignableFrom(valueClass))) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (value instanceof byte[])
|
if (value instanceof byte[]) {
|
||||||
return Util.equal((byte[]) value, (byte[]) pValue);
|
return Arrays.equals((byte[]) value, (byte[]) pValue);
|
||||||
|
}
|
||||||
|
|
||||||
return value.equals(pValue);
|
return value.equals(pValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private boolean typesAreEqual(final long t1, final long t2)
|
private boolean typesAreEqual(final long t1, final long t2) {
|
||||||
{
|
return (t1 == t2 ||
|
||||||
if (t1 == t2 ||
|
|
||||||
(t1 == Variant.VT_LPSTR && t2 == Variant.VT_LPWSTR) ||
|
(t1 == Variant.VT_LPSTR && t2 == Variant.VT_LPWSTR) ||
|
||||||
(t2 == Variant.VT_LPSTR && t1 == Variant.VT_LPWSTR)) {
|
(t2 == Variant.VT_LPSTR && t1 == Variant.VT_LPWSTR));
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -385,15 +388,14 @@ public class Property
|
|||||||
/**
|
/**
|
||||||
* @see Object#hashCode()
|
* @see Object#hashCode()
|
||||||
*/
|
*/
|
||||||
public int hashCode()
|
public int hashCode() {
|
||||||
{
|
|
||||||
long hashCode = 0;
|
long hashCode = 0;
|
||||||
hashCode += id;
|
hashCode += id;
|
||||||
hashCode += type;
|
hashCode += type;
|
||||||
if (value != null)
|
if (value != null) {
|
||||||
hashCode += value.hashCode();
|
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()
|
* @see Object#toString()
|
||||||
*/
|
*/
|
||||||
public String toString()
|
public String toString() {
|
||||||
{
|
|
||||||
final StringBuffer b = new StringBuffer();
|
final StringBuffer b = new StringBuffer();
|
||||||
b.append(getClass().getName());
|
b.append(getClass().getName());
|
||||||
b.append('[');
|
b.append('[');
|
||||||
@ -413,14 +414,12 @@ public class Property
|
|||||||
b.append(getType());
|
b.append(getType());
|
||||||
final Object value = getValue();
|
final Object value = getValue();
|
||||||
b.append(", value: ");
|
b.append(", value: ");
|
||||||
if (value instanceof String)
|
if (value instanceof String) {
|
||||||
{
|
|
||||||
b.append(value.toString());
|
b.append(value.toString());
|
||||||
final String s = (String) value;
|
final String s = (String) value;
|
||||||
final int l = s.length();
|
final int l = s.length();
|
||||||
final byte[] bytes = new byte[l * 2];
|
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 char c = s.charAt(i);
|
||||||
final byte high = (byte) ((c & 0x00ff00) >> 8);
|
final byte high = (byte) ((c & 0x00ff00) >> 8);
|
||||||
final byte low = (byte) ((c & 0x0000ff) >> 0);
|
final byte low = (byte) ((c & 0x0000ff) >> 0);
|
||||||
@ -433,21 +432,43 @@ public class Property
|
|||||||
b.append(hex);
|
b.append(hex);
|
||||||
}
|
}
|
||||||
b.append("]");
|
b.append("]");
|
||||||
}
|
} else if (value instanceof byte[]) {
|
||||||
else if (value instanceof byte[])
|
|
||||||
{
|
|
||||||
byte[] bytes = (byte[])value;
|
byte[] bytes = (byte[])value;
|
||||||
if(bytes.length > 0) {
|
if(bytes.length > 0) {
|
||||||
String hex = HexDump.dump(bytes, 0L, 0);
|
String hex = HexDump.dump(bytes, 0L, 0);
|
||||||
b.append(hex);
|
b.append(hex);
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
b.append(value.toString());
|
b.append(value.toString());
|
||||||
}
|
}
|
||||||
b.append(']');
|
b.append(']');
|
||||||
return b.toString();
|
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.InputStream;
|
||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
|
|
||||||
import org.apache.poi.hpsf.wellknown.SectionIDMap;
|
|
||||||
import org.apache.poi.poifs.filesystem.DirectoryEntry;
|
import org.apache.poi.poifs.filesystem.DirectoryEntry;
|
||||||
import org.apache.poi.poifs.filesystem.DocumentEntry;
|
import org.apache.poi.poifs.filesystem.DocumentEntry;
|
||||||
import org.apache.poi.poifs.filesystem.DocumentInputStream;
|
import org.apache.poi.poifs.filesystem.DocumentInputStream;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Factory class to create instances of {@link SummaryInformation},
|
* Factory class to create instances of {@link SummaryInformation},
|
||||||
* {@link DocumentSummaryInformation} and {@link PropertySet}.</p>
|
* {@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
|
* in the specified POIFS Directory. This is preferrably a {@link
|
||||||
* DocumentSummaryInformation} or a {@link SummaryInformation}. If
|
* DocumentSummaryInformation} or a {@link SummaryInformation}. If
|
||||||
* the specified entry does not contain a property set stream, an
|
* the specified entry does not contain a property set stream, an
|
||||||
* exception is thrown. If no entry is found with the given name,
|
* 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 dir The directory to find the PropertySet in
|
||||||
* @param name The name of the entry containing the PropertySet
|
* @param name The name of the entry containing the PropertySet
|
||||||
@ -52,55 +50,53 @@ public class PropertySetFactory
|
|||||||
* supported.
|
* supported.
|
||||||
*/
|
*/
|
||||||
public static PropertySet create(final DirectoryEntry dir, final String name)
|
public static PropertySet create(final DirectoryEntry dir, final String name)
|
||||||
throws FileNotFoundException, NoPropertySetStreamException,
|
throws FileNotFoundException, NoPropertySetStreamException, IOException, UnsupportedEncodingException {
|
||||||
IOException, UnsupportedEncodingException
|
|
||||||
{
|
|
||||||
InputStream inp = null;
|
InputStream inp = null;
|
||||||
try {
|
try {
|
||||||
DocumentEntry entry = (DocumentEntry)dir.getEntry(name);
|
DocumentEntry entry = (DocumentEntry)dir.getEntry(name);
|
||||||
inp = new DocumentInputStream(entry);
|
inp = new DocumentInputStream(entry);
|
||||||
try {
|
try {
|
||||||
return create(inp);
|
return create(inp);
|
||||||
} catch (MarkUnsupportedException e) { return null; }
|
} catch (MarkUnsupportedException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
} finally {
|
} 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
|
* InputStream}. This is preferrably a {@link
|
||||||
* DocumentSummaryInformation} or a {@link SummaryInformation}. If
|
* DocumentSummaryInformation} or a {@link SummaryInformation}. If
|
||||||
* the specified {@link InputStream} does not contain a property
|
* the specified {@link InputStream} does not contain a property
|
||||||
* set stream, an exception is thrown and the {@link InputStream}
|
* 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.
|
* @param stream Contains the property set stream's data.
|
||||||
* @return The created {@link PropertySet}.
|
* @return The created {@link PropertySet}.
|
||||||
* @throws NoPropertySetStreamException if the stream does not
|
* @throws NoPropertySetStreamException if the stream does not
|
||||||
* contain a property set.
|
* contain a property set.
|
||||||
* @throws MarkUnsupportedException if the stream does not support
|
* @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.
|
* @throws IOException if some I/O problem occurs.
|
||||||
* @exception UnsupportedEncodingException if the specified codepage is not
|
* @exception UnsupportedEncodingException if the specified codepage is not
|
||||||
* supported.
|
* supported.
|
||||||
*/
|
*/
|
||||||
public static PropertySet create(final InputStream stream)
|
public static PropertySet create(final InputStream stream)
|
||||||
throws NoPropertySetStreamException, MarkUnsupportedException,
|
throws NoPropertySetStreamException, MarkUnsupportedException, UnsupportedEncodingException, IOException {
|
||||||
UnsupportedEncodingException, IOException
|
|
||||||
{
|
|
||||||
final PropertySet ps = new PropertySet(stream);
|
final PropertySet ps = new PropertySet(stream);
|
||||||
try
|
try {
|
||||||
{
|
if (ps.isSummaryInformation()) {
|
||||||
if (ps.isSummaryInformation())
|
|
||||||
return new SummaryInformation(ps);
|
return new SummaryInformation(ps);
|
||||||
else if (ps.isDocumentSummaryInformation())
|
} else if (ps.isDocumentSummaryInformation()) {
|
||||||
return new DocumentSummaryInformation(ps);
|
return new DocumentSummaryInformation(ps);
|
||||||
else
|
} else {
|
||||||
return ps;
|
return ps;
|
||||||
}
|
}
|
||||||
catch (UnexpectedPropertySetTypeException ex)
|
} catch (UnexpectedPropertySetTypeException ex) {
|
||||||
{
|
|
||||||
/* This exception will never be throws because we already checked
|
/* This exception will never be throws because we already checked
|
||||||
* explicitly for this case above. */
|
* explicitly for this case above. */
|
||||||
throw new IllegalStateException(ex);
|
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.
|
* @return the new summary information.
|
||||||
*/
|
*/
|
||||||
public static SummaryInformation newSummaryInformation()
|
public static SummaryInformation newSummaryInformation() {
|
||||||
{
|
return new SummaryInformation();
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Creates a new document summary information.</p>
|
* Creates a new document summary information.
|
||||||
*
|
*
|
||||||
* @return the new document summary information.
|
* @return the new document summary information.
|
||||||
*/
|
*/
|
||||||
public static DocumentSummaryInformation newDocumentSummaryInformation()
|
public static DocumentSummaryInformation newDocumentSummaryInformation() {
|
||||||
{
|
return new DocumentSummaryInformation();
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
File diff suppressed because it is too large
Load Diff
@ -17,397 +17,24 @@
|
|||||||
|
|
||||||
package org.apache.poi.hpsf;
|
package org.apache.poi.hpsf;
|
||||||
|
|
||||||
import java.io.IOException;
|
import org.apache.poi.util.Removal;
|
||||||
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;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Abstract superclass for the convenience classes {@link
|
* Interface for the convenience classes {@link SummaryInformation}
|
||||||
* SummaryInformation} and {@link DocumentSummaryInformation}.</p>
|
* and {@link DocumentSummaryInformation}.<p>
|
||||||
*
|
*
|
||||||
* <p>The motivation behind this class is quite nasty if you look
|
* This used to be an abstract class to support late loading
|
||||||
* behind the scenes, but it serves the application programmer well by
|
* of the SummaryInformation classes, as their concrete instance can
|
||||||
* providing him with the easy-to-use {@link SummaryInformation} and
|
* only be determined after the PropertySet has been loaded.
|
||||||
* {@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>
|
|
||||||
*
|
*
|
||||||
* <p>A cleaner implementation would have been like this: The {@link
|
* @deprecated POI 3.16 - use PropertySet as base class instead
|
||||||
* 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>
|
|
||||||
*/
|
*/
|
||||||
public abstract class SpecialPropertySet extends MutablePropertySet
|
@Removal(version="3.18")
|
||||||
{
|
public class SpecialPropertySet extends MutablePropertySet {
|
||||||
/**
|
public SpecialPropertySet() {
|
||||||
* 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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public SpecialPropertySet(final PropertySet ps) throws UnexpectedPropertySetTypeException {
|
||||||
|
super(ps);
|
||||||
/**
|
|
||||||
* <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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @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 java.util.Date;
|
||||||
|
|
||||||
import org.apache.poi.hpsf.wellknown.PropertyIDMap;
|
import org.apache.poi.hpsf.wellknown.PropertyIDMap;
|
||||||
|
import org.apache.poi.hpsf.wellknown.SectionIDMap;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Convenience class representing a Summary Information stream in a
|
* Convenience class representing a Summary Information stream in a
|
||||||
* Microsoft Office document.</p>
|
* Microsoft Office document.
|
||||||
*
|
*
|
||||||
* @see DocumentSummaryInformation
|
* @see DocumentSummaryInformation
|
||||||
*/
|
*/
|
||||||
public final class SummaryInformation extends SpecialPropertySet {
|
public final class SummaryInformation extends SpecialPropertySet {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>The document name a summary information stream usually has in a POIFS
|
* The document name a summary information stream usually has in a POIFS filesystem.
|
||||||
* filesystem.</p>
|
|
||||||
*/
|
*/
|
||||||
public static final String DEFAULT_STREAM_NAME = "\005SummaryInformation";
|
public static final String DEFAULT_STREAM_NAME = "\005SummaryInformation";
|
||||||
|
|
||||||
@ -39,324 +39,291 @@ public final class SummaryInformation extends SpecialPropertySet {
|
|||||||
return PropertyIDMap.getSummaryInformationProperties();
|
return PropertyIDMap.getSummaryInformationProperties();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Creates a {@link SummaryInformation} from a given {@link
|
* Creates a {@link SummaryInformation} from a given {@link
|
||||||
* PropertySet}.</p>
|
* PropertySet}.
|
||||||
*
|
*
|
||||||
* @param ps A property set which should be created from a summary
|
* @param ps A property set which should be created from a summary
|
||||||
* information stream.
|
* information stream.
|
||||||
* @throws UnexpectedPropertySetTypeException if <var>ps</var> does not
|
* @throws UnexpectedPropertySetTypeException if {@code ps} does not
|
||||||
* contain a summary information stream.
|
* contain a summary information stream.
|
||||||
*/
|
*/
|
||||||
public SummaryInformation(final PropertySet ps)
|
public SummaryInformation() {
|
||||||
throws UnexpectedPropertySetTypeException
|
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);
|
super(ps);
|
||||||
if (!isSummaryInformation())
|
if (!isSummaryInformation()) {
|
||||||
throw new UnexpectedPropertySetTypeException("Not a "
|
throw new UnexpectedPropertySetTypeException("Not a " + getClass().getName());
|
||||||
+ getClass().getName());
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Returns the title (or <code>null</code>).</p>
|
* @return The title or {@code null}
|
||||||
*
|
|
||||||
* @return The title or <code>null</code>
|
|
||||||
*/
|
*/
|
||||||
public String getTitle()
|
public String getTitle() {
|
||||||
{
|
|
||||||
return getPropertyStringValue(PropertyIDMap.PID_TITLE);
|
return getPropertyStringValue(PropertyIDMap.PID_TITLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Sets the title.</p>
|
* Sets the title.
|
||||||
*
|
*
|
||||||
* @param title The title to set.
|
* @param title The title to set.
|
||||||
*/
|
*/
|
||||||
public void setTitle(final String title)
|
public void setTitle(final String title) {
|
||||||
{
|
set1stProperty(PropertyIDMap.PID_TITLE, title);
|
||||||
final MutableSection s = (MutableSection) getFirstSection();
|
|
||||||
s.setProperty(PropertyIDMap.PID_TITLE, title);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Removes the title.</p>
|
* Removes the title.
|
||||||
*/
|
*/
|
||||||
public void removeTitle()
|
public void removeTitle() {
|
||||||
{
|
remove1stProperty(PropertyIDMap.PID_TITLE);
|
||||||
final MutableSection s = (MutableSection) getFirstSection();
|
|
||||||
s.removeProperty(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);
|
return getPropertyStringValue(PropertyIDMap.PID_SUBJECT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Sets the subject.</p>
|
* Sets the subject.
|
||||||
*
|
*
|
||||||
* @param subject The subject to set.
|
* @param subject The subject to set.
|
||||||
*/
|
*/
|
||||||
public void setSubject(final String subject)
|
public void setSubject(final String subject) {
|
||||||
{
|
set1stProperty(PropertyIDMap.PID_SUBJECT, subject);
|
||||||
final MutableSection s = (MutableSection) getFirstSection();
|
|
||||||
s.setProperty(PropertyIDMap.PID_SUBJECT, subject);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Removes the subject.</p>
|
* Removes the subject.
|
||||||
*/
|
*/
|
||||||
public void removeSubject()
|
public void removeSubject() {
|
||||||
{
|
remove1stProperty(PropertyIDMap.PID_SUBJECT);
|
||||||
final MutableSection s = (MutableSection) getFirstSection();
|
|
||||||
s.removeProperty(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);
|
return getPropertyStringValue(PropertyIDMap.PID_AUTHOR);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Sets the author.</p>
|
* Sets the author.
|
||||||
*
|
*
|
||||||
* @param author The author to set.
|
* @param author The author to set.
|
||||||
*/
|
*/
|
||||||
public void setAuthor(final String author)
|
public void setAuthor(final String author) {
|
||||||
{
|
set1stProperty(PropertyIDMap.PID_AUTHOR, author);
|
||||||
final MutableSection s = (MutableSection) getFirstSection();
|
|
||||||
s.setProperty(PropertyIDMap.PID_AUTHOR, author);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Removes the author.</p>
|
* Removes the author.
|
||||||
*/
|
*/
|
||||||
public void removeAuthor()
|
public void removeAuthor() {
|
||||||
{
|
remove1stProperty(PropertyIDMap.PID_AUTHOR);
|
||||||
final MutableSection s = (MutableSection) getFirstSection();
|
|
||||||
s.removeProperty(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);
|
return getPropertyStringValue(PropertyIDMap.PID_KEYWORDS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Sets the keywords.</p>
|
* Sets the keywords.
|
||||||
*
|
*
|
||||||
* @param keywords The keywords to set.
|
* @param keywords The keywords to set.
|
||||||
*/
|
*/
|
||||||
public void setKeywords(final String keywords)
|
public void setKeywords(final String keywords) {
|
||||||
{
|
set1stProperty(PropertyIDMap.PID_KEYWORDS, keywords);
|
||||||
final MutableSection s = (MutableSection) getFirstSection();
|
|
||||||
s.setProperty(PropertyIDMap.PID_KEYWORDS, keywords);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Removes the keywords.</p>
|
* Removes the keywords.
|
||||||
*/
|
*/
|
||||||
public void removeKeywords()
|
public void removeKeywords() {
|
||||||
{
|
remove1stProperty(PropertyIDMap.PID_KEYWORDS);
|
||||||
final MutableSection s = (MutableSection) getFirstSection();
|
|
||||||
s.removeProperty(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);
|
return getPropertyStringValue(PropertyIDMap.PID_COMMENTS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Sets the comments.</p>
|
* Sets the comments.
|
||||||
*
|
*
|
||||||
* @param comments The comments to set.
|
* @param comments The comments to set.
|
||||||
*/
|
*/
|
||||||
public void setComments(final String comments)
|
public void setComments(final String comments) {
|
||||||
{
|
set1stProperty(PropertyIDMap.PID_COMMENTS, comments);
|
||||||
final MutableSection s = (MutableSection) getFirstSection();
|
|
||||||
s.setProperty(PropertyIDMap.PID_COMMENTS, comments);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Removes the comments.</p>
|
* Removes the comments.
|
||||||
*/
|
*/
|
||||||
public void removeComments()
|
public void removeComments() {
|
||||||
{
|
remove1stProperty(PropertyIDMap.PID_COMMENTS);
|
||||||
final MutableSection s = (MutableSection) getFirstSection();
|
|
||||||
s.removeProperty(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);
|
return getPropertyStringValue(PropertyIDMap.PID_TEMPLATE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Sets the template.</p>
|
* Sets the template.
|
||||||
*
|
*
|
||||||
* @param template The template to set.
|
* @param template The template to set.
|
||||||
*/
|
*/
|
||||||
public void setTemplate(final String template)
|
public void setTemplate(final String template) {
|
||||||
{
|
set1stProperty(PropertyIDMap.PID_TEMPLATE, template);
|
||||||
final MutableSection s = (MutableSection) getFirstSection();
|
|
||||||
s.setProperty(PropertyIDMap.PID_TEMPLATE, template);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Removes the template.</p>
|
* Removes the template.
|
||||||
*/
|
*/
|
||||||
public void removeTemplate()
|
public void removeTemplate() {
|
||||||
{
|
remove1stProperty(PropertyIDMap.PID_TEMPLATE);
|
||||||
final MutableSection s = (MutableSection) getFirstSection();
|
|
||||||
s.removeProperty(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);
|
return getPropertyStringValue(PropertyIDMap.PID_LASTAUTHOR);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Sets the last author.</p>
|
* Sets the last author.
|
||||||
*
|
*
|
||||||
* @param lastAuthor The last author to set.
|
* @param lastAuthor The last author to set.
|
||||||
*/
|
*/
|
||||||
public void setLastAuthor(final String lastAuthor)
|
public void setLastAuthor(final String lastAuthor) {
|
||||||
{
|
set1stProperty(PropertyIDMap.PID_LASTAUTHOR, lastAuthor);
|
||||||
final MutableSection s = (MutableSection) getFirstSection();
|
|
||||||
s.setProperty(PropertyIDMap.PID_LASTAUTHOR, lastAuthor);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Removes the last author.</p>
|
* Removes the last author.
|
||||||
*/
|
*/
|
||||||
public void removeLastAuthor()
|
public void removeLastAuthor() {
|
||||||
{
|
remove1stProperty(PropertyIDMap.PID_LASTAUTHOR);
|
||||||
final MutableSection s = (MutableSection) getFirstSection();
|
|
||||||
s.removeProperty(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);
|
return getPropertyStringValue(PropertyIDMap.PID_REVNUMBER);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Sets the revision number.</p>
|
* Sets the revision number.
|
||||||
*
|
*
|
||||||
* @param revNumber The revision number to set.
|
* @param revNumber The revision number to set.
|
||||||
*/
|
*/
|
||||||
public void setRevNumber(final String revNumber)
|
public void setRevNumber(final String revNumber) {
|
||||||
{
|
set1stProperty(PropertyIDMap.PID_REVNUMBER, revNumber);
|
||||||
final MutableSection s = (MutableSection) getFirstSection();
|
|
||||||
s.setProperty(PropertyIDMap.PID_REVNUMBER, revNumber);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Removes the revision number.</p>
|
* Removes the revision number.
|
||||||
*/
|
*/
|
||||||
public void removeRevNumber()
|
public void removeRevNumber() {
|
||||||
{
|
remove1stProperty(PropertyIDMap.PID_REVNUMBER);
|
||||||
final MutableSection s = (MutableSection) getFirstSection();
|
|
||||||
s.removeProperty(PropertyIDMap.PID_REVNUMBER);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Returns the total time spent in editing the document (or
|
* Returns the total time spent in editing the document (or
|
||||||
* <code>0</code>).</p>
|
* {@code 0}).
|
||||||
*
|
*
|
||||||
* @return The total time spent in editing the document or 0 if the {@link
|
* @return The total time spent in editing the document or 0 if the {@link
|
||||||
* SummaryInformation} does not contain this information.
|
* SummaryInformation} does not contain this information.
|
||||||
*/
|
*/
|
||||||
public long getEditTime()
|
public long getEditTime() {
|
||||||
{
|
|
||||||
final Date d = (Date) getProperty(PropertyIDMap.PID_EDITTIME);
|
final Date d = (Date) getProperty(PropertyIDMap.PID_EDITTIME);
|
||||||
if (d == null) {
|
if (d == null) {
|
||||||
return 0;
|
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.
|
* @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 Date d = Util.filetimeToDate(time);
|
||||||
final MutableSection s = (MutableSection) getFirstSection();
|
getFirstSection().setProperty(PropertyIDMap.PID_EDITTIME, Variant.VT_FILETIME, d);
|
||||||
s.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()
|
public void removeEditTime() {
|
||||||
{
|
remove1stProperty(PropertyIDMap.PID_EDITTIME);
|
||||||
final MutableSection s = (MutableSection) getFirstSection();
|
|
||||||
s.removeProperty(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);
|
return (Date) getProperty(PropertyIDMap.PID_LASTPRINTED);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Sets the lastPrinted.</p>
|
* Sets the lastPrinted.
|
||||||
*
|
*
|
||||||
* @param lastPrinted The lastPrinted to set.
|
* @param lastPrinted The lastPrinted to set.
|
||||||
*/
|
*/
|
||||||
public void setLastPrinted(final Date lastPrinted)
|
public void setLastPrinted(final Date lastPrinted) {
|
||||||
{
|
getFirstSection().setProperty(PropertyIDMap.PID_LASTPRINTED, Variant.VT_FILETIME, lastPrinted);
|
||||||
final MutableSection s = (MutableSection) getFirstSection();
|
|
||||||
s.setProperty(PropertyIDMap.PID_LASTPRINTED, Variant.VT_FILETIME,
|
|
||||||
lastPrinted);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Removes the lastPrinted.</p>
|
* Removes the lastPrinted.
|
||||||
*/
|
*/
|
||||||
public void removeLastPrinted()
|
public void removeLastPrinted() {
|
||||||
{
|
remove1stProperty(PropertyIDMap.PID_LASTPRINTED);
|
||||||
final MutableSection s = (MutableSection) getFirstSection();
|
|
||||||
s.removeProperty(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);
|
return (Date) getProperty(PropertyIDMap.PID_CREATE_DTM);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Sets the creation time.</p>
|
* Sets the creation time.
|
||||||
*
|
*
|
||||||
* @param createDateTime The creation time to set.
|
* @param createDateTime The creation time to set.
|
||||||
*/
|
*/
|
||||||
public void setCreateDateTime(final Date createDateTime)
|
public void setCreateDateTime(final Date createDateTime) {
|
||||||
{
|
getFirstSection().setProperty(PropertyIDMap.PID_CREATE_DTM, Variant.VT_FILETIME, createDateTime);
|
||||||
final MutableSection s = (MutableSection) getFirstSection();
|
|
||||||
s.setProperty(PropertyIDMap.PID_CREATE_DTM, Variant.VT_FILETIME,
|
|
||||||
createDateTime);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Removes the creation time.</p>
|
* Removes the creation time.
|
||||||
*/
|
*/
|
||||||
public void removeCreateDateTime()
|
public void removeCreateDateTime() {
|
||||||
{
|
remove1stProperty(PropertyIDMap.PID_CREATE_DTM);
|
||||||
final MutableSection s = (MutableSection) getFirstSection();
|
|
||||||
s.removeProperty(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);
|
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.
|
* @param time The time to set.
|
||||||
*/
|
*/
|
||||||
public void setLastSaveDateTime(final Date time)
|
public void setLastSaveDateTime(final Date time) {
|
||||||
{
|
final Section s = getFirstSection();
|
||||||
final MutableSection s = (MutableSection) getFirstSection();
|
|
||||||
s
|
s
|
||||||
.setProperty(PropertyIDMap.PID_LASTSAVE_DTM,
|
.setProperty(PropertyIDMap.PID_LASTSAVE_DTM,
|
||||||
Variant.VT_FILETIME, time);
|
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()
|
public void removeLastSaveDateTime() {
|
||||||
{
|
remove1stProperty(PropertyIDMap.PID_LASTSAVE_DTM);
|
||||||
final MutableSection s = (MutableSection) getFirstSection();
|
|
||||||
s.removeProperty(PropertyIDMap.PID_LASTSAVE_DTM);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Returns the page count or 0 if the {@link SummaryInformation} does
|
* Returns the page count or 0 if the {@link SummaryInformation} does
|
||||||
* not contain a page count.</p>
|
* not contain a page count.
|
||||||
*
|
*
|
||||||
* @return The page count or 0 if the {@link SummaryInformation} does not
|
* @return The page count or 0 if the {@link SummaryInformation} does not
|
||||||
* contain a page count.
|
* contain a page count.
|
||||||
*/
|
*/
|
||||||
public int getPageCount()
|
public int getPageCount() {
|
||||||
{
|
|
||||||
return getPropertyIntValue(PropertyIDMap.PID_PAGECOUNT);
|
return getPropertyIntValue(PropertyIDMap.PID_PAGECOUNT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Sets the page count.</p>
|
* Sets the page count.
|
||||||
*
|
*
|
||||||
* @param pageCount The page count to set.
|
* @param pageCount The page count to set.
|
||||||
*/
|
*/
|
||||||
public void setPageCount(final int pageCount)
|
public void setPageCount(final int pageCount) {
|
||||||
{
|
set1stProperty(PropertyIDMap.PID_PAGECOUNT, pageCount);
|
||||||
final MutableSection s = (MutableSection) getFirstSection();
|
|
||||||
s.setProperty(PropertyIDMap.PID_PAGECOUNT, pageCount);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Removes the page count.</p>
|
* Removes the page count.
|
||||||
*/
|
*/
|
||||||
public void removePageCount()
|
public void removePageCount() {
|
||||||
{
|
remove1stProperty(PropertyIDMap.PID_PAGECOUNT);
|
||||||
final MutableSection s = (MutableSection) getFirstSection();
|
|
||||||
s.removeProperty(PropertyIDMap.PID_PAGECOUNT);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Returns the word count or 0 if the {@link SummaryInformation} does
|
* Returns the word count or 0 if the {@link SummaryInformation} does
|
||||||
* not contain a word count.</p>
|
* 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);
|
return getPropertyIntValue(PropertyIDMap.PID_WORDCOUNT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Sets the word count.</p>
|
* Sets the word count.
|
||||||
*
|
*
|
||||||
* @param wordCount The word count to set.
|
* @param wordCount The word count to set.
|
||||||
*/
|
*/
|
||||||
public void setWordCount(final int wordCount)
|
public void setWordCount(final int wordCount) {
|
||||||
{
|
set1stProperty(PropertyIDMap.PID_WORDCOUNT, wordCount);
|
||||||
final MutableSection s = (MutableSection) getFirstSection();
|
|
||||||
s.setProperty(PropertyIDMap.PID_WORDCOUNT, wordCount);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Removes the word count.</p>
|
* Removes the word count.
|
||||||
*/
|
*/
|
||||||
public void removeWordCount()
|
public void removeWordCount() {
|
||||||
{
|
remove1stProperty(PropertyIDMap.PID_WORDCOUNT);
|
||||||
final MutableSection s = (MutableSection) getFirstSection();
|
|
||||||
s.removeProperty(PropertyIDMap.PID_WORDCOUNT);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Returns the character count or 0 if the {@link SummaryInformation}
|
* Returns the character count or 0 if the {@link SummaryInformation}
|
||||||
* does not contain a char count.</p>
|
* 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);
|
return getPropertyIntValue(PropertyIDMap.PID_CHARCOUNT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Sets the character count.</p>
|
* Sets the character count.
|
||||||
*
|
*
|
||||||
* @param charCount The character count to set.
|
* @param charCount The character count to set.
|
||||||
*/
|
*/
|
||||||
public void setCharCount(final int charCount)
|
public void setCharCount(final int charCount) {
|
||||||
{
|
set1stProperty(PropertyIDMap.PID_CHARCOUNT, charCount);
|
||||||
final MutableSection s = (MutableSection) getFirstSection();
|
|
||||||
s.setProperty(PropertyIDMap.PID_CHARCOUNT, charCount);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Removes the character count.</p>
|
* Removes the character count.
|
||||||
*/
|
*/
|
||||||
public void removeCharCount()
|
public void removeCharCount() {
|
||||||
{
|
remove1stProperty(PropertyIDMap.PID_CHARCOUNT);
|
||||||
final MutableSection s = (MutableSection) getFirstSection();
|
|
||||||
s.removeProperty(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
|
* 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
|
* {@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);
|
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
|
* 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();
|
byte[] data = getThumbnail();
|
||||||
if (data == null) return null;
|
if (data == null) return null;
|
||||||
return new Thumbnail(data);
|
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.
|
* @param thumbnail The thumbnail to set.
|
||||||
*/
|
*/
|
||||||
public void setThumbnail(final byte[] thumbnail)
|
public void setThumbnail(final byte[] thumbnail) {
|
||||||
{
|
getFirstSection().setProperty(PropertyIDMap.PID_THUMBNAIL, /* FIXME: */ Variant.VT_LPSTR, thumbnail);
|
||||||
final MutableSection s = (MutableSection) getFirstSection();
|
|
||||||
s.setProperty(PropertyIDMap.PID_THUMBNAIL, /* FIXME: */
|
|
||||||
Variant.VT_LPSTR, thumbnail);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Removes the thumbnail.</p>
|
* Removes the thumbnail.
|
||||||
*/
|
*/
|
||||||
public void removeThumbnail()
|
public void removeThumbnail() {
|
||||||
{
|
remove1stProperty(PropertyIDMap.PID_THUMBNAIL);
|
||||||
final MutableSection s = (MutableSection) getFirstSection();
|
|
||||||
s.removeProperty(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);
|
return getPropertyStringValue(PropertyIDMap.PID_APPNAME);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Sets the application name.</p>
|
* Sets the application name.
|
||||||
*
|
*
|
||||||
* @param applicationName The application name to set.
|
* @param applicationName The application name to set.
|
||||||
*/
|
*/
|
||||||
public void setApplicationName(final String applicationName)
|
public void setApplicationName(final String applicationName) {
|
||||||
{
|
set1stProperty(PropertyIDMap.PID_APPNAME, applicationName);
|
||||||
final MutableSection s = (MutableSection) getFirstSection();
|
|
||||||
s.setProperty(PropertyIDMap.PID_APPNAME, applicationName);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Removes the application name.</p>
|
* Removes the application name.
|
||||||
*/
|
*/
|
||||||
public void removeApplicationName()
|
public void removeApplicationName() {
|
||||||
{
|
remove1stProperty(PropertyIDMap.PID_APPNAME);
|
||||||
final MutableSection s = (MutableSection) getFirstSection();
|
|
||||||
s.removeProperty(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>
|
* <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
|
* security field or if there is no security on the document. Use
|
||||||
* {@link PropertySet#wasNull()} to distinguish between the two
|
* {@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>
|
* </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);
|
return getPropertyIntValue(PropertyIDMap.PID_SECURITY);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Sets the security code.</p>
|
* Sets the security code.
|
||||||
*
|
*
|
||||||
* @param security The security code to set.
|
* @param security The security code to set.
|
||||||
*/
|
*/
|
||||||
public void setSecurity(final int security)
|
public void setSecurity(final int security) {
|
||||||
{
|
set1stProperty(PropertyIDMap.PID_SECURITY, security);
|
||||||
final MutableSection s = (MutableSection) getFirstSection();
|
|
||||||
s.setProperty(PropertyIDMap.PID_SECURITY, security);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Removes the security code.</p>
|
* Removes the security code.
|
||||||
*/
|
*/
|
||||||
public void removeSecurity()
|
public void removeSecurity() {
|
||||||
{
|
remove1stProperty(PropertyIDMap.PID_SECURITY);
|
||||||
final MutableSection s = (MutableSection) getFirstSection();
|
|
||||||
s.removeProperty(PropertyIDMap.PID_SECURITY);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -20,110 +20,17 @@ package org.apache.poi.hpsf;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.io.StringWriter;
|
import java.io.StringWriter;
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
|
||||||
|
import org.apache.poi.util.Internal;
|
||||||
import org.apache.poi.util.SuppressForbidden;
|
import org.apache.poi.util.SuppressForbidden;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Provides various static utility methods.</p>
|
* <p>Provides various static utility methods.</p>
|
||||||
*/
|
*/
|
||||||
|
@Internal
|
||||||
public class Util
|
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
|
* <p>The difference between the Windows epoch (1601-01-01
|
||||||
* 00:00:00) and the Unix epoch (1970-01-01 00:00:00) in
|
* 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
|
* <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)
|
public static boolean equals(Object[] c1, Object[] c2)
|
||||||
{
|
{
|
||||||
final Object[] o1 = c1.clone();
|
for (int i1 = 0; i1 < c1.length; i1++)
|
||||||
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++)
|
|
||||||
{
|
{
|
||||||
final Object obj1 = o1[i1];
|
final Object obj1 = c1[i1];
|
||||||
boolean matchFound = false;
|
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))
|
if (obj1.equals(obj2))
|
||||||
{
|
{
|
||||||
matchFound = true;
|
matchFound = true;
|
||||||
o2[i2] = null;
|
c2[i2] = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!matchFound)
|
if (!matchFound)
|
||||||
@ -257,8 +127,6 @@ public class Util
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Pads a byte array with 0x00 bytes so that its length is a multiple of
|
* <p>Pads a byte array with 0x00 bytes so that its length is a multiple of
|
||||||
* 4.</p>
|
* 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
|
* <p>Returns a textual representation of a {@link Throwable}, including a
|
||||||
* stacktrace.</p>
|
* stacktrace.</p>
|
||||||
|
@ -27,70 +27,77 @@ import java.util.List;
|
|||||||
import org.apache.poi.util.CodePageUtil;
|
import org.apache.poi.util.CodePageUtil;
|
||||||
import org.apache.poi.util.POILogFactory;
|
import org.apache.poi.util.POILogFactory;
|
||||||
import org.apache.poi.util.POILogger;
|
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
|
* <strong>FIXME (3):</strong> Reading and writing should be made more
|
||||||
* uniform than it is now. The following items should be resolved:
|
* uniform than it is now. The following items should be resolved:<p>
|
||||||
*
|
*
|
||||||
* <ul>
|
* <ul>
|
||||||
*
|
*
|
||||||
* <li><p>Reading requires a length parameter that is 4 byte greater than the
|
* <li>Reading requires a length parameter that is 4 byte greater than the
|
||||||
* actual data, because the variant type field is included. </p></li>
|
* actual data, because the variant type field is included.
|
||||||
*
|
*
|
||||||
* <li><p>Reading reads from a byte array while writing writes to an byte array
|
* <li>Reading reads from a byte array while writing writes to an byte array
|
||||||
* output stream.</p></li>
|
* output stream.
|
||||||
*
|
*
|
||||||
* </ul>
|
* </ul>
|
||||||
*/
|
*/
|
||||||
public class VariantSupport extends Variant
|
public class VariantSupport extends Variant {
|
||||||
{
|
/**
|
||||||
private static final POILogger logger = POILogFactory.getLogger(VariantSupport.class);
|
* 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;
|
private static boolean logUnsupportedTypes = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Specifies whether warnings about unsupported variant types are to be
|
* Keeps a list of the variant types an "unsupported" message has already
|
||||||
* written to <code>System.err</code> or not.</p>
|
* been issued for.
|
||||||
*
|
|
||||||
* @param logUnsupportedTypes If <code>true</code> warnings will be written,
|
|
||||||
* if <code>false</code> they won't.
|
|
||||||
*/
|
*/
|
||||||
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;
|
VariantSupport.logUnsupportedTypes = logUnsupportedTypes;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Checks whether logging of unsupported variant types warning is turned
|
* Checks whether logging of unsupported variant types warning is turned
|
||||||
* on or off.</p>
|
* on or off.
|
||||||
*
|
*
|
||||||
* @return <code>true</code> if logging is turned on, else
|
* @return {@code true} if logging is turned on, else
|
||||||
* <code>false</code>.
|
* {@code false}.
|
||||||
*/
|
*/
|
||||||
public static boolean isLogUnsupportedTypes()
|
public static boolean isLogUnsupportedTypes() {
|
||||||
{
|
|
||||||
return logUnsupportedTypes;
|
return logUnsupportedTypes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Keeps a list of the variant types an "unsupported" message has already
|
* Writes a warning to {@code System.err} that a variant type is
|
||||||
* been issued for.</p>
|
|
||||||
*/
|
|
||||||
protected static List<Long> unsupportedMessage;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <p>Writes a warning to <code>System.err</code> that a variant type is
|
|
||||||
* unsupported by HPSF. Such a warning is written only once for each variant
|
* 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
|
* @param ex The exception to log
|
||||||
*/
|
*/
|
||||||
protected static void writeUnsupportedTypeMessage
|
protected static void writeUnsupportedTypeMessage
|
||||||
(final UnsupportedVariantTypeException ex)
|
(final UnsupportedVariantTypeException ex) {
|
||||||
{
|
|
||||||
if (isLogUnsupportedTypes())
|
if (isLogUnsupportedTypes())
|
||||||
{
|
{
|
||||||
if (unsupportedMessage == null)
|
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}
|
* types should be implemented included in the {@link #SUPPORTED_TYPES}
|
||||||
* array.</p>
|
* array.
|
||||||
*
|
*
|
||||||
* @see Variant
|
* @see Variant
|
||||||
* @param variantType the variant type to check
|
* @param variantType the variant type to check
|
||||||
* @return <code>true</code> if HPFS supports this type, else
|
* @return {@code true} if HPFS supports this type, else
|
||||||
* <code>false</code>
|
* {@code false}
|
||||||
*/
|
*/
|
||||||
public boolean isSupportedType(final int variantType)
|
public boolean isSupportedType(final int variantType) {
|
||||||
{
|
|
||||||
for (int i = 0; i < SUPPORTED_TYPES.length; i++)
|
for (int i = 0; i < SUPPORTED_TYPES.length; i++)
|
||||||
if (variantType == SUPPORTED_TYPES[i])
|
if (variantType == SUPPORTED_TYPES[i])
|
||||||
return true;
|
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 src The byte array
|
||||||
* @param offset The offset in the byte array where the variant starts
|
* @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,
|
public static Object read( final byte[] src, final int offset,
|
||||||
final int length, final long type, final int codepage )
|
final int length, final long type, final int codepage )
|
||||||
throws ReadingNotSupportedException, UnsupportedEncodingException
|
throws ReadingNotSupportedException, UnsupportedEncodingException {
|
||||||
{
|
TypedPropertyValue typedPropertyValue = new TypedPropertyValue( (int) type, null );
|
||||||
TypedPropertyValue typedPropertyValue = new TypedPropertyValue(
|
|
||||||
(int) type, null );
|
|
||||||
int unpadded;
|
int unpadded;
|
||||||
try
|
try {
|
||||||
{
|
|
||||||
unpadded = typedPropertyValue.readValue( src, offset );
|
unpadded = typedPropertyValue.readValue( src, offset );
|
||||||
}
|
} catch ( UnsupportedOperationException exc ) {
|
||||||
catch ( UnsupportedOperationException exc )
|
|
||||||
{
|
|
||||||
int propLength = Math.min( length, src.length - offset );
|
int propLength = Math.min( length, src.length - offset );
|
||||||
final byte[] v = new byte[propLength];
|
final byte[] v = new byte[propLength];
|
||||||
System.arraycopy( src, offset, v, 0, propLength );
|
System.arraycopy( src, offset, v, 0, propLength );
|
||||||
throw new ReadingNotSupportedException( type, v );
|
throw new ReadingNotSupportedException( type, v );
|
||||||
}
|
}
|
||||||
|
|
||||||
switch ( (int) type )
|
switch ( (int) type ) {
|
||||||
{
|
|
||||||
case Variant.VT_EMPTY:
|
|
||||||
case Variant.VT_I4:
|
|
||||||
case Variant.VT_I8:
|
|
||||||
case Variant.VT_R8:
|
|
||||||
/*
|
/*
|
||||||
* we have more property types that can be converted into Java
|
* we have more property types that can be converted into Java
|
||||||
* objects, but current API need to be preserved, and it returns
|
* objects, but current API need to be preserved, and it returns
|
||||||
* other types as byte arrays. In future major versions it shall be
|
* other types as byte arrays. In future major versions it shall be
|
||||||
* changed -- sergey
|
* 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
|
* also for backward-compatibility with prev. versions of POI
|
||||||
* --sergey
|
* --sergey
|
||||||
*/
|
*/
|
||||||
return Integer.valueOf( ( (Short) typedPropertyValue.getValue() )
|
case Variant.VT_I2:
|
||||||
.intValue() );
|
return ( (Short) typedPropertyValue.getValue() ).intValue();
|
||||||
}
|
|
||||||
case Variant.VT_FILETIME:
|
case Variant.VT_FILETIME:
|
||||||
{
|
Filetime filetime = (Filetime) typedPropertyValue.getValue();
|
||||||
Filetime filetime = (Filetime) typedPropertyValue.getValue();
|
return Util.filetimeToDate( (int) filetime.getHigh(), (int) filetime.getLow() );
|
||||||
return Util.filetimeToDate( (int) filetime.getHigh(),
|
|
||||||
(int) filetime.getLow() );
|
case Variant.VT_LPSTR:
|
||||||
}
|
CodePageString cpString = (CodePageString) typedPropertyValue.getValue();
|
||||||
case Variant.VT_LPSTR:
|
return cpString.getJavaValue( codepage );
|
||||||
{
|
|
||||||
CodePageString string = (CodePageString) typedPropertyValue
|
case Variant.VT_LPWSTR:
|
||||||
.getValue();
|
UnicodeString uniString = (UnicodeString) typedPropertyValue.getValue();
|
||||||
return string.getJavaValue( codepage );
|
return uniString.toJavaString();
|
||||||
}
|
|
||||||
case Variant.VT_LPWSTR:
|
|
||||||
{
|
|
||||||
UnicodeString string = (UnicodeString) typedPropertyValue
|
|
||||||
.getValue();
|
|
||||||
return string.toJavaString();
|
|
||||||
}
|
|
||||||
case Variant.VT_CF:
|
|
||||||
{
|
|
||||||
// if(l1 < 0) {
|
// if(l1 < 0) {
|
||||||
/**
|
/**
|
||||||
* YK: reading the ClipboardData packet (VT_CF) is not quite
|
* YK: reading the ClipboardData packet (VT_CF) is not quite
|
||||||
@ -223,7 +204,7 @@ public class VariantSupport extends Variant
|
|||||||
* 45583 clearly show that this approach does not always work. The
|
* 45583 clearly show that this approach does not always work. The
|
||||||
* workaround below attempts to gracefully handle such cases instead
|
* workaround below attempts to gracefully handle such cases instead
|
||||||
* of throwing exceptions.
|
* of throwing exceptions.
|
||||||
*
|
*
|
||||||
* August 20, 2009
|
* August 20, 2009
|
||||||
*/
|
*/
|
||||||
// l1 = LittleEndian.getInt(src, o1); o1 += LittleEndian.INT_SIZE;
|
// l1 = LittleEndian.getInt(src, o1); o1 += LittleEndian.INT_SIZE;
|
||||||
@ -232,33 +213,28 @@ public class VariantSupport extends Variant
|
|||||||
// System.arraycopy(src, o1, v, 0, v.length);
|
// System.arraycopy(src, o1, v, 0, v.length);
|
||||||
// value = v;
|
// value = v;
|
||||||
// break;
|
// break;
|
||||||
ClipboardData clipboardData = (ClipboardData) typedPropertyValue
|
case Variant.VT_CF:
|
||||||
.getValue();
|
ClipboardData clipboardData = (ClipboardData) typedPropertyValue.getValue();
|
||||||
return clipboardData.toByteArray();
|
return clipboardData.toByteArray();
|
||||||
}
|
|
||||||
|
|
||||||
case Variant.VT_BOOL:
|
case Variant.VT_BOOL:
|
||||||
{
|
VariantBool bool = (VariantBool) typedPropertyValue.getValue();
|
||||||
VariantBool bool = (VariantBool) typedPropertyValue.getValue();
|
return bool.getValue();
|
||||||
return Boolean.valueOf( bool.getValue() );
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
{
|
|
||||||
/*
|
/*
|
||||||
* it is not very good, but what can do without breaking current
|
* it is not very good, but what can do without breaking current
|
||||||
* API? --sergey
|
* API? --sergey
|
||||||
*/
|
*/
|
||||||
final byte[] v = new byte[unpadded];
|
default:
|
||||||
System.arraycopy( src, offset, v, 0, unpadded );
|
final byte[] v = new byte[unpadded];
|
||||||
throw new ReadingNotSupportedException( type, v );
|
System.arraycopy( src, offset, v, 0, unpadded );
|
||||||
}
|
throw new ReadingNotSupportedException( type, v );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Turns a codepage number into the equivalent character encoding's
|
* Turns a codepage number into the equivalent character encoding's
|
||||||
* name.</p>
|
* name.
|
||||||
*
|
*
|
||||||
* @param codepage The codepage number
|
* @param codepage The codepage number
|
||||||
*
|
*
|
||||||
@ -269,7 +245,10 @@ public class VariantSupport extends Variant
|
|||||||
*
|
*
|
||||||
* @exception UnsupportedEncodingException if the specified codepage is
|
* @exception UnsupportedEncodingException if the specified codepage is
|
||||||
* less than zero.
|
* less than zero.
|
||||||
|
*
|
||||||
|
* @deprecated POI 3.16 - use {@link CodePageUtil#codepageToEncoding(int)}
|
||||||
*/
|
*/
|
||||||
|
@Removal(version="3.18")
|
||||||
public static String codepageToEncoding(final int codepage)
|
public static String codepageToEncoding(final int codepage)
|
||||||
throws UnsupportedEncodingException
|
throws UnsupportedEncodingException
|
||||||
{
|
{
|
||||||
@ -278,13 +257,13 @@ public class VariantSupport extends Variant
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Writes a variant value to an output stream. This method ensures that
|
* Writes a variant value to an output stream. This method ensures that
|
||||||
* always a multiple of 4 bytes is written.</p>
|
* 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}
|
* <strong>must</strong> always be written as {@link Variant#VT_LPWSTR}
|
||||||
* strings, not as {@link Variant#VT_LPSTR} strings. This method ensure this
|
* 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 out The stream to write the value to.
|
||||||
* @param type The variant's type.
|
* @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,
|
public static int write(final OutputStream out, final long type,
|
||||||
final Object value, final int codepage)
|
final Object value, final int codepage)
|
||||||
throws IOException, WritingNotSupportedException
|
throws IOException, WritingNotSupportedException {
|
||||||
{
|
|
||||||
int length = 0;
|
int length = 0;
|
||||||
switch ((int) type)
|
switch ((int) type) {
|
||||||
{
|
|
||||||
case Variant.VT_BOOL:
|
case Variant.VT_BOOL:
|
||||||
{
|
if ( ( (Boolean) value ).booleanValue() ) {
|
||||||
if ( ( (Boolean) value ).booleanValue() )
|
|
||||||
{
|
|
||||||
out.write( 0xff );
|
out.write( 0xff );
|
||||||
out.write( 0xff );
|
out.write( 0xff );
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
out.write( 0x00 );
|
out.write( 0x00 );
|
||||||
out.write( 0x00 );
|
out.write( 0x00 );
|
||||||
}
|
}
|
||||||
length += 2;
|
length += 2;
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
case Variant.VT_LPSTR:
|
case Variant.VT_LPSTR:
|
||||||
{
|
CodePageString codePageString = new CodePageString( (String) value, codepage );
|
||||||
CodePageString codePageString = new CodePageString( (String) value,
|
|
||||||
codepage );
|
|
||||||
length += codePageString.write( out );
|
length += codePageString.write( out );
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
case Variant.VT_LPWSTR:
|
case Variant.VT_LPWSTR:
|
||||||
{
|
|
||||||
final int nrOfChars = ( (String) value ).length() + 1;
|
final int nrOfChars = ( (String) value ).length() + 1;
|
||||||
length += TypeWriter.writeUIntToStream( out, nrOfChars );
|
length += TypeWriter.writeUIntToStream( out, nrOfChars );
|
||||||
char[] s = ( (String) value ).toCharArray();
|
for ( char s : ( (String) value ).toCharArray() ) {
|
||||||
for ( int i = 0; i < s.length; i++ )
|
final int high = ( ( s & 0x0000ff00 ) >> 8 );
|
||||||
{
|
final int low = ( s & 0x000000ff );
|
||||||
final int high = ( ( s[i] & 0x0000ff00 ) >> 8 );
|
|
||||||
final int low = ( s[i] & 0x000000ff );
|
|
||||||
final byte highb = (byte) high;
|
final byte highb = (byte) high;
|
||||||
final byte lowb = (byte) low;
|
final byte lowb = (byte) low;
|
||||||
out.write( lowb );
|
out.write( lowb );
|
||||||
@ -345,79 +313,63 @@ public class VariantSupport extends Variant
|
|||||||
out.write( 0x00 );
|
out.write( 0x00 );
|
||||||
length += 2;
|
length += 2;
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
case Variant.VT_CF:
|
case Variant.VT_CF:
|
||||||
{
|
final byte[] cf = (byte[]) value;
|
||||||
final byte[] b = (byte[]) value;
|
out.write(cf);
|
||||||
out.write(b);
|
length = cf.length;
|
||||||
length = b.length;
|
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
case Variant.VT_EMPTY:
|
case Variant.VT_EMPTY:
|
||||||
{
|
|
||||||
length += TypeWriter.writeUIntToStream( out, Variant.VT_EMPTY );
|
length += TypeWriter.writeUIntToStream( out, Variant.VT_EMPTY );
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
case Variant.VT_I2:
|
case Variant.VT_I2:
|
||||||
{
|
length += TypeWriter.writeToStream( out, ( (Integer) value ).shortValue() );
|
||||||
length += TypeWriter.writeToStream( out,
|
|
||||||
( (Integer) value ).shortValue() );
|
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
case Variant.VT_I4:
|
case Variant.VT_I4:
|
||||||
{
|
if (!(value instanceof Integer)) {
|
||||||
if (!(value instanceof Integer))
|
|
||||||
{
|
|
||||||
throw new ClassCastException("Could not cast an object to "
|
throw new ClassCastException("Could not cast an object to "
|
||||||
+ Integer.class.toString() + ": "
|
+ Integer.class.toString() + ": "
|
||||||
+ value.getClass().toString() + ", "
|
+ value.getClass().toString() + ", "
|
||||||
+ value.toString());
|
+ value.toString());
|
||||||
}
|
}
|
||||||
length += TypeWriter.writeToStream(out,
|
length += TypeWriter.writeToStream(out, ((Integer) value).intValue());
|
||||||
((Integer) value).intValue());
|
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
case Variant.VT_I8:
|
case Variant.VT_I8:
|
||||||
{
|
|
||||||
length += TypeWriter.writeToStream(out, ((Long) value).longValue());
|
length += TypeWriter.writeToStream(out, ((Long) value).longValue());
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
case Variant.VT_R8:
|
case Variant.VT_R8:
|
||||||
{
|
length += TypeWriter.writeToStream(out, ((Double) value).doubleValue());
|
||||||
length += TypeWriter.writeToStream(out,
|
|
||||||
((Double) value).doubleValue());
|
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
case Variant.VT_FILETIME:
|
case Variant.VT_FILETIME:
|
||||||
{
|
|
||||||
long filetime = Util.dateToFileTime((Date) value);
|
long filetime = Util.dateToFileTime((Date) value);
|
||||||
int high = (int) ((filetime >> 32) & 0x00000000FFFFFFFFL);
|
int high = (int) ((filetime >> 32) & 0x00000000FFFFFFFFL);
|
||||||
int low = (int) (filetime & 0x00000000FFFFFFFFL);
|
int low = (int) (filetime & 0x00000000FFFFFFFFL);
|
||||||
Filetime filetimeValue = new Filetime( low, high);
|
Filetime filetimeValue = new Filetime( low, high);
|
||||||
length += filetimeValue.write( out );
|
length += filetimeValue.write( out );
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
default:
|
default:
|
||||||
{
|
|
||||||
/* The variant type is not supported yet. However, if the value
|
/* The variant type is not supported yet. However, if the value
|
||||||
* is a byte array we can write it nevertheless. */
|
* is a byte array we can write it nevertheless. */
|
||||||
if (value instanceof byte[])
|
if (value instanceof byte[]) {
|
||||||
{
|
|
||||||
final byte[] b = (byte[]) value;
|
final byte[] b = (byte[]) value;
|
||||||
out.write(b);
|
out.write(b);
|
||||||
length = b.length;
|
length = b.length;
|
||||||
writeUnsupportedTypeMessage
|
writeUnsupportedTypeMessage(new WritingNotSupportedException(type, value));
|
||||||
(new WritingNotSupportedException(type, value));
|
} else {
|
||||||
}
|
|
||||||
else
|
|
||||||
throw new WritingNotSupportedException(type, value);
|
throw new WritingNotSupportedException(type, value);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* pad values to 4-bytes */
|
/* pad values to 4-bytes */
|
||||||
while ( ( length & 0x3 ) != 0 )
|
while ( ( length & 0x3 ) != 0 ) {
|
||||||
{
|
|
||||||
out.write( 0x00 );
|
out.write( 0x00 );
|
||||||
length++;
|
length++;
|
||||||
}
|
}
|
||||||
|
@ -27,6 +27,7 @@ import org.apache.poi.hpsf.CustomProperties;
|
|||||||
import org.apache.poi.hpsf.DocumentSummaryInformation;
|
import org.apache.poi.hpsf.DocumentSummaryInformation;
|
||||||
import org.apache.poi.hpsf.HPSFPropertiesOnlyDocument;
|
import org.apache.poi.hpsf.HPSFPropertiesOnlyDocument;
|
||||||
import org.apache.poi.hpsf.Property;
|
import org.apache.poi.hpsf.Property;
|
||||||
|
import org.apache.poi.hpsf.PropertySet;
|
||||||
import org.apache.poi.hpsf.SpecialPropertySet;
|
import org.apache.poi.hpsf.SpecialPropertySet;
|
||||||
import org.apache.poi.hpsf.SummaryInformation;
|
import org.apache.poi.hpsf.SummaryInformation;
|
||||||
import org.apache.poi.hpsf.wellknown.PropertyIDMap;
|
import org.apache.poi.hpsf.wellknown.PropertyIDMap;
|
||||||
@ -67,7 +68,7 @@ public class HPSFPropertiesExtractor extends POIOLE2TextExtractor {
|
|||||||
CustomProperties cps = dsi == null ? null : dsi.getCustomProperties();
|
CustomProperties cps = dsi == null ? null : dsi.getCustomProperties();
|
||||||
if (cps != null) {
|
if (cps != null) {
|
||||||
for (String key : cps.nameSet()) {
|
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");
|
text.append(key).append(" = ").append(val).append("\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -86,7 +87,7 @@ public class HPSFPropertiesExtractor extends POIOLE2TextExtractor {
|
|||||||
return getPropertiesText(si);
|
return getPropertiesText(si);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String getPropertiesText(SpecialPropertySet ps) {
|
private static String getPropertiesText(PropertySet ps) {
|
||||||
if (ps == null) {
|
if (ps == null) {
|
||||||
// Not defined, oh well
|
// Not defined, oh well
|
||||||
return "";
|
return "";
|
||||||
@ -98,12 +99,12 @@ public class HPSFPropertiesExtractor extends POIOLE2TextExtractor {
|
|||||||
Property[] props = ps.getProperties();
|
Property[] props = ps.getProperties();
|
||||||
for (Property prop : props) {
|
for (Property prop : props) {
|
||||||
String type = Long.toString(prop.getID());
|
String type = Long.toString(prop.getID());
|
||||||
Object typeObj = idMap.get(prop.getID());
|
Object typeObj = (idMap == null) ? null : idMap.get(prop.getID());
|
||||||
if (typeObj != null) {
|
if (typeObj != null) {
|
||||||
type = typeObj.toString();
|
type = typeObj.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
String val = HelperPropertySet.getPropertyValueText(prop.getValue());
|
String val = getPropertyValueText(prop.getValue());
|
||||||
text.append(type).append(" = ").append(val).append("\n");
|
text.append(type).append(" = ").append(val).append("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -125,18 +126,12 @@ public class HPSFPropertiesExtractor extends POIOLE2TextExtractor {
|
|||||||
throw new IllegalStateException("You already have the Metadata Text Extractor, not recursing!");
|
throw new IllegalStateException("You already have the Metadata Text Extractor, not recursing!");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static abstract class HelperPropertySet extends SpecialPropertySet {
|
private static String getPropertyValueText(Object val) {
|
||||||
public HelperPropertySet() {
|
return (val == null)
|
||||||
super(null);
|
? "(not set)"
|
||||||
}
|
: PropertySet.getPropertyStringValue(val);
|
||||||
public static String getPropertyValueText(Object val) {
|
|
||||||
if (val == null) {
|
|
||||||
return "(not set)";
|
|
||||||
}
|
|
||||||
return SpecialPropertySet.getPropertyStringValue(val);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object o) {
|
public boolean equals(Object o) {
|
||||||
return super.equals(o);
|
return super.equals(o);
|
||||||
|
@ -45,26 +45,42 @@ public final class IOUtils {
|
|||||||
* @throws EmptyFileException if the stream is empty
|
* @throws EmptyFileException if the stream is empty
|
||||||
*/
|
*/
|
||||||
public static byte[] peekFirst8Bytes(InputStream stream) throws IOException, EmptyFileException {
|
public static byte[] peekFirst8Bytes(InputStream stream) throws IOException, EmptyFileException {
|
||||||
// We want to peek at the first 8 bytes
|
return peekFirstNBytes(stream, 8);
|
||||||
stream.mark(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();
|
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) {
|
if(stream instanceof PushbackInputStream) {
|
||||||
PushbackInputStream pin = (PushbackInputStream)stream;
|
PushbackInputStream pin = (PushbackInputStream)stream;
|
||||||
pin.unread(header, 0, read);
|
pin.unread(peekedBytes, 0, readBytes);
|
||||||
} else {
|
} else {
|
||||||
stream.reset();
|
stream.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
return header;
|
return peekedBytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reads all the data from the input stream, and returns the bytes read.
|
* Reads all the data from the input stream, and returns the bytes read.
|
||||||
*/
|
*/
|
||||||
|
@ -192,8 +192,7 @@ public final class TestBasic {
|
|||||||
(poiFiles[0].getBytes()));
|
(poiFiles[0].getBytes()));
|
||||||
final List<Section> sections = si.getSections();
|
final List<Section> sections = si.getSections();
|
||||||
final Section s = sections.get(0);
|
final Section s = sections.get(0);
|
||||||
assertTrue(org.apache.poi.hpsf.Util.equal
|
assertArrayEquals(s.getFormatID().getBytes(), SectionIDMap.SUMMARY_INFORMATION_ID);
|
||||||
(s.getFormatID().getBytes(), SectionIDMap.SUMMARY_INFORMATION_ID));
|
|
||||||
assertNotNull(s.getProperties());
|
assertNotNull(s.getProperties());
|
||||||
assertEquals(17, s.getPropertyCount());
|
assertEquals(17, s.getPropertyCount());
|
||||||
assertEquals("Titel", s.getProperty(2));
|
assertEquals("Titel", s.getProperty(2));
|
||||||
|
@ -216,7 +216,8 @@ public class TestWrite
|
|||||||
final MutablePropertySet ps = new MutablePropertySet();
|
final MutablePropertySet ps = new MutablePropertySet();
|
||||||
final MutableSection si = new MutableSection();
|
final MutableSection si = new MutableSection();
|
||||||
si.setFormatID(SectionIDMap.SUMMARY_INFORMATION_ID);
|
si.setFormatID(SectionIDMap.SUMMARY_INFORMATION_ID);
|
||||||
ps.getSections().set(0, si);
|
ps.clearSections();
|
||||||
|
ps.addSection(si);
|
||||||
|
|
||||||
final MutableProperty p = new MutableProperty();
|
final MutableProperty p = new MutableProperty();
|
||||||
p.setID(PropertyIDMap.PID_AUTHOR);
|
p.setID(PropertyIDMap.PID_AUTHOR);
|
||||||
|
Loading…
Reference in New Issue
Block a user