diff options
author | Rainer Klute <klute@apache.org> | 2006-03-03 16:57:55 +0000 |
---|---|---|
committer | Rainer Klute <klute@apache.org> | 2006-03-03 16:57:55 +0000 |
commit | 73a9af683f7c986421d12e07e680111d94a609c4 (patch) | |
tree | b315b5320a537d72619cae55c393357f4737b5c0 /src/java | |
parent | a36d020021bb41eade6d653005ef42f60a0a609a (diff) | |
download | poi-73a9af683f7c986421d12e07e680111d94a609c4.tar.gz poi-73a9af683f7c986421d12e07e680111d94a609c4.zip |
* Writing support added to the SummaryInformation and DocumentSummaryInformation classes. These classes now have methods for setting and removing properties. Coherent extensions are:
** Documentation section about writing standard properties added to the HPSF HOW-TO.
** Example application added showing how to modify the document summary information.
** Testcases added for testing modifying summary information and document summary information.
** PropertySetFactory extended to create SummaryInformation and DocumentSummaryInformation instances.
* Added MutablePropertySet.write(DirectoryEntry, String) to ease writing a property set to a POI filesystem document.
* Improved codepage handling.
* Bug fixed: Integral values were read and written as unsigned instead of signed.
* Reworked the mapping between variant types and Java types: Variant.VT_I4 is mapped to Integer now and Variant.VT_I8 to Long. This might cause incompatibilities if you are doing low-level HPSF programming.
* Changed SectionIDMap.DOCUMENT_SUMMARY_INFORMATION_ID from a byte[] to a byte[][] in order to contain the format ID of the first and the second section. This is an incompatible change!
* Added PropertySet.getFirstSection(). This method is similar to getSingleSection() won't choke if the property set has more than one section.
* Support for low-level reading and writing of Variant.VT_I8 type properties added.
* Unnecessary casts removed.
* Poibrowser's display format changed slightly.
git-svn-id: https://svn.apache.org/repos/asf/jakarta/poi/trunk@382887 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'src/java')
32 files changed, 2138 insertions, 271 deletions
diff --git a/src/java/org/apache/poi/hpsf/ClassID.java b/src/java/org/apache/poi/hpsf/ClassID.java index 0f8d12221b..766453f584 100644 --- a/src/java/org/apache/poi/hpsf/ClassID.java +++ b/src/java/org/apache/poi/hpsf/ClassID.java @@ -1,4 +1,3 @@ - /* ==================================================================== Copyright 2002-2004 Apache Software Foundation diff --git a/src/java/org/apache/poi/hpsf/Constants.java b/src/java/org/apache/poi/hpsf/Constants.java index 3cd1138c5d..32a0addf20 100644 --- a/src/java/org/apache/poi/hpsf/Constants.java +++ b/src/java/org/apache/poi/hpsf/Constants.java @@ -1,3 +1,19 @@ +/* ==================================================================== + Copyright 2002-2006 Apache Software Foundation + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +==================================================================== */ + package org.apache.poi.hpsf; /** diff --git a/src/java/org/apache/poi/hpsf/CustomProperties.java b/src/java/org/apache/poi/hpsf/CustomProperties.java new file mode 100644 index 0000000000..77f577d5ce --- /dev/null +++ b/src/java/org/apache/poi/hpsf/CustomProperties.java @@ -0,0 +1,367 @@ +/* ==================================================================== + Copyright 2002-2006 Apache Software Foundation + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +==================================================================== */ + +package org.apache.poi.hpsf; + +import java.util.Date; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import org.apache.poi.hpsf.wellknown.PropertyIDMap; + +/** + * <p>Maintains the instances of {@link CustomProperty} that belong to a + * {@link DocumentSummaryInformation}. The class maintains the names of the + * custom properties in a dictionary. It implements the {@link Map} interface + * and by this provides a simplified view on custom properties: A property's + * name is the key that maps to a typed value. This implementation hides + * property IDs from the developer and regards the property names as keys to + * typed values.</p> + * + * <p>While this class provides a simple API to custom properties, it ignores + * the fact that not names, but IDs are the real keys to properties. Under the + * hood this class maintains a 1:1 relationship between IDs and names. Therefore + * you should not use this class to process property sets with several IDs + * mapping to the same name or with properties without a name: the result will + * contain only a subset of the original properties. If you really need to deal + * such property sets, use HPSF's low-level access methods.</p> + * + * <p>An application can call the {@link #isPure} method to check whether a + * property set parsed by {@link CustomProperties} is still pure (i.e. + * unmodified) or whether one or more properties have been dropped.</p> + * + * <p>This class is not thread-safe; concurrent access to instances of this + * class must be syncronized.</p> + * + * @author Rainer Klute <a + * href="mailto:klute@rainer-klute.de"><klute@rainer-klute.de></a> + * @since 2006-02-09 + * @version $Id$ + */ +public class CustomProperties extends HashMap +{ + + /** + * <p>Maps property IDs to property names.</p> + */ + private Map dictionaryIDToName = new HashMap(); + + /** + * <p>Maps property names to property IDs.</p> + */ + private Map dictionaryNameToID = new HashMap(); + + /** + * <p>Tells whether this object is pure or not.</p> + */ + private boolean isPure = true; + + + + /** + * <p>Puts a {@link CustomProperty} into this map. It is assumed that the + * {@link CustomProperty} already has a valid ID. Otherwise use + * {@link #put(CustomProperty)}.</p> + */ + public Object put(final Object name, final Object customProperty) throws ClassCastException + { + final CustomProperty cp = (CustomProperty) customProperty; + if (name == null) + { + /* Ignoring a property without a name. */ + isPure = false; + return null; + } + if (!(name instanceof String)) + throw new ClassCastException("The name of a custom property must " + + "be a java.lang.String, but it is a " + + name.getClass().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 = new Long(cp.getID()); + final Object oldID = dictionaryNameToID.get(name); + dictionaryIDToName.remove(oldID); + dictionaryNameToID.put(name, idKey); + dictionaryIDToName.put(idKey, name); + + /* Put the custom property into this map. */ + final Object oldCp = super.remove(oldID); + super.put(idKey, cp); + return oldCp; + } + + + + /** + * <p>Puts a {@link CustomProperty} that has not yet a valid ID into this + * map. The method will allocate a suitable ID for the custom property:</p> + * + * <ul> + * + * <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> + * + * </ul> + * + * @param customProperty + * @return If the was already a property with the same name, the + * @throws ClassCastException + */ + private Object put(final CustomProperty customProperty) throws ClassCastException + { + final String name = customProperty.getName(); + + /* Check whether a property with this name is in the map already. */ + final Long oldId = (Long) dictionaryNameToID.get(name); + if (oldId != null) + customProperty.setID(oldId.longValue()); + else + { + long max = 1; + for (final Iterator i = dictionaryIDToName.keySet().iterator(); i.hasNext();) + { + final long id = ((Long) i.next()).longValue(); + if (id > max) + max = id; + } + customProperty.setID(max + 1); + } + return this.put(name, customProperty); + } + + + + /** + * <p>Removes a custom property.</p> + * @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. + * + * @see java.util.HashSet#remove(java.lang.Object) + */ + public Object remove(final String name) + { + final Long id = (Long) dictionaryNameToID.get(name); + if (id == null) + return null; + dictionaryIDToName.remove(id); + dictionaryNameToID.remove(name); + return super.remove(id); + } + + /** + * <p>Adds a named string property.</p> + * + * @param name The property's name. + * @param value The property's value. + * @return the property that was stored under the specified name before, or + * <code>null</code> if there was no such property before. + */ + public Object put(final String name, final String value) + { + final MutableProperty p = new MutableProperty(); + p.setID(-1); + p.setType(Variant.VT_LPWSTR); + p.setValue(value); + final CustomProperty cp = new CustomProperty(p, name); + return put(cp); + } + + /** + * <p>Adds a named long property.</p> + * + * @param name The property's name. + * @param value The property's value. + * @return the property that was stored under the specified name before, or + * <code>null</code> if there was no such property before. + */ + public Object put(final String name, final Long value) + { + final MutableProperty p = new MutableProperty(); + p.setID(-1); + p.setType(Variant.VT_I8); + p.setValue(value); + final CustomProperty cp = new CustomProperty(p, name); + return put(cp); + } + + /** + * <p>Adds a named double property.</p> + * + * @param name The property's name. + * @param value The property's value. + * @return the property that was stored under the specified name before, or + * <code>null</code> if there was no such property before. + */ + public Object put(final String name, final Double value) + { + final MutableProperty p = new MutableProperty(); + p.setID(-1); + p.setType(Variant.VT_R8); + p.setValue(value); + final CustomProperty cp = new CustomProperty(p, name); + return put(cp); + } + + /** + * <p>Adds a named integer property.</p> + * + * @param name The property's name. + * @param value The property's value. + * @return the property that was stored under the specified name before, or + * <code>null</code> if there was no such property before. + */ + public Object put(final String name, final Integer value) + { + final MutableProperty p = new MutableProperty(); + p.setID(-1); + p.setType(Variant.VT_I4); + p.setValue(value); + final CustomProperty cp = new CustomProperty(p, name); + return put(cp); + } + + /** + * <p>Adds a named boolean property.</p> + * + * @param name The property's name. + * @param value The property's value. + * @return the property that was stored under the specified name before, or + * <code>null</code> if there was no such property before. + */ + public Object put(final String name, final Boolean value) + { + final MutableProperty p = new MutableProperty(); + p.setID(-1); + p.setType(Variant.VT_BOOL); + p.setValue(value); + final CustomProperty cp = new CustomProperty(p, name); + return put(cp); + } + + + /** + * <p>Gets a named value from the custom properties.</p> + * + * @param name the name of the value to get + * @return the value or <code>null</code> if a value with the specified + * name is not found in the custom properties. + */ + public Object get(final String name) + { + final Long id = (Long) dictionaryNameToID.get(name); + final CustomProperty cp = (CustomProperty) super.get(id); + return cp != null ? cp.getValue() : null; + } + + + + /** + * <p>Adds a named date property.</p> + * + * @param name The property's name. + * @param value The property's value. + * @return the property that was stored under the specified name before, or + * <code>null</code> if there was no such property before. + */ + public Object put(final String name, final Date value) + { + final MutableProperty p = new MutableProperty(); + p.setID(-1); + p.setType(Variant.VT_FILETIME); + p.setValue(value); + final CustomProperty cp = new CustomProperty(p, name); + return put(cp); + } + + /** + * <p>Sets the codepage.</p> + * + * @param codepage the codepage + */ + public void setCodepage(final int codepage) + { + final MutableProperty p = new MutableProperty(); + p.setID(PropertyIDMap.PID_CODEPAGE); + p.setType(Variant.VT_I2); + p.setValue(new Integer(codepage)); + put(new CustomProperty(p)); + } + + + + /** + * <p>Gets the dictionary which contains IDs and names of the named custom + * properties. + * + * @return the dictionary. + */ + Map getDictionary() + { + return dictionaryIDToName; + } + + + + /** + * <p>Gets the codepage.</p> + * + * @return the codepage or -1 if the codepage is undefined. + */ + public int getCodepage() + { + int codepage = -1; + for (final Iterator i = this.values().iterator(); codepage == -1 && i.hasNext();) + { + final CustomProperty cp = (CustomProperty) i.next(); + if (cp.getID() == PropertyIDMap.PID_CODEPAGE) + codepage = ((Integer) cp.getValue()).intValue(); + } + return codepage; + } + + + + /** + * <p>Tells whether this {@link CustomProperties} instance is pure or one or + * more properties of the underlying low-level property set has been + * dropped.</p> + * + * @return <code>true</code> if the {@link CustomProperties} is pure, else + * <code>false</code>. + */ + public boolean isPure() + { + return isPure; + } + + /** + * <p>Sets the purity of the custom property set.</p> + * + * @param isPure the purity + */ + public void setPure(final boolean isPure) + { + this.isPure = isPure; + } + +} diff --git a/src/java/org/apache/poi/hpsf/CustomProperty.java b/src/java/org/apache/poi/hpsf/CustomProperty.java new file mode 100644 index 0000000000..7c87e1f9a0 --- /dev/null +++ b/src/java/org/apache/poi/hpsf/CustomProperty.java @@ -0,0 +1,123 @@ +/* ==================================================================== + Copyright 2002-2006 Apache Software Foundation + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +==================================================================== */ + +package org.apache.poi.hpsf; + +/** + * <p>This class represents custum 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> + * + * @author Rainer Klute <a + * href="mailto:klute@rainer-klute.de"><klute@rainer-klute.de></a> + * @since 2006-02-09 + * @version $Id$ + */ +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> + */ + public CustomProperty() + { + this.name = null; + } + + /** + * <p>Creates a {@link CustomProperty} without a name by copying the + * underlying {@link Property}' attributes.</p> + * + * @param property the property to copy + */ + public CustomProperty(final Property property) + { + this(property, null); + } + + /** + * <p>Creates a {@link CustomProperty} with a name.</p> + * + * @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) + { + super(property); + this.name = name; + } + + /** + * <p>Gets the property's name.</p> + * + * @return the property's name. + */ + public String getName() + { + return name; + } + + /** + * <p>Sets the property's name.</p> + * + * @param name The name to set. + */ + 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> + * + * @param o The custom property to compare with. + * @return <code>true</code> if both custom properties are equal, else + * <code>false</code>. + * + * @see java.util.AbstractSet#equals(java.lang.Object) + */ + 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) + equalNames = name2 == null; + else + equalNames = name1.equals(name2); + return equalNames && c.getID() == this.getID() + && c.getType() == this.getType() + && c.getValue().equals(this.getValue()); + } + + /** + * @see java.util.AbstractSet#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 21b1b2a9fe..1fce462d62 100644 --- a/src/java/org/apache/poi/hpsf/DocumentSummaryInformation.java +++ b/src/java/org/apache/poi/hpsf/DocumentSummaryInformation.java @@ -1,6 +1,5 @@ - /* ==================================================================== - Copyright 2002-2004 Apache Software Foundation + Copyright 2002-2006 Apache Software Foundation Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -17,11 +16,11 @@ package org.apache.poi.hpsf; -import java.util.HashMap; import java.util.Iterator; import java.util.Map; import org.apache.poi.hpsf.wellknown.PropertyIDMap; +import org.apache.poi.hpsf.wellknown.SectionIDMap; /** * <p>Convenience class representing a DocumentSummary Information stream in a @@ -68,7 +67,7 @@ public class DocumentSummaryInformation extends SpecialPropertySet /** - * <p>Returns the stream's category (or <code>null</code>).</p> + * <p>Returns the category (or <code>null</code>).</p> * * @return The category value */ @@ -77,23 +76,63 @@ public class DocumentSummaryInformation extends SpecialPropertySet return (String) getProperty(PropertyIDMap.PID_CATEGORY); } + /** + * <p>Sets the category.</p> + * + * @param category The category to set. + */ + public void setCategory(final String category) + { + final MutableSection s = (MutableSection) getFirstSection(); + s.setProperty(PropertyIDMap.PID_CATEGORY, category); + } + + /** + * <p>Removes the category.</p> + */ + public void removeCategory() + { + final MutableSection s = (MutableSection) getFirstSection(); + s.removeProperty(PropertyIDMap.PID_CATEGORY); + } + /** - * <p>Returns the stream's presentation format (or + * <p>Returns the presentation format (or * <code>null</code>).</p> * - * @return The presentationFormat value + * @return The presentation format value */ public String getPresentationFormat() { return (String) getProperty(PropertyIDMap.PID_PRESFORMAT); } + /** + * <p>Sets the presentation format.</p> + * + * @param presentationFormat The presentation format to set. + */ + public void setPresentationFormat(final String presentationFormat) + { + final MutableSection s = (MutableSection) getFirstSection(); + s.setProperty(PropertyIDMap.PID_PRESFORMAT, presentationFormat); + } + + /** + * <p>Removes the presentation format.</p> + */ + public void removePresentationFormat() + { + final MutableSection s = (MutableSection) getFirstSection(); + s.removeProperty(PropertyIDMap.PID_PRESFORMAT); + } + /** - * <p>Returns the stream's byte count or 0 if the {@link + * <p>Returns the byte count or 0 if the {@link * DocumentSummaryInformation} does not contain a byte count.</p> * * @return The byteCount value @@ -103,86 +142,226 @@ public class DocumentSummaryInformation extends SpecialPropertySet return getPropertyIntValue(PropertyIDMap.PID_BYTECOUNT); } + /** + * <p>Sets the byte count.</p> + * + * @param byteCount The byte count to set. + */ + public void setByteCount(final int byteCount) + { + final MutableSection s = (MutableSection) getFirstSection(); + s.setProperty(PropertyIDMap.PID_BYTECOUNT, byteCount); + } + + /** + * <p>Removes the byte count.</p> + */ + public void removeByteCount() + { + final MutableSection s = (MutableSection) getFirstSection(); + s.removeProperty(PropertyIDMap.PID_BYTECOUNT); + } + /** - * <p>Returns the stream's line count or 0 if the {@link + * <p>Returns the line count or 0 if the {@link * DocumentSummaryInformation} does not contain a line count.</p> * - * @return The lineCount value + * @return The line count value */ public int getLineCount() { return getPropertyIntValue(PropertyIDMap.PID_LINECOUNT); } + /** + * <p>Sets the line count.</p> + * + * @param lineCount The line count to set. + */ + public void setLineCount(final int lineCount) + { + final MutableSection s = (MutableSection) getFirstSection(); + s.setProperty(PropertyIDMap.PID_LINECOUNT, lineCount); + } + + /** + * <p>Removes the line count.</p> + */ + public void removeLineCount() + { + final MutableSection s = (MutableSection) getFirstSection(); + s.removeProperty(PropertyIDMap.PID_LINECOUNT); + } + /** - * <p>Returns the stream's par count or 0 if the {@link + * <p>Returns the par count or 0 if the {@link * DocumentSummaryInformation} does not contain a par count.</p> * - * @return The parCount value + * @return The par count value */ public int getParCount() { return getPropertyIntValue(PropertyIDMap.PID_PARCOUNT); } + /** + * <p>Sets the par count.</p> + * + * @param parCount The par count to set. + */ + public void setParCount(final int parCount) + { + final MutableSection s = (MutableSection) getFirstSection(); + s.setProperty(PropertyIDMap.PID_PARCOUNT, parCount); + } + + /** + * <p>Removes the par count.</p> + */ + public void removeParCount() + { + final MutableSection s = (MutableSection) getFirstSection(); + s.removeProperty(PropertyIDMap.PID_PARCOUNT); + } + /** - * <p>Returns the stream's slide count or 0 if the {@link + * <p>Returns the slide count or 0 if the {@link * DocumentSummaryInformation} does not contain a slide count.</p> * - * @return The slideCount value + * @return The slide count value */ public int getSlideCount() { return getPropertyIntValue(PropertyIDMap.PID_SLIDECOUNT); } + /** + * <p>Sets the slideCount.</p> + * + * @param slideCount The slide count to set. + */ + public void setSlideCount(final int slideCount) + { + final MutableSection s = (MutableSection) getFirstSection(); + s.setProperty(PropertyIDMap.PID_SLIDECOUNT, slideCount); + } + + /** + * <p>Removes the slide count.</p> + */ + public void removeSlideCount() + { + final MutableSection s = (MutableSection) getFirstSection(); + s.removeProperty(PropertyIDMap.PID_SLIDECOUNT); + } + /** - * <p>Returns the stream's note count or 0 if the {@link + * <p>Returns the note count or 0 if the {@link * DocumentSummaryInformation} does not contain a note count.</p> * - * @return The noteCount value + * @return The note count value */ public int getNoteCount() { return getPropertyIntValue(PropertyIDMap.PID_NOTECOUNT); } + /** + * <p>Sets the note count.</p> + * + * @param noteCount The note count to set. + */ + public void setNoteCount(final int noteCount) + { + final MutableSection s = (MutableSection) getFirstSection(); + s.setProperty(PropertyIDMap.PID_NOTECOUNT, noteCount); + } + + /** + * <p>Removes the noteCount.</p> + */ + public void removeNoteCount() + { + final MutableSection s = (MutableSection) getFirstSection(); + s.removeProperty(PropertyIDMap.PID_NOTECOUNT); + } + /** - * <p>Returns the stream's hidden count or 0 if the {@link + * <p>Returns the hidden count or 0 if the {@link * DocumentSummaryInformation} does not contain a hidden * count.</p> * - * @return The hiddenCount value + * @return The hidden count value */ public int getHiddenCount() { return getPropertyIntValue(PropertyIDMap.PID_HIDDENCOUNT); } + /** + * <p>Sets the hidden count.</p> + * + * @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); + } + + /** + * <p>Removes the hidden count.</p> + */ + public void removeHiddenCount() + { + final MutableSection s = (MutableSection) getFirstSection(); + s.removeProperty(PropertyIDMap.PID_HIDDENCOUNT); + } + /** - * <p>Returns the stream's mmclip count or 0 if the {@link + * <p>Returns the mmclip count or 0 if the {@link * DocumentSummaryInformation} does not contain a mmclip * count.</p> * - * @return The mMClipCount value + * @return The mmclip count value */ public int getMMClipCount() { return getPropertyIntValue(PropertyIDMap.PID_MMCLIPCOUNT); } + /** + * <p>Sets the mmclip count.</p> + * + * @param mmClipCount The mmclip count to set. + */ + public void setMMClipCount(final int mmClipCount) + { + final MutableSection s = (MutableSection) getFirstSection(); + s.setProperty(PropertyIDMap.PID_MMCLIPCOUNT, mmClipCount); + } + + /** + * <p>Removes the mmclip count.</p> + */ + public void removeMMClipCount() + { + final MutableSection s = (MutableSection) getFirstSection(); + s.removeProperty(PropertyIDMap.PID_MMCLIPCOUNT); + } + /** @@ -196,42 +375,100 @@ public class DocumentSummaryInformation extends SpecialPropertySet return getPropertyBooleanValue(PropertyIDMap.PID_SCALE); } + /** + * <p>Sets the scale.</p> + * + * @param scale The scale to set. + */ + public void setScale(final boolean scale) + { + final MutableSection s = (MutableSection) getFirstSection(); + s.setProperty(PropertyIDMap.PID_SCALE, scale); + } + + /** + * <p>Removes the scale.</p> + */ + public void removeScale() + { + final MutableSection s = (MutableSection) getFirstSection(); + s.removeProperty(PropertyIDMap.PID_SCALE); + } + /** - * <p>Returns the stream's heading pair (or <code>null</code>) + * <p>Returns the heading pair (or <code>null</code>) * <strong>when this method is implemented. Please note that the * return type is likely to change!</strong> * - * @return The headingPair value + * @return The heading pair value */ public byte[] getHeadingPair() { - if (true) - throw new UnsupportedOperationException("FIXME"); + notYetImplemented("Reading byte arrays "); return (byte[]) getProperty(PropertyIDMap.PID_HEADINGPAIR); } + /** + * <p>Sets the heading pair.</p> + * + * @param headingPair The heading pair to set. + */ + public void setHeadingPair(final byte[] headingPair) + { + notYetImplemented("Writing byte arrays "); + } + + /** + * <p>Removes the heading pair.</p> + */ + public void removeHeadingPair() + { + final MutableSection s = (MutableSection) getFirstSection(); + s.removeProperty(PropertyIDMap.PID_HEADINGPAIR); + } + /** - * <p>Returns the stream's doc parts (or <code>null</code>) + * <p>Returns the doc parts (or <code>null</code>) * <strong>when this method is implemented. Please note that the * return type is likely to change!</strong> * - * @return The docparts value + * @return The doc parts value */ public byte[] getDocparts() { - if (true) - throw new UnsupportedOperationException("FIXME"); + notYetImplemented("Reading byte arrays"); return (byte[]) getProperty(PropertyIDMap.PID_DOCPARTS); } /** - * <p>Returns the stream's manager (or <code>null</code>).</p> + * <p>Sets the doc parts.</p> + * + * @param docparts The doc parts to set. + */ + public void setDocparts(final byte[] docparts) + { + notYetImplemented("Writing byte arrays"); + } + + /** + * <p>Removes the doc parts.</p> + */ + public void removeDocparts() + { + final MutableSection s = (MutableSection) getFirstSection(); + s.removeProperty(PropertyIDMap.PID_DOCPARTS); + } + + + + /** + * <p>Returns the manager (or <code>null</code>).</p> * * @return The manager value */ @@ -240,10 +477,30 @@ public class DocumentSummaryInformation extends SpecialPropertySet return (String) getProperty(PropertyIDMap.PID_MANAGER); } + /** + * <p>Sets the manager.</p> + * + * @param manager The manager to set. + */ + public void setManager(final String manager) + { + final MutableSection s = (MutableSection) getFirstSection(); + s.setProperty(PropertyIDMap.PID_MANAGER, manager); + } + + /** + * <p>Removes the manager.</p> + */ + public void removeManager() + { + final MutableSection s = (MutableSection) getFirstSection(); + s.removeProperty(PropertyIDMap.PID_MANAGER); + } + /** - * <p>Returns the stream's company (or <code>null</code>).</p> + * <p>Returns the company (or <code>null</code>).</p> * * @return The company value */ @@ -252,47 +509,168 @@ public class DocumentSummaryInformation extends SpecialPropertySet return (String) getProperty(PropertyIDMap.PID_COMPANY); } + /** + * <p>Sets the company.</p> + * + * @param company The company to set. + */ + public void setCompany(final String company) + { + final MutableSection s = (MutableSection) getFirstSection(); + s.setProperty(PropertyIDMap.PID_COMPANY, company); + } + + /** + * <p>Removes the company.</p> + */ + public void removeCompany() + { + final MutableSection s = (MutableSection) getFirstSection(); + s.removeProperty(PropertyIDMap.PID_COMPANY); + } + /** * <p>Returns <code>true</code> if the custom links are dirty.</p> <p> * - * @return The linksDirty value + * @return The links dirty value */ public boolean getLinksDirty() { return getPropertyBooleanValue(PropertyIDMap.PID_LINKSDIRTY); } + /** + * <p>Sets the linksDirty.</p> + * + * @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); + } + + /** + * <p>Removes the links dirty.</p> + */ + public void removeLinksDirty() + { + final MutableSection s = (MutableSection) getFirstSection(); + s.removeProperty(PropertyIDMap.PID_LINKSDIRTY); + } + /** - * <p>Gets the custom properties as a map from the property name to - * value.</p> + * <p>Gets the custom properties.</p> * - * @return The custom properties if any exist, <code>null</code> otherwise. - * @since 2003-10-22 + * @return The custom properties. + * @since 2006-02-09 */ - public Map getCustomProperties() + public CustomProperties getCustomProperties() { - Map nameToValue = null; + CustomProperties cps = null; if (getSectionCount() >= 2) { + cps = new CustomProperties(); final Section section = (Section) getSections().get(1); - final Map pidToName = - (Map) section.getProperty(PropertyIDMap.PID_DICTIONARY); - if (pidToName != null) + final Map dictionary = section.getDictionary(); + final Property[] properties = section.getProperties(); + int propertyCount = 0; + for (int i = 0; i < properties.length; i++) { - nameToValue = new HashMap(pidToName.size()); - for (Iterator i = pidToName.entrySet().iterator(); i.hasNext();) + final Property p = properties[i]; + final long id = p.getID(); + if (id != 0 && id != 1) { - final Map.Entry e = (Map.Entry) i.next(); - final long pid = ((Number) e.getKey()).longValue(); - nameToValue.put(e.getValue(), section.getProperty(pid)); + propertyCount++; + final CustomProperty cp = new CustomProperty(p, + (String) dictionary.get(new Long(id))); + cps.put(cp.getName(), cp); } } + if (cps.size() != propertyCount) + cps.setPure(false); + } + return cps; + } + + /** + * <p>Sets the custom properties.</p> + * + * @param customProperties The custom properties + * @since 2006-02-07 + */ + public void setCustomProperties(final CustomProperties customProperties) + { + ensureSection2(); + final MutableSection section = (MutableSection) getSections().get(1); + final Map dictionary = customProperties.getDictionary(); + section.clear(); + + /* Set the codepage. If both custom properties and section have a + * 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) + cpCodepage = section.getCodepage(); + if (cpCodepage < 0) + cpCodepage = Constants.CP_UNICODE; + customProperties.setCodepage(cpCodepage); + section.setCodepage(cpCodepage); + section.setDictionary(dictionary); + for (final Iterator i = customProperties.values().iterator(); i.hasNext();) + { + final Property p = (Property) i.next(); + section.setProperty(p); + } + } + + + + /** + * <p>Creates section 2 if it is not already present.</p> + * + */ + private void ensureSection2() + { + if (getSectionCount() < 2) + { + MutableSection s2 = new MutableSection(); + s2.setFormatID(SectionIDMap.DOCUMENT_SUMMARY_INFORMATION_ID[1]); + addSection(s2); } - return nameToValue; + } + + + + /** + * <p>Removes the custom properties.</p> + * + * @since 2006-02-08 + */ + public void removeCustomProperties() + { + if (getSectionCount() >= 2) + getSections().remove(1); + else + throw new HPSFRuntimeException("Illegal internal format of Document SummaryInformation stream: second section is missing."); + } + + + + /** + * <p>Throws an {@link UnsupportedOperationException} with a message text + * telling which functionality is not yet implemented.</p> + * + * @param msg text telling was leaves to be implemented, e.g. + * "Reading byte arrays". + */ + private void notYetImplemented(final String msg) + { + throw new UnsupportedOperationException(msg + " is not yet implemented."); } } diff --git a/src/java/org/apache/poi/hpsf/HPSFException.java b/src/java/org/apache/poi/hpsf/HPSFException.java index ed6aa4ea44..7588be679e 100644 --- a/src/java/org/apache/poi/hpsf/HPSFException.java +++ b/src/java/org/apache/poi/hpsf/HPSFException.java @@ -1,4 +1,3 @@ - /* ==================================================================== Copyright 2002-2004 Apache Software Foundation @@ -18,12 +17,12 @@ package org.apache.poi.hpsf; /** - * <p>This exception is the superclass of all other checked exceptions - * thrown in this package. It supports a nested "reason" throwable, - * i.e. an exception that caused this one to be thrown.</p> - * + * <p>This exception is the superclass of all other checked exceptions thrown + * in this package. It supports a nested "reason" throwable, i.e. an exception + * that caused this one to be thrown.</p> + * * @author Rainer Klute <a - * href="mailto:klute@rainer-klute.de"><klute@rainer-klute.de></a> + * href="mailto:klute@rainer-klute.de"><klute@rainer-klute.de></a> * @version $Id$ * @since 2002-02-09 */ diff --git a/src/java/org/apache/poi/hpsf/HPSFRuntimeException.java b/src/java/org/apache/poi/hpsf/HPSFRuntimeException.java index 57c1a68f84..b997226bb1 100644 --- a/src/java/org/apache/poi/hpsf/HPSFRuntimeException.java +++ b/src/java/org/apache/poi/hpsf/HPSFRuntimeException.java @@ -1,4 +1,3 @@ - /* ==================================================================== Copyright 2002-2004 Apache Software Foundation diff --git a/src/java/org/apache/poi/hpsf/IllegalPropertySetDataException.java b/src/java/org/apache/poi/hpsf/IllegalPropertySetDataException.java index a0ad11df71..6e2b06d5ba 100644 --- a/src/java/org/apache/poi/hpsf/IllegalPropertySetDataException.java +++ b/src/java/org/apache/poi/hpsf/IllegalPropertySetDataException.java @@ -1,4 +1,3 @@ - /* ==================================================================== Copyright 2002-2004 Apache Software Foundation diff --git a/src/java/org/apache/poi/hpsf/MarkUnsupportedException.java b/src/java/org/apache/poi/hpsf/MarkUnsupportedException.java index ced098c323..a03c1738b8 100644 --- a/src/java/org/apache/poi/hpsf/MarkUnsupportedException.java +++ b/src/java/org/apache/poi/hpsf/MarkUnsupportedException.java @@ -1,4 +1,3 @@ - /* ==================================================================== Copyright 2002-2004 Apache Software Foundation diff --git a/src/java/org/apache/poi/hpsf/MissingSectionException.java b/src/java/org/apache/poi/hpsf/MissingSectionException.java new file mode 100644 index 0000000000..76aba80bd8 --- /dev/null +++ b/src/java/org/apache/poi/hpsf/MissingSectionException.java @@ -0,0 +1,76 @@ +/* ==================================================================== + Copyright 2002-2006 Apache Software Foundation + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +==================================================================== */ + +package org.apache.poi.hpsf; + +/** + * <p>This exception is thrown if one of the {@link PropertySet}'s + * convenience methods does not find a required {@link Section}.</p> + * + * <p>The constructors of this class are analogous to those of its + * superclass and documented there.</p> + * + * @author Rainer Klute <a + * href="mailto:klute@rainer-klute.de"><klute@rainer-klute.de></a> + * @version $Id: NoSingleSectionException.java 353545 2004-04-09 13:05:39Z glens $ + * @since 2006-02-08 + */ +public class MissingSectionException extends HPSFRuntimeException +{ + + /** + * <p>Constructor</p> + */ + public MissingSectionException() + { + super(); + } + + + /** + * <p>Constructor</p> + * + * @param msg The exception's message string + */ + public MissingSectionException(final String msg) + { + super(msg); + } + + + /** + * <p>Constructor</p> + * + * @param reason This exception's underlying reason + */ + public MissingSectionException(final Throwable reason) + { + super(reason); + } + + + /** + * <p>Constructor</p> + * + * @param msg The exception's message string + * @param reason This exception's underlying reason + */ + public MissingSectionException(final String msg, final Throwable reason) + { + super(msg, reason); + } + +} diff --git a/src/java/org/apache/poi/hpsf/MutablePropertySet.java b/src/java/org/apache/poi/hpsf/MutablePropertySet.java index d1dccf2949..bff912be3f 100644 --- a/src/java/org/apache/poi/hpsf/MutablePropertySet.java +++ b/src/java/org/apache/poi/hpsf/MutablePropertySet.java @@ -1,5 +1,5 @@ /* ==================================================================== - Copyright 2002-2004 Apache Software Foundation + Copyright 2002-2006 Apache Software Foundation Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -18,6 +18,7 @@ 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; @@ -26,6 +27,8 @@ import java.util.Iterator; import java.util.LinkedList; import java.util.ListIterator; +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; @@ -151,7 +154,7 @@ public class MutablePropertySet extends PropertySet * * @param classID The property set stream's low-level "class ID" field. * - * @see #getClassID + * @see PropertySet#getClassID() */ public void setClassID(final ClassID classID) { @@ -205,9 +208,9 @@ public class MutablePropertySet extends PropertySet /* Write the property set's header. */ length += TypeWriter.writeToStream(out, (short) getByteOrder()); length += TypeWriter.writeToStream(out, (short) getFormat()); - length += TypeWriter.writeToStream(out, (int) getOSVersion()); + length += TypeWriter.writeToStream(out, getOSVersion()); length += TypeWriter.writeToStream(out, getClassID()); - length += TypeWriter.writeToStream(out, (int) nrSections); + length += TypeWriter.writeToStream(out, nrSections); int offset = OFFSET_HEADER; /* Write the section list, i.e. the references to the sections. Each @@ -272,4 +275,31 @@ public class MutablePropertySet extends PropertySet 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 + * @throws IOException + */ + 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 6d6c1b11e9..96928051ab 100644 --- a/src/java/org/apache/poi/hpsf/MutableSection.java +++ b/src/java/org/apache/poi/hpsf/MutableSection.java @@ -1,5 +1,5 @@ /* ==================================================================== - Copyright 2002-2004 Apache Software Foundation + Copyright 2002-2006 Apache Software Foundation Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -21,6 +21,7 @@ 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; @@ -107,7 +108,7 @@ public class MutableSection extends Section * @param formatID The section's format ID * * @see #setFormatID(byte[]) - * @see #getFormatID + * @see Section#getFormatID */ public void setFormatID(final ClassID formatID) { @@ -123,7 +124,7 @@ public class MutableSection extends Section * are in big-endian format. * * @see #setFormatID(ClassID) - * @see #getFormatID + * @see Section#getFormatID */ public void setFormatID(final byte[] formatID) { @@ -155,10 +156,7 @@ public class MutableSection extends Section /** - * <p>Sets the value 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.</p> + * <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 @@ -176,6 +174,57 @@ public class MutableSection extends Section /** + * <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, new Integer(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, new Long(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, new Boolean(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 @@ -204,15 +253,11 @@ public class MutableSection extends Section /** - * <p>Sets a property. If a property with the same ID is not yet present in - * the section, the property will be added to the section. If there is - * already a property with the same ID present in the section, it will be - * overwritten.</p> + * <p>Sets a property.</p> * - * @param p The property to be added to the section + * @param p The property to be set. * * @see #setProperty(int, long, Object) - * @see #setProperty(int, String) * @see #getProperty * @see Variant */ @@ -257,7 +302,7 @@ public class MutableSection extends Section */ protected void setPropertyBooleanValue(final int id, final boolean value) { - setProperty(id, (long) Variant.VT_BOOL, new Boolean(value)); + setProperty(id, Variant.VT_BOOL, new Boolean(value)); } @@ -296,6 +341,8 @@ public class MutableSection extends Section * properties) and the properties themselves.</p> * * @return the section's length in bytes. + * @throws WritingNotSupportedException + * @throws IOException */ private int calcSize() throws WritingNotSupportedException, IOException { @@ -372,7 +419,7 @@ public class MutableSection extends Section /* 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, (long) Variant.VT_I2, + setProperty(PropertyIDMap.PID_CODEPAGE, Variant.VT_I2, new Integer(Constants.CP_UNICODE)); codepage = getCodepage(); } @@ -474,16 +521,15 @@ public class MutableSection extends Section sLength++; length += TypeWriter.writeUIntToStream(out, key.longValue()); length += TypeWriter.writeUIntToStream(out, sLength); - final char[] ca = value.toCharArray(); - for (int j = 0; j < ca.length; j++) + final byte[] ca = + value.getBytes(VariantSupport.codepageToEncoding(codepage)); + for (int j = 2; j < ca.length; j += 2) { - int high = (ca[j] & 0x0ff00) >> 8; - int low = (ca[j] & 0x000ff); - out.write(low); - out.write(high); + out.write(ca[j+1]); + out.write(ca[j]); length += 2; - sLength--; } + sLength -= value.length(); while (sLength > 0) { out.write(0x00); @@ -610,4 +656,60 @@ public class MutableSection extends Section 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, + new Integer(codepage)); + } + } diff --git a/src/java/org/apache/poi/hpsf/NoFormatIDException.java b/src/java/org/apache/poi/hpsf/NoFormatIDException.java index 661189d7b7..4bcdf3311c 100644 --- a/src/java/org/apache/poi/hpsf/NoFormatIDException.java +++ b/src/java/org/apache/poi/hpsf/NoFormatIDException.java @@ -1,4 +1,3 @@ - /* ==================================================================== Copyright 2002-2004 Apache Software Foundation diff --git a/src/java/org/apache/poi/hpsf/NoSingleSectionException.java b/src/java/org/apache/poi/hpsf/NoSingleSectionException.java index 9e5de03ab3..7cdf0d2d97 100644 --- a/src/java/org/apache/poi/hpsf/NoSingleSectionException.java +++ b/src/java/org/apache/poi/hpsf/NoSingleSectionException.java @@ -1,4 +1,3 @@ - /* ==================================================================== Copyright 2002-2004 Apache Software Foundation diff --git a/src/java/org/apache/poi/hpsf/Property.java b/src/java/org/apache/poi/hpsf/Property.java index 81c346b815..e27b8beb35 100644 --- a/src/java/org/apache/poi/hpsf/Property.java +++ b/src/java/org/apache/poi/hpsf/Property.java @@ -1,5 +1,5 @@ /* ==================================================================== - Copyright 2002-2004 Apache Software Foundation + Copyright 2002-2006 Apache Software Foundation Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -108,6 +108,22 @@ public class Property /** + * <p>Creates a property.</p> + * + * @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) + { + 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> * @@ -222,12 +238,15 @@ public class Property { /* The length is the number of characters, i.e. the number * of bytes is twice the number of the characters. */ - for (int j = 0; j < sLength; j++) + final int nrBytes = (int) (sLength * 2); + final byte[] h = new byte[nrBytes]; + for (int i2 = 0; i2 < nrBytes; i2 += 2) { - final int i1 = o + (j * 2); - final int i2 = i1 + 1; - b.append((char) ((src[i2] << 8) + src[i1])); + h[i2] = src[o + i2 + 1]; + h[i2 + 1] = src[o + i2]; } + b.append(new String(h, 0, nrBytes, + VariantSupport.codepageToEncoding(codepage))); break; } default: diff --git a/src/java/org/apache/poi/hpsf/PropertySet.java b/src/java/org/apache/poi/hpsf/PropertySet.java index 134df3564b..821acf2d40 100644 --- a/src/java/org/apache/poi/hpsf/PropertySet.java +++ b/src/java/org/apache/poi/hpsf/PropertySet.java @@ -1,6 +1,5 @@ - /* ==================================================================== - Copyright 2002-2004 Apache Software Foundation + Copyright 2002-2006 Apache Software Foundation Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -402,8 +401,10 @@ public class PropertySet * * @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</src> + * from the beginning of <var>src</var> * @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 @@ -482,7 +483,7 @@ public class PropertySet public boolean isDocumentSummaryInformation() { return Util.equal(((Section) sections.get(0)).getFormatID().getBytes(), - SectionIDMap.DOCUMENT_SUMMARY_INFORMATION_ID); + SectionIDMap.DOCUMENT_SUMMARY_INFORMATION_ID[0]); } @@ -492,9 +493,7 @@ public class PropertySet * 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}. However, it can only be used if the {@link - * PropertySet} contains exactly one {@link Section}, so check - * {@link #getSectionCount} first!</p> + * Section}.</p> * * @return The properties of the only {@link Section} of this * {@link PropertySet}. @@ -504,7 +503,7 @@ public class PropertySet public Property[] getProperties() throws NoSingleSectionException { - return getSingleSection().getProperties(); + return getFirstSection().getProperties(); } @@ -522,7 +521,7 @@ public class PropertySet */ protected Object getProperty(final int id) throws NoSingleSectionException { - return getSingleSection().getProperty(id); + return getFirstSection().getProperty(id); } @@ -543,7 +542,7 @@ public class PropertySet protected boolean getPropertyBooleanValue(final int id) throws NoSingleSectionException { - return getSingleSection().getPropertyBooleanValue(id); + return getFirstSection().getPropertyBooleanValue(id); } @@ -563,7 +562,7 @@ public class PropertySet protected int getPropertyIntValue(final int id) throws NoSingleSectionException { - return getSingleSection().getPropertyIntValue(id); + return getFirstSection().getPropertyIntValue(id); } @@ -585,7 +584,21 @@ public class PropertySet */ public boolean wasNull() throws NoSingleSectionException { - return getSingleSection().wasNull(); + return getFirstSection().wasNull(); + } + + + + /** + * <p>Gets the {@link PropertySet}'s first section.</p> + * + * @return The {@link PropertySet}'s first section. + */ + public Section getFirstSection() + { + if (getSectionCount() < 1) + throw new MissingSectionException("Property set does not contain any sections."); + return ((Section) sections.get(0)); } diff --git a/src/java/org/apache/poi/hpsf/PropertySetFactory.java b/src/java/org/apache/poi/hpsf/PropertySetFactory.java index e7b9576792..0a93982043 100644 --- a/src/java/org/apache/poi/hpsf/PropertySetFactory.java +++ b/src/java/org/apache/poi/hpsf/PropertySetFactory.java @@ -1,6 +1,5 @@ - /* ==================================================================== - Copyright 2002-2004 Apache Software Foundation + Copyright 2002-2006 Apache Software Foundation Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -22,6 +21,8 @@ import java.io.IOException; import java.io.UnsupportedEncodingException; import java.rmi.UnexpectedException; +import org.apache.poi.hpsf.wellknown.SectionIDMap; + /** * <p>Factory class to create instances of {@link SummaryInformation}, * {@link DocumentSummaryInformation} and {@link PropertySet}.</p> @@ -74,4 +75,50 @@ public class PropertySetFactory } } + + + /** + * <p>Creates a new summary information.</p> + * + * @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); + } + } + + + + /** + * <p>Creates a new document summary information.</p> + * + * @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); + } + } + } diff --git a/src/java/org/apache/poi/hpsf/ReadingNotSupportedException.java b/src/java/org/apache/poi/hpsf/ReadingNotSupportedException.java index a6a97f1ee7..c7fed7fd0c 100644 --- a/src/java/org/apache/poi/hpsf/ReadingNotSupportedException.java +++ b/src/java/org/apache/poi/hpsf/ReadingNotSupportedException.java @@ -1,4 +1,3 @@ - /* ==================================================================== Copyright 2002-2004 Apache Software Foundation diff --git a/src/java/org/apache/poi/hpsf/Section.java b/src/java/org/apache/poi/hpsf/Section.java index 913fc9116b..ee28158107 100644 --- a/src/java/org/apache/poi/hpsf/Section.java +++ b/src/java/org/apache/poi/hpsf/Section.java @@ -1,6 +1,5 @@ - /* ==================================================================== - Copyright 2002-2004 Apache Software Foundation + Copyright 2002-2006 Apache Software Foundation Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -278,9 +277,12 @@ public class Section for (final Iterator i = propertyList.iterator(); i.hasNext();) { ple = (PropertyListEntry) i.next(); - properties[i1++] = new Property(ple.id, src, - this.offset + ple.offset, - ple.length, codepage); + 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(), new Integer(codepage)); + properties[i1++] = p; } /* @@ -359,15 +361,15 @@ public class Section */ protected int getPropertyIntValue(final long id) { - final Long i; + final Number i; final Object o = getProperty(id); if (o == null) return 0; - if (!(o instanceof Long)) + if (!(o instanceof Long || o instanceof Integer)) throw new HPSFRuntimeException ("This property is not an integer type, but " + o.getClass().getName() + "."); - i = (Long) o; + i = (Number) o; return i.intValue(); } @@ -545,6 +547,10 @@ public class Section /** * <p>Removes a field from a property array. The resulting array is * compactified and returned.</p> + * + * @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) { @@ -629,7 +635,10 @@ public class Section { final Integer codepage = (Integer) getProperty(PropertyIDMap.PID_CODEPAGE); - return codepage != null ? codepage.intValue() : -1; + 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 d114e41abf..e65de3c11b 100644 --- a/src/java/org/apache/poi/hpsf/SpecialPropertySet.java +++ b/src/java/org/apache/poi/hpsf/SpecialPropertySet.java @@ -1,6 +1,5 @@ - /* ==================================================================== - Copyright 2002-2004 Apache Software Foundation + Copyright 2002-2006 Apache Software Foundation Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -17,8 +16,13 @@ package org.apache.poi.hpsf; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; import java.util.List; +import org.apache.poi.poifs.filesystem.DirectoryEntry; + /** * <p>Abstract superclass for the convenience classes {@link * SummaryInformation} and {@link DocumentSummaryInformation}.</p> @@ -50,25 +54,38 @@ import java.util.List; * @version $Id$ * @since 2002-02-09 */ -public abstract class SpecialPropertySet extends PropertySet +public abstract class SpecialPropertySet extends MutablePropertySet { /** * <p>The "real" property set <code>SpecialPropertySet</code> * delegates to.</p> */ - private PropertySet delegate; + private MutablePropertySet delegate; /** * <p>Creates a <code>SpecialPropertySet</code>. * - * @param ps The property set encapsulated by the + * @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; } @@ -157,9 +174,178 @@ public abstract class SpecialPropertySet extends PropertySet /** * @see PropertySet#getSingleSection */ - public Section getSingleSection() + public Section getFirstSection() + { + return delegate.getFirstSection(); + } + + + /** + * @see org.apache.poi.hpsf.MutablePropertySet#addSection(org.apache.poi.hpsf.Section) + */ + public void addSection(final Section section) + { + delegate.addSection(section); + } + + + + /** + * @see org.apache.poi.hpsf.MutablePropertySet#clearSections() + */ + public void clearSections() + { + delegate.clearSections(); + } + + + + /** + * @see org.apache.poi.hpsf.MutablePropertySet#setByteOrder(int) + */ + public void setByteOrder(final int byteOrder) + { + delegate.setByteOrder(byteOrder); + } + + + + /** + * @see org.apache.poi.hpsf.MutablePropertySet#setClassID(org.apache.poi.hpsf.ClassID) + */ + public void setClassID(final ClassID classID) + { + delegate.setClassID(classID); + } + + + + /** + * @see org.apache.poi.hpsf.MutablePropertySet#setFormat(int) + */ + public void setFormat(final int format) + { + delegate.setFormat(format); + } + + + + /** + * @see org.apache.poi.hpsf.MutablePropertySet#setOSVersion(int) + */ + public void setOSVersion(final int osVersion) + { + delegate.setOSVersion(osVersion); + } + + + + /** + * @see org.apache.poi.hpsf.MutablePropertySet#toInputStream() + */ + 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) + */ + 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) + */ + public void write(final OutputStream out) throws WritingNotSupportedException, IOException + { + delegate.write(out); + } + + + + /** + * @see org.apache.poi.hpsf.PropertySet#equals(java.lang.Object) + */ + public boolean equals(final Object o) + { + return delegate.equals(o); + } + + + + /** + * @see org.apache.poi.hpsf.PropertySet#getProperties() + */ + public Property[] getProperties() throws NoSingleSectionException + { + return delegate.getProperties(); + } + + + + /** + * @see org.apache.poi.hpsf.PropertySet#getProperty(int) + */ + protected Object getProperty(final int id) throws NoSingleSectionException + { + return delegate.getProperty(id); + } + + + + /** + * @see org.apache.poi.hpsf.PropertySet#getPropertyBooleanValue(int) + */ + protected boolean getPropertyBooleanValue(final int id) throws NoSingleSectionException + { + return delegate.getPropertyBooleanValue(id); + } + + + + /** + * @see org.apache.poi.hpsf.PropertySet#getPropertyIntValue(int) + */ + protected int getPropertyIntValue(final int id) throws NoSingleSectionException + { + return delegate.getPropertyIntValue(id); + } + + + + /** + * @see org.apache.poi.hpsf.PropertySet#hashCode() + */ + public int hashCode() + { + return delegate.hashCode(); + } + + + + /** + * @see org.apache.poi.hpsf.PropertySet#toString() + */ + public String toString() + { + return delegate.toString(); + } + + + + /** + * @see org.apache.poi.hpsf.PropertySet#wasNull() + */ + public boolean wasNull() throws NoSingleSectionException { - return delegate.getSingleSection(); + return delegate.wasNull(); } } diff --git a/src/java/org/apache/poi/hpsf/SummaryInformation.java b/src/java/org/apache/poi/hpsf/SummaryInformation.java index a170225df0..7a4941448e 100644 --- a/src/java/org/apache/poi/hpsf/SummaryInformation.java +++ b/src/java/org/apache/poi/hpsf/SummaryInformation.java @@ -1,31 +1,33 @@ +/* + * ==================================================================== + * Copyright 2002-2006 Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + * ==================================================================== + */ -/* ==================================================================== - Copyright 2002-2004 Apache Software Foundation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -==================================================================== */ - package org.apache.poi.hpsf; import java.util.Date; + import org.apache.poi.hpsf.wellknown.PropertyIDMap; /** * <p>Convenience class representing a Summary Information stream in a * Microsoft Office document.</p> - * + * * @author Rainer Klute <a - * href="mailto:klute@rainer-klute.de"><klute@rainer-klute.de></a> + * href="mailto:klute@rainer-klute.de"><klute@rainer-klute.de></a> * @see DocumentSummaryInformation * @version $Id$ * @since 2002-02-09 @@ -34,8 +36,8 @@ public class SummaryInformation extends SpecialPropertySet { /** - * <p>The document name a summary information stream usually has - * in a POIFS filesystem.</p> + * <p>The document name a summary information stream usually has in a POIFS + * filesystem.</p> */ public static final String DEFAULT_STREAM_NAME = "\005SummaryInformation"; @@ -44,26 +46,26 @@ public class SummaryInformation extends SpecialPropertySet /** * <p>Creates a {@link SummaryInformation} from a given {@link * PropertySet}.</p> - * + * * @param ps A property set which should be created from a summary - * information stream. - * @throws UnexpectedPropertySetTypeException if <var>ps</var> - * does not contain a summary information stream. + * information stream. + * @throws UnexpectedPropertySetTypeException if <var>ps</var> does not + * contain a summary information stream. */ public SummaryInformation(final PropertySet ps) - throws UnexpectedPropertySetTypeException + throws UnexpectedPropertySetTypeException { super(ps); if (!isSummaryInformation()) - throw new UnexpectedPropertySetTypeException - ("Not a " + getClass().getName()); + throw new UnexpectedPropertySetTypeException("Not a " + + getClass().getName()); } /** - * <p>Returns the stream's title (or <code>null</code>).</p> - * + * <p>Returns the title (or <code>null</code>).</p> + * * @return The title or <code>null</code> */ public String getTitle() @@ -74,8 +76,32 @@ public class SummaryInformation extends SpecialPropertySet /** - * <p>Returns the stream's subject (or <code>null</code>).</p> - * + * <p>Sets the title.</p> + * + * @param title The title to set. + */ + public void setTitle(final String title) + { + final MutableSection s = (MutableSection) getFirstSection(); + s.setProperty(PropertyIDMap.PID_TITLE, title); + } + + + + /** + * <p>Removes the title.</p> + */ + public void removeTitle() + { + final MutableSection s = (MutableSection) getFirstSection(); + s.removeProperty(PropertyIDMap.PID_TITLE); + } + + + + /** + * <p>Returns the subject (or <code>null</code>).</p> + * * @return The subject or <code>null</code> */ public String getSubject() @@ -86,8 +112,32 @@ public class SummaryInformation extends SpecialPropertySet /** - * <p>Returns the stream's author (or <code>null</code>).</p> - * + * <p>Sets the subject.</p> + * + * @param subject The subject to set. + */ + public void setSubject(final String subject) + { + final MutableSection s = (MutableSection) getFirstSection(); + s.setProperty(PropertyIDMap.PID_SUBJECT, subject); + } + + + + /** + * <p>Removes the subject.</p> + */ + public void removeSubject() + { + final MutableSection s = (MutableSection) getFirstSection(); + s.removeProperty(PropertyIDMap.PID_SUBJECT); + } + + + + /** + * <p>Returns the author (or <code>null</code>).</p> + * * @return The author or <code>null</code> */ public String getAuthor() @@ -98,8 +148,32 @@ public class SummaryInformation extends SpecialPropertySet /** - * <p>Returns the stream's keywords (or <code>null</code>).</p> - * + * <p>Sets the author.</p> + * + * @param author The author to set. + */ + public void setAuthor(final String author) + { + final MutableSection s = (MutableSection) getFirstSection(); + s.setProperty(PropertyIDMap.PID_AUTHOR, author); + } + + + + /** + * <p>Removes the author.</p> + */ + public void removeAuthor() + { + final MutableSection s = (MutableSection) getFirstSection(); + s.removeProperty(PropertyIDMap.PID_AUTHOR); + } + + + + /** + * <p>Returns the keywords (or <code>null</code>).</p> + * * @return The keywords or <code>null</code> */ public String getKeywords() @@ -110,8 +184,32 @@ public class SummaryInformation extends SpecialPropertySet /** - * <p>Returns the stream's comments (or <code>null</code>).</p> - * + * <p>Sets the keywords.</p> + * + * @param keywords The keywords to set. + */ + public void setKeywords(final String keywords) + { + final MutableSection s = (MutableSection) getFirstSection(); + s.setProperty(PropertyIDMap.PID_KEYWORDS, keywords); + } + + + + /** + * <p>Removes the keywords.</p> + */ + public void removeKeywords() + { + final MutableSection s = (MutableSection) getFirstSection(); + s.removeProperty(PropertyIDMap.PID_KEYWORDS); + } + + + + /** + * <p>Returns the comments (or <code>null</code>).</p> + * * @return The comments or <code>null</code> */ public String getComments() @@ -122,8 +220,32 @@ public class SummaryInformation extends SpecialPropertySet /** - * <p>Returns the stream's template (or <code>null</code>).</p> - * + * <p>Sets the comments.</p> + * + * @param comments The comments to set. + */ + public void setComments(final String comments) + { + final MutableSection s = (MutableSection) getFirstSection(); + s.setProperty(PropertyIDMap.PID_COMMENTS, comments); + } + + + + /** + * <p>Removes the comments.</p> + */ + public void removeComments() + { + final MutableSection s = (MutableSection) getFirstSection(); + s.removeProperty(PropertyIDMap.PID_COMMENTS); + } + + + + /** + * <p>Returns the template (or <code>null</code>).</p> + * * @return The template or <code>null</code> */ public String getTemplate() @@ -134,8 +256,32 @@ public class SummaryInformation extends SpecialPropertySet /** - * <p>Returns the stream's last author (or <code>null</code>).</p> - * + * <p>Sets the template.</p> + * + * @param template The template to set. + */ + public void setTemplate(final String template) + { + final MutableSection s = (MutableSection) getFirstSection(); + s.setProperty(PropertyIDMap.PID_TEMPLATE, template); + } + + + + /** + * <p>Removes the template.</p> + */ + public void removeTemplate() + { + final MutableSection s = (MutableSection) getFirstSection(); + s.removeProperty(PropertyIDMap.PID_TEMPLATE); + } + + + + /** + * <p>Returns the last author (or <code>null</code>).</p> + * * @return The last author or <code>null</code> */ public String getLastAuthor() @@ -146,9 +292,32 @@ public class SummaryInformation extends SpecialPropertySet /** - * <p>Returns the stream's revision number (or - * <code>null</code>). </p> - * + * <p>Sets the last author.</p> + * + * @param lastAuthor The last author to set. + */ + public void setLastAuthor(final String lastAuthor) + { + final MutableSection s = (MutableSection) getFirstSection(); + s.setProperty(PropertyIDMap.PID_LASTAUTHOR, lastAuthor); + } + + + + /** + * <p>Removes the last author.</p> + */ + public void removeLastAuthor() + { + final MutableSection s = (MutableSection) getFirstSection(); + s.removeProperty(PropertyIDMap.PID_LASTAUTHOR); + } + + + + /** + * <p>Returns the revision number (or <code>null</code>). </p> + * * @return The revision number or <code>null</code> */ public String getRevNumber() @@ -159,11 +328,35 @@ public class SummaryInformation extends SpecialPropertySet /** - * <p>Returns the total time spent in editing the document - * (or <code>0</code>).</p> - * + * <p>Sets the revision number.</p> + * + * @param revNumber The revision number to set. + */ + public void setRevNumber(final String revNumber) + { + final MutableSection s = (MutableSection) getFirstSection(); + s.setProperty(PropertyIDMap.PID_REVNUMBER, revNumber); + } + + + + /** + * <p>Removes the revision number.</p> + */ + public void removeRevNumber() + { + final MutableSection s = (MutableSection) getFirstSection(); + s.removeProperty(PropertyIDMap.PID_REVNUMBER); + } + + + + /** + * <p>Returns the total time spent in editing the document (or + * <code>0</code>).</p> + * * @return The total time spent in editing the document or 0 if the {@link - * SummaryInformation} does not contain this information. + * SummaryInformation} does not contain this information. */ public long getEditTime() { @@ -177,9 +370,33 @@ public class SummaryInformation extends SpecialPropertySet /** - * <p>Returns the stream's last printed time (or - * <code>null</code>).</p> - * + * <p>Sets the total time spent in editing the document.</p> + * + * @param time The time to set. + */ + 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); + } + + + + /** + * <p>Remove the total time spent in editing the document.</p> + */ + public void removeEditTime() + { + final MutableSection s = (MutableSection) getFirstSection(); + s.removeProperty(PropertyIDMap.PID_EDITTIME); + } + + + + /** + * <p>Returns the last printed time (or <code>null</code>).</p> + * * @return The last printed time or <code>null</code> */ public Date getLastPrinted() @@ -190,9 +407,33 @@ public class SummaryInformation extends SpecialPropertySet /** - * <p>Returns the stream's creation time (or - * <code>null</code>).</p> - * + * <p>Sets the lastPrinted.</p> + * + * @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); + } + + + + /** + * <p>Removes the lastPrinted.</p> + */ + public void removeLastPrinted() + { + final MutableSection s = (MutableSection) getFirstSection(); + s.removeProperty(PropertyIDMap.PID_LASTPRINTED); + } + + + + /** + * <p>Returns the creation time (or <code>null</code>).</p> + * * @return The creation time or <code>null</code> */ public Date getCreateDateTime() @@ -203,9 +444,33 @@ public class SummaryInformation extends SpecialPropertySet /** - * <p>Returns the stream's last save time (or - * <code>null</code>).</p> - * + * <p>Sets the creation time.</p> + * + * @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); + } + + + + /** + * <p>Removes the creation time.</p> + */ + public void removeCreateDateTime() + { + final MutableSection s = (MutableSection) getFirstSection(); + s.removeProperty(PropertyIDMap.PID_CREATE_DTM); + } + + + + /** + * <p>Returns the last save time (or <code>null</code>).</p> + * * @return The last save time or <code>null</code> */ public Date getLastSaveDateTime() @@ -216,11 +481,37 @@ public class SummaryInformation extends SpecialPropertySet /** - * <p>Returns the stream's page count or 0 if the {@link - * SummaryInformation} does not contain a page count.</p> - * + * <p>Sets the total time spent in editing the document.</p> + * + * @param time The time to set. + */ + public void setLastSaveDateTime(final Date time) + { + final MutableSection s = (MutableSection) getFirstSection(); + s + .setProperty(PropertyIDMap.PID_LASTSAVE_DTM, + Variant.VT_FILETIME, time); + } + + + + /** + * <p>Remove the total time spent in editing the document.</p> + */ + public void removeLastSaveDateTime() + { + final MutableSection s = (MutableSection) getFirstSection(); + s.removeProperty(PropertyIDMap.PID_LASTSAVE_DTM); + } + + + + /** + * <p>Returns the page count or 0 if the {@link SummaryInformation} does + * not contain a page count.</p> + * * @return The page count or 0 if the {@link SummaryInformation} does not - * contain a page count. + * contain a page count. */ public int getPageCount() { @@ -230,9 +521,33 @@ public class SummaryInformation extends SpecialPropertySet /** - * <p>Returns the stream's word count or 0 if the {@link - * SummaryInformation} does not contain a word count.</p> - * + * <p>Sets the page count.</p> + * + * @param pageCount The page count to set. + */ + public void setPageCount(final int pageCount) + { + final MutableSection s = (MutableSection) getFirstSection(); + s.setProperty(PropertyIDMap.PID_PAGECOUNT, pageCount); + } + + + + /** + * <p>Removes the page count.</p> + */ + public void removePageCount() + { + final MutableSection s = (MutableSection) getFirstSection(); + s.removeProperty(PropertyIDMap.PID_PAGECOUNT); + } + + + + /** + * <p>Returns the word count or 0 if the {@link SummaryInformation} does + * not contain a word count.</p> + * * @return The word count or <code>null</code> */ public int getWordCount() @@ -243,9 +558,33 @@ public class SummaryInformation extends SpecialPropertySet /** - * <p>Returns the stream's character count or 0 if the {@link - * SummaryInformation} does not contain a char count.</p> - * + * <p>Sets the word count.</p> + * + * @param wordCount The word count to set. + */ + public void setWordCount(final int wordCount) + { + final MutableSection s = (MutableSection) getFirstSection(); + s.setProperty(PropertyIDMap.PID_WORDCOUNT, wordCount); + } + + + + /** + * <p>Removes the word count.</p> + */ + public void removeWordCount() + { + final MutableSection s = (MutableSection) getFirstSection(); + s.removeProperty(PropertyIDMap.PID_WORDCOUNT); + } + + + + /** + * <p>Returns the character count or 0 if the {@link SummaryInformation} + * does not contain a char count.</p> + * * @return The character count or <code>null</code> */ public int getCharCount() @@ -256,15 +595,39 @@ public class SummaryInformation extends SpecialPropertySet /** - * <p>Returns the stream's thumbnail (or <code>null</code>) - * <strong>when this method is implemented. Please note that the - * return type is likely to change!</strong></p> - * - * <p><strong>Hint to developers:</strong> Drew Varner <Drew.Varner -at- - * sc.edu> said that this is an image in WMF or Clipboard (BMP?) format. - * However, we won't do any conversion into any image type but instead just - * return a byte array.</p> - * + * <p>Sets the character count.</p> + * + * @param charCount The character count to set. + */ + public void setCharCount(final int charCount) + { + final MutableSection s = (MutableSection) getFirstSection(); + s.setProperty(PropertyIDMap.PID_CHARCOUNT, charCount); + } + + + + /** + * <p>Removes the character count.</p> + */ + public void removeCharCount() + { + final MutableSection s = (MutableSection) getFirstSection(); + s.removeProperty(PropertyIDMap.PID_CHARCOUNT); + } + + + + /** + * <p>Returns the thumbnail (or <code>null</code>) <strong>when this + * method is implemented. Please note that the return type is likely to + * change!</strong></p> + * + * <p><strong>Hint to developers:</strong> Drew Varner <Drew.Varner + * -at- sc.edu> said that this is an image in WMF or Clipboard (BMP?) + * format. However, we won't do any conversion into any image type but + * instead just return a byte array.</p> + * * @return The thumbnail or <code>null</code> */ public byte[] getThumbnail() @@ -275,9 +638,33 @@ public class SummaryInformation extends SpecialPropertySet /** - * <p>Returns the stream's application name (or - * <code>null</code>).</p> - * + * <p>Sets the thumbnail.</p> + * + * @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); + } + + + + /** + * <p>Removes the thumbnail.</p> + */ + public void removeThumbnail() + { + final MutableSection s = (MutableSection) getFirstSection(); + s.removeProperty(PropertyIDMap.PID_THUMBNAIL); + } + + + + /** + * <p>Returns the application name (or <code>null</code>).</p> + * * @return The application name or <code>null</code> */ public String getApplicationName() @@ -288,35 +675,49 @@ public class SummaryInformation extends SpecialPropertySet /** - * <p>Returns a security code which is one of the following - * values:</p> - * + * <p>Sets the application name.</p> + * + * @param applicationName The application name to set. + */ + public void setApplicationName(final String applicationName) + { + final MutableSection s = (MutableSection) getFirstSection(); + s.setProperty(PropertyIDMap.PID_APPNAME, applicationName); + } + + + + /** + * <p>Removes the application name.</p> + */ + public void removeApplicationName() + { + final MutableSection s = (MutableSection) getFirstSection(); + s.removeProperty(PropertyIDMap.PID_APPNAME); + } + + + + /** + * <p>Returns a security code which is one of the following values:</p> + * * <ul> - * <li> - * <p>0 if the {@link SummaryInformation} does not contain a - * security field or if there is no security on the - * document. Use {@link #wasNull} to distinguish between the - * two cases!</p> - * </li> - * - * <li> - * <p>1 if the document is password protected</p> - * </li> - * - * <li> - * <p>2 if the document is read-only recommended</p> - * </li> - * - * <li> - * <p>4 if the document is read-only enforced</p> - * </li> - * - * <li> - * <p>8 if the document is locked for annotations</p> - * </li> - * + * + * <li><p>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> + * + * <li><p>1 if the document is password protected</p></li> + * + * <li><p>2 if the document is read-only recommended</p></li> + * + * <li><p>4 if the document is read-only enforced</p></li> + * + * <li><p>8 if the document is locked for annotations</p></li> + * * </ul> - * + * * @return The security code or <code>null</code> */ public int getSecurity() @@ -324,4 +725,28 @@ public class SummaryInformation extends SpecialPropertySet return getPropertyIntValue(PropertyIDMap.PID_SECURITY); } + + + /** + * <p>Sets the security code.</p> + * + * @param security The security code to set. + */ + public void setSecurity(final int security) + { + final MutableSection s = (MutableSection) getFirstSection(); + s.setProperty(PropertyIDMap.PID_SECURITY, security); + } + + + + /** + * <p>Removes the security code.</p> + */ + public void removeSecurity() + { + final MutableSection s = (MutableSection) getFirstSection(); + s.removeProperty(PropertyIDMap.PID_SECURITY); + } + } diff --git a/src/java/org/apache/poi/hpsf/Thumbnail.java b/src/java/org/apache/poi/hpsf/Thumbnail.java index fd168822a8..14d1a0f3f7 100644 --- a/src/java/org/apache/poi/hpsf/Thumbnail.java +++ b/src/java/org/apache/poi/hpsf/Thumbnail.java @@ -1,4 +1,3 @@ - /* ==================================================================== Copyright 2002-2004 Apache Software Foundation diff --git a/src/java/org/apache/poi/hpsf/TypeWriter.java b/src/java/org/apache/poi/hpsf/TypeWriter.java index c88e98d07b..acfcfa6c34 100644 --- a/src/java/org/apache/poi/hpsf/TypeWriter.java +++ b/src/java/org/apache/poi/hpsf/TypeWriter.java @@ -1,6 +1,5 @@ - /* ==================================================================== - Copyright 2002-2004 Apache Software Foundation + Copyright 2002-2006 Apache Software Foundation Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -46,7 +45,7 @@ public class TypeWriter { final int length = LittleEndian.SHORT_SIZE; byte[] buffer = new byte[length]; - LittleEndian.putUShort(buffer, 0, n); + LittleEndian.putShort(buffer, 0, n); // FIXME: unsigned out.write(buffer, 0, length); return length; } @@ -75,6 +74,27 @@ public class TypeWriter /** + * <p>Writes a eight-byte value to an output stream.</p> + * + * @param out The stream to write to. + * @param n The value to write. + * @exception IOException if an I/O error occurs + * @return The number of bytes written to the output stream. + */ + public static int writeToStream(final OutputStream out, final long n) + throws IOException + { + final int l = LittleEndian.LONG_SIZE; + final byte[] buffer = new byte[l]; + LittleEndian.putLong(buffer, 0, n); + out.write(buffer, 0, l); + return l; + + } + + + + /** * <p>Writes an unsigned two-byte value to an output stream.</p> * * @param out The stream to write to @@ -118,6 +138,7 @@ public class TypeWriter * * @param out The stream to write to * @param n The value to write + * @return The number of bytes written * @exception IOException if an I/O error occurs */ public static int writeToStream(final OutputStream out, final ClassID n) @@ -134,10 +155,13 @@ public class TypeWriter /** * <p>Writes an array of {@link Property} instances to an output stream * according to the Horrible Property Stream Format.</p> - * + * * @param out The stream to write to * @param properties The array to write to the stream + * @param codepage The codepage number to use for writing strings * @exception IOException if an I/O error occurs + * @throws UnsupportedVariantTypeException if HPSF does not support some + * variant type. */ public static void writeToStream(final OutputStream out, final Property[] properties, @@ -152,7 +176,7 @@ public class TypeWriter * ID and offset into the stream. */ for (int i = 0; i < properties.length; i++) { - final Property p = (Property) properties[i]; + final Property p = properties[i]; writeUIntToStream(out, p.getID()); writeUIntToStream(out, p.getSize()); } @@ -160,7 +184,7 @@ public class TypeWriter /* Write the properties themselves. */ for (int i = 0; i < properties.length; i++) { - final Property p = (Property) properties[i]; + final Property p = properties[i]; long type = p.getType(); writeUIntToStream(out, type); VariantSupport.write(out, (int) type, p.getValue(), codepage); diff --git a/src/java/org/apache/poi/hpsf/UnexpectedPropertySetTypeException.java b/src/java/org/apache/poi/hpsf/UnexpectedPropertySetTypeException.java index 02a47b1986..4f0aa8a9e2 100644 --- a/src/java/org/apache/poi/hpsf/UnexpectedPropertySetTypeException.java +++ b/src/java/org/apache/poi/hpsf/UnexpectedPropertySetTypeException.java @@ -1,4 +1,3 @@ - /* ==================================================================== Copyright 2002-2004 Apache Software Foundation diff --git a/src/java/org/apache/poi/hpsf/UnsupportedVariantTypeException.java b/src/java/org/apache/poi/hpsf/UnsupportedVariantTypeException.java index 8503fdfb03..9cdf1805bd 100644 --- a/src/java/org/apache/poi/hpsf/UnsupportedVariantTypeException.java +++ b/src/java/org/apache/poi/hpsf/UnsupportedVariantTypeException.java @@ -21,7 +21,7 @@ import org.apache.poi.util.HexDump; /** * <p>This exception is thrown if HPSF encounters a variant type that isn't * supported yet. Although a variant type is unsupported the value can still be - * retrieved using the {@link #getValue} method.</p> + * retrieved using the {@link VariantTypeException#getValue} method.</p> * * <p>Obviously this class should disappear some day.</p> * diff --git a/src/java/org/apache/poi/hpsf/Util.java b/src/java/org/apache/poi/hpsf/Util.java index 60e9e9b9d3..d58e254256 100644 --- a/src/java/org/apache/poi/hpsf/Util.java +++ b/src/java/org/apache/poi/hpsf/Util.java @@ -1,6 +1,5 @@ - /* ==================================================================== - Copyright 2002-2004 Apache Software Foundation + Copyright 2002-2006 Apache Software Foundation Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -152,6 +151,21 @@ public class Util public static Date filetimeToDate(final int high, final int low) { final long filetime = ((long) high) << 32 | (low & 0xffffffffL); + return filetimeToDate(filetime); + } + + /** + * <p>Converts a Windows FILETIME into a {@link Date}. The Windows + * FILETIME structure holds a date and time associated with a + * file. The structure identifies a 64-bit integer specifying the + * number of 100-nanosecond intervals which have passed since + * January 1, 1601.</p> + * + * @param filetime The filetime to convert. + * @return The Windows FILETIME as a {@link Date}. + */ + public static Date filetimeToDate(final long filetime) + { final long ms_since_16010101 = filetime / (1000 * 10); final long ms_since_19700101 = ms_since_16010101 - EPOCH_DIFF; return new Date(ms_since_19700101); @@ -165,7 +179,8 @@ public class Util * @param date The date to be converted * @return The filetime * - * @see #filetimeToDate + * @see #filetimeToDate(long) + * @see #filetimeToDate(int, int) */ public static long dateToFileTime(final Date date) { diff --git a/src/java/org/apache/poi/hpsf/Variant.java b/src/java/org/apache/poi/hpsf/Variant.java index 7b300bc0e1..a64b05b661 100644 --- a/src/java/org/apache/poi/hpsf/Variant.java +++ b/src/java/org/apache/poi/hpsf/Variant.java @@ -1,4 +1,3 @@ - /* ==================================================================== Copyright 2002-2004 Apache Software Foundation diff --git a/src/java/org/apache/poi/hpsf/VariantSupport.java b/src/java/org/apache/poi/hpsf/VariantSupport.java index 703f925de1..8994bb2fa1 100644 --- a/src/java/org/apache/poi/hpsf/VariantSupport.java +++ b/src/java/org/apache/poi/hpsf/VariantSupport.java @@ -1,5 +1,5 @@ /* ==================================================================== - Copyright 2002-2004 Apache Software Foundation + Copyright 2002-2006 Apache Software Foundation Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -157,16 +157,25 @@ public class VariantSupport extends Variant * Read a short. In Java it is represented as an * Integer object. */ - value = new Integer(LittleEndian.getUShort(src, o1)); + value = new Integer(LittleEndian.getShort(src, o1)); break; } case Variant.VT_I4: { /* - * Read a word. In Java it is represented as a + * Read a word. In Java it is represented as an + * Integer object. + */ + value = new Integer(LittleEndian.getInt(src, o1)); + break; + } + case Variant.VT_I8: + { + /* + * Read a double word. In Java it is represented as a * Long object. */ - value = new Long(LittleEndian.getUInt(src, o1)); + value = new Long(LittleEndian.getLong(src, o1)); break; } case Variant.VT_R8: @@ -204,9 +213,9 @@ public class VariantSupport extends Variant last--; final int l = (int) (last - first + 1); value = codepage != -1 ? - new String(src, (int) first, l, + new String(src, first, l, codepageToEncoding(codepage)) : - new String(src, (int) first, l); + new String(src, first, l); break; } case Variant.VT_LPWSTR: @@ -240,7 +249,7 @@ public class VariantSupport extends Variant { final byte[] v = new byte[l1]; for (int i = 0; i < l1; i++) - v[i] = src[(int) (o1 + i)]; + v[i] = src[(o1 + i)]; value = v; break; } @@ -263,7 +272,7 @@ public class VariantSupport extends Variant { final byte[] v = new byte[l1]; for (int i = 0; i < l1; i++) - v[i] = src[(int) (o1 + i)]; + v[i] = src[(o1 + i)]; throw new ReadingNotSupportedException(type, v); } } @@ -397,8 +406,8 @@ public class VariantSupport extends Variant char[] s = Util.pad4((String) value); for (int i = 0; i < s.length; i++) { - final int high = (int) ((s[i] & 0x0000ff00) >> 8); - final int low = (int) (s[i] & 0x000000ff); + final int high = ((s[i] & 0x0000ff00) >> 8); + final int low = (s[i] & 0x000000ff); final byte highb = (byte) high; final byte lowb = (byte) low; out.write(lowb); @@ -431,8 +440,21 @@ public class VariantSupport extends Variant } case Variant.VT_I4: { + 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, - ((Long) value).intValue()); + ((Integer) value).intValue()); + break; + } + case Variant.VT_I8: + { + TypeWriter.writeToStream(out, ((Long) value).longValue()); + length = LittleEndianConsts.LONG_SIZE; break; } case Variant.VT_R8: diff --git a/src/java/org/apache/poi/hpsf/WritingNotSupportedException.java b/src/java/org/apache/poi/hpsf/WritingNotSupportedException.java index 64e955a703..f35b5c6070 100644 --- a/src/java/org/apache/poi/hpsf/WritingNotSupportedException.java +++ b/src/java/org/apache/poi/hpsf/WritingNotSupportedException.java @@ -1,4 +1,3 @@ - /* ==================================================================== Copyright 2002-2004 Apache Software Foundation diff --git a/src/java/org/apache/poi/hpsf/wellknown/PropertyIDMap.java b/src/java/org/apache/poi/hpsf/wellknown/PropertyIDMap.java index 77a17b5224..517af3b1ed 100644 --- a/src/java/org/apache/poi/hpsf/wellknown/PropertyIDMap.java +++ b/src/java/org/apache/poi/hpsf/wellknown/PropertyIDMap.java @@ -1,4 +1,3 @@ - /* ==================================================================== Copyright 2002-2004 Apache Software Foundation @@ -231,6 +230,11 @@ public class PropertyIDMap extends HashMap * re-evaluated.</p> */ public static final int PID_LINKSDIRTY = 16; + + /** + * <p>The highest well-known property ID. Applications are free to use higher values for custom purposes.</p> + */ + public static final int PID_MAX = PID_LINKSDIRTY; diff --git a/src/java/org/apache/poi/hpsf/wellknown/SectionIDMap.java b/src/java/org/apache/poi/hpsf/wellknown/SectionIDMap.java index 93e837f2c6..e5e2045896 100644 --- a/src/java/org/apache/poi/hpsf/wellknown/SectionIDMap.java +++ b/src/java/org/apache/poi/hpsf/wellknown/SectionIDMap.java @@ -1,6 +1,5 @@ - /* ==================================================================== - Copyright 2002-2004 Apache Software Foundation + Copyright 2002-2006 Apache Software Foundation Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -53,16 +52,23 @@ public class SectionIDMap extends HashMap }; /** - * <p>The DocumentSummaryInformation's first section's format - * ID. The second section has a different format ID which is not - * well-known.</p> + * <p>The DocumentSummaryInformation's first and second sections' format + * ID.</p> */ - public static final byte[] DOCUMENT_SUMMARY_INFORMATION_ID = new byte[] + public static final byte[][] DOCUMENT_SUMMARY_INFORMATION_ID = new byte[][] { - (byte) 0xD5, (byte) 0xCD, (byte) 0xD5, (byte) 0x02, - (byte) 0x2E, (byte) 0x9C, (byte) 0x10, (byte) 0x1B, - (byte) 0x93, (byte) 0x97, (byte) 0x08, (byte) 0x00, - (byte) 0x2B, (byte) 0x2C, (byte) 0xF9, (byte) 0xAE + { + (byte) 0xD5, (byte) 0xCD, (byte) 0xD5, (byte) 0x02, + (byte) 0x2E, (byte) 0x9C, (byte) 0x10, (byte) 0x1B, + (byte) 0x93, (byte) 0x97, (byte) 0x08, (byte) 0x00, + (byte) 0x2B, (byte) 0x2C, (byte) 0xF9, (byte) 0xAE + }, + { + (byte) 0xD5, (byte) 0xCD, (byte) 0xD5, (byte) 0x05, + (byte) 0x2E, (byte) 0x9C, (byte) 0x10, (byte) 0x1B, + (byte) 0x93, (byte) 0x97, (byte) 0x08, (byte) 0x00, + (byte) 0x2B, (byte) 0x2C, (byte) 0xF9, (byte) 0xAE + } }; /** @@ -91,7 +97,7 @@ public class SectionIDMap extends HashMap final SectionIDMap m = new SectionIDMap(); m.put(SUMMARY_INFORMATION_ID, PropertyIDMap.getSummaryInformationProperties()); - m.put(DOCUMENT_SUMMARY_INFORMATION_ID, + m.put(DOCUMENT_SUMMARY_INFORMATION_ID[0], PropertyIDMap.getDocumentSummaryInformationProperties()); defaultMap = m; } @@ -116,8 +122,7 @@ public class SectionIDMap extends HashMap public static String getPIDString(final byte[] sectionFormatID, final long pid) { - final PropertyIDMap m = - (PropertyIDMap) getInstance().get(sectionFormatID); + final PropertyIDMap m = getInstance().get(sectionFormatID); if (m == null) return UNDEFINED; else @@ -178,7 +183,8 @@ public class SectionIDMap extends HashMap /** * @deprecated Use {@link #put(byte[], PropertyIDMap)} instead! - * @link #put(byte[], PropertyIDMap) + * + * @see #put(byte[], PropertyIDMap) * * @param key This parameter remains undocumented since the method is * deprecated. diff --git a/src/java/org/apache/poi/util/LittleEndian.java b/src/java/org/apache/poi/util/LittleEndian.java index d215469247..f6c95d3d41 100644 --- a/src/java/org/apache/poi/util/LittleEndian.java +++ b/src/java/org/apache/poi/util/LittleEndian.java @@ -1,5 +1,5 @@ /* ==================================================================== - Copyright 2003-2004 Apache Software Foundation + Copyright 2003-2006 Apache Software Foundation Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -64,9 +64,9 @@ public class LittleEndian short num = (short) getNumber(data, offset, SHORT_SIZE); int retNum; if (num < 0) { - retNum = ((int) Short.MAX_VALUE + 1) * 2 + (int) num; + retNum = (Short.MAX_VALUE + 1) * 2 + num; } else { - retNum = (int) num; + retNum = num; } return retNum; } @@ -163,9 +163,9 @@ public class LittleEndian int num = (int) getNumber(data, offset, INT_SIZE); long retNum; if (num < 0) { - retNum = ((long) Integer.MAX_VALUE + 1) * 2 + (long) num; + retNum = ((long) Integer.MAX_VALUE + 1) * 2 + num; } else { - retNum = (int) num; + retNum = num; } return retNum; } @@ -522,7 +522,7 @@ public class LittleEndian *@return Description of the Return Value */ public static int ubyteToInt(byte b) { - return ((b & 0x80) == 0 ? (int) b : (int) (b & (byte) 0x7f) + 0x80); + return ((b & 0x80) == 0 ? (int) b : (b & (byte) 0x7f) + 0x80); } @@ -566,5 +566,22 @@ public class LittleEndian return copy; } + /** + * <p>Gets an unsigned int value (8 bytes) from a byte array.</p> + * + * @param data the byte array + * @param offset a starting offset into the byte array + * @return the unsigned int (32-bit) value in a long + */ + public static long getULong(final byte[] data, final int offset) + { + int num = (int) getNumber(data, offset, LONG_SIZE); + long retNum; + if (num < 0) + retNum = ((long) Integer.MAX_VALUE + 1) * 2 + num; + else + retNum = num; + return retNum; + } } |