aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/java/org/apache/poi/hpsf/CustomProperties.java311
-rw-r--r--src/java/org/apache/poi/hpsf/CustomProperty.java56
-rw-r--r--src/java/org/apache/poi/hpsf/DocumentSummaryInformation.java566
-rw-r--r--src/java/org/apache/poi/hpsf/MutableProperty.java97
-rw-r--r--src/java/org/apache/poi/hpsf/MutablePropertySet.java291
-rw-r--r--src/java/org/apache/poi/hpsf/MutableSection.java680
-rw-r--r--src/java/org/apache/poi/hpsf/NoFormatIDException.java30
-rw-r--r--src/java/org/apache/poi/hpsf/Property.java319
-rw-r--r--src/java/org/apache/poi/hpsf/PropertySet.java799
-rw-r--r--src/java/org/apache/poi/hpsf/PropertySetFactory.java84
-rw-r--r--src/java/org/apache/poi/hpsf/Section.java945
-rw-r--r--src/java/org/apache/poi/hpsf/SpecialPropertySet.java397
-rw-r--r--src/java/org/apache/poi/hpsf/SummaryInformation.java485
-rw-r--r--src/java/org/apache/poi/hpsf/Util.java186
-rw-r--r--src/java/org/apache/poi/hpsf/VariantSupport.java288
-rw-r--r--src/java/org/apache/poi/hpsf/extractor/HPSFPropertiesExtractor.java25
-rw-r--r--src/java/org/apache/poi/util/IOUtils.java36
-rw-r--r--src/testcases/org/apache/poi/hpsf/basic/TestBasic.java3
-rw-r--r--src/testcases/org/apache/poi/hpsf/basic/TestWrite.java3
19 files changed, 2128 insertions, 3473 deletions
diff --git a/src/java/org/apache/poi/hpsf/CustomProperties.java b/src/java/org/apache/poi/hpsf/CustomProperties.java
index f305836fa8..c420f1d3a0 100644
--- a/src/java/org/apache/poi/hpsf/CustomProperties.java
+++ b/src/java/org/apache/poi/hpsf/CustomProperties.java
@@ -19,132 +19,112 @@ package org.apache.poi.hpsf;
import java.util.Date;
import java.util.HashMap;
-import java.util.Iterator;
import java.util.Map;
import java.util.Set;
+import org.apache.commons.collections4.bidimap.TreeBidiMap;
import org.apache.poi.hpsf.wellknown.PropertyIDMap;
/**
- * <p>Maintains the instances of {@link CustomProperty} that belong to a
+ * Maintains the instances of {@link CustomProperty} that belong to a
* {@link DocumentSummaryInformation}. The class maintains the names of the
* custom properties in a dictionary. It implements the {@link Map} interface
* and by this provides a simplified view on custom properties: A property's
* name is the key that maps to a typed value. This implementation hides
* property IDs from the developer and regards the property names as keys to
- * typed values.</p>
+ * typed values.<p>
*
- * <p>While this class provides a simple API to custom properties, it ignores
+ * While this class provides a simple API to custom properties, it ignores
* the fact that not names, but IDs are the real keys to properties. Under the
* hood this class maintains a 1:1 relationship between IDs and names. Therefore
* you should not use this class to process property sets with several IDs
* mapping to the same name or with properties without a name: the result will
* contain only a subset of the original properties. If you really need to deal
- * such property sets, use HPSF's low-level access methods.</p>
+ * such property sets, use HPSF's low-level access methods.<p>
*
- * <p>An application can call the {@link #isPure} method to check whether a
+ * An application can call the {@link #isPure} method to check whether a
* property set parsed by {@link CustomProperties} is still pure (i.e.
- * unmodified) or whether one or more properties have been dropped.</p>
+ * unmodified) or whether one or more properties have been dropped.<p>
*
- * <p>This class is not thread-safe; concurrent access to instances of this
- * class must be synchronized.</p>
+ * This class is not thread-safe; concurrent access to instances of this
+ * class must be synchronized.<p>
*
- * <p>While this class is roughly HashMap&lt;Long,CustomProperty&gt;, that's the
- * internal representation. To external calls, it should appear as
- * HashMap&lt;String,Object&gt; mapping between Names and Custom Property Values.</p>
+ * While this class is roughly HashMap&lt;Long,CustomProperty&gt;, that's the
+ * internal representation. To external calls, it should appear as
+ * HashMap&lt;String,Object&gt; mapping between Names and Custom Property Values.
*/
@SuppressWarnings("serial")
-public class CustomProperties extends HashMap<Object,CustomProperty>
-{
+public class CustomProperties extends HashMap<Long,CustomProperty> {
/**
- * <p>Maps property IDs to property names.</p>
+ * Maps property IDs to property names and vice versa.
*/
- private final Map<Long,String> dictionaryIDToName = new HashMap<Long,String>();
+ private final TreeBidiMap<Long,String> dictionary = new TreeBidiMap<Long,String>();
/**
- * <p>Maps property names to property IDs.</p>
- */
- private final Map<String,Long> dictionaryNameToID = new HashMap<String,Long>();
-
- /**
- * <p>Tells whether this object is pure or not.</p>
+ * Tells whether this object is pure or not.
*/
private boolean isPure = true;
/**
- * <p>Puts a {@link CustomProperty} into this map. It is assumed that the
+ * Puts a {@link CustomProperty} into this map. It is assumed that the
* {@link CustomProperty} already has a valid ID. Otherwise use
- * {@link #put(CustomProperty)}.</p>
+ * {@link #put(CustomProperty)}.
*
* @param name the property name
* @param cp the property
*
* @return the previous property stored under this name
*/
- public CustomProperty put(final String name, final CustomProperty cp)
- {
- if (name == null)
- {
+ public CustomProperty put(final String name, final CustomProperty cp) {
+ if (name == null) {
/* Ignoring a property without a name. */
isPure = false;
return null;
}
- if (!(name.equals(cp.getName())))
+
+ if (!name.equals(cp.getName())) {
throw new IllegalArgumentException("Parameter \"name\" (" + name +
") and custom property's name (" + cp.getName() +
") do not match.");
+ }
/* Register name and ID in the dictionary. Mapping in both directions is possible. If there is already a */
- final Long idKey = Long.valueOf(cp.getID());
- final Long oldID = dictionaryNameToID.get(name);
- dictionaryIDToName.remove(oldID);
- dictionaryNameToID.put(name, idKey);
- dictionaryIDToName.put(idKey, name);
+ super.remove(dictionary.getKey(name));
+ dictionary.put(cp.getID(), name);
/* Put the custom property into this map. */
- final CustomProperty oldCp = super.remove(oldID);
- super.put(idKey, cp);
- return oldCp;
+ return super.put(cp.getID(), cp);
}
/**
- * <p>Puts a {@link CustomProperty} that has not yet a valid ID into this
- * map. The method will allocate a suitable ID for the custom property:</p>
+ * Puts a {@link CustomProperty} that has not yet a valid ID into this
+ * map. The method will allocate a suitable ID for the custom property:
*
* <ul>
+ * <li>If there is already a property with the same name, take the ID
+ * of that property.
*
- * <li><p>If there is already a property with the same name, take the ID
- * of that property.</p></li>
- *
- * <li><p>Otherwise find the highest ID and use its value plus one.</p></li>
- *
+ * <li>Otherwise find the highest ID and use its value plus one.
* </ul>
*
* @param customProperty
- * @return If the was already a property with the same name, the
+ * @return If there was already a property with the same name, the old property
* @throws ClassCastException
*/
- private Object put(final CustomProperty customProperty) throws ClassCastException
- {
+ private Object put(final CustomProperty customProperty) throws ClassCastException {
final String name = customProperty.getName();
/* Check whether a property with this name is in the map already. */
- final Long oldId = dictionaryNameToID.get(name);
- if (oldId != null)
- customProperty.setID(oldId.longValue());
- else
- {
- long max = 1;
- for (Long long1 : dictionaryIDToName.keySet()) {
- final long id = long1.longValue();
- if (id > max)
- max = id;
- }
- customProperty.setID(max + 1);
+ final Long oldId = (name == null) ? null : dictionary.getKey(name);
+ if (oldId != null) {
+ customProperty.setID(oldId);
+ } else {
+ long lastKey = (dictionary.isEmpty()) ? 0 : dictionary.lastKey();
+ customProperty.setID(Math.max(lastKey,PropertyIDMap.PID_MAX) + 1);
}
return this.put(name, customProperty);
}
@@ -152,123 +132,92 @@ public class CustomProperties extends HashMap<Object,CustomProperty>
/**
- * <p>Removes a custom property.</p>
+ * Removes a custom property.
* @param name The name of the custom property to remove
- * @return The removed property or <code>null</code> if the specified property was not found.
+ * @return The removed property or {@code null} if the specified property was not found.
*
* @see java.util.HashSet#remove(java.lang.Object)
*/
- public Object remove(final String name)
- {
- final Long id = dictionaryNameToID.get(name);
- if (id == null)
- return null;
- dictionaryIDToName.remove(id);
- dictionaryNameToID.remove(name);
+ public Object remove(final String name) {
+ final Long id = dictionary.removeValue(name);
return super.remove(id);
}
/**
- * <p>Adds a named string property.</p>
+ * Adds a named string property.
*
* @param name The property's name.
* @param value The property's value.
* @return the property that was stored under the specified name before, or
- * <code>null</code> if there was no such property before.
+ * {@code null} if there was no such property before.
*/
- public Object put(final String name, final String value)
- {
- final MutableProperty p = new MutableProperty();
- p.setID(-1);
- p.setType(Variant.VT_LPWSTR);
- p.setValue(value);
- final CustomProperty cp = new CustomProperty(p, name);
- return put(cp);
+ public Object put(final String name, final String value) {
+ final Property p = new Property(-1, Variant.VT_LPWSTR, value);
+ return put(new CustomProperty(p, name));
}
/**
- * <p>Adds a named long property.</p>
+ * Adds a named long property.
*
* @param name The property's name.
* @param value The property's value.
* @return the property that was stored under the specified name before, or
- * <code>null</code> if there was no such property before.
+ * {@code null} if there was no such property before.
*/
- public Object put(final String name, final Long value)
- {
- final MutableProperty p = new MutableProperty();
- p.setID(-1);
- p.setType(Variant.VT_I8);
- p.setValue(value);
- final CustomProperty cp = new CustomProperty(p, name);
- return put(cp);
+ public Object put(final String name, final Long value) {
+ final Property p = new Property(-1, Variant.VT_I8, value);
+ return put(new CustomProperty(p, name));
}
/**
- * <p>Adds a named double property.</p>
+ * Adds a named double property.
*
* @param name The property's name.
* @param value The property's value.
* @return the property that was stored under the specified name before, or
- * <code>null</code> if there was no such property before.
+ * {@code null} if there was no such property before.
*/
- public Object put(final String name, final Double value)
- {
- final MutableProperty p = new MutableProperty();
- p.setID(-1);
- p.setType(Variant.VT_R8);
- p.setValue(value);
- final CustomProperty cp = new CustomProperty(p, name);
- return put(cp);
+ public Object put(final String name, final Double value) {
+ final Property p = new Property(-1, Variant.VT_R8, value);
+ return put(new CustomProperty(p, name));
}
/**
- * <p>Adds a named integer property.</p>
+ * Adds a named integer property.
*
* @param name The property's name.
* @param value The property's value.
* @return the property that was stored under the specified name before, or
- * <code>null</code> if there was no such property before.
+ * {@code null} if there was no such property before.
*/
- public Object put(final String name, final Integer value)
- {
- final MutableProperty p = new MutableProperty();
- p.setID(-1);
- p.setType(Variant.VT_I4);
- p.setValue(value);
- final CustomProperty cp = new CustomProperty(p, name);
- return put(cp);
+ public Object put(final String name, final Integer value) {
+ final Property p = new Property(-1, Variant.VT_I4, value);
+ return put(new CustomProperty(p, name));
}
/**
- * <p>Adds a named boolean property.</p>
+ * Adds a named boolean property.
*
* @param name The property's name.
* @param value The property's value.
* @return the property that was stored under the specified name before, or
- * <code>null</code> if there was no such property before.
+ * {@code null} if there was no such property before.
*/
- public Object put(final String name, final Boolean value)
- {
- final MutableProperty p = new MutableProperty();
- p.setID(-1);
- p.setType(Variant.VT_BOOL);
- p.setValue(value);
- final CustomProperty cp = new CustomProperty(p, name);
- return put(cp);
+ public Object put(final String name, final Boolean value) {
+ final Property p = new Property(-1, Variant.VT_BOOL, value);
+ return put(new CustomProperty(p, name));
}
/**
- * <p>Gets a named value from the custom properties.</p>
+ * Gets a named value from the custom properties.
*
* @param name the name of the value to get
- * @return the value or <code>null</code> if a value with the specified
+ * @return the value or {@code null} if a value with the specified
* name is not found in the custom properties.
*/
- public Object get(final String name)
- {
- final Long id = dictionaryNameToID.get(name);
+ public Object get(final String name) {
+ final Long id = dictionary.getKey(name);
final CustomProperty cp = super.get(id);
return cp != null ? cp.getValue() : null;
}
@@ -276,21 +225,16 @@ public class CustomProperties extends HashMap<Object,CustomProperty>
/**
- * <p>Adds a named date property.</p>
+ * Adds a named date property.
*
* @param name The property's name.
* @param value The property's value.
* @return the property that was stored under the specified name before, or
- * <code>null</code> if there was no such property before.
+ * {@code null} if there was no such property before.
*/
- public Object put(final String name, final Date value)
- {
- final MutableProperty p = new MutableProperty();
- p.setID(-1);
- p.setType(Variant.VT_FILETIME);
- p.setValue(value);
- final CustomProperty cp = new CustomProperty(p, name);
- return put(cp);
+ public Object put(final String name, final Date value) {
+ final Property p = new Property(-1, Variant.VT_FILETIME, value);
+ return put(new CustomProperty(p, name));
}
/**
@@ -302,7 +246,7 @@ public class CustomProperties extends HashMap<Object,CustomProperty>
@Override
@SuppressWarnings({ "rawtypes", "unchecked" })
public Set keySet() {
- return dictionaryNameToID.keySet();
+ return dictionary.values();
}
/**
@@ -311,7 +255,7 @@ public class CustomProperties extends HashMap<Object,CustomProperty>
* @return a set of all the names of our custom properties
*/
public Set<String> nameSet() {
- return dictionaryNameToID.keySet();
+ return dictionary.values();
}
/**
@@ -320,21 +264,17 @@ public class CustomProperties extends HashMap<Object,CustomProperty>
* @return a set of all the IDs of our custom properties
*/
public Set<String> idSet() {
- return dictionaryNameToID.keySet();
+ return dictionary.values();
}
/**
- * <p>Sets the codepage.</p>
+ * Sets the codepage.
*
* @param codepage the codepage
*/
- public void setCodepage(final int codepage)
- {
- final MutableProperty p = new MutableProperty();
- p.setID(PropertyIDMap.PID_CODEPAGE);
- p.setType(Variant.VT_I2);
- p.setValue(Integer.valueOf(codepage));
+ public void setCodepage(final int codepage) {
+ Property p = new Property(PropertyIDMap.PID_CODEPAGE, Variant.VT_I2, codepage);
put(new CustomProperty(p));
}
@@ -346,84 +286,65 @@ public class CustomProperties extends HashMap<Object,CustomProperty>
*
* @return the dictionary.
*/
- Map<Long,String> getDictionary()
- {
- return dictionaryIDToName;
+ Map<Long,String> getDictionary() {
+ return dictionary;
}
/**
* Checks against both String Name and Long ID
*/
- @Override
- public boolean containsKey(Object key) {
- if(key instanceof Long) {
- return super.containsKey(key);
- }
- if(key instanceof String) {
- return super.containsKey(dictionaryNameToID.get(key));
- }
- return false;
- }
-
- /**
- * Checks against both the property, and its values.
- */
- @Override
- public boolean containsValue(Object value) {
- if(value instanceof CustomProperty) {
- return super.containsValue(value);
- } else {
- for(CustomProperty cp : super.values()) {
+ @Override
+ public boolean containsKey(Object key) {
+ return ((key instanceof Long && dictionary.containsKey(key)) || dictionary.containsValue(key));
+ }
+
+ /**
+ * Checks against both the property, and its values.
+ */
+ @Override
+ public boolean containsValue(Object value) {
+ if(value instanceof CustomProperty) {
+ return super.containsValue(value);
+ }
+
+ for(CustomProperty cp : super.values()) {
if(cp.getValue() == value) {
- return true;
+ return true;
}
- }
- }
- return false;
- }
-
+ }
+ return false;
+ }
- /**
- * <p>Gets the codepage.</p>
+ /**
+ * Gets the codepage.
*
* @return the codepage or -1 if the codepage is undefined.
*/
- public int getCodepage()
- {
- int codepage = -1;
- for (final Iterator<CustomProperty> i = this.values().iterator(); codepage == -1 && i.hasNext();)
- {
- final CustomProperty cp = i.next();
- if (cp.getID() == PropertyIDMap.PID_CODEPAGE)
- codepage = ((Integer) cp.getValue()).intValue();
- }
- return codepage;
+ public int getCodepage() {
+ CustomProperty cp = get(PropertyIDMap.PID_CODEPAGE);
+ return (cp == null) ? -1 : (Integer)cp.getValue();
}
-
-
/**
- * <p>Tells whether this {@link CustomProperties} instance is pure or one or
+ * Tells whether this {@link CustomProperties} instance is pure or one or
* more properties of the underlying low-level property set has been
- * dropped.</p>
+ * dropped.
*
- * @return <code>true</code> if the {@link CustomProperties} is pure, else
- * <code>false</code>.
+ * @return {@code true} if the {@link CustomProperties} is pure, else
+ * {@code false}.
*/
- public boolean isPure()
- {
+ public boolean isPure() {
return isPure;
}
/**
- * <p>Sets the purity of the custom property set.</p>
+ * Sets the purity of the custom property set.
*
* @param isPure the purity
*/
- public void setPure(final boolean isPure)
- {
+ public void setPure(final boolean isPure) {
this.isPure = isPure;
}
}
diff --git a/src/java/org/apache/poi/hpsf/CustomProperty.java b/src/java/org/apache/poi/hpsf/CustomProperty.java
index a256d94d3a..83511655ab 100644
--- a/src/java/org/apache/poi/hpsf/CustomProperty.java
+++ b/src/java/org/apache/poi/hpsf/CustomProperty.java
@@ -18,10 +18,10 @@
package org.apache.poi.hpsf;
/**
- * <p>This class represents custom properties in the document summary
+ * This class represents custom properties in the document summary
* information stream. The difference to normal properties is that custom
- * properties have an optional name. If the name is not <code>null</code> it
- * will be maintained in the section's dictionary.</p>
+ * properties have an optional name. If the name is not {@code null} it
+ * will be maintained in the section's dictionary.
*/
public class CustomProperty extends MutableProperty
{
@@ -29,80 +29,75 @@ public class CustomProperty extends MutableProperty
private String name;
/**
- * <p>Creates an empty {@link CustomProperty}. The set methods must be
- * called to make it usable.</p>
+ * Creates an empty {@link CustomProperty}. The set methods must be
+ * called to make it usable.
*/
- public CustomProperty()
- {
+ public CustomProperty() {
this.name = null;
}
/**
- * <p>Creates a {@link CustomProperty} without a name by copying the
- * underlying {@link Property}' attributes.</p>
+ * Creates a {@link CustomProperty} without a name by copying the
+ * underlying {@link Property}' attributes.
*
* @param property the property to copy
*/
- public CustomProperty(final Property property)
- {
+ public CustomProperty(final Property property) {
this(property, null);
}
/**
- * <p>Creates a {@link CustomProperty} with a name.</p>
+ * Creates a {@link CustomProperty} with a name.
*
* @param property This property's attributes are copied to the new custom
* property.
* @param name The new custom property's name.
*/
- public CustomProperty(final Property property, final String name)
- {
+ public CustomProperty(final Property property, final String name) {
super(property);
this.name = name;
}
/**
- * <p>Gets the property's name.</p>
+ * Gets the property's name.
*
* @return the property's name.
*/
- public String getName()
- {
+ public String getName() {
return name;
}
/**
- * <p>Sets the property's name.</p>
+ * Sets the property's name.
*
* @param name The name to set.
*/
- public void setName(final String name)
- {
+ public void setName(final String name) {
this.name = name;
}
/**
- * <p>Compares two custom properties for equality. The method returns
- * <code>true</code> if all attributes of the two custom properties are
- * equal.</p>
+ * Compares two custom properties for equality. The method returns
+ * {@code true} if all attributes of the two custom properties are
+ * equal.
*
* @param o The custom property to compare with.
- * @return <code>true</code> if both custom properties are equal, else
- * <code>false</code>.
+ * @return {@code true} if both custom properties are equal, else
+ * {@code false}.
*
* @see java.util.AbstractSet#equals(java.lang.Object)
*/
- public boolean equalsContents(final Object o)
- {
+ public boolean equalsContents(final Object o) {
final CustomProperty c = (CustomProperty) o;
final String name1 = c.getName();
final String name2 = this.getName();
boolean equalNames = true;
- if (name1 == null)
+ if (name1 == null) {
equalNames = name2 == null;
- else
+ } else {
equalNames = name1.equals(name2);
+ }
return equalNames && c.getID() == this.getID()
&& c.getType() == this.getType()
&& c.getValue().equals(this.getValue());
@@ -112,8 +107,7 @@ public class CustomProperty extends MutableProperty
* @see java.util.AbstractSet#hashCode()
*/
@Override
- public int hashCode()
- {
+ public int hashCode() {
return (int) this.getID();
}
diff --git a/src/java/org/apache/poi/hpsf/DocumentSummaryInformation.java b/src/java/org/apache/poi/hpsf/DocumentSummaryInformation.java
index 0f8c629cf8..f63ab8d92d 100644
--- a/src/java/org/apache/poi/hpsf/DocumentSummaryInformation.java
+++ b/src/java/org/apache/poi/hpsf/DocumentSummaryInformation.java
@@ -17,7 +17,8 @@
package org.apache.poi.hpsf;
-import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
import java.util.Map;
import org.apache.poi.hpsf.wellknown.PropertyIDMap;
@@ -25,16 +26,15 @@ import org.apache.poi.hpsf.wellknown.SectionIDMap;
import org.apache.poi.util.CodePageUtil;
/**
- * <p>Convenience class representing a DocumentSummary Information stream in a
- * Microsoft Office document.</p>
+ * Convenience class representing a DocumentSummary Information stream in a
+ * Microsoft Office document.
*
* @see SummaryInformation
*/
-public class DocumentSummaryInformation extends SpecialPropertySet
-{
+public class DocumentSummaryInformation extends SpecialPropertySet {
/**
- * <p>The document name a document summary information stream
- * usually has in a POIFS filesystem.</p>
+ * The document name a document summary information stream
+ * usually has in a POIFS filesystem.
*/
public static final String DEFAULT_STREAM_NAME =
"\005DocumentSummaryInformation";
@@ -46,351 +46,308 @@ public class DocumentSummaryInformation extends SpecialPropertySet
/**
- * <p>Creates a {@link DocumentSummaryInformation} from a given
- * {@link PropertySet}.</p>
+ * Creates an empty {@link DocumentSummaryInformation}.
+ */
+ public DocumentSummaryInformation() {
+ getFirstSection().setFormatID(SectionIDMap.DOCUMENT_SUMMARY_INFORMATION_ID[0]);
+ }
+
+
+ /**
+ * Creates a {@link DocumentSummaryInformation} from a given
+ * {@link PropertySet}.
*
* @param ps A property set which should be created from a
* document summary information stream.
- * @throws UnexpectedPropertySetTypeException if <var>ps</var>
+ * @throws UnexpectedPropertySetTypeException if {@code ps}
* does not contain a document summary information stream.
*/
public DocumentSummaryInformation(final PropertySet ps)
- throws UnexpectedPropertySetTypeException
- {
+ throws UnexpectedPropertySetTypeException {
super(ps);
- if (!isDocumentSummaryInformation())
- throw new UnexpectedPropertySetTypeException
- ("Not a " + getClass().getName());
+ if (!isDocumentSummaryInformation()) {
+ throw new UnexpectedPropertySetTypeException("Not a " + getClass().getName());
+ }
}
/**
- * <p>Returns the category (or {@code null}).</p>
+ * Returns the category (or {@code null}).
*
* @return The category value
*/
- public String getCategory()
- {
+ public String getCategory() {
return getPropertyStringValue(PropertyIDMap.PID_CATEGORY);
}
/**
- * <p>Sets the category.</p>
+ * Sets the category.
*
* @param category The category to set.
*/
- public void setCategory(final String category)
- {
- final MutableSection s = (MutableSection) getFirstSection();
- s.setProperty(PropertyIDMap.PID_CATEGORY, category);
+ public void setCategory(final String category) {
+ getFirstSection().setProperty(PropertyIDMap.PID_CATEGORY, category);
}
/**
- * <p>Removes the category.</p>
+ * Removes the category.
*/
- public void removeCategory()
- {
- final MutableSection s = (MutableSection) getFirstSection();
- s.removeProperty(PropertyIDMap.PID_CATEGORY);
+ public void removeCategory() {
+ remove1stProperty(PropertyIDMap.PID_CATEGORY);
}
/**
- * <p>Returns the presentation format (or
- * {@code null}).</p>
+ * Returns the presentation format (or
+ * {@code null}).
*
* @return The presentation format value
*/
- public String getPresentationFormat()
- {
+ public String getPresentationFormat() {
return getPropertyStringValue(PropertyIDMap.PID_PRESFORMAT);
}
/**
- * <p>Sets the presentation format.</p>
+ * Sets the presentation format.
*
* @param presentationFormat The presentation format to set.
*/
- public void setPresentationFormat(final String presentationFormat)
- {
- final MutableSection s = (MutableSection) getFirstSection();
- s.setProperty(PropertyIDMap.PID_PRESFORMAT, presentationFormat);
+ public void setPresentationFormat(final String presentationFormat) {
+ getFirstSection().setProperty(PropertyIDMap.PID_PRESFORMAT, presentationFormat);
}
/**
- * <p>Removes the presentation format.</p>
+ * Removes the presentation format.
*/
- public void removePresentationFormat()
- {
- final MutableSection s = (MutableSection) getFirstSection();
- s.removeProperty(PropertyIDMap.PID_PRESFORMAT);
+ public void removePresentationFormat() {
+ remove1stProperty(PropertyIDMap.PID_PRESFORMAT);
}
/**
- * <p>Returns the byte count or 0 if the {@link
- * DocumentSummaryInformation} does not contain a byte count.</p>
+ * Returns the byte count or 0 if the {@link
+ * DocumentSummaryInformation} does not contain a byte count.
*
* @return The byteCount value
*/
- public int getByteCount()
- {
+ public int getByteCount() {
return getPropertyIntValue(PropertyIDMap.PID_BYTECOUNT);
}
/**
- * <p>Sets the byte count.</p>
+ * Sets the byte count.
*
* @param byteCount The byte count to set.
*/
- public void setByteCount(final int byteCount)
- {
- final MutableSection s = (MutableSection) getFirstSection();
- s.setProperty(PropertyIDMap.PID_BYTECOUNT, byteCount);
+ public void setByteCount(final int byteCount) {
+ set1stProperty(PropertyIDMap.PID_BYTECOUNT, byteCount);
}
/**
- * <p>Removes the byte count.</p>
+ * Removes the byte count.
*/
- public void removeByteCount()
- {
- final MutableSection s = (MutableSection) getFirstSection();
- s.removeProperty(PropertyIDMap.PID_BYTECOUNT);
+ public void removeByteCount() {
+ remove1stProperty(PropertyIDMap.PID_BYTECOUNT);
}
/**
- * <p>Returns the line count or 0 if the {@link
- * DocumentSummaryInformation} does not contain a line count.</p>
+ * Returns the line count or 0 if the {@link
+ * DocumentSummaryInformation} does not contain a line count.
*
* @return The line count value
*/
- public int getLineCount()
- {
+ public int getLineCount() {
return getPropertyIntValue(PropertyIDMap.PID_LINECOUNT);
}
/**
- * <p>Sets the line count.</p>
+ * Sets the line count.
*
* @param lineCount The line count to set.
*/
- public void setLineCount(final int lineCount)
- {
- final MutableSection s = (MutableSection) getFirstSection();
- s.setProperty(PropertyIDMap.PID_LINECOUNT, lineCount);
+ public void setLineCount(final int lineCount) {
+ set1stProperty(PropertyIDMap.PID_LINECOUNT, lineCount);
}
/**
- * <p>Removes the line count.</p>
+ * Removes the line count.
*/
- public void removeLineCount()
- {
- final MutableSection s = (MutableSection) getFirstSection();
- s.removeProperty(PropertyIDMap.PID_LINECOUNT);
+ public void removeLineCount() {
+ remove1stProperty(PropertyIDMap.PID_LINECOUNT);
}
/**
- * <p>Returns the par count or 0 if the {@link
- * DocumentSummaryInformation} does not contain a par count.</p>
+ * Returns the par count or 0 if the {@link
+ * DocumentSummaryInformation} does not contain a par count.
*
* @return The par count value
*/
- public int getParCount()
- {
+ public int getParCount() {
return getPropertyIntValue(PropertyIDMap.PID_PARCOUNT);
}
/**
- * <p>Sets the par count.</p>
+ * Sets the par count.
*
* @param parCount The par count to set.
*/
- public void setParCount(final int parCount)
- {
- final MutableSection s = (MutableSection) getFirstSection();
- s.setProperty(PropertyIDMap.PID_PARCOUNT, parCount);
+ public void setParCount(final int parCount) {
+ set1stProperty(PropertyIDMap.PID_PARCOUNT, parCount);
}
/**
- * <p>Removes the par count.</p>
+ * Removes the par count.
*/
- public void removeParCount()
- {
- final MutableSection s = (MutableSection) getFirstSection();
- s.removeProperty(PropertyIDMap.PID_PARCOUNT);
+ public void removeParCount() {
+ remove1stProperty(PropertyIDMap.PID_PARCOUNT);
}
/**
- * <p>Returns the slide count or 0 if the {@link
- * DocumentSummaryInformation} does not contain a slide count.</p>
+ * Returns the slide count or 0 if the {@link
+ * DocumentSummaryInformation} does not contain a slide count.
*
* @return The slide count value
*/
- public int getSlideCount()
- {
+ public int getSlideCount() {
return getPropertyIntValue(PropertyIDMap.PID_SLIDECOUNT);
}
/**
- * <p>Sets the slideCount.</p>
+ * Sets the slideCount.
*
* @param slideCount The slide count to set.
*/
- public void setSlideCount(final int slideCount)
- {
- final MutableSection s = (MutableSection) getFirstSection();
- s.setProperty(PropertyIDMap.PID_SLIDECOUNT, slideCount);
+ public void setSlideCount(final int slideCount) {
+ set1stProperty(PropertyIDMap.PID_SLIDECOUNT, slideCount);
}
/**
- * <p>Removes the slide count.</p>
+ * Removes the slide count.
*/
- public void removeSlideCount()
- {
- final MutableSection s = (MutableSection) getFirstSection();
- s.removeProperty(PropertyIDMap.PID_SLIDECOUNT);
+ public void removeSlideCount() {
+ remove1stProperty(PropertyIDMap.PID_SLIDECOUNT);
}
/**
- * <p>Returns the note count or 0 if the {@link
- * DocumentSummaryInformation} does not contain a note count.</p>
+ * Returns the note count or 0 if the {@link
+ * DocumentSummaryInformation} does not contain a note count.
*
* @return The note count value
*/
- public int getNoteCount()
- {
+ public int getNoteCount() {
return getPropertyIntValue(PropertyIDMap.PID_NOTECOUNT);
}
/**
- * <p>Sets the note count.</p>
+ * Sets the note count.
*
* @param noteCount The note count to set.
*/
- public void setNoteCount(final int noteCount)
- {
- final MutableSection s = (MutableSection) getFirstSection();
- s.setProperty(PropertyIDMap.PID_NOTECOUNT, noteCount);
+ public void setNoteCount(final int noteCount) {
+ set1stProperty(PropertyIDMap.PID_NOTECOUNT, noteCount);
}
/**
- * <p>Removes the noteCount.</p>
+ * Removes the noteCount.
*/
- public void removeNoteCount()
- {
- final MutableSection s = (MutableSection) getFirstSection();
- s.removeProperty(PropertyIDMap.PID_NOTECOUNT);
+ public void removeNoteCount() {
+ remove1stProperty(PropertyIDMap.PID_NOTECOUNT);
}
/**
- * <p>Returns the hidden count or 0 if the {@link
+ * Returns the hidden count or 0 if the {@link
* DocumentSummaryInformation} does not contain a hidden
- * count.</p>
+ * count.
*
* @return The hidden count value
*/
- public int getHiddenCount()
- {
+ public int getHiddenCount() {
return getPropertyIntValue(PropertyIDMap.PID_HIDDENCOUNT);
}
/**
- * <p>Sets the hidden count.</p>
+ * Sets the hidden count.
*
* @param hiddenCount The hidden count to set.
*/
- public void setHiddenCount(final int hiddenCount)
- {
- final MutableSection s = (MutableSection) getSections().get(0);
- s.setProperty(PropertyIDMap.PID_HIDDENCOUNT, hiddenCount);
+ public void setHiddenCount(final int hiddenCount) {
+ set1stProperty(PropertyIDMap.PID_HIDDENCOUNT, hiddenCount);
}
/**
- * <p>Removes the hidden count.</p>
+ * Removes the hidden count.
*/
- public void removeHiddenCount()
- {
- final MutableSection s = (MutableSection) getFirstSection();
- s.removeProperty(PropertyIDMap.PID_HIDDENCOUNT);
+ public void removeHiddenCount() {
+ remove1stProperty(PropertyIDMap.PID_HIDDENCOUNT);
}
/**
- * <p>Returns the mmclip count or 0 if the {@link
+ * Returns the mmclip count or 0 if the {@link
* DocumentSummaryInformation} does not contain a mmclip
- * count.</p>
+ * count.
*
* @return The mmclip count value
*/
- public int getMMClipCount()
- {
+ public int getMMClipCount() {
return getPropertyIntValue(PropertyIDMap.PID_MMCLIPCOUNT);
}
/**
- * <p>Sets the mmclip count.</p>
+ * Sets the mmclip count.
*
* @param mmClipCount The mmclip count to set.
*/
- public void setMMClipCount(final int mmClipCount)
- {
- final MutableSection s = (MutableSection) getFirstSection();
- s.setProperty(PropertyIDMap.PID_MMCLIPCOUNT, mmClipCount);
+ public void setMMClipCount(final int mmClipCount) {
+ set1stProperty(PropertyIDMap.PID_MMCLIPCOUNT, mmClipCount);
}
/**
- * <p>Removes the mmclip count.</p>
+ * Removes the mmclip count.
*/
- public void removeMMClipCount()
- {
- final MutableSection s = (MutableSection) getFirstSection();
- s.removeProperty(PropertyIDMap.PID_MMCLIPCOUNT);
+ public void removeMMClipCount() {
+ remove1stProperty(PropertyIDMap.PID_MMCLIPCOUNT);
}
/**
- * <p>Returns <code>true</code> when scaling of the thumbnail is
- * desired, <code>false</code> if cropping is desired.</p>
+ * Returns {@code true} when scaling of the thumbnail is
+ * desired, {@code false} if cropping is desired.
*
* @return The scale value
*/
- public boolean getScale()
- {
+ public boolean getScale() {
return getPropertyBooleanValue(PropertyIDMap.PID_SCALE);
}
/**
- * <p>Sets the scale.</p>
+ * Sets the scale.
*
* @param scale The scale to set.
*/
- public void setScale(final boolean scale)
- {
- final MutableSection s = (MutableSection) getFirstSection();
- s.setProperty(PropertyIDMap.PID_SCALE, scale);
+ public void setScale(final boolean scale) {
+ set1stProperty(PropertyIDMap.PID_SCALE, scale);
}
/**
- * <p>Removes the scale.</p>
+ * Removes the scale.
*/
- public void removeScale()
- {
- final MutableSection s = (MutableSection) getFirstSection();
- s.removeProperty(PropertyIDMap.PID_SCALE);
+ public void removeScale() {
+ remove1stProperty(PropertyIDMap.PID_SCALE);
}
@@ -402,29 +359,25 @@ public class DocumentSummaryInformation extends SpecialPropertySet
*
* @return The heading pair value
*/
- public byte[] getHeadingPair()
- {
+ public byte[] getHeadingPair() {
notYetImplemented("Reading byte arrays ");
return (byte[]) getProperty(PropertyIDMap.PID_HEADINGPAIR);
}
/**
- * <p>Sets the heading pair.</p>
+ * Sets the heading pair.
*
* @param headingPair The heading pair to set.
*/
- public void setHeadingPair(final byte[] headingPair)
- {
+ public void setHeadingPair(final byte[] headingPair) {
notYetImplemented("Writing byte arrays ");
}
/**
- * <p>Removes the heading pair.</p>
+ * Removes the heading pair.
*/
- public void removeHeadingPair()
- {
- final MutableSection s = (MutableSection) getFirstSection();
- s.removeProperty(PropertyIDMap.PID_HEADINGPAIR);
+ public void removeHeadingPair() {
+ remove1stProperty(PropertyIDMap.PID_HEADINGPAIR);
}
@@ -436,8 +389,7 @@ public class DocumentSummaryInformation extends SpecialPropertySet
*
* @return The doc parts value
*/
- public byte[] getDocparts()
- {
+ public byte[] getDocparts() {
notYetImplemented("Reading byte arrays");
return (byte[]) getProperty(PropertyIDMap.PID_DOCPARTS);
}
@@ -445,129 +397,110 @@ public class DocumentSummaryInformation extends SpecialPropertySet
/**
- * <p>Sets the doc parts.</p>
+ * Sets the doc parts.
*
* @param docparts The doc parts to set.
*/
- public void setDocparts(final byte[] docparts)
- {
+ public void setDocparts(final byte[] docparts) {
notYetImplemented("Writing byte arrays");
}
/**
- * <p>Removes the doc parts.</p>
+ * Removes the doc parts.
*/
- public void removeDocparts()
- {
- final MutableSection s = (MutableSection) getFirstSection();
- s.removeProperty(PropertyIDMap.PID_DOCPARTS);
+ public void removeDocparts() {
+ remove1stProperty(PropertyIDMap.PID_DOCPARTS);
}
/**
- * <p>Returns the manager (or {@code null}).</p>
+ * Returns the manager (or {@code null}).
*
* @return The manager value
*/
- public String getManager()
- {
+ public String getManager() {
return getPropertyStringValue(PropertyIDMap.PID_MANAGER);
}
/**
- * <p>Sets the manager.</p>
+ * Sets the manager.
*
* @param manager The manager to set.
*/
- public void setManager(final String manager)
- {
- final MutableSection s = (MutableSection) getFirstSection();
- s.setProperty(PropertyIDMap.PID_MANAGER, manager);
+ public void setManager(final String manager) {
+ set1stProperty(PropertyIDMap.PID_MANAGER, manager);
}
/**
- * <p>Removes the manager.</p>
+ * Removes the manager.
*/
- public void removeManager()
- {
- final MutableSection s = (MutableSection) getFirstSection();
- s.removeProperty(PropertyIDMap.PID_MANAGER);
+ public void removeManager() {
+ remove1stProperty(PropertyIDMap.PID_MANAGER);
}
/**
- * <p>Returns the company (or {@code null}).</p>
+ * Returns the company (or {@code null}).
*
* @return The company value
*/
- public String getCompany()
- {
+ public String getCompany() {
return getPropertyStringValue(PropertyIDMap.PID_COMPANY);
}
/**
- * <p>Sets the company.</p>
+ * Sets the company.
*
* @param company The company to set.
*/
- public void setCompany(final String company)
- {
- final MutableSection s = (MutableSection) getFirstSection();
- s.setProperty(PropertyIDMap.PID_COMPANY, company);
+ public void setCompany(final String company) {
+ set1stProperty(PropertyIDMap.PID_COMPANY, company);
}
/**
- * <p>Removes the company.</p>
+ * Removes the company.
*/
- public void removeCompany()
- {
- final MutableSection s = (MutableSection) getFirstSection();
- s.removeProperty(PropertyIDMap.PID_COMPANY);
+ public void removeCompany() {
+ remove1stProperty(PropertyIDMap.PID_COMPANY);
}
/**
- * <p>Returns <code>true</code> if the custom links are dirty.</p> <p>
+ * Returns {@code true} if the custom links are dirty. <p>
*
* @return The links dirty value
*/
- public boolean getLinksDirty()
- {
+ public boolean getLinksDirty() {
return getPropertyBooleanValue(PropertyIDMap.PID_LINKSDIRTY);
}
/**
- * <p>Sets the linksDirty.</p>
+ * Sets the linksDirty.
*
* @param linksDirty The links dirty value to set.
*/
- public void setLinksDirty(final boolean linksDirty)
- {
- final MutableSection s = (MutableSection) getFirstSection();
- s.setProperty(PropertyIDMap.PID_LINKSDIRTY, linksDirty);
+ public void setLinksDirty(final boolean linksDirty) {
+ set1stProperty(PropertyIDMap.PID_LINKSDIRTY, linksDirty);
}
/**
- * <p>Removes the links dirty.</p>
+ * Removes the links dirty.
*/
- public void removeLinksDirty()
- {
- final MutableSection s = (MutableSection) getFirstSection();
- s.removeProperty(PropertyIDMap.PID_LINKSDIRTY);
+ public void removeLinksDirty() {
+ remove1stProperty(PropertyIDMap.PID_LINKSDIRTY);
}
/**
- * <p>Returns the character count including whitespace, or 0 if the
- * {@link DocumentSummaryInformation} does not contain this char count.</p>
+ * Returns the character count including whitespace, or 0 if the
+ * {@link DocumentSummaryInformation} does not contain this char count.
* <p>This is the whitespace-including version of {@link SummaryInformation#getCharCount()}
*
* @return The character count or {@code null}
*/
- public int getCharCountWithSpaces()
- {
+ public int getCharCountWithSpaces() {
return getPropertyIntValue(PropertyIDMap.PID_CCHWITHSPACES);
}
@@ -576,19 +509,15 @@ public class DocumentSummaryInformation extends SpecialPropertySet
*
* @param count The character count to set.
*/
- public void setCharCountWithSpaces(int count)
- {
- final MutableSection s = (MutableSection) getFirstSection();
- s.setProperty(PropertyIDMap.PID_CCHWITHSPACES, count);
+ public void setCharCountWithSpaces(int count) {
+ set1stProperty(PropertyIDMap.PID_CCHWITHSPACES, count);
}
/**
* Removes the character count
*/
- public void removeCharCountWithSpaces()
- {
- final MutableSection s = (MutableSection) getFirstSection();
- s.removeProperty(PropertyIDMap.PID_CCHWITHSPACES);
+ public void removeCharCountWithSpaces() {
+ remove1stProperty(PropertyIDMap.PID_CCHWITHSPACES);
}
@@ -599,8 +528,7 @@ public class DocumentSummaryInformation extends SpecialPropertySet
*
* @return true, if the hyperlinks should be updated on document load
*/
- public boolean getHyperlinksChanged()
- {
+ public boolean getHyperlinksChanged() {
return getPropertyBooleanValue(PropertyIDMap.PID_HYPERLINKSCHANGED);
}
@@ -610,20 +538,16 @@ public class DocumentSummaryInformation extends SpecialPropertySet
*
* @param changed true, if the User Defined Property Set has been updated
*/
- public void setHyperlinksChanged(boolean changed)
- {
- final MutableSection s = (MutableSection) getFirstSection();
- s.setProperty(PropertyIDMap.PID_HYPERLINKSCHANGED, changed);
+ public void setHyperlinksChanged(boolean changed) {
+ set1stProperty(PropertyIDMap.PID_HYPERLINKSCHANGED, changed);
}
/**
* Removes the flag for if the User Defined Property Set has been updated
* outside of the Application.
*/
- public void removeHyperlinksChanged()
- {
- final MutableSection s = (MutableSection) getFirstSection();
- s.removeProperty(PropertyIDMap.PID_HYPERLINKSCHANGED);
+ public void removeHyperlinksChanged() {
+ remove1stProperty(PropertyIDMap.PID_HYPERLINKSCHANGED);
}
@@ -635,8 +559,7 @@ public class DocumentSummaryInformation extends SpecialPropertySet
*
* @return the Application version
*/
- public int getApplicationVersion()
- {
+ public int getApplicationVersion() {
return getPropertyIntValue(PropertyIDMap.PID_VERSION);
}
@@ -647,19 +570,15 @@ public class DocumentSummaryInformation extends SpecialPropertySet
*
* @param version the Application version
*/
- public void setApplicationVersion(int version)
- {
- final MutableSection s = (MutableSection) getFirstSection();
- s.setProperty(PropertyIDMap.PID_VERSION, version);
+ public void setApplicationVersion(int version) {
+ set1stProperty(PropertyIDMap.PID_VERSION, version);
}
/**
* Removes the Application Version
*/
- public void removeApplicationVersion()
- {
- final MutableSection s = (MutableSection) getFirstSection();
- s.removeProperty(PropertyIDMap.PID_VERSION);
+ public void removeApplicationVersion() {
+ remove1stProperty(PropertyIDMap.PID_VERSION);
}
@@ -669,8 +588,7 @@ public class DocumentSummaryInformation extends SpecialPropertySet
*
* @return the VBA digital signature
*/
- public byte[] getVBADigitalSignature()
- {
+ public byte[] getVBADigitalSignature() {
Object value = getProperty(PropertyIDMap.PID_DIGSIG);
if (value != null && value instanceof byte[]) {
return (byte[])value;
@@ -679,24 +597,20 @@ public class DocumentSummaryInformation extends SpecialPropertySet
}
/**
- * <p>Sets the VBA digital signature for the VBA project
- * embedded in the document.</p>
+ * Sets the VBA digital signature for the VBA project
+ * embedded in the document.
*
* @param signature VBA Digital Signature for the project
*/
- public void setVBADigitalSignature(byte[] signature)
- {
- final MutableSection s = (MutableSection) getFirstSection();
- s.setProperty(PropertyIDMap.PID_DIGSIG, signature);
+ public void setVBADigitalSignature(byte[] signature) {
+ set1stProperty(PropertyIDMap.PID_DIGSIG, signature);
}
/**
* Removes the VBA Digital Signature
*/
- public void removeVBADigitalSignature()
- {
- final MutableSection s = (MutableSection) getFirstSection();
- s.removeProperty(PropertyIDMap.PID_DIGSIG);
+ public void removeVBADigitalSignature() {
+ remove1stProperty(PropertyIDMap.PID_DIGSIG);
}
@@ -705,8 +619,7 @@ public class DocumentSummaryInformation extends SpecialPropertySet
*
* @return the content type of the file
*/
- public String getContentType()
- {
+ public String getContentType() {
return getPropertyStringValue(PropertyIDMap.PID_CONTENTTYPE);
}
@@ -715,19 +628,15 @@ public class DocumentSummaryInformation extends SpecialPropertySet
*
* @param type the content type of the file
*/
- public void setContentType(String type)
- {
- final MutableSection s = (MutableSection) getFirstSection();
- s.setProperty(PropertyIDMap.PID_CONTENTTYPE, type);
+ public void setContentType(String type) {
+ set1stProperty(PropertyIDMap.PID_CONTENTTYPE, type);
}
/**
* Removes the content type of the file
*/
- public void removeContentType()
- {
- final MutableSection s = (MutableSection) getFirstSection();
- s.removeProperty(PropertyIDMap.PID_CONTENTTYPE);
+ public void removeContentType() {
+ remove1stProperty(PropertyIDMap.PID_CONTENTTYPE);
}
@@ -736,8 +645,7 @@ public class DocumentSummaryInformation extends SpecialPropertySet
*
* @return the content status of the file
*/
- public String getContentStatus()
- {
+ public String getContentStatus() {
return getPropertyStringValue(PropertyIDMap.PID_CONTENTSTATUS);
}
@@ -746,19 +654,15 @@ public class DocumentSummaryInformation extends SpecialPropertySet
*
* @param status the content status of the file
*/
- public void setContentStatus(String status)
- {
- final MutableSection s = (MutableSection) getFirstSection();
- s.setProperty(PropertyIDMap.PID_CONTENTSTATUS, status);
+ public void setContentStatus(String status) {
+ set1stProperty(PropertyIDMap.PID_CONTENTSTATUS, status);
}
/**
* Removes the content status of the file
*/
- public void removeContentStatus()
- {
- final MutableSection s = (MutableSection) getFirstSection();
- s.removeProperty(PropertyIDMap.PID_CONTENTSTATUS);
+ public void removeContentStatus() {
+ remove1stProperty(PropertyIDMap.PID_CONTENTSTATUS);
}
@@ -767,8 +671,7 @@ public class DocumentSummaryInformation extends SpecialPropertySet
*
* @return the document language
*/
- public String getLanguage()
- {
+ public String getLanguage() {
return getPropertyStringValue(PropertyIDMap.PID_LANGUAGE);
}
@@ -777,30 +680,25 @@ public class DocumentSummaryInformation extends SpecialPropertySet
*
* @param language the document language
*/
- public void setLanguage(String language)
- {
- final MutableSection s = (MutableSection) getFirstSection();
- s.setProperty(PropertyIDMap.PID_LANGUAGE, language);
+ public void setLanguage(String language) {
+ set1stProperty(PropertyIDMap.PID_LANGUAGE, language);
}
/**
* Removes the document language
*/
- public void removeLanguage()
- {
- final MutableSection s = (MutableSection) getFirstSection();
- s.removeProperty(PropertyIDMap.PID_LANGUAGE);
+ public void removeLanguage() {
+ remove1stProperty(PropertyIDMap.PID_LANGUAGE);
}
/**
- * <p>Gets the document version as a string, which is normally unset and empty
- * (or {@code null}).</p>
+ * Gets the document version as a string, which is normally unset and empty
+ * (or {@code null}).
*
* @return the document verion
*/
- public String getDocumentVersion()
- {
+ public String getDocumentVersion() {
return getPropertyStringValue(PropertyIDMap.PID_DOCVERSION);
}
@@ -809,64 +707,56 @@ public class DocumentSummaryInformation extends SpecialPropertySet
*
* @param version the document version string
*/
- public void setDocumentVersion(String version)
- {
- final MutableSection s = (MutableSection) getFirstSection();
- s.setProperty(PropertyIDMap.PID_DOCVERSION, version);
+ public void setDocumentVersion(String version) {
+ set1stProperty(PropertyIDMap.PID_DOCVERSION, version);
}
/**
* Removes the document version string
*/
- public void removeDocumentVersion()
- {
- final MutableSection s = (MutableSection) getFirstSection();
- s.removeProperty(PropertyIDMap.PID_DOCVERSION);
+ public void removeDocumentVersion() {
+ remove1stProperty(PropertyIDMap.PID_DOCVERSION);
}
/**
- * <p>Gets the custom properties.</p>
+ * Gets the custom properties.
*
* @return The custom properties.
*/
- public CustomProperties getCustomProperties()
- {
+ public CustomProperties getCustomProperties() {
CustomProperties cps = null;
- if (getSectionCount() >= 2)
- {
+ if (getSectionCount() >= 2) {
cps = new CustomProperties();
final Section section = getSections().get(1);
final Map<Long,String> dictionary = section.getDictionary();
final Property[] properties = section.getProperties();
int propertyCount = 0;
- for (int i = 0; i < properties.length; i++)
- {
+ for (int i = 0; i < properties.length; i++) {
final Property p = properties[i];
final long id = p.getID();
- if (id != 0 && id != 1)
- {
+ if (id != 0 && id != 1) {
propertyCount++;
final CustomProperty cp = new CustomProperty(p,
dictionary.get(Long.valueOf(id)));
cps.put(cp.getName(), cp);
}
}
- if (cps.size() != propertyCount)
+ if (cps.size() != propertyCount) {
cps.setPure(false);
+ }
}
return cps;
}
/**
- * <p>Sets the custom properties.</p>
+ * Sets the custom properties.
*
* @param customProperties The custom properties
*/
- public void setCustomProperties(final CustomProperties customProperties)
- {
+ public void setCustomProperties(final CustomProperties customProperties) {
ensureSection2();
- final MutableSection section = (MutableSection) getSections().get(1);
+ final Section section = getSections().get(1);
final Map<Long,String> dictionary = customProperties.getDictionary();
section.clear();
@@ -874,55 +764,57 @@ public class DocumentSummaryInformation extends SpecialPropertySet
* codepage, the codepage from the custom properties wins, else take the
* one that is defined. If none is defined, take Unicode. */
int cpCodepage = customProperties.getCodepage();
- if (cpCodepage < 0)
+ if (cpCodepage < 0) {
cpCodepage = section.getCodepage();
- if (cpCodepage < 0)
+ }
+ if (cpCodepage < 0) {
cpCodepage = CodePageUtil.CP_UNICODE;
+ }
customProperties.setCodepage(cpCodepage);
section.setCodepage(cpCodepage);
section.setDictionary(dictionary);
- for (final Iterator<CustomProperty> i = customProperties.values().iterator(); i.hasNext();)
- {
- final Property p = i.next();
+ for (CustomProperty p : customProperties.values()) {
section.setProperty(p);
}
}
/**
- * <p>Creates section 2 if it is not already present.</p>
- *
+ * Creates section 2 if it is not already present.
*/
- private void ensureSection2()
- {
- if (getSectionCount() < 2)
- {
- MutableSection s2 = new MutableSection();
+ private void ensureSection2() {
+ if (getSectionCount() < 2) {
+ Section s2 = new MutableSection();
s2.setFormatID(SectionIDMap.DOCUMENT_SUMMARY_INFORMATION_ID[1]);
addSection(s2);
}
}
/**
- * <p>Removes the custom properties.</p>
+ * Removes the custom properties.
*/
- public void removeCustomProperties()
- {
- if (getSectionCount() >= 2)
- getSections().remove(1);
- else
+ public void removeCustomProperties() {
+ if (getSectionCount() < 2) {
throw new HPSFRuntimeException("Illegal internal format of Document SummaryInformation stream: second section is missing.");
- }
-
+ }
+ List<Section> l = new LinkedList<Section>(getSections());
+ clearSections();
+ int idx = 0;
+ for (Section s : l) {
+ if (idx++ != 1) {
+ addSection(s);
+ }
+ }
+ }
+
/**
- * <p>Throws an {@link UnsupportedOperationException} with a message text
- * telling which functionality is not yet implemented.</p>
+ * Throws an {@link UnsupportedOperationException} with a message text
+ * telling which functionality is not yet implemented.
*
* @param msg text telling was leaves to be implemented, e.g.
* "Reading byte arrays".
*/
- private void notYetImplemented(final String msg)
- {
+ private void notYetImplemented(final String msg) {
throw new UnsupportedOperationException(msg + " is not yet implemented.");
}
}
diff --git a/src/java/org/apache/poi/hpsf/MutableProperty.java b/src/java/org/apache/poi/hpsf/MutableProperty.java
index 9d77c0dd08..10d86be040 100644
--- a/src/java/org/apache/poi/hpsf/MutableProperty.java
+++ b/src/java/org/apache/poi/hpsf/MutableProperty.java
@@ -17,103 +17,22 @@
package org.apache.poi.hpsf;
-import java.io.IOException;
-import java.io.OutputStream;
-
-import org.apache.poi.util.CodePageUtil;
+import org.apache.poi.util.Removal;
/**
* <p>Adds writing capability to the {@link Property} class.</p>
*
* <p>Please be aware that this class' functionality will be merged into the
* {@link Property} class at a later time, so the API will change.</p>
+ *
+ * @deprecated POI 3.16 - use Property as base class instead
*/
-public class MutableProperty extends Property
-{
-
- /**
- * <p>Creates an empty property. It must be filled using the set method to
- * be usable.</p>
- */
- public MutableProperty()
- { }
-
-
-
- /**
- * <p>Creates a <code>MutableProperty</code> as a copy of an existing
- * <code>Property</code>.</p>
- *
- * @param p The property to copy.
- */
- public MutableProperty(final Property p)
- {
- setID(p.getID());
- setType(p.getType());
- setValue(p.getValue());
- }
-
-
- /**
- * <p>Sets the property's ID.</p>
- *
- * @param id the ID
- */
- public void setID(final long id)
- {
- this.id = id;
- }
-
-
-
- /**
- * <p>Sets the property's type.</p>
- *
- * @param type the property's type
- */
- public void setType(final long type)
- {
- this.type = type;
- }
-
-
-
- /**
- * <p>Sets the property's value.</p>
- *
- * @param value the property's value
- */
- public void setValue(final Object value)
- {
- this.value = value;
- }
-
-
-
- /**
- * <p>Writes the property to an output stream.</p>
- *
- * @param out The output stream to write to.
- * @param codepage The codepage to use for writing non-wide strings
- * @return the number of bytes written to the stream
- *
- * @exception IOException if an I/O error occurs
- * @exception WritingNotSupportedException if a variant type is to be
- * written that is not yet supported
- */
- public int write(final OutputStream out, final int codepage)
- throws IOException, WritingNotSupportedException
- {
- int length = 0;
- long variantType = getType();
-
- /* Ensure that wide strings are written if the codepage is Unicode. */
- if (codepage == CodePageUtil.CP_UNICODE && variantType == Variant.VT_LPSTR)
- variantType = Variant.VT_LPWSTR;
+@Removal(version="3.18")
+public class MutableProperty extends Property {
+ public MutableProperty() {}
- length += TypeWriter.writeUIntToStream(out, variantType);
- length += VariantSupport.write(out, variantType, getValue(), codepage);
- return length;
+ public MutableProperty(final Property p) {
+ super(p);
}
}
diff --git a/src/java/org/apache/poi/hpsf/MutablePropertySet.java b/src/java/org/apache/poi/hpsf/MutablePropertySet.java
index 5c7d386587..16978a4d6a 100644
--- a/src/java/org/apache/poi/hpsf/MutablePropertySet.java
+++ b/src/java/org/apache/poi/hpsf/MutablePropertySet.java
@@ -17,288 +17,21 @@
package org.apache.poi.hpsf;
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.io.UnsupportedEncodingException;
-import java.util.LinkedList;
-
-import org.apache.poi.poifs.filesystem.DirectoryEntry;
-import org.apache.poi.poifs.filesystem.Entry;
-import org.apache.poi.util.LittleEndian;
-import org.apache.poi.util.LittleEndianConsts;
+import org.apache.poi.util.Removal;
/**
- * <p>Adds writing support to the {@link PropertySet} class.</p>
+ * dds writing support to the {@link PropertySet} class.<p>
+ *
+ * Please be aware that this class' functionality will be merged into the
+ * {@link PropertySet} class at a later time, so the API will change.
*
- * <p>Please be aware that this class' functionality will be merged into the
- * {@link PropertySet} class at a later time, so the API will change.</p>
+ * @deprecated POI 3.16 - use PropertySet as base class instead
*/
-public class MutablePropertySet extends PropertySet
-{
-
- /**
- * <p>Constructs a <code>MutablePropertySet</code> instance. Its
- * primary task is to initialize the immutable field with their proper
- * values. It also sets fields that might change to reasonable defaults.</p>
- */
- public MutablePropertySet()
- {
- /* Initialize the "byteOrder" field. */
- byteOrder = LittleEndian.getUShort(BYTE_ORDER_ASSERTION);
-
- /* Initialize the "format" field. */
- format = LittleEndian.getUShort(FORMAT_ASSERTION);
-
- /* Initialize "osVersion" field as if the property has been created on
- * a Win32 platform, whether this is the case or not. */
- osVersion = (OS_WIN32 << 16) | 0x0A04;
-
- /* Initailize the "classID" field. */
- classID = new ClassID();
-
- /* Initialize the sections. Since property set must have at least
- * one section it is added right here. */
- sections = new LinkedList<Section>();
- sections.add(new MutableSection());
- }
-
-
-
- /**
- * <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;
+@Removal(version="3.18")
+public class MutablePropertySet extends PropertySet {
+ public MutablePropertySet() {}
+
+ public MutablePropertySet(final PropertySet ps) {
+ super(ps);
}
-
-
-
- /**
- * <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());
- }
-
}
diff --git a/src/java/org/apache/poi/hpsf/MutableSection.java b/src/java/org/apache/poi/hpsf/MutableSection.java
index b0d71acf0e..c5f0d6e0c8 100644
--- a/src/java/org/apache/poi/hpsf/MutableSection.java
+++ b/src/java/org/apache/poi/hpsf/MutableSection.java
@@ -17,683 +17,27 @@
package org.apache.poi.hpsf;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.Date;
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.ListIterator;
-import java.util.Map;
+import java.io.UnsupportedEncodingException;
-import org.apache.poi.hpsf.wellknown.PropertyIDMap;
-import org.apache.poi.util.CodePageUtil;
-import org.apache.poi.util.LittleEndian;
+import org.apache.poi.util.Removal;
/**
* <p>Adds writing capability to the {@link Section} class.</p>
*
* <p>Please be aware that this class' functionality will be merged into the
* {@link Section} class at a later time, so the API will change.</p>
+ *
+ * @deprecated POI 3.16 - use Section as base class instead
*/
-public class MutableSection extends Section
-{
- /**
- * <p>If the "dirty" flag is true, the section's size must be
- * (re-)calculated before the section is written.</p>
- */
- private boolean dirty = true;
-
-
-
- /**
- * <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>();
- }
-
-
-
- /**
- * <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;
- }
-
+@Removal(version="3.18")
+public class MutableSection extends Section {
+ public MutableSection() {}
-
- /**
- * <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();
+ public MutableSection(final Section s) {
+ super(s);
}
-
-
-
- /**
- * <p>Gets this section's properties.</p>
- *
- * @return this section's properties.
- */
- public Property[] getProperties()
- {
- properties = preprops.toArray(new Property[0]);
- return properties;
- }
-
-
-
- /**
- * <p>Gets a property.</p>
- *
- * @param id The ID of the property to get
- * @return The property or <code>null</code> if there is no such property
- */
- public Object getProperty(final long id)
- {
- /* Calling getProperties() ensures that properties and preprops are in
- * sync.</p> */
- getProperties();
- return super.getProperty(id);
- }
-
-
-
- /**
- * <p>Sets the section's dictionary. All keys in the dictionary must be
- * {@link java.lang.Long} instances, all values must be
- * {@link java.lang.String}s. This method overwrites the properties with IDs
- * 0 and 1 since they are reserved for the dictionary and the dictionary's
- * codepage. Setting these properties explicitly might have surprising
- * effects. An application should never do this but always use this
- * method.</p>
- *
- * @param dictionary The dictionary
- *
- * @exception IllegalPropertySetDataException if the dictionary's key and
- * value types are not correct.
- *
- * @see Section#getDictionary()
- */
- public void setDictionary(final Map<Long,String> dictionary)
- throws IllegalPropertySetDataException
- {
- if (dictionary != null)
- {
- this.dictionary = dictionary;
-
- /* Set the dictionary property (ID 0). Please note that the second
- * parameter in the method call below is unused because dictionaries
- * don't have a type. */
- setProperty(PropertyIDMap.PID_DICTIONARY, -1, dictionary);
-
- /* If the codepage property (ID 1) for the strings (keys and
- * values) used in the dictionary is not yet defined, set it to
- * Unicode. */
- final Integer codepage =
- (Integer) getProperty(PropertyIDMap.PID_CODEPAGE);
- if (codepage == null)
- setProperty(PropertyIDMap.PID_CODEPAGE, Variant.VT_I2,
- Integer.valueOf(CodePageUtil.CP_UNICODE));
- }
- else
- /* Setting the dictionary to null means to remove property 0.
- * However, it does not mean to remove property 1 (codepage). */
- removeProperty(PropertyIDMap.PID_DICTIONARY);
- }
-
-
-
- /**
- * <p>Sets a property.</p>
- *
- * @param id The property ID.
- * @param value The property's value. The value's class must be one of those
- * supported by HPSF.
- */
- public void setProperty(final int id, final Object value)
- {
- if (value instanceof String)
- setProperty(id, (String) value);
- else if (value instanceof Long)
- setProperty(id, ((Long) value).longValue());
- else if (value instanceof Integer)
- setProperty(id, ((Integer) value).intValue());
- else if (value instanceof Short)
- setProperty(id, ((Short) value).intValue());
- else if (value instanceof Boolean)
- setProperty(id, ((Boolean) value).booleanValue());
- else if (value instanceof Date)
- setProperty(id, Variant.VT_FILETIME, value);
- else
- throw new HPSFRuntimeException(
- "HPSF does not support properties of type " +
- value.getClass().getName() + ".");
- }
-
-
-
- /**
- * <p>Removes all properties from the section including 0 (dictionary) and
- * 1 (codepage).</p>
- */
- public void clear()
- {
- final Property[] properties = getProperties();
- for (int i = 0; i < properties.length; i++)
- {
- final Property p = properties[i];
- removeProperty(p.getID());
- }
- }
-
- /**
- * <p>Sets the codepage.</p>
- *
- * @param codepage the codepage
- */
- public void setCodepage(final int codepage)
- {
- setProperty(PropertyIDMap.PID_CODEPAGE, Variant.VT_I2,
- Integer.valueOf(codepage));
+
+ public MutableSection(final byte[] src, final int offset) throws UnsupportedEncodingException {
+ super(src,offset);
}
}
diff --git a/src/java/org/apache/poi/hpsf/NoFormatIDException.java b/src/java/org/apache/poi/hpsf/NoFormatIDException.java
index f1a09438e1..51f76c3486 100644
--- a/src/java/org/apache/poi/hpsf/NoFormatIDException.java
+++ b/src/java/org/apache/poi/hpsf/NoFormatIDException.java
@@ -18,53 +18,47 @@
package org.apache.poi.hpsf;
/**
- * <p>This exception is thrown if a {@link MutablePropertySet} is to be written
- * but does not have a formatID set (see {@link
- * MutableSection#setFormatID(ClassID)} or
- * {@link org.apache.poi.hpsf.MutableSection#setFormatID(byte[])}.
+ * This exception is thrown if a {@link PropertySet} is to be written
+ * but does not have a formatID set (see {@link Section#setFormatID(ClassID)} or
+ * {@link org.apache.poi.hpsf.Section#setFormatID(byte[])}.
*/
-public class NoFormatIDException extends HPSFRuntimeException
-{
+public class NoFormatIDException extends HPSFRuntimeException {
/**
- * <p>Constructor</p>
+ * Constructor
*/
- public NoFormatIDException()
- {
+ public NoFormatIDException() {
super();
}
/**
- * <p>Constructor</p>
+ * Constructor
*
* @param msg The exception's message string
*/
- public NoFormatIDException(final String msg)
- {
+ public NoFormatIDException(final String msg) {
super(msg);
}
/**
- * <p>Constructor</p>
+ * Constructor
*
* @param reason This exception's underlying reason
*/
- public NoFormatIDException(final Throwable reason)
- {
+ public NoFormatIDException(final Throwable reason) {
super(reason);
}
/**
- * <p>Constructor</p>
+ * Constructor
*
* @param msg The exception's message string
* @param reason This exception's underlying reason
*/
- public NoFormatIDException(final String msg, final Throwable reason)
- {
+ public NoFormatIDException(final String msg, final Throwable reason) {
super(msg, reason);
}
diff --git a/src/java/org/apache/poi/hpsf/Property.java b/src/java/org/apache/poi/hpsf/Property.java
index 14dbee0276..b9841dbcc9 100644
--- a/src/java/org/apache/poi/hpsf/Property.java
+++ b/src/java/org/apache/poi/hpsf/Property.java
@@ -17,8 +17,11 @@
package org.apache.poi.hpsf;
+import java.io.IOException;
+import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
+import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.Map;
@@ -29,104 +32,74 @@ import org.apache.poi.util.POILogFactory;
import org.apache.poi.util.POILogger;
/**
- * <p>A property in a {@link Section} of a {@link PropertySet}.</p>
+ * A property in a {@link Section} of a {@link PropertySet}.<p>
*
- * <p>The property's <strong>ID</strong> gives the property a meaning
+ * The property's {@code ID} gives the property a meaning
* in the context of its {@link Section}. Each {@link Section} spans
- * its own name space of property IDs.</p>
+ * its own name space of property IDs.<p>
*
- * <p>The property's <strong>type</strong> determines how its
- * <strong>value </strong> is interpreted. For example, if the type is
+ * The property's {@code type} determines how its
+ * {@code value} is interpreted. For example, if the type is
* {@link Variant#VT_LPSTR} (byte string), the value consists of a
* DWord telling how many bytes the string contains. The bytes follow
* immediately, including any null bytes that terminate the
* string. The type {@link Variant#VT_I4} denotes a four-byte integer
- * value, {@link Variant#VT_FILETIME} some date and time (of a
- * file).</p>
+ * value, {@link Variant#VT_FILETIME} some date and time (of a file).<p>
*
- * <p>Please note that not all {@link Variant} types yet. This might change
+ * Please note that not all {@link Variant} types yet. This might change
* over time but largely depends on your feedback so that the POI team knows
* which variant types are really needed. So please feel free to submit error
- * reports or patches for the types you need.</p>
- *
- * <p>Microsoft documentation: <a
- * href="http://msdn.microsoft.com/library/en-us/stg/stg/property_set_display_name_dictionary.asp?frame=true">
- * Property Set Display Name Dictionary</a>.
+ * reports or patches for the types you need.
*
* @see Section
* @see Variant
+ * @see <a href="https://msdn.microsoft.com/en-us/library/dd942421.aspx">
+ * [MS-OLEPS]: Object Linking and Embedding (OLE) Property Set Data Structures</a>
*/
-public class Property
-{
+public class Property {
- /** <p>The property's ID.</p> */
- protected long id;
+ /** The property's ID. */
+ private long id;
+ /** The property's type. */
+ private long type;
- /**
- * <p>Returns the property's ID.</p>
- *
- * @return The ID value
- */
- public long getID()
- {
- return id;
- }
-
-
-
- /** <p>The property's type.</p> */
- protected long type;
+ /** The property's value. */
+ protected Object value;
/**
- * <p>Returns the property's type.</p>
- *
- * @return The type value
+ * Creates an empty property. It must be filled using the set method to be usable.
*/
- public long getType()
- {
- return type;
+ public Property() {
}
-
-
- /** <p>The property's value.</p> */
- protected Object value;
-
-
/**
- * <p>Returns the property's value.</p>
+ * Creates a {@code Property} as a copy of an existing {@code Property}.
*
- * @return The property's value
+ * @param p The property to copy.
*/
- public Object getValue()
- {
- return value;
+ public Property(Property p) {
+ this(p.id, p.type, p.value);
}
-
-
-
+
/**
- * <p>Creates a property.</p>
+ * Creates a property.
*
* @param id the property's ID.
* @param type the property's type, see {@link Variant}.
* @param value the property's value. Only certain types are allowed, see
* {@link Variant}.
*/
- public Property(final long id, final long type, final Object value)
- {
+ public Property(final long id, final long type, final Object value) {
this.id = id;
this.type = type;
this.value = value;
}
-
-
/**
- * <p>Creates a {@link Property} instance by reading its bytes
- * from the property set stream.</p>
+ * Creates a {@link Property} instance by reading its bytes
+ * from the property set stream.
*
* @param id The property's ID.
* @param src The bytes the property set stream consists of.
@@ -138,18 +111,15 @@ public class Property
* @exception UnsupportedEncodingException if the specified codepage is not
* supported.
*/
- public Property(final long id, final byte[] src, final long offset,
- final int length, final int codepage)
- throws UnsupportedEncodingException
- {
+ public Property(final long id, final byte[] src, final long offset, final int length, final int codepage)
+ throws UnsupportedEncodingException {
this.id = id;
/*
* ID 0 is a special case since it specifies a dictionary of
* property IDs and property names.
*/
- if (id == 0)
- {
+ if (id == 0) {
value = readDictionary(src, offset, length, codepage);
return;
}
@@ -158,12 +128,9 @@ public class Property
type = LittleEndian.getUInt(src, o);
o += LittleEndian.INT_SIZE;
- try
- {
+ try {
value = VariantSupport.read(src, o, length, (int) type, codepage);
- }
- catch (UnsupportedVariantTypeException ex)
- {
+ } catch (UnsupportedVariantTypeException ex) {
VariantSupport.writeUnsupportedTypeMessage(ex);
value = ex.getValue();
}
@@ -172,19 +139,68 @@ public class Property
/**
- * <p>Creates an empty property. It must be filled using the set method to
- * be usable.</p>
+ * Returns the property's ID.
+ *
+ * @return The ID value
+ */
+ public long getID() {
+ return id;
+ }
+
+ /**
+ * Sets the property's ID.
+ *
+ * @param id the ID
+ */
+ public void setID(final long id) {
+ this.id = id;
+ }
+
+ /**
+ * Returns the property's type.
+ *
+ * @return The type value
+ */
+ public long getType() {
+ return type;
+ }
+
+ /**
+ * Sets the property's type.
+ *
+ * @param type the property's type
+ */
+ public void setType(final long type) {
+ this.type = type;
+ }
+
+ /**
+ * Returns the property's value.
+ *
+ * @return The property's value
+ */
+ public Object getValue() {
+ return value;
+ }
+
+ /**
+ * Sets the property's value.
+ *
+ * @param value the property's value
*/
- protected Property()
- { }
+ public void setValue(final Object value) {
+ this.value = value;
+ }
+
+
/**
- * <p>Reads a dictionary.</p>
+ * Reads a dictionary.
*
* @param src The byte array containing the bytes making out the dictionary.
- * @param offset At this offset within <var>src </var> the dictionary
+ * @param offset At this offset within {@code src} the dictionary
* starts.
* @param length The dictionary contains at most this many bytes.
* @param codepage The codepage of the string values.
@@ -192,15 +208,14 @@ public class Property
* @throws UnsupportedEncodingException if the dictionary's codepage is not
* (yet) supported.
*/
- protected Map<?, ?> readDictionary(final byte[] src, final long offset,
- final int length, final int codepage)
- throws UnsupportedEncodingException
- {
+ protected Map<?, ?> readDictionary(final byte[] src, final long offset, final int length, final int codepage)
+ throws UnsupportedEncodingException {
/* Check whether "offset" points into the "src" array". */
- if (offset < 0 || offset > src.length)
+ if (offset < 0 || offset > src.length) {
throw new HPSFRuntimeException
("Illegal offset " + offset + " while HPSF stream contains " +
length + " bytes.");
+ }
int o = (int) offset;
/*
@@ -209,13 +224,10 @@ public class Property
final long nrEntries = LittleEndian.getUInt(src, o);
o += LittleEndian.INT_SIZE;
- final Map<Object, Object> m = new LinkedHashMap<Object, Object>(
- (int) nrEntries, (float) 1.0 );
+ final Map<Object, Object> m = new LinkedHashMap<Object, Object>((int) nrEntries, (float) 1.0 );
- try
- {
- for (int i = 0; i < nrEntries; i++)
- {
+ try {
+ for (int i = 0; i < nrEntries; i++) {
/* The key. */
final Long id = Long.valueOf(LittleEndian.getUInt(src, o));
o += LittleEndian.INT_SIZE;
@@ -230,17 +242,13 @@ public class Property
/* Read the string. */
final StringBuffer b = new StringBuffer();
- switch (codepage)
- {
+ switch (codepage) {
case -1:
- {
/* Without a codepage the length is equal to the number of
* bytes. */
b.append(new String(src, o, (int) sLength, Charset.forName("ASCII")));
break;
- }
case CodePageUtil.CP_UNICODE:
- {
/* The length is the number of characters, i.e. the number
* of bytes is twice the number of the characters. */
final int nrBytes = (int) (sLength * 2);
@@ -250,36 +258,30 @@ public class Property
h[i2] = src[o + i2 + 1];
h[i2 + 1] = src[o + i2];
}
- b.append(new String(h, 0, nrBytes,
- CodePageUtil.codepageToEncoding(codepage)));
+ b.append(new String(h, 0, nrBytes, CodePageUtil.codepageToEncoding(codepage)));
break;
- }
default:
- {
/* For encodings other than Unicode the length is the number
* of bytes. */
- b.append(new String(src, o, (int) sLength,
- VariantSupport.codepageToEncoding(codepage)));
+ b.append(new String(src, o, (int) sLength, CodePageUtil.codepageToEncoding(codepage)));
break;
- }
}
/* Strip 0x00 characters from the end of the string: */
- while (b.length() > 0 && b.charAt(b.length() - 1) == 0x00)
+ while (b.length() > 0 && b.charAt(b.length() - 1) == 0x00) {
b.setLength(b.length() - 1);
- if (codepage == CodePageUtil.CP_UNICODE)
- {
- if (sLength % 2 == 1)
+ }
+ if (codepage == CodePageUtil.CP_UNICODE) {
+ if (sLength % 2 == 1) {
sLength++;
+ }
o += (sLength + sLength);
- }
- else
+ } else {
o += sLength;
+ }
m.put(id, b.toString());
}
- }
- catch (RuntimeException ex)
- {
+ } catch (RuntimeException ex) {
final POILogger l = POILogFactory.getLogger(getClass());
l.log(POILogger.WARN,
"The property set's dictionary contains bogus data. "
@@ -292,8 +294,7 @@ public class Property
/**
- * <p>Returns the property's size in bytes. This is always a multiple of
- * 4.</p>
+ * Returns the property's size in bytes. This is always a multiple of 4.
*
* @return the property's size in bytes
*
@@ -303,18 +304,18 @@ public class Property
protected int getSize() throws WritingNotSupportedException
{
int length = VariantSupport.getVariantLength(type);
- if (length >= 0)
+ if (length >= 0) {
return length; /* Fixed length */
- if (length == -2)
+ }
+ if (length == -2) {
/* Unknown length */
throw new WritingNotSupportedException(type, null);
+ }
/* Variable length: */
final int PADDING = 4; /* Pad to multiples of 4. */
- switch ((int) type)
- {
- case Variant.VT_LPSTR:
- {
+ switch ((int) type) {
+ case Variant.VT_LPSTR: {
int l = ((String) value).length() + 1;
int r = l % PADDING;
if (r > 0)
@@ -333,51 +334,53 @@ public class Property
/**
- * <p>Compares two properties.</p> <p>Please beware that a property with
+ * Compares two properties.<p>
+ *
+ * Please beware that a property with
* ID == 0 is a special case: It does not have a type, and its value is the
* section's dictionary. Another special case are strings: Two properties
- * may have the different types Variant.VT_LPSTR and Variant.VT_LPWSTR;</p>
+ * may have the different types Variant.VT_LPSTR and Variant.VT_LPWSTR;
*
* @see Object#equals(java.lang.Object)
*/
- public boolean equals(final Object o)
- {
+ public boolean equals(final Object o) {
if (!(o instanceof Property)) {
return false;
}
final Property p = (Property) o;
final Object pValue = p.getValue();
final long pId = p.getID();
- if (id != pId || (id != 0 && !typesAreEqual(type, p.getType())))
+ if (id != pId || (id != 0 && !typesAreEqual(type, p.getType()))) {
return false;
- if (value == null && pValue == null)
+ }
+ if (value == null && pValue == null) {
return true;
- if (value == null || pValue == null)
+ }
+ if (value == null || pValue == null) {
return false;
+ }
/* It's clear now that both values are non-null. */
final Class<?> valueClass = value.getClass();
final Class<?> pValueClass = pValue.getClass();
if (!(valueClass.isAssignableFrom(pValueClass)) &&
- !(pValueClass.isAssignableFrom(valueClass)))
+ !(pValueClass.isAssignableFrom(valueClass))) {
return false;
+ }
- if (value instanceof byte[])
- return Util.equal((byte[]) value, (byte[]) pValue);
+ if (value instanceof byte[]) {
+ return Arrays.equals((byte[]) value, (byte[]) pValue);
+ }
return value.equals(pValue);
}
- private boolean typesAreEqual(final long t1, final long t2)
- {
- if (t1 == t2 ||
+ private boolean typesAreEqual(final long t1, final long t2) {
+ return (t1 == t2 ||
(t1 == Variant.VT_LPSTR && t2 == Variant.VT_LPWSTR) ||
- (t2 == Variant.VT_LPSTR && t1 == Variant.VT_LPWSTR)) {
- return true;
- }
- return false;
+ (t2 == Variant.VT_LPSTR && t1 == Variant.VT_LPWSTR));
}
@@ -385,15 +388,14 @@ public class Property
/**
* @see Object#hashCode()
*/
- public int hashCode()
- {
+ public int hashCode() {
long hashCode = 0;
hashCode += id;
hashCode += type;
- if (value != null)
+ if (value != null) {
hashCode += value.hashCode();
- final int returnHashCode = (int) (hashCode & 0x0ffffffffL );
- return returnHashCode;
+ }
+ return (int) (hashCode & 0x0ffffffffL );
}
@@ -402,8 +404,7 @@ public class Property
/**
* @see Object#toString()
*/
- public String toString()
- {
+ public String toString() {
final StringBuffer b = new StringBuffer();
b.append(getClass().getName());
b.append('[');
@@ -413,14 +414,12 @@ public class Property
b.append(getType());
final Object value = getValue();
b.append(", value: ");
- if (value instanceof String)
- {
+ if (value instanceof String) {
b.append(value.toString());
final String s = (String) value;
final int l = s.length();
final byte[] bytes = new byte[l * 2];
- for (int i = 0; i < l; i++)
- {
+ for (int i = 0; i < l; i++) {
final char c = s.charAt(i);
final byte high = (byte) ((c & 0x00ff00) >> 8);
final byte low = (byte) ((c & 0x0000ff) >> 0);
@@ -433,21 +432,43 @@ public class Property
b.append(hex);
}
b.append("]");
- }
- else if (value instanceof byte[])
- {
+ } else if (value instanceof byte[]) {
byte[] bytes = (byte[])value;
if(bytes.length > 0) {
String hex = HexDump.dump(bytes, 0L, 0);
b.append(hex);
}
- }
- else
- {
+ } else {
b.append(value.toString());
}
b.append(']');
return b.toString();
}
+ /**
+ * Writes the property to an output stream.
+ *
+ * @param out The output stream to write to.
+ * @param codepage The codepage to use for writing non-wide strings
+ * @return the number of bytes written to the stream
+ *
+ * @exception IOException if an I/O error occurs
+ * @exception WritingNotSupportedException if a variant type is to be
+ * written that is not yet supported
+ */
+ public int write(final OutputStream out, final int codepage)
+ throws IOException, WritingNotSupportedException {
+ int length = 0;
+ long variantType = getType();
+
+ /* Ensure that wide strings are written if the codepage is Unicode. */
+ if (codepage == CodePageUtil.CP_UNICODE && variantType == Variant.VT_LPSTR) {
+ variantType = Variant.VT_LPWSTR;
+ }
+
+ length += TypeWriter.writeUIntToStream(out, variantType);
+ length += VariantSupport.write(out, variantType, getValue(), codepage);
+ return length;
+ }
+
}
diff --git a/src/java/org/apache/poi/hpsf/PropertySet.java b/src/java/org/apache/poi/hpsf/PropertySet.java
index 9c9b1876a0..30a636ffce 100644
--- a/src/java/org/apache/poi/hpsf/PropertySet.java
+++ b/src/java/org/apache/poi/hpsf/PropertySet.java
@@ -17,246 +17,195 @@
package org.apache.poi.hpsf;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
-import java.util.ArrayList;
+import java.nio.charset.Charset;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.LinkedList;
import java.util.List;
+import org.apache.poi.EmptyFileException;
+import org.apache.poi.hpsf.wellknown.PropertyIDMap;
import org.apache.poi.hpsf.wellknown.SectionIDMap;
+import org.apache.poi.poifs.filesystem.DirectoryEntry;
+import org.apache.poi.poifs.filesystem.Entry;
import org.apache.poi.util.IOUtils;
import org.apache.poi.util.LittleEndian;
+import org.apache.poi.util.LittleEndianConsts;
/**
- * <p>Represents a property set in the Horrible Property Set Format
+ * Represents a property set in the Horrible Property Set Format
* (HPSF). These are usually metadata of a Microsoft Office
- * document.</p>
+ * document.<p>
*
- * <p>An application that wants to access these metadata should create
+ * An application that wants to access these metadata should create
* an instance of this class or one of its subclasses by calling the
* factory method {@link PropertySetFactory#create} and then retrieve
- * the information its needs by calling appropriate methods.</p>
+ * the information its needs by calling appropriate methods.<p>
*
- * <p>{@link PropertySetFactory#create} does its work by calling one
+ * {@link PropertySetFactory#create} does its work by calling one
* of the constructors {@link PropertySet#PropertySet(InputStream)} or
* {@link PropertySet#PropertySet(byte[])}. If the constructor's
* argument is not in the Horrible Property Set Format, i.e. not a
* property set stream, or if any other error occurs, an appropriate
- * exception is thrown.</p>
+ * exception is thrown.<p>
*
- * <p>A {@link PropertySet} has a list of {@link Section}s, and each
+ * A {@link PropertySet} has a list of {@link Section}s, and each
* {@link Section} has a {@link Property} array. Use {@link
* #getSections} to retrieve the {@link Section}s, then call {@link
* Section#getProperties} for each {@link Section} to get hold of the
- * {@link Property} arrays.</p> Since the vast majority of {@link
- * PropertySet}s contains only a single {@link Section}, the
- * convenience method {@link #getProperties} returns the properties of
- * a {@link PropertySet}'s {@link Section} (throwing a {@link
- * NoSingleSectionException} if the {@link PropertySet} contains more
- * (or less) than exactly one {@link Section}).
+ * {@link Property} arrays.<p>
+ *
+ * Since the vast majority of {@link PropertySet}s contains only a single
+ * {@link Section}, the convenience method {@link #getProperties} returns
+ * the properties of a {@link PropertySet}'s {@link Section} (throwing a
+ * {@link NoSingleSectionException} if the {@link PropertySet} contains
+ * more (or less) than exactly one {@link Section}).
*/
-public class PropertySet
-{
-
- /**
- * <p>The "byteOrder" field must equal this value.</p>
- */
- static final byte[] BYTE_ORDER_ASSERTION =
- {(byte) 0xFE, (byte) 0xFF};
-
- /**
- * <p>Specifies this {@link PropertySet}'s byte order. See the
- * HPFS documentation for details!</p>
- */
- protected int byteOrder;
-
+public class PropertySet {
/**
- * <p>Returns the property set stream's low-level "byte order"
- * field. It is always <tt>0xFFFE</tt> .</p>
- *
- * @return The property set stream's low-level "byte order" field.
+ * If the OS version field holds this value the property set stream was
+ * created on a 16-bit Windows system.
*/
- public int getByteOrder()
- {
- return byteOrder;
- }
-
-
+ public static final int OS_WIN16 = 0x0000;
/**
- * <p>The "format" field must equal this value.</p>
+ * If the OS version field holds this value the property set stream was
+ * created on a Macintosh system.
*/
- static final byte[] FORMAT_ASSERTION =
- {(byte) 0x00, (byte) 0x00};
+ public static final int OS_MACINTOSH = 0x0001;
/**
- * <p>Specifies this {@link PropertySet}'s format. See the HPFS
- * documentation for details!</p>
+ * If the OS version field holds this value the property set stream was
+ * created on a 32-bit Windows system.
*/
- protected int format;
+ public static final int OS_WIN32 = 0x0002;
/**
- * <p>Returns the property set stream's low-level "format"
- * field. It is always <tt>0x0000</tt> .</p>
- *
- * @return The property set stream's low-level "format" field.
+ * The "byteOrder" field must equal this value.
*/
- public int getFormat()
- {
- return format;
- }
+ private static final int BYTE_ORDER_ASSERTION = 0xFFFE;
-
-
/**
- * <p>Specifies the version of the operating system that created
- * this {@link PropertySet}. See the HPFS documentation for
- * details!</p>
+ * The "format" field must equal this value.
*/
- protected int osVersion;
-
+ private static final int FORMAT_ASSERTION = 0x0000;
/**
- * <p>If the OS version field holds this value the property set stream was
- * created on a 16-bit Windows system.</p>
+ * The length of the property set stream header.
*/
- public static final int OS_WIN16 = 0x0000;
+ private static final int OFFSET_HEADER =
+ LittleEndianConsts.SHORT_SIZE + /* Byte order */
+ LittleEndianConsts.SHORT_SIZE + /* Format */
+ LittleEndianConsts.INT_SIZE + /* OS version */
+ ClassID.LENGTH + /* Class ID */
+ LittleEndianConsts.INT_SIZE; /* Section count */
+
/**
- * <p>If the OS version field holds this value the property set stream was
- * created on a Macintosh system.</p>
+ * Specifies this {@link PropertySet}'s byte order. See the
+ * HPFS documentation for details!
*/
- public static final int OS_MACINTOSH = 0x0001;
+ private int byteOrder;
/**
- * <p>If the OS version field holds this value the property set stream was
- * created on a 32-bit Windows system.</p>
+ * Specifies this {@link PropertySet}'s format. See the HPFS
+ * documentation for details!
*/
- public static final int OS_WIN32 = 0x0002;
-
+ private int format;
+
/**
- * <p>Returns the property set stream's low-level "OS version"
- * field.</p>
- *
- * @return The property set stream's low-level "OS version" field.
+ * Specifies the version of the operating system that created this
+ * {@link PropertySet}. See the HPFS documentation for details!
*/
- public int getOSVersion()
- {
- return osVersion;
- }
-
-
+ private int osVersion;
/**
- * <p>Specifies this {@link PropertySet}'s "classID" field. See
- * the HPFS documentation for details!</p>
+ * Specifies this {@link PropertySet}'s "classID" field. See
+ * the HPFS documentation for details!
*/
- protected ClassID classID;
+ private ClassID classID;
/**
- * <p>Returns the property set stream's low-level "class ID"
- * field.</p>
- *
- * @return The property set stream's low-level "class ID" field.
+ * The sections in this {@link PropertySet}.
*/
- public ClassID getClassID()
- {
- return classID;
- }
-
-
+ private final List<Section> sections = new LinkedList<Section>();
+
/**
- * <p>Returns the number of {@link Section}s in the property
- * set.</p>
- *
- * @return The number of {@link Section}s in the property set.
+ * Constructs a {@code PropertySet} instance. Its
+ * primary task is to initialize the field with their proper values.
+ * It also sets fields that might change to reasonable defaults.
*/
- public int getSectionCount()
- {
- return sections.size();
- }
+ public PropertySet() {
+ /* Initialize the "byteOrder" field. */
+ byteOrder = BYTE_ORDER_ASSERTION;
+ /* Initialize the "format" field. */
+ format = 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;
- /**
- * <p>The sections in this {@link PropertySet}.</p>
- */
- protected List<Section> sections;
+ /* Initialize the "classID" field. */
+ classID = new ClassID();
- /**
- * <p>Returns the {@link Section}s in the property set.</p>
- *
- * @return The {@link Section}s in the property set.
- */
- public List<Section> getSections()
- {
- return sections;
+ /* Initialize the sections. Since property set must have at least
+ * one section it is added right here. */
+ addSection(new MutableSection());
}
/**
- * <p>Creates an empty (uninitialized) {@link PropertySet}.</p>
- *
- * <p><strong>Please note:</strong> For the time being this
- * constructor is protected since it is used for internal purposes
- * only, but expect it to become public once the property set's
- * writing functionality is implemented.</p>
- */
- protected PropertySet()
- { }
-
-
-
- /**
- * <p>Creates a {@link PropertySet} instance from an {@link
- * InputStream} in the Horrible Property Set Format.</p>
+ * Creates a {@link PropertySet} instance from an {@link
+ * InputStream} in the Horrible Property Set Format.<p>
*
- * <p>The constructor reads the first few bytes from the stream
+ * The constructor reads the first few bytes from the stream
* and determines whether it is really a property set stream. If
* it is, it parses the rest of the stream. If it is not, it
* resets the stream to its beginning in order to let other
* components mess around with the data and throws an
- * exception.</p>
+ * exception.
*
* @param stream Holds the data making out the property set
* stream.
- * @throws MarkUnsupportedException if the stream does not support
- * the {@link InputStream#markSupported} method.
- * @throws IOException if the {@link InputStream} cannot be
- * accessed as needed.
- * @exception NoPropertySetStreamException if the input stream does not
- * contain a property set.
- * @exception UnsupportedEncodingException if a character encoding is not
- * supported.
+ * @throws MarkUnsupportedException
+ * if the stream does not support the {@link InputStream#markSupported} method.
+ * @throws IOException
+ * if the {@link InputStream} cannot be accessed as needed.
+ * @exception NoPropertySetStreamException
+ * if the input stream does not contain a property set.
+ * @exception UnsupportedEncodingException
+ * if a character encoding is not supported.
*/
public PropertySet(final InputStream stream)
- throws NoPropertySetStreamException, MarkUnsupportedException,
- IOException, UnsupportedEncodingException
- {
- if (isPropertySetStream(stream))
- {
- final int avail = stream.available();
- final byte[] buffer = new byte[avail];
- IOUtils.readFully(stream, buffer);
- init(buffer, 0, buffer.length);
- }
- else
+ throws NoPropertySetStreamException, MarkUnsupportedException,
+ IOException, UnsupportedEncodingException {
+ if (!isPropertySetStream(stream)) {
throw new NoPropertySetStreamException();
+ }
+
+ final byte[] buffer = IOUtils.toByteArray(stream);
+ init(buffer, 0, buffer.length);
}
/**
- * <p>Creates a {@link PropertySet} instance from a byte array
- * that represents a stream in the Horrible Property Set
- * Format.</p>
+ * Creates a {@link PropertySet} instance from a byte array that
+ * represents a stream in the Horrible Property Set Format.
*
* @param stream The byte array holding the stream data.
- * @param offset The offset in <var>stream</var> where the stream
+ * @param offset The offset in {@code stream} where the stream
* data begin. If the stream data begin with the first byte in the
- * array, the <var>offset</var> is 0.
+ * array, the {@code offset} is 0.
* @param length The length of the stream data.
* @throws NoPropertySetStreamException if the byte array is not a
* property set stream.
@@ -264,20 +213,16 @@ public class PropertySet
* @exception UnsupportedEncodingException if the codepage is not supported.
*/
public PropertySet(final byte[] stream, final int offset, final int length)
- throws NoPropertySetStreamException, UnsupportedEncodingException
- {
- if (isPropertySetStream(stream, offset, length))
- init(stream, offset, length);
- else
+ throws NoPropertySetStreamException, UnsupportedEncodingException {
+ if (!isPropertySetStream(stream, offset, length)) {
throw new NoPropertySetStreamException();
+ }
+ init(stream, offset, length);
}
-
-
/**
- * <p>Creates a {@link PropertySet} instance from a byte array
- * that represents a stream in the Horrible Property Set
- * Format.</p>
+ * Creates a {@link PropertySet} instance from a byte array
+ * that represents a stream in the Horrible Property Set Format.
*
* @param stream The byte array holding the stream data. The
* complete byte array contents is the stream data.
@@ -287,75 +232,185 @@ public class PropertySet
* @exception UnsupportedEncodingException if the codepage is not supported.
*/
public PropertySet(final byte[] stream)
- throws NoPropertySetStreamException, UnsupportedEncodingException
- {
+ throws NoPropertySetStreamException, UnsupportedEncodingException {
this(stream, 0, stream.length);
}
+
+ /**
+ * Constructs a {@code PropertySet} by doing a deep copy of
+ * an existing {@code PropertySet}. All nested elements, i.e.
+ * {@code Section}s and {@code Property} instances, will be their
+ * counterparts in the new {@code PropertySet}.
+ *
+ * @param ps The property set to copy
+ */
+ public PropertySet(PropertySet ps) {
+ setByteOrder(ps.getByteOrder());
+ setFormat(ps.getFormat());
+ setOSVersion(ps.getOSVersion());
+ setClassID(ps.getClassID());
+ for (final Section section : ps.getSections()) {
+ sections.add(new MutableSection(section));
+ }
+ }
+
+
+ /**
+ * @return The property set stream's low-level "byte order" field. It is always {@code 0xFFFE}.
+ */
+ public int getByteOrder() {
+ return byteOrder;
+ }
+
+ /**
+ * Returns the property set stream's low-level "byte order" field.
+ *
+ * @param byteOrder The property set stream's low-level "byte order" field.
+ */
+ public void setByteOrder(int byteOrder) {
+ this.byteOrder = byteOrder;
+ }
+ /**
+ * @return The property set stream's low-level "format" field. It is always {@code 0x0000}.
+ */
+ public int getFormat() {
+ return format;
+ }
+ /**
+ * Sets the property set stream's low-level "format" field.
+ *
+ * @param format The property set stream's low-level "format" field.
+ */
+ public void setFormat(int format) {
+ this.format = format;
+ }
/**
- * <p>Checks whether an {@link InputStream} is in the Horrible
- * Property Set Format.</p>
+ * @return The property set stream's low-level "OS version" field.
+ */
+ public int getOSVersion() {
+ return osVersion;
+ }
+
+ /**
+ * Sets the property set stream's low-level "OS version" field.
+ *
+ * @param osVersion The property set stream's low-level "OS version" field.
+ */
+ public void setOSVersion(int osVersion) {
+ this.osVersion = osVersion;
+ }
+
+
+ /**
+ * @return The property set stream's low-level "class ID" field.
+ */
+ public ClassID getClassID() {
+ return classID;
+ }
+
+ /**
+ * Sets the property set stream's low-level "class ID" field.
+ *
+ * @param classID The property set stream's low-level "class ID" field.
+ */
+ public void setClassID(ClassID classID) {
+ this.classID = classID;
+ }
+
+ /**
+ * @return The number of {@link Section}s in the property set.
+ */
+ public int getSectionCount() {
+ return sections.size();
+ }
+
+ /**
+ * @return The unmodifiable list of {@link Section}s in the property set.
+ */
+ public List<Section> getSections() {
+ return Collections.unmodifiableList(sections);
+ }
+
+
+
+ /**
+ * Adds a section to this property set.
+ *
+ * @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) {
+ sections.add(section);
+ }
+
+ /**
+ * Removes all sections from this property set.
+ */
+ public void clearSections() {
+ sections.clear();
+ }
+
+ /**
+ * The id to name mapping of the properties in this set.
+ *
+ * @return the id to name mapping of the properties in this set or {@code null} if not applicable
+ */
+ public PropertyIDMap getPropertySetIDMap() {
+ return null;
+ }
+
+
+ /**
+ * Checks whether an {@link InputStream} is in the Horrible
+ * Property Set Format.
*
* @param stream The {@link InputStream} to check. In order to
* perform the check, the method reads the first bytes from the
* stream. After reading, the stream is reset to the position it
* had before reading. The {@link InputStream} must support the
* {@link InputStream#mark} method.
- * @return <code>true</code> if the stream is a property set
- * stream, else <code>false</code>.
+ * @return {@code true} if the stream is a property set
+ * stream, else {@code false}.
* @throws MarkUnsupportedException if the {@link InputStream}
* does not support the {@link InputStream#mark} method.
* @exception IOException if an I/O error occurs
*/
public static boolean isPropertySetStream(final InputStream stream)
- throws MarkUnsupportedException, IOException
- {
+ throws MarkUnsupportedException, IOException {
/*
* Read at most this many bytes.
*/
final int BUFFER_SIZE = 50;
/*
- * Mark the current position in the stream so that we can
- * reset to this position if the stream does not contain a
- * property set.
- */
- if (!stream.markSupported())
- throw new MarkUnsupportedException(stream.getClass().getName());
- stream.mark(BUFFER_SIZE);
-
- /*
* Read a couple of bytes from the stream.
*/
- final byte[] buffer = new byte[BUFFER_SIZE];
- final int bytes =
- stream.read(buffer, 0,
- Math.min(buffer.length, stream.available()));
- final boolean isPropertySetStream =
- isPropertySetStream(buffer, 0, bytes);
- stream.reset();
- return isPropertySetStream;
+ try {
+ final byte[] buffer = IOUtils.peekFirstNBytes(stream, BUFFER_SIZE);
+ final boolean isPropertySetStream = isPropertySetStream(buffer, 0, buffer.length);
+ return isPropertySetStream;
+ } catch (EmptyFileException e) {
+ return false;
+ }
}
/**
- * <p>Checks whether a byte array is in the Horrible Property Set
- * Format.</p>
+ * Checks whether a byte array is in the Horrible Property Set Format.
*
* @param src The byte array to check.
* @param offset The offset in the byte array.
* @param length The significant number of bytes in the byte
* array. Only this number of bytes will be checked.
- * @return <code>true</code> if the byte array is a property set
- * stream, <code>false</code> if not.
+ * @return {@code true} if the byte array is a property set
+ * stream, {@code false} if not.
*/
- public static boolean isPropertySetStream(final byte[] src,
- final int offset,
- final int length)
- {
+ public static boolean isPropertySetStream(final byte[] src, final int offset, final int length) {
/* FIXME (3): Ensure that at most "length" bytes are read. */
/*
@@ -365,45 +420,39 @@ public class PropertySet
int o = offset;
final int byteOrder = LittleEndian.getUShort(src, o);
o += LittleEndian.SHORT_SIZE;
- byte[] temp = new byte[LittleEndian.SHORT_SIZE];
- LittleEndian.putShort(temp, 0, (short) byteOrder);
- if (!Util.equal(temp, BYTE_ORDER_ASSERTION))
+ if (byteOrder != BYTE_ORDER_ASSERTION) {
return false;
+ }
final int format = LittleEndian.getUShort(src, o);
o += LittleEndian.SHORT_SIZE;
- temp = new byte[LittleEndian.SHORT_SIZE];
- LittleEndian.putShort(temp, 0, (short) format);
- if (!Util.equal(temp, FORMAT_ASSERTION))
+ if (format != FORMAT_ASSERTION) {
return false;
+ }
// final long osVersion = LittleEndian.getUInt(src, offset);
o += LittleEndian.INT_SIZE;
// final ClassID classID = new ClassID(src, offset);
o += ClassID.LENGTH;
final long sectionCount = LittleEndian.getUInt(src, o);
- o += LittleEndian.INT_SIZE;
- if (sectionCount < 0)
- return false;
- return true;
+ return (sectionCount >= 0);
}
/**
- * <p>Initializes this {@link PropertySet} instance from a byte
+ * Initializes this {@link PropertySet} instance from a byte
* array. The method assumes that it has been checked already that
* the byte array indeed represents a property set stream. It does
- * no more checks on its own.</p>
+ * no more checks on its own.
*
* @param src Byte array containing the property set stream
* @param offset The property set stream starts at this offset
- * from the beginning of <var>src</var>
+ * from the beginning of {@code src}
* @param length Length of the property set stream.
* @throws UnsupportedEncodingException if HPSF does not (yet) support the
* property set's character encoding.
*/
private void init(final byte[] src, final int offset, final int length)
- throws UnsupportedEncodingException
- {
+ throws UnsupportedEncodingException {
/* FIXME (3): Ensure that at most "length" bytes are read. */
/*
@@ -420,9 +469,9 @@ public class PropertySet
o += ClassID.LENGTH;
final int sectionCount = LittleEndian.getInt(src, o);
o += LittleEndian.INT_SIZE;
- if (sectionCount < 0)
- throw new HPSFRuntimeException("Section count " + sectionCount +
- " is negative.");
+ if (sectionCount < 0) {
+ throw new HPSFRuntimeException("Section count " + sectionCount + " is negative.");
+ }
/*
* Read the sections, which are following the header. They
@@ -430,209 +479,334 @@ public class PropertySet
* consists of a format ID telling what the section contains
* and an offset telling how many bytes from the start of the
* stream the section begins.
- */
- /*
+ *
* Most property sets have only one section. The Document
* Summary Information stream has 2. Everything else is a rare
* exception and is no longer fostered by Microsoft.
*/
- sections = new ArrayList<Section>( sectionCount );
/*
* Loop over the section descriptor array. Each descriptor
* consists of a ClassID and a DWord, and we have to increment
* "offset" accordingly.
*/
- for (int i = 0; i < sectionCount; i++)
- {
- final Section s = new Section(src, o);
+ for (int i = 0; i < sectionCount; i++) {
+ final Section s = new MutableSection(src, o);
o += ClassID.LENGTH + LittleEndian.INT_SIZE;
sections.add(s);
}
}
+ /**
+ * Writes the property set to an output stream.
+ *
+ * @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 = getSectionCount();
+
+ /* 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 : getSections()) {
+ final ClassID formatID = section.getFormatID();
+ if (formatID == null) {
+ throw new NoFormatIDException();
+ }
+ TypeWriter.writeToStream(out, section.getFormatID());
+ TypeWriter.writeUIntToStream(out, offset);
+ try {
+ offset += section.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 : getSections()) {
+ offset += section.write(out);
+ }
+
+ /* Indicate that we're done */
+ out.close();
+ }
/**
- * <p>Checks whether this {@link PropertySet} represents a Summary
- * Information.</p>
+ * Writes a property set to a document in a POI filesystem directory.
*
- * @return <code>true</code> if this {@link PropertySet}
- * represents a Summary Information, else <code>false</code>.
- */
- public boolean isSummaryInformation()
- {
- if (sections.size() <= 0)
- return false;
- return Util.equal(sections.get(0).getFormatID().getBytes(),
- SectionIDMap.SUMMARY_INFORMATION_ID);
+ * @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. */
+ if (dir.hasEntry(name)) {
+ final Entry e = dir.getEntry(name);
+ e.delete();
+ }
+
+ /* Create the new entry. */
+ dir.createDocument(name, toInputStream());
}
+ /**
+ * 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.
+ *
+ * @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);
+ }
+ /**
+ * 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);
+ }
/**
- * <p>Checks whether this {@link PropertySet} is a Document
- * Summary Information.</p>
+ * Return the string representation of a property value
+ *
+ * @param propertyValue the property value
+ *
+ * @return The property value as a String, or null if unavailable
+ */
+ public 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;
+ switch (b.length) {
+ case 0:
+ return "";
+ case 1:
+ return Byte.toString(b[0]);
+ case 2:
+ return Integer.toString( LittleEndian.getUShort(b) );
+ case 4:
+ return Long.toString( LittleEndian.getUInt(b) );
+ default:
+ // Maybe it's a string? who knows!
+ return new String(b, Charset.forName("ASCII"));
+ }
+ }
+ return propertyValue.toString();
+ }
+
+ /**
+ * Checks whether this {@link PropertySet} represents a Summary Information.
*
- * @return <code>true</code> if this {@link PropertySet}
- * represents a Document Summary Information, else <code>false</code>.
+ * @return {@code true} if this {@link PropertySet}
+ * represents a Summary Information, else {@code false}.
*/
- public boolean isDocumentSummaryInformation()
- {
- if (sections.size() <= 0)
- return false;
- return Util.equal(sections.get(0).getFormatID().getBytes(),
- SectionIDMap.DOCUMENT_SUMMARY_INFORMATION_ID[0]);
+ public boolean isSummaryInformation() {
+ return matchesSummary(SectionIDMap.SUMMARY_INFORMATION_ID);
}
+ /**
+ * Checks whether this {@link PropertySet} is a Document Summary Information.
+ *
+ * @return {@code true} if this {@link PropertySet}
+ * represents a Document Summary Information, else {@code false}.
+ */
+ public boolean isDocumentSummaryInformation() {
+ return matchesSummary(SectionIDMap.DOCUMENT_SUMMARY_INFORMATION_ID[0]);
+ }
+ private boolean matchesSummary(byte[] summaryBytes) {
+ return !sections.isEmpty() &&
+ Arrays.equals(getFirstSection().getFormatID().getBytes(), summaryBytes);
+ }
+
+
/**
- * <p>Convenience method returning the {@link Property} array
- * contained in this property set. It is a shortcut for getting
- * the {@link PropertySet}'s {@link Section}s list and then
- * getting the {@link Property} array from the first {@link
- * Section}.</p>
+ * Convenience method returning the {@link Property} array contained in this
+ * property set. It is a shortcut for getting he {@link PropertySet}'s
+ * {@link Section}s list and then getting the {@link Property} array from the
+ * first {@link Section}.
*
* @return The properties of the only {@link Section} of this
* {@link PropertySet}.
* @throws NoSingleSectionException if the {@link PropertySet} has
* more or less than one {@link Section}.
*/
- public Property[] getProperties()
- throws NoSingleSectionException
- {
+ public Property[] getProperties() throws NoSingleSectionException {
return getFirstSection().getProperties();
}
/**
- * <p>Convenience method returning the value of the property with
- * the specified ID. If the property is not available,
- * <code>null</code> is returned and a subsequent call to {@link
- * #wasNull} will return <code>true</code> .</p>
+ * Convenience method returning the value of the property with the specified ID.
+ * If the property is not available, {@code null} is returned and a subsequent
+ * call to {@link #wasNull} will return {@code true}.
*
* @param id The property ID
* @return The property value
* @throws NoSingleSectionException if the {@link PropertySet} has
* more or less than one {@link Section}.
*/
- protected Object getProperty(final int id) throws NoSingleSectionException
- {
+ protected Object getProperty(final int id) throws NoSingleSectionException {
return getFirstSection().getProperty(id);
}
/**
- * <p>Convenience method returning the value of a boolean property
- * with the specified ID. If the property is not available,
- * <code>false</code> is returned. A subsequent call to {@link
- * #wasNull} will return <code>true</code> to let the caller
- * distinguish that case from a real property value of
- * <code>false</code>.</p>
+ * Convenience method returning the value of a boolean property with the
+ * specified ID. If the property is not available, {@code false} is returned.
+ * A subsequent call to {@link #wasNull} will return {@code true} to let the
+ * caller distinguish that case from a real property value of {@code false}.
*
* @param id The property ID
* @return The property value
* @throws NoSingleSectionException if the {@link PropertySet} has
* more or less than one {@link Section}.
*/
- protected boolean getPropertyBooleanValue(final int id)
- throws NoSingleSectionException
- {
+ protected boolean getPropertyBooleanValue(final int id) throws NoSingleSectionException {
return getFirstSection().getPropertyBooleanValue(id);
}
/**
- * <p>Convenience method returning the value of the numeric
+ * Convenience method returning the value of the numeric
* property with the specified ID. If the property is not
* available, 0 is returned. A subsequent call to {@link #wasNull}
- * will return <code>true</code> to let the caller distinguish
- * that case from a real property value of 0.</p>
+ * will return {@code true} to let the caller distinguish
+ * that case from a real property value of 0.
*
* @param id The property ID
* @return The propertyIntValue value
* @throws NoSingleSectionException if the {@link PropertySet} has
* more or less than one {@link Section}.
*/
- protected int getPropertyIntValue(final int id)
- throws NoSingleSectionException
- {
+ protected int getPropertyIntValue(final int id) throws NoSingleSectionException {
return getFirstSection().getPropertyIntValue(id);
}
/**
- * <p>Checks whether the property which the last call to {@link
+ * Checks whether the property which the last call to {@link
* #getPropertyIntValue} or {@link #getProperty} tried to access
* was available or not. This information might be important for
* callers of {@link #getPropertyIntValue} since the latter
* returns 0 if the property does not exist. Using {@link
* #wasNull}, the caller can distiguish this case from a
- * property's real value of 0.</p>
+ * property's real value of 0.
*
- * @return <code>true</code> if the last call to {@link
+ * @return {@code true} if the last call to {@link
* #getPropertyIntValue} or {@link #getProperty} tried to access a
- * property that was not available, else <code>false</code>.
+ * property that was not available, else {@code false}.
* @throws NoSingleSectionException if the {@link PropertySet} has
* more than one {@link Section}.
*/
- public boolean wasNull() throws NoSingleSectionException
- {
+ public boolean wasNull() throws NoSingleSectionException {
return getFirstSection().wasNull();
}
/**
- * <p>Gets the {@link PropertySet}'s first section.</p>
+ * Gets the {@link PropertySet}'s first section.
*
* @return The {@link PropertySet}'s first section.
*/
- public Section getFirstSection()
- {
- if (getSectionCount() < 1)
+ public Section getFirstSection() {
+ if (sections.isEmpty()) {
throw new MissingSectionException("Property set does not contain any sections.");
+ }
return sections.get(0);
}
/**
- * <p>If the {@link PropertySet} has only a single section this
- * method returns it.</p>
+ * If the {@link PropertySet} has only a single section this method returns it.
*
* @return The singleSection value
*/
- public Section getSingleSection()
- {
+ public Section getSingleSection() {
final int sectionCount = getSectionCount();
- if (sectionCount != 1)
- throw new NoSingleSectionException
- ("Property set contains " + sectionCount + " sections.");
+ if (sectionCount != 1) {
+ throw new NoSingleSectionException("Property set contains " + sectionCount + " sections.");
+ }
return sections.get(0);
}
/**
- * <p>Returns <code>true</code> if the <code>PropertySet</code> is equal
- * to the specified parameter, else <code>false</code>.</p>
+ * Returns {@code true} if the {@code PropertySet} is equal
+ * to the specified parameter, else {@code false}.
*
- * @param o the object to compare this <code>PropertySet</code> with
+ * @param o the object to compare this {@code PropertySet} with
*
- * @return <code>true</code> if the objects are equal, <code>false</code>
+ * @return {@code true} if the objects are equal, {@code false}
* if not
*/
@Override
- public boolean equals(final Object o)
- {
- if (o == null || !(o instanceof PropertySet))
+ public boolean equals(final Object o) {
+ if (o == null || !(o instanceof PropertySet)) {
return false;
+ }
final PropertySet ps = (PropertySet) o;
int byteOrder1 = ps.getByteOrder();
int byteOrder2 = getByteOrder();
@@ -648,11 +822,12 @@ public class PropertySet
!classID1.equals(classID2) ||
format1 != format2 ||
osVersion1 != osVersion2 ||
- sectionCount1 != sectionCount2)
+ sectionCount1 != sectionCount2) {
return false;
+ }
/* Compare the sections: */
- return Util.equals(getSections(), ps.getSections());
+ return getSections().containsAll(ps.getSections());
}
@@ -660,8 +835,7 @@ public class PropertySet
/**
* @see Object#hashCode()
*/
- public int hashCode()
- {
+ public int hashCode() {
throw new UnsupportedOperationException("FIXME: Not yet implemented.");
}
@@ -670,8 +844,7 @@ public class PropertySet
/**
* @see Object#toString()
*/
- public String toString()
- {
+ public String toString() {
final StringBuilder b = new StringBuilder();
final int sectionCount = getSectionCount();
b.append(getClass().getName());
@@ -687,10 +860,32 @@ public class PropertySet
b.append(", sectionCount: ");
b.append(sectionCount);
b.append(", sections: [\n");
- for (Section section: getSections())
+ for (Section section: getSections()) {
b.append(section);
+ }
b.append(']');
b.append(']');
return b.toString();
}
+
+
+ protected void remove1stProperty(long id) {
+ getFirstSection().removeProperty(id);
+ }
+
+ protected void set1stProperty(long id, String value) {
+ getFirstSection().setProperty((int)id, value);
+ }
+
+ protected void set1stProperty(long id, int value) {
+ getFirstSection().setProperty((int)id, value);
+ }
+
+ protected void set1stProperty(long id, boolean value) {
+ getFirstSection().setProperty((int)id, value);
+ }
+
+ protected void set1stProperty(long id, byte[] value) {
+ getFirstSection().setProperty((int)id, value);
+ }
}
diff --git a/src/java/org/apache/poi/hpsf/PropertySetFactory.java b/src/java/org/apache/poi/hpsf/PropertySetFactory.java
index 4d4b4e6386..db0a73d135 100644
--- a/src/java/org/apache/poi/hpsf/PropertySetFactory.java
+++ b/src/java/org/apache/poi/hpsf/PropertySetFactory.java
@@ -22,24 +22,22 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
-import org.apache.poi.hpsf.wellknown.SectionIDMap;
import org.apache.poi.poifs.filesystem.DirectoryEntry;
import org.apache.poi.poifs.filesystem.DocumentEntry;
import org.apache.poi.poifs.filesystem.DocumentInputStream;
/**
- * <p>Factory class to create instances of {@link SummaryInformation},
- * {@link DocumentSummaryInformation} and {@link PropertySet}.</p>
+ * Factory class to create instances of {@link SummaryInformation},
+ * {@link DocumentSummaryInformation} and {@link PropertySet}.
*/
-public class PropertySetFactory
-{
+public class PropertySetFactory {
/**
- * <p>Creates the most specific {@link PropertySet} from an entry
+ * Creates the most specific {@link PropertySet} from an entry
* in the specified POIFS Directory. This is preferrably a {@link
* DocumentSummaryInformation} or a {@link SummaryInformation}. If
* the specified entry does not contain a property set stream, an
* exception is thrown. If no entry is found with the given name,
- * an exception is thrown.</p>
+ * an exception is thrown.
*
* @param dir The directory to find the PropertySet in
* @param name The name of the entry containing the PropertySet
@@ -52,55 +50,53 @@ public class PropertySetFactory
* supported.
*/
public static PropertySet create(final DirectoryEntry dir, final String name)
- throws FileNotFoundException, NoPropertySetStreamException,
- IOException, UnsupportedEncodingException
- {
+ throws FileNotFoundException, NoPropertySetStreamException, IOException, UnsupportedEncodingException {
InputStream inp = null;
try {
DocumentEntry entry = (DocumentEntry)dir.getEntry(name);
inp = new DocumentInputStream(entry);
try {
return create(inp);
- } catch (MarkUnsupportedException e) { return null; }
+ } catch (MarkUnsupportedException e) {
+ return null;
+ }
} finally {
- if (inp != null) inp.close();
+ if (inp != null) {
+ inp.close();
+ }
}
}
/**
- * <p>Creates the most specific {@link PropertySet} from an {@link
+ * Creates the most specific {@link PropertySet} from an {@link
* InputStream}. This is preferrably a {@link
* DocumentSummaryInformation} or a {@link SummaryInformation}. If
* the specified {@link InputStream} does not contain a property
* set stream, an exception is thrown and the {@link InputStream}
- * is repositioned at its beginning.</p>
+ * is repositioned at its beginning.
*
* @param stream Contains the property set stream's data.
* @return The created {@link PropertySet}.
* @throws NoPropertySetStreamException if the stream does not
* contain a property set.
* @throws MarkUnsupportedException if the stream does not support
- * the <code>mark</code> operation.
+ * the {@code mark} operation.
* @throws IOException if some I/O problem occurs.
* @exception UnsupportedEncodingException if the specified codepage is not
* supported.
*/
public static PropertySet create(final InputStream stream)
- throws NoPropertySetStreamException, MarkUnsupportedException,
- UnsupportedEncodingException, IOException
- {
+ throws NoPropertySetStreamException, MarkUnsupportedException, UnsupportedEncodingException, IOException {
final PropertySet ps = new PropertySet(stream);
- try
- {
- if (ps.isSummaryInformation())
+ try {
+ if (ps.isSummaryInformation()) {
return new SummaryInformation(ps);
- else if (ps.isDocumentSummaryInformation())
+ } else if (ps.isDocumentSummaryInformation()) {
return new DocumentSummaryInformation(ps);
- else
+ } else {
return ps;
- }
- catch (UnexpectedPropertySetTypeException ex)
- {
+ }
+ } catch (UnexpectedPropertySetTypeException ex) {
/* This exception will never be throws because we already checked
* explicitly for this case above. */
throw new IllegalStateException(ex);
@@ -108,44 +104,20 @@ public class PropertySetFactory
}
/**
- * <p>Creates a new summary information.</p>
+ * Creates a new summary information.
*
* @return the new summary information.
*/
- public static SummaryInformation newSummaryInformation()
- {
- final MutablePropertySet ps = new MutablePropertySet();
- final MutableSection s = (MutableSection) ps.getFirstSection();
- s.setFormatID(SectionIDMap.SUMMARY_INFORMATION_ID);
- try
- {
- return new SummaryInformation(ps);
- }
- catch (UnexpectedPropertySetTypeException ex)
- {
- /* This should never happen. */
- throw new HPSFRuntimeException(ex);
- }
+ public static SummaryInformation newSummaryInformation() {
+ return new SummaryInformation();
}
/**
- * <p>Creates a new document summary information.</p>
+ * Creates a new document summary information.
*
* @return the new document summary information.
*/
- public static DocumentSummaryInformation newDocumentSummaryInformation()
- {
- final MutablePropertySet ps = new MutablePropertySet();
- final MutableSection s = (MutableSection) ps.getFirstSection();
- s.setFormatID(SectionIDMap.DOCUMENT_SUMMARY_INFORMATION_ID[0]);
- try
- {
- return new DocumentSummaryInformation(ps);
- }
- catch (UnexpectedPropertySetTypeException ex)
- {
- /* This should never happen. */
- throw new HPSFRuntimeException(ex);
- }
+ public static DocumentSummaryInformation newDocumentSummaryInformation() {
+ return new DocumentSummaryInformation();
}
} \ No newline at end of file
diff --git a/src/java/org/apache/poi/hpsf/Section.java b/src/java/org/apache/poi/hpsf/Section.java
index c54fe51ab2..a27e9485cd 100644
--- a/src/java/org/apache/poi/hpsf/Section.java
+++ b/src/java/org/apache/poi/hpsf/Section.java
@@ -17,127 +17,96 @@
package org.apache.poi.hpsf;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.List;
+import java.util.Date;
import java.util.Map;
+import java.util.TreeMap;
+import org.apache.commons.collections4.bidimap.TreeBidiMap;
import org.apache.poi.hpsf.wellknown.PropertyIDMap;
import org.apache.poi.hpsf.wellknown.SectionIDMap;
+import org.apache.poi.util.CodePageUtil;
import org.apache.poi.util.LittleEndian;
/**
- * <p>Represents a section in a {@link PropertySet}.</p>
+ * Represents a section in a {@link PropertySet}.
*/
-public class Section
-{
+public class Section {
/**
- * <p>Maps property IDs to section-private PID strings. These
- * strings can be found in the property with ID 0.</p>
+ * Maps property IDs to section-private PID strings. These
+ * strings can be found in the property with ID 0.
*/
- protected Map<Long,String> dictionary;
+ private Map<Long,String> dictionary;
/**
- * <p>The section's format ID, {@link #getFormatID}.</p>
+ * The section's format ID, {@link #getFormatID}.
*/
- protected ClassID formatID;
-
-
+ private ClassID formatID;
/**
- * <p>Returns the format ID. The format ID is the "type" of the
- * section. For example, if the format ID of the first {@link
- * Section} contains the bytes specified by
- * <code>org.apache.poi.hpsf.wellknown.SectionIDMap.SUMMARY_INFORMATION_ID</code>
- * the section (and thus the property set) is a SummaryInformation.</p>
- *
- * @return The format ID
+ * If the "dirty" flag is true, the section's size must be
+ * (re-)calculated before the section is written.
*/
- public ClassID getFormatID()
- {
- return formatID;
- }
-
-
+ private boolean dirty = true;
/**
- * @see #getOffset
+ * 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.
*/
- protected long offset;
-
+ private byte[] sectionBytes;
/**
- * <p>Returns the offset of the section in the stream.</p>
- *
- * @return The offset of the section in the stream.
+ * The offset of the section in the stream.
*/
- public long getOffset()
- {
- return offset;
- }
-
-
+ private long offset = -1;
/**
- * @see #getSize
+ * The section's size in bytes.
*/
- protected int size;
-
+ private int size;
/**
- * <p>Returns the section's size in bytes.</p>
- *
- * @return The section's size in bytes.
+ * This section's properties.
*/
- public int getSize()
- {
- return size;
- }
-
-
+ private final Map<Long,Property> properties = new TreeMap<Long,Property>();
/**
- * <p>Returns the number of properties in this section.</p>
- *
- * @return The number of properties in this section.
+ * This member is {@code true} if the last call to {@link
+ * #getPropertyIntValue} or {@link #getProperty} tried to access a
+ * property that was not available, else {@code false}.
*/
- public int getPropertyCount()
- {
- return properties.length;
- }
-
-
+ private boolean wasNull;
/**
- * @see #getProperties
+ * Creates an empty {@link Section}.
*/
- protected Property[] properties;
-
+ public Section() {
+ }
/**
- * <p>Returns this section's properties.</p>
+ * Constructs a {@code Section} by doing a deep copy of an
+ * existing {@code Section}. All nested {@code Property}
+ * instances, will be their mutable counterparts in the new
+ * {@code MutableSection}.
*
- * @return This section's properties.
+ * @param s The section set to copy
*/
- public Property[] getProperties()
- {
- return properties;
+ public Section(final Section s) {
+ setFormatID(s.getFormatID());
+ for (Property p : s.properties.values()) {
+ properties.put(p.getID(), new MutableProperty(p));
+ }
+ setDictionary(s.getDictionary());
}
/**
- * <p>Creates an empty and uninitialized {@link Section}.
- */
- protected Section()
- { }
-
-
-
- /**
- * <p>Creates a {@link Section} instance from a byte array.</p>
+ * Creates a {@link Section} instance from a byte array.
*
* @param src Contains the complete property set stream.
* @param offset The position in the stream that points to the
@@ -147,9 +116,7 @@ public class Section
* supported.
*/
@SuppressWarnings("unchecked")
- public Section(final byte[] src, final int offset)
- throws UnsupportedEncodingException
- {
+ public Section(final byte[] src, final int offset) throws UnsupportedEncodingException {
int o1 = offset;
/*
@@ -201,82 +168,59 @@ public class Section
* one looks for property ID 1 and extracts the codepage number. The
* seconds pass reads the other properties.
*/
- properties = new Property[propertyCount];
-
/* Pass 1: Read the property list. */
int pass1Offset = o1;
- final List<PropertyListEntry> propertyList = new ArrayList<PropertyListEntry>(propertyCount);
- PropertyListEntry ple;
- for (int i = 0; i < properties.length; i++)
- {
- ple = new PropertyListEntry();
-
+ long cpOffset = -1;
+ final TreeBidiMap<Long,Long> offset2Id = new TreeBidiMap<Long,Long>();
+ for (int i = 0; i < propertyCount; i++) {
/* Read the property ID. */
- ple.id = (int) LittleEndian.getUInt(src, pass1Offset);
+ long id = LittleEndian.getUInt(src, pass1Offset);
pass1Offset += LittleEndian.INT_SIZE;
/* Offset from the section's start. */
- ple.offset = (int) LittleEndian.getUInt(src, pass1Offset);
+ long off = LittleEndian.getUInt(src, pass1Offset);
pass1Offset += LittleEndian.INT_SIZE;
- /* Add the entry to the property list. */
- propertyList.add(ple);
- }
-
- /* Sort the property list by ascending offsets: */
- Collections.sort(propertyList);
-
- /* Calculate the properties' lengths. */
- for (int i = 0; i < propertyCount - 1; i++)
- {
- PropertyListEntry ple1 = propertyList.get(i);
- PropertyListEntry ple2 = propertyList.get(i + 1);
- ple1.length = ple2.offset - ple1.offset;
- }
- if (propertyCount > 0)
- {
- ple = propertyList.get(propertyCount - 1);
- ple.length = size - ple.offset;
+ offset2Id.put(off, id);
+
+ if (id == PropertyIDMap.PID_CODEPAGE) {
+ cpOffset = off;
+ }
}
/* Look for the codepage. */
int codepage = -1;
- for (final Iterator<PropertyListEntry> i = propertyList.iterator();
- codepage == -1 && i.hasNext();)
- {
- ple = i.next();
-
- /* Read the codepage if the property ID is 1. */
- if (ple.id == PropertyIDMap.PID_CODEPAGE)
- {
- /* Read the property's value type. It must be
- * VT_I2. */
- int o = (int) (this.offset + ple.offset);
- final long type = LittleEndian.getUInt(src, o);
- o += LittleEndian.INT_SIZE;
-
- if (type != Variant.VT_I2)
- throw new HPSFRuntimeException
- ("Value type of property ID 1 is not VT_I2 but " +
- type + ".");
-
- /* Read the codepage number. */
- codepage = LittleEndian.getUShort(src, o);
+ if (cpOffset != -1) {
+ /* Read the property's value type. It must be VT_I2. */
+ long o = this.offset + cpOffset;
+ final long type = LittleEndian.getUInt(src, (int)o);
+ o += LittleEndian.INT_SIZE;
+
+ if (type != Variant.VT_I2) {
+ throw new HPSFRuntimeException
+ ("Value type of property ID 1 is not VT_I2 but " +
+ type + ".");
}
+
+ /* Read the codepage number. */
+ codepage = LittleEndian.getUShort(src, (int)o);
}
+
/* Pass 2: Read all properties - including the codepage property,
* if available. */
- int i1 = 0;
- for (final Iterator<PropertyListEntry> i = propertyList.iterator(); i.hasNext();)
- {
- ple = i.next();
- Property p = new Property(ple.id, src,
- this.offset + ple.offset,
- ple.length, codepage);
- if (p.getID() == PropertyIDMap.PID_CODEPAGE)
- p = new Property(p.getID(), p.getType(), Integer.valueOf(codepage));
- properties[i1++] = p;
+ for (Map.Entry<Long,Long> me : offset2Id.entrySet()) {
+ long off = me.getKey();
+ long id = me.getValue();
+ Property p;
+ if (id == PropertyIDMap.PID_CODEPAGE) {
+ p = new Property(PropertyIDMap.PID_CODEPAGE, Variant.VT_I2, codepage);
+ } else {
+ int pLen = propLen(offset2Id, off, size);
+ long o = this.offset + off;
+ p = new Property(id, src, o, pLen, codepage);
+ }
+ properties.put(id, p);
}
/*
@@ -285,202 +229,367 @@ public class Section
dictionary = (Map<Long,String>) getProperty(0);
}
+ /**
+ * Retrieves the length of the given property (by key)
+ *
+ * @param offset2Id the offset to id map
+ * @param entryOffset the current entry key
+ * @param maxSize the maximum offset/size of the section stream
+ * @return the length of the current property
+ */
+ private static int propLen(
+ TreeBidiMap<Long,Long> offset2Id,
+ Long entryOffset,
+ long maxSize) {
+ Long nextKey = offset2Id.nextKey(entryOffset);
+ long begin = entryOffset;
+ long end = (nextKey != null) ? nextKey : maxSize;
+ return (int)(end - begin);
+ }
+
+
+ /**
+ * Returns the format ID. The format ID is the "type" of the
+ * section. For example, if the format ID of the first {@link
+ * Section} contains the bytes specified by
+ * {@code org.apache.poi.hpsf.wellknown.SectionIDMap.SUMMARY_INFORMATION_ID}
+ * the section (and thus the property set) is a SummaryInformation.
+ *
+ * @return The format ID
+ */
+ public ClassID getFormatID() {
+ return formatID;
+ }
+ /**
+ * Sets the section's format ID.
+ *
+ * @param formatID The section's format ID
+ */
+ public void setFormatID(final ClassID formatID) {
+ this.formatID = formatID;
+ }
/**
- * <p>Represents an entry in the property list and holds a property's ID and
- * its offset from the section's beginning.</p>
+ * Sets the section's format ID.
+ *
+ * @param formatID The section's format ID as a byte array. It components
+ * are in big-endian format.
*/
- static class PropertyListEntry implements Comparable<PropertyListEntry>
- {
- int id;
- int offset;
- int length;
-
- /**
- * <p>Compares this {@link PropertyListEntry} with another one by their
- * offsets. A {@link PropertyListEntry} is "smaller" than another one if
- * its offset from the section's begin is smaller.</p>
- *
- * @see Comparable#compareTo(java.lang.Object)
- */
- public int compareTo(final PropertyListEntry o)
- {
- final int otherOffset = o.offset;
- if (offset < otherOffset)
- return -1;
- else if (offset == otherOffset)
- return 0;
- else
- return 1;
+ public void setFormatID(final byte[] formatID) {
+ ClassID fid = getFormatID();
+ if (fid == null) {
+ fid = new ClassID();
+ setFormatID(fid);
}
+ fid.setBytes(formatID);
+ }
-
-
- public int hashCode() {
- final int prime = 31;
- int result = 1;
- result = prime * result + id;
- result = prime * result + length;
- result = prime * result + offset;
- return result;
- }
+ /**
+ * Returns the offset of the section in the stream.
+ *
+ * @return The offset of the section in the stream.
+ */
+ public long getOffset() {
+ return offset;
+ }
+ /**
+ * Returns the number of properties in this section.
+ *
+ * @return The number of properties in this section.
+ */
+ public int getPropertyCount() {
+ return properties.size();
+ }
+ /**
+ * Returns this section's properties.
+ *
+ * @return This section's properties.
+ */
+ public Property[] getProperties() {
+ return properties.values().toArray(new Property[properties.size()]);
+ }
- public boolean equals(Object obj) {
- if (this == obj) {
- return true;
- }
- if (obj == null) {
- return false;
- }
- if (getClass() != obj.getClass()) {
- return false;
- }
- PropertyListEntry other = (PropertyListEntry) obj;
- if (id != other.id) {
- return false;
- }
- if (length != other.length) {
- return false;
- }
- if (offset != other.offset) {
- return false;
- }
- return true;
+ /**
+ * Sets this section's properties. Any former values are overwritten.
+ *
+ * @param properties This section's new properties.
+ */
+ public void setProperties(final Property[] properties) {
+ this.properties.clear();
+ for (Property p : properties) {
+ this.properties.put(p.getID(), p);
}
+ dirty = true;
+ }
+ /**
+ * Returns the value of the property with the specified ID. If
+ * the property is not available, {@code null} is returned
+ * and a subsequent call to {@link #wasNull} will return
+ * {@code true}.
+ *
+ * @param id The property's ID
+ *
+ * @return The property's value
+ */
+ public Object getProperty(final long id) {
+ wasNull = !properties.containsKey(id);
+ return (wasNull) ? null : properties.get(id).getValue();
+ }
+ /**
+ * Sets the string value of the property with the specified ID.
+ *
+ * @param id The property's ID
+ * @param value The property's value. It will be written as a Unicode
+ * string.
+ */
+ public void setProperty(final int id, final String value) {
+ setProperty(id, Variant.VT_LPWSTR, value);
+ }
- public String toString()
- {
- final StringBuffer b = new StringBuffer();
- b.append(getClass().getName());
- b.append("[id=");
- b.append(id);
- b.append(", offset=");
- b.append(offset);
- b.append(", length=");
- b.append(length);
- b.append(']');
- return b.toString();
- }
+ /**
+ * Sets the int value of the property with the specified ID.
+ *
+ * @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));
}
/**
- * <p>Returns the value of the property with the specified ID. If
- * the property is not available, <code>null</code> is returned
- * and a subsequent call to {@link #wasNull} will return
- * <code>true</code>.</p>
+ * Sets the long value of the property with the specified ID.
*
* @param id The property's ID
+ * @param value The property's value.
*
- * @return The property's value
+ * @see #setProperty(int, long, Object)
+ * @see #getProperty
*/
- public Object getProperty(final long id)
- {
- wasNull = false;
- for (int i = 0; i < properties.length; i++)
- if (id == properties[i].getID())
- return properties[i].getValue();
- wasNull = true;
- return null;
+ public void setProperty(final int id, final long value) {
+ setProperty(id, Variant.VT_I8, Long.valueOf(value));
}
/**
- * <p>Returns the value of the numeric property with the specified
+ * Sets the boolean value of the property with the specified ID.
+ *
+ * @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));
+ }
+
+
+
+ /**
+ * 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.
+ *
+ * @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) {
+ setProperty(new Property(id, variantType, value));
+ }
+
+
+
+ /**
+ * Sets a property.
+ *
+ * @param p The property to be set.
+ *
+ * @see #setProperty(int, long, Object)
+ * @see #getProperty
+ * @see Variant
+ */
+ public void setProperty(final Property p) {
+ Property old = properties.get(p.getID());
+ if (old == null || !old.equals(p)) {
+ properties.put(p.getID(), p);
+ dirty = true;
+ }
+ }
+
+ /**
+ * Sets a property.
+ *
+ * @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() + ".");
+ }
+ }
+
+ /**
+ * Returns the value of the numeric property with the specified
* ID. If the property is not available, 0 is returned. A
* subsequent call to {@link #wasNull} will return
- * <code>true</code> to let the caller distinguish that case from
- * a real property value of 0.</p>
+ * {@code true} to let the caller distinguish that case from
+ * a real property value of 0.
*
* @param id The property's ID
*
* @return The property's value
*/
- protected int getPropertyIntValue(final long id)
- {
+ protected int getPropertyIntValue(final long id) {
final Number i;
final Object o = getProperty(id);
- if (o == null)
+ if (o == null) {
return 0;
- if (!(o instanceof Long || o instanceof Integer))
+ }
+ if (!(o instanceof Long || o instanceof Integer)) {
throw new HPSFRuntimeException
("This property is not an integer type, but " +
o.getClass().getName() + ".");
+ }
i = (Number) o;
return i.intValue();
}
-
-
/**
- * <p>Returns the value of the boolean property with the specified
- * ID. If the property is not available, <code>false</code> is
+ * Returns the value of the boolean property with the specified
+ * ID. If the property is not available, {@code false} is
* returned. A subsequent call to {@link #wasNull} will return
- * <code>true</code> to let the caller distinguish that case from
- * a real property value of <code>false</code>.</p>
+ * {@code true} to let the caller distinguish that case from
+ * a real property value of {@code false}.
*
* @param id The property's ID
*
* @return The property's value
*/
- protected boolean getPropertyBooleanValue(final int id)
- {
+ protected boolean getPropertyBooleanValue(final int id) {
final Boolean b = (Boolean) getProperty(id);
if (b == null) {
return false;
}
return b.booleanValue();
- }
+ }
+ /**
+ * Sets the value of the boolean property with the specified
+ * ID.
+ *
+ * @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));
+ }
+ /**
+ * @return the section's size in bytes.
+ */
+ 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>This member is <code>true</code> if the last call to {@link
- * #getPropertyIntValue} or {@link #getProperty} tried to access a
- * property that was not available, else <code>false</code>.</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.
+ *
+ * @return the section's length in bytes.
+ * @throws WritingNotSupportedException
+ * @throws IOException
*/
- private boolean wasNull;
+ 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>Checks whether the property which the last call to {@link
+ * Checks whether the property which the last call to {@link
* #getPropertyIntValue} or {@link #getProperty} tried to access
* was available or not. This information might be important for
* callers of {@link #getPropertyIntValue} since the latter
* returns 0 if the property does not exist. Using {@link
* #wasNull} the caller can distiguish this case from a property's
- * real value of 0.</p>
+ * real value of 0.
*
- * @return <code>true</code> if the last call to {@link
+ * @return {@code true} if the last call to {@link
* #getPropertyIntValue} or {@link #getProperty} tried to access a
- * property that was not available, else <code>false</code>.
+ * property that was not available, else {@code false}.
*/
- public boolean wasNull()
- {
+ public boolean wasNull() {
return wasNull;
}
/**
- * <p>Returns the PID string associated with a property ID. The ID
+ * Returns the PID string associated with a property ID. The ID
* is first looked up in the {@link Section}'s private
* dictionary. If it is not found there, the method calls {@link
- * SectionIDMap#getPIDString}.</p>
+ * SectionIDMap#getPIDString}.
*
* @param pid The property ID
*
* @return The property ID's string value
*/
- public String getPIDString(final long pid)
- {
+ public String getPIDString(final long pid) {
String s = null;
if (dictionary != null) {
s = dictionary.get(Long.valueOf(pid));
@@ -491,39 +600,65 @@ public class Section
return s;
}
+ /**
+ * Removes all properties from the section including 0 (dictionary) and
+ * 1 (codepage).
+ */
+ public void clear()
+ {
+ final Property[] properties = getProperties();
+ for (int i = 0; i < properties.length; i++)
+ {
+ final Property p = properties[i];
+ removeProperty(p.getID());
+ }
+ }
+
+ /**
+ * Sets the codepage.
+ *
+ * @param codepage the codepage
+ */
+ public void setCodepage(final int codepage)
+ {
+ setProperty(PropertyIDMap.PID_CODEPAGE, Variant.VT_I2,
+ Integer.valueOf(codepage));
+ }
+
/**
- * <p>Checks whether this section is equal to another object. The result is
- * <code>false</code> if one of the the following conditions holds:</p>
+ * Checks whether this section is equal to another object. The result is
+ * {@code false} if one of the the following conditions holds:
*
* <ul>
*
- * <li><p>The other object is not a {@link Section}.</p></li>
+ * <li>The other object is not a {@link Section}.</li>
*
- * <li><p>The format IDs of the two sections are not equal.</p></li>
+ * <li>The format IDs of the two sections are not equal.</li>
*
- * <li><p>The sections have a different number of properties. However,
- * properties with ID 1 (codepage) are not counted.</p></li>
+ * <li>The sections have a different number of properties. However,
+ * properties with ID 1 (codepage) are not counted.</li>
*
- * <li><p>The other object is not a {@link Section}.</p></li>
+ * <li>The other object is not a {@link Section}.</li>
*
- * <li><p>The properties have different values. The order of the properties
- * is irrelevant.</p></li>
+ * <li>The properties have different values. The order of the properties
+ * is irrelevant.</li>
*
* </ul>
*
* @param o The object to compare this section with
- * @return <code>true</code> if the objects are equal, <code>false</code> if
+ * @return {@code true} if the objects are equal, {@code false} if
* not
*/
- public boolean equals(final Object o)
- {
- if (o == null || !(o instanceof Section))
+ public boolean equals(final Object o) {
+ if (o == null || !(o instanceof Section)) {
return false;
+ }
final Section s = (Section) o;
- if (!s.getFormatID().equals(getFormatID()))
+ if (!s.getFormatID().equals(getFormatID())) {
return false;
+ }
/* Compare all properties except 0 and 1 as they must be handled
* specially. */
@@ -536,34 +671,26 @@ public class Section
* arrays. */
Property p10 = null;
Property p20 = null;
- for (int i = 0; i < pa1.length; i++)
- {
+ for (int i = 0; i < pa1.length; i++) {
final long id = pa1[i].getID();
- if (id == 0)
- {
+ if (id == 0) {
p10 = pa1[i];
pa1 = remove(pa1, i);
i--;
}
- if (id == 1)
- {
- // p11 = pa1[i];
+ if (id == 1) {
pa1 = remove(pa1, i);
i--;
}
}
- for (int i = 0; i < pa2.length; i++)
- {
+ for (int i = 0; i < pa2.length; i++) {
final long id = pa2[i].getID();
- if (id == 0)
- {
+ if (id == 0) {
p20 = pa2[i];
pa2 = remove(pa2, i);
i--;
}
- if (id == 1)
- {
- // p21 = pa2[i];
+ if (id == 1) {
pa2 = remove(pa2, i);
i--;
}
@@ -571,52 +698,266 @@ public class Section
/* If the number of properties (not counting property 1) is unequal the
* sections are unequal. */
- if (pa1.length != pa2.length)
+ if (pa1.length != pa2.length) {
return false;
+ }
/* If the dictionaries are unequal the sections are unequal. */
boolean dictionaryEqual = true;
- if (p10 != null && p20 != null)
+ if (p10 != null && p20 != null) {
dictionaryEqual = p10.getValue().equals(p20.getValue());
- else if (p10 != null || p20 != null)
+ } else if (p10 != null || p20 != null) {
dictionaryEqual = false;
+ }
if (dictionaryEqual) {
return Util.equals(pa1, pa2);
}
return false;
}
-
+ /**
+ * Removes a property.
+ *
+ * @param id The ID of the property to be removed
+ */
+ public void removeProperty(final long id) {
+ dirty |= (properties.remove(id) != null);
+ }
/**
- * <p>Removes a field from a property array. The resulting array is
- * compactified and returned.</p>
+ * Removes a field from a property array. The resulting array is
+ * compactified and returned.
*
* @param pa The property array.
* @param i The index of the field to be removed.
* @return the compactified array.
*/
- private Property[] remove(final Property[] pa, final int i)
- {
+ private Property[] remove(final Property[] pa, final int i) {
final Property[] h = new Property[pa.length - 1];
- if (i > 0)
+ if (i > 0) {
System.arraycopy(pa, 0, h, 0, i);
+ }
System.arraycopy(pa, i + 1, h, i, h.length - i);
return h;
}
+ /**
+ * Writes this section into an output stream.<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.
+ *
+ * @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();
+ }
+
+ /* Write the properties and the property list into their respective
+ * streams: */
+ for (Property p : properties.values()) {
+ 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;
+ }
+
+
+
+ /**
+ * Writes the section's dictionary.
+ *
+ * @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;
+ }
+
+ /**
+ * 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.
+ *
+ * @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);
+ }
+ }
/**
* @see Object#hashCode()
*/
- public int hashCode()
- {
+ public int hashCode() {
long hashCode = 0;
hashCode += getFormatID().hashCode();
final Property[] pa = getProperties();
- for (int i = 0; i < pa.length; i++)
+ for (int i = 0; i < pa.length; i++) {
hashCode += pa[i].hashCode();
+ }
final int returnHashCode = (int) (hashCode & 0x0ffffffffL);
return returnHashCode;
}
@@ -626,8 +967,7 @@ public class Section
/**
* @see Object#toString()
*/
- public String toString()
- {
+ public String toString() {
final StringBuffer b = new StringBuffer();
final Property[] pa = getProperties();
b.append(getClass().getName());
@@ -641,8 +981,7 @@ public class Section
b.append(", size: ");
b.append(getSize());
b.append(", properties: [\n");
- for (int i = 0; i < pa.length; i++)
- {
+ for (int i = 0; i < pa.length; i++) {
b.append(pa[i].toString());
b.append(",\n");
}
@@ -654,35 +993,33 @@ public class Section
/**
- * <p>Gets the section's dictionary. A dictionary allows an application to
+ * Gets the section's dictionary. A dictionary allows an application to
* use human-readable property names instead of numeric property IDs. It
* contains mappings from property IDs to their associated string
* values. The dictionary is stored as the property with ID 0. The codepage
- * for the strings in the dictionary is defined by property with ID 1.</p>
+ * for the strings in the dictionary is defined by property with ID 1.
*
- * @return the dictionary or <code>null</code> if the section does not have
+ * @return the dictionary or {@code null} if the section does not have
* a dictionary.
*/
- public Map<Long,String> getDictionary()
- {
+ public Map<Long,String> getDictionary() {
return dictionary;
}
/**
- * <p>Gets the section's codepage, if any.</p>
+ * Gets the section's codepage, if any.
*
* @return The section's codepage if one is defined, else -1.
*/
public int getCodepage()
{
- final Integer codepage =
- (Integer) getProperty(PropertyIDMap.PID_CODEPAGE);
- if (codepage == null)
+ final Integer codepage = (Integer) getProperty(PropertyIDMap.PID_CODEPAGE);
+ if (codepage == null) {
return -1;
+ }
int cp = codepage.intValue();
return cp;
}
-
}
diff --git a/src/java/org/apache/poi/hpsf/SpecialPropertySet.java b/src/java/org/apache/poi/hpsf/SpecialPropertySet.java
index aa8f8051d2..843d681ebc 100644
--- a/src/java/org/apache/poi/hpsf/SpecialPropertySet.java
+++ b/src/java/org/apache/poi/hpsf/SpecialPropertySet.java
@@ -17,397 +17,24 @@
package org.apache.poi.hpsf;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.nio.charset.Charset;
-import java.util.List;
-
-import org.apache.poi.hpsf.wellknown.PropertyIDMap;
-import org.apache.poi.poifs.filesystem.DirectoryEntry;
-import org.apache.poi.util.LittleEndian;
+import org.apache.poi.util.Removal;
/**
- * <p>Abstract superclass for the convenience classes {@link
- * SummaryInformation} and {@link DocumentSummaryInformation}.</p>
+ * Interface for the convenience classes {@link SummaryInformation}
+ * and {@link DocumentSummaryInformation}.<p>
*
- * <p>The motivation behind this class is quite nasty if you look
- * behind the scenes, but it serves the application programmer well by
- * providing him with the easy-to-use {@link SummaryInformation} and
- * {@link DocumentSummaryInformation} classes. When parsing the data a
- * property set stream consists of (possibly coming from an {@link
- * java.io.InputStream}) we want to read and process each byte only
- * once. Since we don't know in advance which kind of property set we
- * have, we can expect only the most general {@link
- * PropertySet}. Creating a special subclass should be as easy as
- * calling the special subclass' constructor and pass the general
- * {@link PropertySet} in. To make things easy internally, the special
- * class just holds a reference to the general {@link PropertySet} and
- * delegates all method calls to it.</p>
+ * This used to be an abstract class to support late loading
+ * of the SummaryInformation classes, as their concrete instance can
+ * only be determined after the PropertySet has been loaded.
*
- * <p>A cleaner implementation would have been like this: The {@link
- * PropertySetFactory} parses the stream data into some internal
- * object first. Then it finds out whether the stream is a {@link
- * SummaryInformation}, a {@link DocumentSummaryInformation} or a
- * general {@link PropertySet}. However, the current implementation
- * went the other way round historically: the convenience classes came
- * only late to my mind.</p>
+ * @deprecated POI 3.16 - use PropertySet as base class instead
*/
-public abstract class SpecialPropertySet extends MutablePropertySet
-{
- /**
- * The id to name mapping of the properties in this set.
- *
- * @return the id to name mapping of the properties in this set
- */
- public abstract PropertyIDMap getPropertySetIDMap();
-
- /**
- * <p>The "real" property set <code>SpecialPropertySet</code>
- * delegates to.</p>
- */
- private final MutablePropertySet delegate;
-
-
-
- /**
- * <p>Creates a <code>SpecialPropertySet</code>.
- *
- * @param ps The property set to be encapsulated by the
- * <code>SpecialPropertySet</code>
- */
- public SpecialPropertySet(final PropertySet ps)
- {
- delegate = new MutablePropertySet(ps);
- }
-
-
-
- /**
- * <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);
+@Removal(version="3.18")
+public class SpecialPropertySet extends MutablePropertySet {
+ public SpecialPropertySet() {
}
- /**
- * @see org.apache.poi.hpsf.PropertySet#getProperties()
- */
- @Override
- public Property[] getProperties() throws NoSingleSectionException
- {
- return delegate.getProperties();
+ public SpecialPropertySet(final PropertySet ps) throws UnexpectedPropertySetTypeException {
+ super(ps);
}
-
- /**
- * @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();
- }
-
}
diff --git a/src/java/org/apache/poi/hpsf/SummaryInformation.java b/src/java/org/apache/poi/hpsf/SummaryInformation.java
index 5c260187c0..856f566e75 100644
--- a/src/java/org/apache/poi/hpsf/SummaryInformation.java
+++ b/src/java/org/apache/poi/hpsf/SummaryInformation.java
@@ -20,18 +20,18 @@ package org.apache.poi.hpsf;
import java.util.Date;
import org.apache.poi.hpsf.wellknown.PropertyIDMap;
+import org.apache.poi.hpsf.wellknown.SectionIDMap;
/**
- * <p>Convenience class representing a Summary Information stream in a
- * Microsoft Office document.</p>
+ * Convenience class representing a Summary Information stream in a
+ * Microsoft Office document.
*
* @see DocumentSummaryInformation
*/
public final class SummaryInformation extends SpecialPropertySet {
/**
- * <p>The document name a summary information stream usually has in a POIFS
- * filesystem.</p>
+ * The document name a summary information stream usually has in a POIFS filesystem.
*/
public static final String DEFAULT_STREAM_NAME = "\005SummaryInformation";
@@ -39,324 +39,291 @@ public final class SummaryInformation extends SpecialPropertySet {
return PropertyIDMap.getSummaryInformationProperties();
}
-
/**
- * <p>Creates a {@link SummaryInformation} from a given {@link
- * PropertySet}.</p>
+ * Creates a {@link SummaryInformation} from a given {@link
+ * PropertySet}.
+ *
+ * @param ps A property set which should be created from a summary
+ * information stream.
+ * @throws UnexpectedPropertySetTypeException if {@code ps} does not
+ * contain a summary information stream.
+ */
+ public SummaryInformation() {
+ getFirstSection().setFormatID(SectionIDMap.SUMMARY_INFORMATION_ID);
+ }
+
+ /**
+ * Creates a {@link SummaryInformation} from a given {@link
+ * PropertySet}.
*
* @param ps A property set which should be created from a summary
* information stream.
- * @throws UnexpectedPropertySetTypeException if <var>ps</var> does not
+ * @throws UnexpectedPropertySetTypeException if {@code ps} does not
* contain a summary information stream.
*/
- public SummaryInformation(final PropertySet ps)
- throws UnexpectedPropertySetTypeException
- {
+ public SummaryInformation(final PropertySet ps) throws UnexpectedPropertySetTypeException {
super(ps);
- if (!isSummaryInformation())
- throw new UnexpectedPropertySetTypeException("Not a "
- + getClass().getName());
+ if (!isSummaryInformation()) {
+ throw new UnexpectedPropertySetTypeException("Not a " + getClass().getName());
+ }
}
/**
- * <p>Returns the title (or <code>null</code>).</p>
- *
- * @return The title or <code>null</code>
+ * @return The title or {@code null}
*/
- public String getTitle()
- {
+ public String getTitle() {
return getPropertyStringValue(PropertyIDMap.PID_TITLE);
}
/**
- * <p>Sets the title.</p>
+ * Sets the title.
*
* @param title The title to set.
*/
- public void setTitle(final String title)
- {
- final MutableSection s = (MutableSection) getFirstSection();
- s.setProperty(PropertyIDMap.PID_TITLE, title);
+ public void setTitle(final String title) {
+ set1stProperty(PropertyIDMap.PID_TITLE, title);
}
/**
- * <p>Removes the title.</p>
+ * Removes the title.
*/
- public void removeTitle()
- {
- final MutableSection s = (MutableSection) getFirstSection();
- s.removeProperty(PropertyIDMap.PID_TITLE);
+ public void removeTitle() {
+ remove1stProperty(PropertyIDMap.PID_TITLE);
}
/**
- * <p>Returns the subject (or <code>null</code>).</p>
+ * Returns the subject (or {@code null}).
*
- * @return The subject or <code>null</code>
+ * @return The subject or {@code null}
*/
- public String getSubject()
- {
+ public String getSubject() {
return getPropertyStringValue(PropertyIDMap.PID_SUBJECT);
}
/**
- * <p>Sets the subject.</p>
+ * Sets the subject.
*
* @param subject The subject to set.
*/
- public void setSubject(final String subject)
- {
- final MutableSection s = (MutableSection) getFirstSection();
- s.setProperty(PropertyIDMap.PID_SUBJECT, subject);
+ public void setSubject(final String subject) {
+ set1stProperty(PropertyIDMap.PID_SUBJECT, subject);
}
/**
- * <p>Removes the subject.</p>
+ * Removes the subject.
*/
- public void removeSubject()
- {
- final MutableSection s = (MutableSection) getFirstSection();
- s.removeProperty(PropertyIDMap.PID_SUBJECT);
+ public void removeSubject() {
+ remove1stProperty(PropertyIDMap.PID_SUBJECT);
}
/**
- * <p>Returns the author (or <code>null</code>).</p>
+ * Returns the author (or {@code null}).
*
- * @return The author or <code>null</code>
+ * @return The author or {@code null}
*/
- public String getAuthor()
- {
+ public String getAuthor() {
return getPropertyStringValue(PropertyIDMap.PID_AUTHOR);
}
/**
- * <p>Sets the author.</p>
+ * Sets the author.
*
* @param author The author to set.
*/
- public void setAuthor(final String author)
- {
- final MutableSection s = (MutableSection) getFirstSection();
- s.setProperty(PropertyIDMap.PID_AUTHOR, author);
+ public void setAuthor(final String author) {
+ set1stProperty(PropertyIDMap.PID_AUTHOR, author);
}
/**
- * <p>Removes the author.</p>
+ * Removes the author.
*/
- public void removeAuthor()
- {
- final MutableSection s = (MutableSection) getFirstSection();
- s.removeProperty(PropertyIDMap.PID_AUTHOR);
+ public void removeAuthor() {
+ remove1stProperty(PropertyIDMap.PID_AUTHOR);
}
/**
- * <p>Returns the keywords (or <code>null</code>).</p>
+ * Returns the keywords (or {@code null}).
*
- * @return The keywords or <code>null</code>
+ * @return The keywords or {@code null}
*/
- public String getKeywords()
- {
+ public String getKeywords() {
return getPropertyStringValue(PropertyIDMap.PID_KEYWORDS);
}
/**
- * <p>Sets the keywords.</p>
+ * Sets the keywords.
*
* @param keywords The keywords to set.
*/
- public void setKeywords(final String keywords)
- {
- final MutableSection s = (MutableSection) getFirstSection();
- s.setProperty(PropertyIDMap.PID_KEYWORDS, keywords);
+ public void setKeywords(final String keywords) {
+ set1stProperty(PropertyIDMap.PID_KEYWORDS, keywords);
}
/**
- * <p>Removes the keywords.</p>
+ * Removes the keywords.
*/
- public void removeKeywords()
- {
- final MutableSection s = (MutableSection) getFirstSection();
- s.removeProperty(PropertyIDMap.PID_KEYWORDS);
+ public void removeKeywords() {
+ remove1stProperty(PropertyIDMap.PID_KEYWORDS);
}
/**
- * <p>Returns the comments (or <code>null</code>).</p>
+ * Returns the comments (or {@code null}).
*
- * @return The comments or <code>null</code>
+ * @return The comments or {@code null}
*/
- public String getComments()
- {
+ public String getComments() {
return getPropertyStringValue(PropertyIDMap.PID_COMMENTS);
}
/**
- * <p>Sets the comments.</p>
+ * Sets the comments.
*
* @param comments The comments to set.
*/
- public void setComments(final String comments)
- {
- final MutableSection s = (MutableSection) getFirstSection();
- s.setProperty(PropertyIDMap.PID_COMMENTS, comments);
+ public void setComments(final String comments) {
+ set1stProperty(PropertyIDMap.PID_COMMENTS, comments);
}
/**
- * <p>Removes the comments.</p>
+ * Removes the comments.
*/
- public void removeComments()
- {
- final MutableSection s = (MutableSection) getFirstSection();
- s.removeProperty(PropertyIDMap.PID_COMMENTS);
+ public void removeComments() {
+ remove1stProperty(PropertyIDMap.PID_COMMENTS);
}
/**
- * <p>Returns the template (or <code>null</code>).</p>
+ * Returns the template (or {@code null}).
*
- * @return The template or <code>null</code>
+ * @return The template or {@code null}
*/
- public String getTemplate()
- {
+ public String getTemplate() {
return getPropertyStringValue(PropertyIDMap.PID_TEMPLATE);
}
/**
- * <p>Sets the template.</p>
+ * Sets the template.
*
* @param template The template to set.
*/
- public void setTemplate(final String template)
- {
- final MutableSection s = (MutableSection) getFirstSection();
- s.setProperty(PropertyIDMap.PID_TEMPLATE, template);
+ public void setTemplate(final String template) {
+ set1stProperty(PropertyIDMap.PID_TEMPLATE, template);
}
/**
- * <p>Removes the template.</p>
+ * Removes the template.
*/
- public void removeTemplate()
- {
- final MutableSection s = (MutableSection) getFirstSection();
- s.removeProperty(PropertyIDMap.PID_TEMPLATE);
+ public void removeTemplate() {
+ remove1stProperty(PropertyIDMap.PID_TEMPLATE);
}
/**
- * <p>Returns the last author (or <code>null</code>).</p>
+ * Returns the last author (or {@code null}).
*
- * @return The last author or <code>null</code>
+ * @return The last author or {@code null}
*/
- public String getLastAuthor()
- {
+ public String getLastAuthor() {
return getPropertyStringValue(PropertyIDMap.PID_LASTAUTHOR);
}
/**
- * <p>Sets the last author.</p>
+ * Sets the last author.
*
* @param lastAuthor The last author to set.
*/
- public void setLastAuthor(final String lastAuthor)
- {
- final MutableSection s = (MutableSection) getFirstSection();
- s.setProperty(PropertyIDMap.PID_LASTAUTHOR, lastAuthor);
+ public void setLastAuthor(final String lastAuthor) {
+ set1stProperty(PropertyIDMap.PID_LASTAUTHOR, lastAuthor);
}
/**
- * <p>Removes the last author.</p>
+ * Removes the last author.
*/
- public void removeLastAuthor()
- {
- final MutableSection s = (MutableSection) getFirstSection();
- s.removeProperty(PropertyIDMap.PID_LASTAUTHOR);
+ public void removeLastAuthor() {
+ remove1stProperty(PropertyIDMap.PID_LASTAUTHOR);
}
/**
- * <p>Returns the revision number (or <code>null</code>). </p>
+ * Returns the revision number (or {@code null}).
*
- * @return The revision number or <code>null</code>
+ * @return The revision number or {@code null}
*/
- public String getRevNumber()
- {
+ public String getRevNumber() {
return getPropertyStringValue(PropertyIDMap.PID_REVNUMBER);
}
/**
- * <p>Sets the revision number.</p>
+ * Sets the revision number.
*
* @param revNumber The revision number to set.
*/
- public void setRevNumber(final String revNumber)
- {
- final MutableSection s = (MutableSection) getFirstSection();
- s.setProperty(PropertyIDMap.PID_REVNUMBER, revNumber);
+ public void setRevNumber(final String revNumber) {
+ set1stProperty(PropertyIDMap.PID_REVNUMBER, revNumber);
}
/**
- * <p>Removes the revision number.</p>
+ * Removes the revision number.
*/
- public void removeRevNumber()
- {
- final MutableSection s = (MutableSection) getFirstSection();
- s.removeProperty(PropertyIDMap.PID_REVNUMBER);
+ public void removeRevNumber() {
+ remove1stProperty(PropertyIDMap.PID_REVNUMBER);
}
/**
- * <p>Returns the total time spent in editing the document (or
- * <code>0</code>).</p>
+ * Returns the total time spent in editing the document (or
+ * {@code 0}).
*
* @return The total time spent in editing the document or 0 if the {@link
* SummaryInformation} does not contain this information.
*/
- public long getEditTime()
- {
+ public long getEditTime() {
final Date d = (Date) getProperty(PropertyIDMap.PID_EDITTIME);
if (d == null) {
return 0;
@@ -367,124 +334,106 @@ public final class SummaryInformation extends SpecialPropertySet {
/**
- * <p>Sets the total time spent in editing the document.</p>
+ * Sets the total time spent in editing the document.
*
* @param time The time to set.
*/
- public void setEditTime(final long time)
- {
+ public void setEditTime(final long time) {
final Date d = Util.filetimeToDate(time);
- final MutableSection s = (MutableSection) getFirstSection();
- s.setProperty(PropertyIDMap.PID_EDITTIME, Variant.VT_FILETIME, d);
+ getFirstSection().setProperty(PropertyIDMap.PID_EDITTIME, Variant.VT_FILETIME, d);
}
/**
- * <p>Remove the total time spent in editing the document.</p>
+ * Remove the total time spent in editing the document.
*/
- public void removeEditTime()
- {
- final MutableSection s = (MutableSection) getFirstSection();
- s.removeProperty(PropertyIDMap.PID_EDITTIME);
+ public void removeEditTime() {
+ remove1stProperty(PropertyIDMap.PID_EDITTIME);
}
/**
- * <p>Returns the last printed time (or <code>null</code>).</p>
+ * Returns the last printed time (or {@code null}).
*
- * @return The last printed time or <code>null</code>
+ * @return The last printed time or {@code null}
*/
- public Date getLastPrinted()
- {
+ public Date getLastPrinted() {
return (Date) getProperty(PropertyIDMap.PID_LASTPRINTED);
}
/**
- * <p>Sets the lastPrinted.</p>
+ * Sets the lastPrinted.
*
* @param lastPrinted The lastPrinted to set.
*/
- public void setLastPrinted(final Date lastPrinted)
- {
- final MutableSection s = (MutableSection) getFirstSection();
- s.setProperty(PropertyIDMap.PID_LASTPRINTED, Variant.VT_FILETIME,
- lastPrinted);
+ public void setLastPrinted(final Date lastPrinted) {
+ getFirstSection().setProperty(PropertyIDMap.PID_LASTPRINTED, Variant.VT_FILETIME, lastPrinted);
}
/**
- * <p>Removes the lastPrinted.</p>
+ * Removes the lastPrinted.
*/
- public void removeLastPrinted()
- {
- final MutableSection s = (MutableSection) getFirstSection();
- s.removeProperty(PropertyIDMap.PID_LASTPRINTED);
+ public void removeLastPrinted() {
+ remove1stProperty(PropertyIDMap.PID_LASTPRINTED);
}
/**
- * <p>Returns the creation time (or <code>null</code>).</p>
+ * Returns the creation time (or {@code null}).
*
- * @return The creation time or <code>null</code>
+ * @return The creation time or {@code null}
*/
- public Date getCreateDateTime()
- {
+ public Date getCreateDateTime() {
return (Date) getProperty(PropertyIDMap.PID_CREATE_DTM);
}
/**
- * <p>Sets the creation time.</p>
+ * Sets the creation time.
*
* @param createDateTime The creation time to set.
*/
- public void setCreateDateTime(final Date createDateTime)
- {
- final MutableSection s = (MutableSection) getFirstSection();
- s.setProperty(PropertyIDMap.PID_CREATE_DTM, Variant.VT_FILETIME,
- createDateTime);
+ public void setCreateDateTime(final Date createDateTime) {
+ getFirstSection().setProperty(PropertyIDMap.PID_CREATE_DTM, Variant.VT_FILETIME, createDateTime);
}
/**
- * <p>Removes the creation time.</p>
+ * Removes the creation time.
*/
- public void removeCreateDateTime()
- {
- final MutableSection s = (MutableSection) getFirstSection();
- s.removeProperty(PropertyIDMap.PID_CREATE_DTM);
+ public void removeCreateDateTime() {
+ remove1stProperty(PropertyIDMap.PID_CREATE_DTM);
}
/**
- * <p>Returns the last save time (or <code>null</code>).</p>
+ * Returns the last save time (or {@code null}).
*
- * @return The last save time or <code>null</code>
+ * @return The last save time or {@code null}
*/
- public Date getLastSaveDateTime()
- {
+ public Date getLastSaveDateTime() {
return (Date) getProperty(PropertyIDMap.PID_LASTSAVE_DTM);
}
/**
- * <p>Sets the total time spent in editing the document.</p>
+ * Sets the total time spent in editing the document.
*
* @param time The time to set.
*/
- public void setLastSaveDateTime(final Date time)
- {
- final MutableSection s = (MutableSection) getFirstSection();
+ public void setLastSaveDateTime(final Date time) {
+ final Section s = getFirstSection();
s
.setProperty(PropertyIDMap.PID_LASTSAVE_DTM,
Variant.VT_FILETIME, time);
@@ -493,153 +442,134 @@ public final class SummaryInformation extends SpecialPropertySet {
/**
- * <p>Remove the total time spent in editing the document.</p>
+ * Remove the total time spent in editing the document.
*/
- public void removeLastSaveDateTime()
- {
- final MutableSection s = (MutableSection) getFirstSection();
- s.removeProperty(PropertyIDMap.PID_LASTSAVE_DTM);
+ public void removeLastSaveDateTime() {
+ remove1stProperty(PropertyIDMap.PID_LASTSAVE_DTM);
}
/**
- * <p>Returns the page count or 0 if the {@link SummaryInformation} does
- * not contain a page count.</p>
+ * Returns the page count or 0 if the {@link SummaryInformation} does
+ * not contain a page count.
*
* @return The page count or 0 if the {@link SummaryInformation} does not
* contain a page count.
*/
- public int getPageCount()
- {
+ public int getPageCount() {
return getPropertyIntValue(PropertyIDMap.PID_PAGECOUNT);
}
/**
- * <p>Sets the page count.</p>
+ * Sets the page count.
*
* @param pageCount The page count to set.
*/
- public void setPageCount(final int pageCount)
- {
- final MutableSection s = (MutableSection) getFirstSection();
- s.setProperty(PropertyIDMap.PID_PAGECOUNT, pageCount);
+ public void setPageCount(final int pageCount) {
+ set1stProperty(PropertyIDMap.PID_PAGECOUNT, pageCount);
}
/**
- * <p>Removes the page count.</p>
+ * Removes the page count.
*/
- public void removePageCount()
- {
- final MutableSection s = (MutableSection) getFirstSection();
- s.removeProperty(PropertyIDMap.PID_PAGECOUNT);
+ public void removePageCount() {
+ remove1stProperty(PropertyIDMap.PID_PAGECOUNT);
}
/**
- * <p>Returns the word count or 0 if the {@link SummaryInformation} does
- * not contain a word count.</p>
+ * Returns the word count or 0 if the {@link SummaryInformation} does
+ * not contain a word count.
*
- * @return The word count or <code>null</code>
+ * @return The word count or {@code null}
*/
- public int getWordCount()
- {
+ public int getWordCount() {
return getPropertyIntValue(PropertyIDMap.PID_WORDCOUNT);
}
/**
- * <p>Sets the word count.</p>
+ * Sets the word count.
*
* @param wordCount The word count to set.
*/
- public void setWordCount(final int wordCount)
- {
- final MutableSection s = (MutableSection) getFirstSection();
- s.setProperty(PropertyIDMap.PID_WORDCOUNT, wordCount);
+ public void setWordCount(final int wordCount) {
+ set1stProperty(PropertyIDMap.PID_WORDCOUNT, wordCount);
}
/**
- * <p>Removes the word count.</p>
+ * Removes the word count.
*/
- public void removeWordCount()
- {
- final MutableSection s = (MutableSection) getFirstSection();
- s.removeProperty(PropertyIDMap.PID_WORDCOUNT);
+ public void removeWordCount() {
+ remove1stProperty(PropertyIDMap.PID_WORDCOUNT);
}
/**
- * <p>Returns the character count or 0 if the {@link SummaryInformation}
- * does not contain a char count.</p>
+ * Returns the character count or 0 if the {@link SummaryInformation}
+ * does not contain a char count.
*
- * @return The character count or <code>null</code>
+ * @return The character count or {@code null}
*/
- public int getCharCount()
- {
+ public int getCharCount() {
return getPropertyIntValue(PropertyIDMap.PID_CHARCOUNT);
}
/**
- * <p>Sets the character count.</p>
+ * Sets the character count.
*
* @param charCount The character count to set.
*/
- public void setCharCount(final int charCount)
- {
- final MutableSection s = (MutableSection) getFirstSection();
- s.setProperty(PropertyIDMap.PID_CHARCOUNT, charCount);
+ public void setCharCount(final int charCount) {
+ set1stProperty(PropertyIDMap.PID_CHARCOUNT, charCount);
}
/**
- * <p>Removes the character count.</p>
+ * Removes the character count.
*/
- public void removeCharCount()
- {
- final MutableSection s = (MutableSection) getFirstSection();
- s.removeProperty(PropertyIDMap.PID_CHARCOUNT);
+ public void removeCharCount() {
+ remove1stProperty(PropertyIDMap.PID_CHARCOUNT);
}
/**
- * <p>Returns the thumbnail (or <code>null</code>) <strong>when this
+ * Returns the thumbnail (or {@code null}) <strong>when this
* method is implemented. Please note that the return type is likely to
- * change!</strong></p>
+ * change!</strong><p>
*
- * <p>To process this data, you may wish to make use of the
+ * To process this data, you may wish to make use of the
* {@link Thumbnail} class. The raw data is generally
- * an image in WMF or Clipboard (BMP?) format</p>
+ * an image in WMF or Clipboard (BMP?) format
*
- * @return The thumbnail or <code>null</code>
+ * @return The thumbnail or {@code null}
*/
- public byte[] getThumbnail()
- {
+ public byte[] getThumbnail() {
return (byte[]) getProperty(PropertyIDMap.PID_THUMBNAIL);
}
/**
- * <p>Returns the thumbnail (or <code>null</code>), processed
+ * Returns the thumbnail (or {@code null}), processed
* as an object which is (largely) able to unpack the thumbnail
- * image data.</p>
+ * image data.
*
- * @return The thumbnail or <code>null</code>
+ * @return The thumbnail or {@code null}
*/
- public Thumbnail getThumbnailThumbnail()
- {
+ public Thumbnail getThumbnailThumbnail() {
byte[] data = getThumbnail();
if (data == null) return null;
return new Thumbnail(data);
@@ -648,115 +578,100 @@ public final class SummaryInformation extends SpecialPropertySet {
/**
- * <p>Sets the thumbnail.</p>
+ * Sets the thumbnail.
*
* @param thumbnail The thumbnail to set.
*/
- public void setThumbnail(final byte[] thumbnail)
- {
- final MutableSection s = (MutableSection) getFirstSection();
- s.setProperty(PropertyIDMap.PID_THUMBNAIL, /* FIXME: */
- Variant.VT_LPSTR, thumbnail);
+ public void setThumbnail(final byte[] thumbnail) {
+ getFirstSection().setProperty(PropertyIDMap.PID_THUMBNAIL, /* FIXME: */ Variant.VT_LPSTR, thumbnail);
}
/**
- * <p>Removes the thumbnail.</p>
+ * Removes the thumbnail.
*/
- public void removeThumbnail()
- {
- final MutableSection s = (MutableSection) getFirstSection();
- s.removeProperty(PropertyIDMap.PID_THUMBNAIL);
+ public void removeThumbnail() {
+ remove1stProperty(PropertyIDMap.PID_THUMBNAIL);
}
/**
- * <p>Returns the application name (or <code>null</code>).</p>
+ * Returns the application name (or {@code null}).
*
- * @return The application name or <code>null</code>
+ * @return The application name or {@code null}
*/
- public String getApplicationName()
- {
+ public String getApplicationName() {
return getPropertyStringValue(PropertyIDMap.PID_APPNAME);
}
/**
- * <p>Sets the application name.</p>
+ * Sets the application name.
*
* @param applicationName The application name to set.
*/
- public void setApplicationName(final String applicationName)
- {
- final MutableSection s = (MutableSection) getFirstSection();
- s.setProperty(PropertyIDMap.PID_APPNAME, applicationName);
+ public void setApplicationName(final String applicationName) {
+ set1stProperty(PropertyIDMap.PID_APPNAME, applicationName);
}
/**
- * <p>Removes the application name.</p>
+ * Removes the application name.
*/
- public void removeApplicationName()
- {
- final MutableSection s = (MutableSection) getFirstSection();
- s.removeProperty(PropertyIDMap.PID_APPNAME);
+ public void removeApplicationName() {
+ remove1stProperty(PropertyIDMap.PID_APPNAME);
}
/**
- * <p>Returns a security code which is one of the following values:</p>
+ * Returns a security code which is one of the following values:
*
* <ul>
*
- * <li><p>0 if the {@link SummaryInformation} does not contain a
+ * <li>0 if the {@link SummaryInformation} does not contain a
* security field or if there is no security on the document. Use
* {@link PropertySet#wasNull()} to distinguish between the two
- * cases!</p></li>
+ * cases!
*
- * <li><p>1 if the document is password protected</p></li>
+ * <li>1 if the document is password protected
*
- * <li><p>2 if the document is read-only recommended</p></li>
+ * <li>2 if the document is read-only recommended
*
- * <li><p>4 if the document is read-only enforced</p></li>
+ * <li>4 if the document is read-only enforced
*
- * <li><p>8 if the document is locked for annotations</p></li>
+ * <li>8 if the document is locked for annotations
*
* </ul>
*
- * @return The security code or <code>null</code>
+ * @return The security code or {@code null}
*/
- public int getSecurity()
- {
+ public int getSecurity() {
return getPropertyIntValue(PropertyIDMap.PID_SECURITY);
}
/**
- * <p>Sets the security code.</p>
+ * Sets the security code.
*
* @param security The security code to set.
*/
- public void setSecurity(final int security)
- {
- final MutableSection s = (MutableSection) getFirstSection();
- s.setProperty(PropertyIDMap.PID_SECURITY, security);
+ public void setSecurity(final int security) {
+ set1stProperty(PropertyIDMap.PID_SECURITY, security);
}
/**
- * <p>Removes the security code.</p>
+ * Removes the security code.
*/
- public void removeSecurity()
- {
- final MutableSection s = (MutableSection) getFirstSection();
- s.removeProperty(PropertyIDMap.PID_SECURITY);
+ public void removeSecurity() {
+ remove1stProperty(PropertyIDMap.PID_SECURITY);
}
}
diff --git a/src/java/org/apache/poi/hpsf/Util.java b/src/java/org/apache/poi/hpsf/Util.java
index 5d75cb986f..d8ab7315db 100644
--- a/src/java/org/apache/poi/hpsf/Util.java
+++ b/src/java/org/apache/poi/hpsf/Util.java
@@ -20,110 +20,17 @@ package org.apache.poi.hpsf;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
-import java.util.Collection;
import java.util.Date;
+import org.apache.poi.util.Internal;
import org.apache.poi.util.SuppressForbidden;
/**
* <p>Provides various static utility methods.</p>
*/
+@Internal
public class Util
{
-
- /**
- * <p>Checks whether two byte arrays <var>a</var> and <var>b</var>
- * are equal. They are equal</p>
- *
- * <ul>
- *
- * <li><p>if they have the same length and</p></li>
- *
- * <li><p>if for each <var>i</var> with
- * <var>i</var>&nbsp;&gt;=&nbsp;0 and
- * <var>i</var>&nbsp;&lt;&nbsp;<var>a.length</var> holds
- * <var>a</var>[<var>i</var>]&nbsp;== <var>b</var>[<var>i</var>].</p></li>
- *
- * </ul>
- *
- * @param a The first byte array
- * @param b The first byte array
- * @return <code>true</code> if the byte arrays are equal, else
- * <code>false</code>
- */
- public static boolean equal(final byte[] a, final byte[] b)
- {
- if (a.length != b.length)
- return false;
- for (int i = 0; i < a.length; i++)
- if (a[i] != b[i])
- return false;
- return true;
- }
-
-
-
- /**
- * <p>Copies a part of a byte array into another byte array.</p>
- *
- * @param src The source byte array.
- * @param srcOffset Offset in the source byte array.
- * @param length The number of bytes to copy.
- * @param dst The destination byte array.
- * @param dstOffset Offset in the destination byte array.
- */
- public static void copy(final byte[] src, final int srcOffset,
- final int length, final byte[] dst,
- final int dstOffset)
- {
- for (int i = 0; i < length; i++)
- dst[dstOffset + i] = src[srcOffset + i];
- }
-
-
-
- /**
- * <p>Concatenates the contents of several byte arrays into a
- * single one.</p>
- *
- * @param byteArrays The byte arrays to be concatened.
- * @return A new byte array containing the concatenated byte
- * arrays.
- */
- public static byte[] cat(final byte[][] byteArrays)
- {
- int capacity = 0;
- for (int i = 0; i < byteArrays.length; i++)
- capacity += byteArrays[i].length;
- final byte[] result = new byte[capacity];
- int r = 0;
- for (int i = 0; i < byteArrays.length; i++)
- for (int j = 0; j < byteArrays[i].length; j++)
- result[r++] = byteArrays[i][j];
- return result;
- }
-
-
-
- /**
- * <p>Copies bytes from a source byte array into a new byte
- * array.</p>
- *
- * @param src Copy from this byte array.
- * @param offset Start copying here.
- * @param length Copy this many bytes.
- * @return The new byte array. Its length is number of copied bytes.
- */
- public static byte[] copy(final byte[] src, final int offset,
- final int length)
- {
- final byte[] result = new byte[length];
- copy(src, offset, length, result, 0);
- return result;
- }
-
-
-
/**
* <p>The difference between the Windows epoch (1601-01-01
* 00:00:00) and the Unix epoch (1970-01-01 00:00:00) in
@@ -189,36 +96,6 @@ public class Util
}
- /**
- * <p>Checks whether two collections are equal. Two collections
- * C<sub>1</sub> and C<sub>2</sub> are equal, if the following conditions
- * are true:</p>
- *
- * <ul>
- *
- * <li><p>For each c<sub>1<em>i</em></sub> (element of C<sub>1</sub>) there
- * is a c<sub>2<em>j</em></sub> (element of C<sub>2</sub>), and
- * c<sub>1<em>i</em></sub> equals c<sub>2<em>j</em></sub>.</p></li>
- *
- * <li><p>For each c<sub>2<em>i</em></sub> (element of C<sub>2</sub>) there
- * is a c<sub>1<em>j</em></sub> (element of C<sub>1</sub>) and
- * c<sub>2<em>i</em></sub> equals c<sub>1<em>j</em></sub>.</p></li>
- *
- * </ul>
- *
- * @param c1 the first collection
- * @param c2 the second collection
- * @return <code>true</code> if the collections are equal, else
- * <code>false</code>.
- */
- public static boolean equals(Collection<?> c1, Collection<?> c2)
- {
- Object[] o1 = c1.toArray();
- Object[] o2 = c2.toArray();
- return internalEquals(o1, o2);
- }
-
-
/**
* <p>Compares to object arrays with regarding the objects' order. For
@@ -231,24 +108,17 @@ public class Util
*/
public static boolean equals(Object[] c1, Object[] c2)
{
- final Object[] o1 = c1.clone();
- final Object[] o2 = c2.clone();
- return internalEquals(o1, o2);
- }
-
- private static boolean internalEquals(Object[] o1, Object[] o2)
- {
- for (int i1 = 0; i1 < o1.length; i1++)
+ for (int i1 = 0; i1 < c1.length; i1++)
{
- final Object obj1 = o1[i1];
+ final Object obj1 = c1[i1];
boolean matchFound = false;
- for (int i2 = 0; !matchFound && i2 < o1.length; i2++)
+ for (int i2 = 0; !matchFound && i2 < c1.length; i2++)
{
- final Object obj2 = o2[i2];
+ final Object obj2 = c2[i2];
if (obj1.equals(obj2))
{
matchFound = true;
- o2[i2] = null;
+ c2[i2] = null;
}
}
if (!matchFound)
@@ -257,8 +127,6 @@ public class Util
return true;
}
-
-
/**
* <p>Pads a byte array with 0x00 bytes so that its length is a multiple of
* 4.</p>
@@ -283,46 +151,6 @@ public class Util
}
-
- /**
- * <p>Pads a character array with 0x0000 characters so that its length is a
- * multiple of 4.</p>
- *
- * @param ca The character array to pad.
- * @return The padded character array.
- */
- public static char[] pad4(final char[] ca)
- {
- final int PAD = 4;
- final char[] result;
- int l = ca.length % PAD;
- if (l == 0)
- result = ca;
- else
- {
- l = PAD - l;
- result = new char[ca.length + l];
- System.arraycopy(ca, 0, result, 0, ca.length);
- }
- return result;
- }
-
-
-
- /**
- * <p>Pads a string with 0x0000 characters so that its length is a
- * multiple of 4.</p>
- *
- * @param s The string to pad.
- * @return The padded string as a character array.
- */
- public static char[] pad4(final String s)
- {
- return pad4(s.toCharArray());
- }
-
-
-
/**
* <p>Returns a textual representation of a {@link Throwable}, including a
* stacktrace.</p>
diff --git a/src/java/org/apache/poi/hpsf/VariantSupport.java b/src/java/org/apache/poi/hpsf/VariantSupport.java
index b4816eb912..61fc30d883 100644
--- a/src/java/org/apache/poi/hpsf/VariantSupport.java
+++ b/src/java/org/apache/poi/hpsf/VariantSupport.java
@@ -27,70 +27,77 @@ import java.util.List;
import org.apache.poi.util.CodePageUtil;
import org.apache.poi.util.POILogFactory;
import org.apache.poi.util.POILogger;
+import org.apache.poi.util.Removal;
/**
- * <p>Supports reading and writing of variant data.</p>
+ * Supports reading and writing of variant data.<p>
*
- * <p><strong>FIXME (3):</strong> Reading and writing should be made more
- * uniform than it is now. The following items should be resolved:
+ * <strong>FIXME (3):</strong> Reading and writing should be made more
+ * uniform than it is now. The following items should be resolved:<p>
*
* <ul>
*
- * <li><p>Reading requires a length parameter that is 4 byte greater than the
- * actual data, because the variant type field is included. </p></li>
+ * <li>Reading requires a length parameter that is 4 byte greater than the
+ * actual data, because the variant type field is included.
*
- * <li><p>Reading reads from a byte array while writing writes to an byte array
- * output stream.</p></li>
+ * <li>Reading reads from a byte array while writing writes to an byte array
+ * output stream.
*
* </ul>
*/
-public class VariantSupport extends Variant
-{
- private static final POILogger logger = POILogFactory.getLogger(VariantSupport.class);
+public class VariantSupport extends Variant {
+ /**
+ * HPSF is able to read these {@link Variant} types.
+ */
+ public static final int[] SUPPORTED_TYPES = { Variant.VT_EMPTY,
+ Variant.VT_I2, Variant.VT_I4, Variant.VT_I8, Variant.VT_R8,
+ Variant.VT_FILETIME, Variant.VT_LPSTR, Variant.VT_LPWSTR,
+ Variant.VT_CF, Variant.VT_BOOL };
+
+
+ private static final POILogger logger = POILogFactory.getLogger(VariantSupport.class);
private static boolean logUnsupportedTypes = false;
/**
- * <p>Specifies whether warnings about unsupported variant types are to be
- * written to <code>System.err</code> or not.</p>
+ * Keeps a list of the variant types an "unsupported" message has already
+ * been issued for.
+ */
+ 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</code> warnings will be written,
- * if <code>false</code> they won't.
+ * @param logUnsupportedTypes If {@code true} warnings will be written,
+ * if {@code false} they won't.
*/
- public static void setLogUnsupportedTypes(final boolean logUnsupportedTypes)
- {
+ public static void setLogUnsupportedTypes(final boolean logUnsupportedTypes) {
VariantSupport.logUnsupportedTypes = logUnsupportedTypes;
}
/**
- * <p>Checks whether logging of unsupported variant types warning is turned
- * on or off.</p>
+ * Checks whether logging of unsupported variant types warning is turned
+ * on or off.
*
- * @return <code>true</code> if logging is turned on, else
- * <code>false</code>.
+ * @return {@code true} if logging is turned on, else
+ * {@code false}.
*/
- public static boolean isLogUnsupportedTypes()
- {
+ public static boolean isLogUnsupportedTypes() {
return logUnsupportedTypes;
}
/**
- * <p>Keeps a list of the variant types an "unsupported" message has already
- * been issued for.</p>
- */
- protected static List<Long> unsupportedMessage;
-
- /**
- * <p>Writes a warning to <code>System.err</code> that a variant type is
+ * Writes a warning to {@code System.err} that a variant type is
* unsupported by HPSF. Such a warning is written only once for each variant
- * type. Log messages can be turned on or off by </p>
+ * type. Log messages can be turned on or off by
*
* @param ex The exception to log
*/
protected static void writeUnsupportedTypeMessage
- (final UnsupportedVariantTypeException ex)
- {
+ (final UnsupportedVariantTypeException ex) {
if (isLogUnsupportedTypes())
{
if (unsupportedMessage == null)
@@ -105,28 +112,18 @@ public class VariantSupport extends Variant
}
- /**
- * <p>HPSF is able to read these {@link Variant} types.</p>
- */
- final static public int[] SUPPORTED_TYPES = { Variant.VT_EMPTY,
- Variant.VT_I2, Variant.VT_I4, Variant.VT_I8, Variant.VT_R8,
- Variant.VT_FILETIME, Variant.VT_LPSTR, Variant.VT_LPWSTR,
- Variant.VT_CF, Variant.VT_BOOL };
-
-
/**
- * <p>Checks whether HPSF supports the specified variant type. Unsupported
+ * Checks whether HPSF supports the specified variant type. Unsupported
* types should be implemented included in the {@link #SUPPORTED_TYPES}
- * array.</p>
+ * array.
*
* @see Variant
* @param variantType the variant type to check
- * @return <code>true</code> if HPFS supports this type, else
- * <code>false</code>
+ * @return {@code true} if HPFS supports this type, else
+ * {@code false}
*/
- public boolean isSupportedType(final int variantType)
- {
+ public boolean isSupportedType(final int variantType) {
for (int i = 0; i < SUPPORTED_TYPES.length; i++)
if (variantType == SUPPORTED_TYPES[i])
return true;
@@ -136,7 +133,7 @@ public class VariantSupport extends Variant
/**
- * <p>Reads a variant type from a byte array.</p>
+ * Reads a variant type from a byte array.
*
* @param src The byte array
* @param offset The offset in the byte array where the variant starts
@@ -154,66 +151,50 @@ public class VariantSupport extends Variant
*/
public static Object read( final byte[] src, final int offset,
final int length, final long type, final int codepage )
- throws ReadingNotSupportedException, UnsupportedEncodingException
- {
- TypedPropertyValue typedPropertyValue = new TypedPropertyValue(
- (int) type, null );
+ throws ReadingNotSupportedException, UnsupportedEncodingException {
+ TypedPropertyValue typedPropertyValue = new TypedPropertyValue( (int) type, null );
int unpadded;
- try
- {
+ try {
unpadded = typedPropertyValue.readValue( src, offset );
- }
- catch ( UnsupportedOperationException exc )
- {
+ } catch ( UnsupportedOperationException exc ) {
int propLength = Math.min( length, src.length - offset );
final byte[] v = new byte[propLength];
System.arraycopy( src, offset, v, 0, propLength );
throw new ReadingNotSupportedException( type, v );
}
- switch ( (int) type )
- {
- case Variant.VT_EMPTY:
- case Variant.VT_I4:
- case Variant.VT_I8:
- case Variant.VT_R8:
+ switch ( (int) type ) {
/*
* we have more property types that can be converted into Java
* objects, but current API need to be preserved, and it returns
* other types as byte arrays. In future major versions it shall be
* changed -- sergey
*/
- return typedPropertyValue.getValue();
+ case Variant.VT_EMPTY:
+ case Variant.VT_I4:
+ case Variant.VT_I8:
+ case Variant.VT_R8:
+ return typedPropertyValue.getValue();
- case Variant.VT_I2:
- {
/*
* also for backward-compatibility with prev. versions of POI
* --sergey
*/
- return Integer.valueOf( ( (Short) typedPropertyValue.getValue() )
- .intValue() );
- }
- case Variant.VT_FILETIME:
- {
- Filetime filetime = (Filetime) typedPropertyValue.getValue();
- return Util.filetimeToDate( (int) filetime.getHigh(),
- (int) filetime.getLow() );
- }
- case Variant.VT_LPSTR:
- {
- CodePageString string = (CodePageString) typedPropertyValue
- .getValue();
- return string.getJavaValue( codepage );
- }
- case Variant.VT_LPWSTR:
- {
- UnicodeString string = (UnicodeString) typedPropertyValue
- .getValue();
- return string.toJavaString();
- }
- case Variant.VT_CF:
- {
+ case Variant.VT_I2:
+ return ( (Short) typedPropertyValue.getValue() ).intValue();
+
+ case Variant.VT_FILETIME:
+ Filetime filetime = (Filetime) typedPropertyValue.getValue();
+ return Util.filetimeToDate( (int) filetime.getHigh(), (int) filetime.getLow() );
+
+ case Variant.VT_LPSTR:
+ CodePageString cpString = (CodePageString) typedPropertyValue.getValue();
+ return cpString.getJavaValue( codepage );
+
+ case Variant.VT_LPWSTR:
+ UnicodeString uniString = (UnicodeString) typedPropertyValue.getValue();
+ return uniString.toJavaString();
+
// if(l1 < 0) {
/**
* YK: reading the ClipboardData packet (VT_CF) is not quite
@@ -223,7 +204,7 @@ public class VariantSupport extends Variant
* 45583 clearly show that this approach does not always work. The
* workaround below attempts to gracefully handle such cases instead
* of throwing exceptions.
- *
+ *
* August 20, 2009
*/
// 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);
// value = v;
// break;
- ClipboardData clipboardData = (ClipboardData) typedPropertyValue
- .getValue();
- return clipboardData.toByteArray();
- }
+ case Variant.VT_CF:
+ ClipboardData clipboardData = (ClipboardData) typedPropertyValue.getValue();
+ return clipboardData.toByteArray();
- case Variant.VT_BOOL:
- {
- VariantBool bool = (VariantBool) typedPropertyValue.getValue();
- return Boolean.valueOf( bool.getValue() );
- }
+ case Variant.VT_BOOL:
+ VariantBool bool = (VariantBool) typedPropertyValue.getValue();
+ return bool.getValue();
- default:
- {
/*
* it is not very good, but what can do without breaking current
* API? --sergey
*/
- final byte[] v = new byte[unpadded];
- System.arraycopy( src, offset, v, 0, unpadded );
- throw new ReadingNotSupportedException( type, v );
- }
+ default:
+ final byte[] v = new byte[unpadded];
+ System.arraycopy( src, offset, v, 0, unpadded );
+ throw new ReadingNotSupportedException( type, v );
}
}
/**
- * <p>Turns a codepage number into the equivalent character encoding's
- * name.</p>
+ * Turns a codepage number into the equivalent character encoding's
+ * name.
*
* @param codepage The codepage number
*
@@ -269,7 +245,10 @@ public class VariantSupport extends Variant
*
* @exception UnsupportedEncodingException if the specified codepage is
* less than zero.
+ *
+ * @deprecated POI 3.16 - use {@link CodePageUtil#codepageToEncoding(int)}
*/
+ @Removal(version="3.18")
public static String codepageToEncoding(final int codepage)
throws UnsupportedEncodingException
{
@@ -278,13 +257,13 @@ public class VariantSupport extends Variant
/**
- * <p>Writes a variant value to an output stream. This method ensures that
- * always a multiple of 4 bytes is written.</p>
+ * Writes a variant value to an output stream. This method ensures that
+ * always a multiple of 4 bytes is written.<p>
*
- * <p>If the codepage is UTF-16, which is encouraged, strings
+ * If the codepage is UTF-16, which is encouraged, strings
* <strong>must</strong> always be written as {@link Variant#VT_LPWSTR}
* strings, not as {@link Variant#VT_LPSTR} strings. This method ensure this
- * by converting strings appropriately, if needed.</p>
+ * by converting strings appropriately, if needed.
*
* @param out The stream to write the value to.
* @param type The variant's type.
@@ -298,42 +277,31 @@ public class VariantSupport extends Variant
*/
public static int write(final OutputStream out, final long type,
final Object value, final int codepage)
- throws IOException, WritingNotSupportedException
- {
+ throws IOException, WritingNotSupportedException {
int length = 0;
- switch ((int) type)
- {
+ switch ((int) type) {
case Variant.VT_BOOL:
- {
- if ( ( (Boolean) value ).booleanValue() )
- {
+ if ( ( (Boolean) value ).booleanValue() ) {
out.write( 0xff );
out.write( 0xff );
- }
- else
- {
+ } else {
out.write( 0x00 );
out.write( 0x00 );
}
length += 2;
break;
- }
+
case Variant.VT_LPSTR:
- {
- CodePageString codePageString = new CodePageString( (String) value,
- codepage );
+ CodePageString codePageString = new CodePageString( (String) value, codepage );
length += codePageString.write( out );
break;
- }
+
case Variant.VT_LPWSTR:
- {
final int nrOfChars = ( (String) value ).length() + 1;
length += TypeWriter.writeUIntToStream( out, nrOfChars );
- char[] s = ( (String) value ).toCharArray();
- for ( int i = 0; i < s.length; i++ )
- {
- final int high = ( ( s[i] & 0x0000ff00 ) >> 8 );
- final int low = ( s[i] & 0x000000ff );
+ for ( char s : ( (String) value ).toCharArray() ) {
+ final int high = ( ( s & 0x0000ff00 ) >> 8 );
+ final int low = ( s & 0x000000ff );
final byte highb = (byte) high;
final byte lowb = (byte) low;
out.write( lowb );
@@ -345,79 +313,63 @@ public class VariantSupport extends Variant
out.write( 0x00 );
length += 2;
break;
- }
+
case Variant.VT_CF:
- {
- final byte[] b = (byte[]) value;
- out.write(b);
- length = b.length;
+ final byte[] cf = (byte[]) value;
+ out.write(cf);
+ length = cf.length;
break;
- }
+
case Variant.VT_EMPTY:
- {
length += TypeWriter.writeUIntToStream( out, Variant.VT_EMPTY );
break;
- }
+
case Variant.VT_I2:
- {
- length += TypeWriter.writeToStream( out,
- ( (Integer) value ).shortValue() );
+ length += TypeWriter.writeToStream( out, ( (Integer) value ).shortValue() );
break;
- }
+
case Variant.VT_I4:
- {
- if (!(value instanceof Integer))
- {
+ if (!(value instanceof Integer)) {
throw new ClassCastException("Could not cast an object to "
+ Integer.class.toString() + ": "
+ value.getClass().toString() + ", "
+ value.toString());
}
- length += TypeWriter.writeToStream(out,
- ((Integer) value).intValue());
+ length += TypeWriter.writeToStream(out, ((Integer) value).intValue());
break;
- }
+
case Variant.VT_I8:
- {
length += TypeWriter.writeToStream(out, ((Long) value).longValue());
break;
- }
+
case Variant.VT_R8:
- {
- length += TypeWriter.writeToStream(out,
- ((Double) value).doubleValue());
+ length += TypeWriter.writeToStream(out, ((Double) value).doubleValue());
break;
- }
+
case Variant.VT_FILETIME:
- {
long filetime = Util.dateToFileTime((Date) value);
int high = (int) ((filetime >> 32) & 0x00000000FFFFFFFFL);
int low = (int) (filetime & 0x00000000FFFFFFFFL);
Filetime filetimeValue = new Filetime( low, high);
length += filetimeValue.write( out );
break;
- }
+
default:
- {
/* The variant type is not supported yet. However, if the value
* is a byte array we can write it nevertheless. */
- if (value instanceof byte[])
- {
+ if (value instanceof byte[]) {
final byte[] b = (byte[]) value;
out.write(b);
length = b.length;
- writeUnsupportedTypeMessage
- (new WritingNotSupportedException(type, value));
- }
- else
+ writeUnsupportedTypeMessage(new WritingNotSupportedException(type, value));
+ } else {
throw new WritingNotSupportedException(type, value);
+ }
break;
- }
}
/* pad values to 4-bytes */
- while ( ( length & 0x3 ) != 0 )
- {
+ while ( ( length & 0x3 ) != 0 ) {
out.write( 0x00 );
length++;
}
diff --git a/src/java/org/apache/poi/hpsf/extractor/HPSFPropertiesExtractor.java b/src/java/org/apache/poi/hpsf/extractor/HPSFPropertiesExtractor.java
index fc526ee619..36c3f3a9cb 100644
--- a/src/java/org/apache/poi/hpsf/extractor/HPSFPropertiesExtractor.java
+++ b/src/java/org/apache/poi/hpsf/extractor/HPSFPropertiesExtractor.java
@@ -27,6 +27,7 @@ import org.apache.poi.hpsf.CustomProperties;
import org.apache.poi.hpsf.DocumentSummaryInformation;
import org.apache.poi.hpsf.HPSFPropertiesOnlyDocument;
import org.apache.poi.hpsf.Property;
+import org.apache.poi.hpsf.PropertySet;
import org.apache.poi.hpsf.SpecialPropertySet;
import org.apache.poi.hpsf.SummaryInformation;
import org.apache.poi.hpsf.wellknown.PropertyIDMap;
@@ -67,7 +68,7 @@ public class HPSFPropertiesExtractor extends POIOLE2TextExtractor {
CustomProperties cps = dsi == null ? null : dsi.getCustomProperties();
if (cps != null) {
for (String key : cps.nameSet()) {
- String val = HelperPropertySet.getPropertyValueText(cps.get(key));
+ String val = getPropertyValueText(cps.get(key));
text.append(key).append(" = ").append(val).append("\n");
}
}
@@ -86,7 +87,7 @@ public class HPSFPropertiesExtractor extends POIOLE2TextExtractor {
return getPropertiesText(si);
}
- private static String getPropertiesText(SpecialPropertySet ps) {
+ private static String getPropertiesText(PropertySet ps) {
if (ps == null) {
// Not defined, oh well
return "";
@@ -98,12 +99,12 @@ public class HPSFPropertiesExtractor extends POIOLE2TextExtractor {
Property[] props = ps.getProperties();
for (Property prop : props) {
String type = Long.toString(prop.getID());
- Object typeObj = idMap.get(prop.getID());
+ Object typeObj = (idMap == null) ? null : idMap.get(prop.getID());
if (typeObj != null) {
type = typeObj.toString();
}
- String val = HelperPropertySet.getPropertyValueText(prop.getValue());
+ String val = getPropertyValueText(prop.getValue());
text.append(type).append(" = ").append(val).append("\n");
}
@@ -125,18 +126,12 @@ public class HPSFPropertiesExtractor extends POIOLE2TextExtractor {
throw new IllegalStateException("You already have the Metadata Text Extractor, not recursing!");
}
- private static abstract class HelperPropertySet extends SpecialPropertySet {
- public HelperPropertySet() {
- super(null);
- }
- public static String getPropertyValueText(Object val) {
- if (val == null) {
- return "(not set)";
- }
- return SpecialPropertySet.getPropertyStringValue(val);
- }
+ private static String getPropertyValueText(Object val) {
+ return (val == null)
+ ? "(not set)"
+ : PropertySet.getPropertyStringValue(val);
}
-
+
@Override
public boolean equals(Object o) {
return super.equals(o);
diff --git a/src/java/org/apache/poi/util/IOUtils.java b/src/java/org/apache/poi/util/IOUtils.java
index 2f61fd1d73..929ade06c1 100644
--- a/src/java/org/apache/poi/util/IOUtils.java
+++ b/src/java/org/apache/poi/util/IOUtils.java
@@ -45,26 +45,42 @@ public final class IOUtils {
* @throws EmptyFileException if the stream is empty
*/
public static byte[] peekFirst8Bytes(InputStream stream) throws IOException, EmptyFileException {
- // We want to peek at the first 8 bytes
- stream.mark(8);
+ return peekFirstNBytes(stream, 8);
+ }
- byte[] header = new byte[8];
- int read = IOUtils.readFully(stream, header);
+ /**
+ * Peeks at the first N bytes of the stream. Returns those bytes, but
+ * with the stream unaffected. Requires a stream that supports mark/reset,
+ * or a PushbackInputStream. If the stream has &gt;0 but &lt;N bytes,
+ * remaining bytes will be zero.
+ * @throws EmptyFileException if the stream is empty
+ */
+ public static byte[] peekFirstNBytes(InputStream stream, int limit) throws IOException, EmptyFileException {
+ stream.mark(limit);
+ ByteArrayOutputStream bos = new ByteArrayOutputStream(limit);
+ copy(new BoundedInputStream(stream, limit), bos);
- if (read < 1)
+ int readBytes = bos.size();
+ if (readBytes == 0) {
throw new EmptyFileException();
-
- // Wind back those 8 bytes
+ }
+
+ if (readBytes < limit) {
+ bos.write(new byte[limit-readBytes]);
+ }
+ byte peekedBytes[] = bos.toByteArray();
if(stream instanceof PushbackInputStream) {
PushbackInputStream pin = (PushbackInputStream)stream;
- pin.unread(header, 0, read);
+ pin.unread(peekedBytes, 0, readBytes);
} else {
stream.reset();
}
- return header;
+ return peekedBytes;
}
-
+
+
+
/**
* Reads all the data from the input stream, and returns the bytes read.
*/
diff --git a/src/testcases/org/apache/poi/hpsf/basic/TestBasic.java b/src/testcases/org/apache/poi/hpsf/basic/TestBasic.java
index 8937dca585..b40e7a463b 100644
--- a/src/testcases/org/apache/poi/hpsf/basic/TestBasic.java
+++ b/src/testcases/org/apache/poi/hpsf/basic/TestBasic.java
@@ -192,8 +192,7 @@ public final class TestBasic {
(poiFiles[0].getBytes()));
final List<Section> sections = si.getSections();
final Section s = sections.get(0);
- assertTrue(org.apache.poi.hpsf.Util.equal
- (s.getFormatID().getBytes(), SectionIDMap.SUMMARY_INFORMATION_ID));
+ assertArrayEquals(s.getFormatID().getBytes(), SectionIDMap.SUMMARY_INFORMATION_ID);
assertNotNull(s.getProperties());
assertEquals(17, s.getPropertyCount());
assertEquals("Titel", s.getProperty(2));
diff --git a/src/testcases/org/apache/poi/hpsf/basic/TestWrite.java b/src/testcases/org/apache/poi/hpsf/basic/TestWrite.java
index ffcaa3a799..20f7d8fae6 100644
--- a/src/testcases/org/apache/poi/hpsf/basic/TestWrite.java
+++ b/src/testcases/org/apache/poi/hpsf/basic/TestWrite.java
@@ -216,7 +216,8 @@ public class TestWrite
final MutablePropertySet ps = new MutablePropertySet();
final MutableSection si = new MutableSection();
si.setFormatID(SectionIDMap.SUMMARY_INFORMATION_ID);
- ps.getSections().set(0, si);
+ ps.clearSections();
+ ps.addSection(si);
final MutableProperty p = new MutableProperty();
p.setID(PropertyIDMap.PID_AUTHOR);