HPSF writing capability added.
git-svn-id: https://svn.apache.org/repos/asf/jakarta/poi/trunk@353321 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
aeaa6a0856
commit
f85ef3c94e
@ -61,7 +61,8 @@ package org.apache.poi.hpsf;
|
||||
* order. Instead, it is a double word (4 bytes) followed by two
|
||||
* words (2 bytes each) followed by 8 bytes.</p>
|
||||
*
|
||||
* @author Rainer Klute (klute@rainer-klute.de)
|
||||
* @author Rainer Klute <a
|
||||
* href="mailto:klute@rainer-klute.de"><klute@rainer-klute.de></a>
|
||||
* @version $Id$
|
||||
* @since 2002-02-09
|
||||
*/
|
||||
@ -228,4 +229,14 @@ public class ClassID
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @see Object#hashCode()
|
||||
*/
|
||||
public int hashCode()
|
||||
{
|
||||
throw new UnsupportedOperationException("FIXME: Not yet implemented.");
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -60,7 +60,8 @@ import org.apache.poi.hpsf.wellknown.PropertyIDMap;
|
||||
* <p>Convenience class representing a DocumentSummary Information stream in a
|
||||
* Microsoft Office document.</p>
|
||||
*
|
||||
* @author Rainer Klute (klute@rainer-klute.de)
|
||||
* @author Rainer Klute <a
|
||||
* href="mailto:klute@rainer-klute.de"><klute@rainer-klute.de></a>
|
||||
* @author Drew Varner (Drew.Varner closeTo sc.edu)
|
||||
* @see SummaryInformation
|
||||
* @version $Id$
|
||||
@ -289,7 +290,7 @@ public class DocumentSummaryInformation extends SpecialPropertySet
|
||||
* <p>Returns <code>true</code> if the custom links are hampered
|
||||
* by excessive noise, for all applications.</p> <p>
|
||||
*
|
||||
* <strong>FIXME:</strong> Explain this some more! I (Rainer)
|
||||
* <strong>FIXME (3):</strong> Explain this some more! I (Rainer)
|
||||
* don't understand it.</p>
|
||||
*
|
||||
* @return The linksDirty value
|
||||
|
@ -59,7 +59,8 @@ package org.apache.poi.hpsf;
|
||||
* 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 (klute@rainer-klute.de)
|
||||
* @author Rainer Klute <a
|
||||
* href="mailto:klute@rainer-klute.de"><klute@rainer-klute.de></a>
|
||||
* @version $Id$
|
||||
* @since 2002-02-09
|
||||
*/
|
||||
|
@ -54,12 +54,16 @@
|
||||
*/
|
||||
package org.apache.poi.hpsf;
|
||||
|
||||
import java.io.PrintStream;
|
||||
import java.io.PrintWriter;
|
||||
|
||||
/**
|
||||
* <p>This exception is the superclass of all other unchecked
|
||||
* 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 (klute@rainer-klute.de)
|
||||
* @author Rainer Klute <a
|
||||
* href="mailto:klute@rainer-klute.de"><klute@rainer-klute.de></a>
|
||||
* @version $Id$
|
||||
* @since 2002-02-09
|
||||
*/
|
||||
@ -138,4 +142,46 @@ public class HPSFRuntimeException extends RuntimeException
|
||||
return reason;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @see Throwable#printStackTrace()
|
||||
*/
|
||||
public void printStackTrace()
|
||||
{
|
||||
printStackTrace(System.err);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @see Throwable#printStackTrace(java.io.PrintStream)
|
||||
*/
|
||||
public void printStackTrace(final PrintStream p)
|
||||
{
|
||||
final Throwable reason = getReason();
|
||||
super.printStackTrace(p);
|
||||
if (reason != null)
|
||||
{
|
||||
p.println("Caused by:");
|
||||
reason.printStackTrace(p);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @see Throwable#printStackTrace(java.io.PrintWriter)
|
||||
*/
|
||||
public void printStackTrace(final PrintWriter p)
|
||||
{
|
||||
final Throwable reason = getReason();
|
||||
super.printStackTrace(p);
|
||||
if (reason != null)
|
||||
{
|
||||
p.println("Caused by:");
|
||||
reason.printStackTrace(p);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -58,7 +58,8 @@ package org.apache.poi.hpsf;
|
||||
* <p>This exception is thrown if an {@link java.io.InputStream} does
|
||||
* not support the {@link java.io.InputStream#mark} operation.</p>
|
||||
*
|
||||
* @author Rainer Klute (klute@rainer-klute.de)
|
||||
* @author Rainer Klute <a
|
||||
* href="mailto:klute@rainer-klute.de"><klute@rainer-klute.de></a>
|
||||
* @version $Id$
|
||||
* @since 2002-02-09
|
||||
*/
|
||||
|
85
src/java/org/apache/poi/hpsf/MutableProperty.java
Normal file
85
src/java/org/apache/poi/hpsf/MutableProperty.java
Normal file
@ -0,0 +1,85 @@
|
||||
package org.apache.poi.hpsf;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
/**
|
||||
* <p>Adds writing capability to the {@link Property} class.</p>
|
||||
*
|
||||
* <p>Please be aware that this class' functionality will be merged into the
|
||||
* {@link Property} class at a later time, so the API will change.</p>
|
||||
*
|
||||
* @author Rainer Klute <a
|
||||
* href="mailto:klute@rainer-klute.de"><klute@rainer-klute.de></a>
|
||||
* @since 2003-08-03
|
||||
* @version $Id$
|
||||
*/
|
||||
public class MutableProperty extends Property
|
||||
{
|
||||
|
||||
/**
|
||||
* <p>Creates an empty property. It must be filled using the set method to
|
||||
* be usable.</p>
|
||||
*/
|
||||
public MutableProperty()
|
||||
{ }
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Sets the property's ID.</p>
|
||||
*
|
||||
* @param id the ID
|
||||
*/
|
||||
public void setID(final long id)
|
||||
{
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Sets the property's type.</p>
|
||||
*
|
||||
* @param type the property's type
|
||||
*/
|
||||
public void setType(final long type)
|
||||
{
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Sets the property's value.</p>
|
||||
*
|
||||
* @param value the property's value
|
||||
*/
|
||||
public void setValue(final Object value)
|
||||
{
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Writes the property to an output stream.</p>
|
||||
*
|
||||
* @param out The output stream to write to.
|
||||
* @return the number of bytes written to the stream
|
||||
*
|
||||
* @exception IOException if an I/O error occurs
|
||||
* @exception WritingNotSupportedException if a variant type is to be
|
||||
* written that is not yet supported
|
||||
*/
|
||||
public int write(final OutputStream out)
|
||||
throws IOException, WritingNotSupportedException
|
||||
{
|
||||
int length = 0;
|
||||
long variantType = getType();
|
||||
length += TypeWriter.writeUIntToStream(out, variantType);
|
||||
length += VariantSupport.write(out, variantType, getValue());
|
||||
return length;
|
||||
}
|
||||
|
||||
}
|
243
src/java/org/apache/poi/hpsf/MutablePropertySet.java
Normal file
243
src/java/org/apache/poi/hpsf/MutablePropertySet.java
Normal file
@ -0,0 +1,243 @@
|
||||
/*
|
||||
* ====================================================================
|
||||
* The Apache Software License, Version 1.1
|
||||
*
|
||||
* Copyright (c) 2003 The Apache Software Foundation. All rights
|
||||
* reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* 3. The end-user documentation included with the redistribution,
|
||||
* if any, must include the following acknowledgment:
|
||||
* "This product includes software developed by the
|
||||
* Apache Software Foundation (http://www.apache.org/)."
|
||||
* Alternately, this acknowledgment may appear in the software itself,
|
||||
* if and wherever such third-party acknowledgments normally appear.
|
||||
*
|
||||
* 4. The names "Apache" and "Apache Software Foundation" must
|
||||
* not be used to endorse or promote products derived from this
|
||||
* software without prior written permission. For written
|
||||
* permission, please contact apache@apache.org.
|
||||
*
|
||||
* 5. Products derived from this software may not be called "Apache",
|
||||
* nor may "Apache" appear in their name, without prior written
|
||||
* permission of the Apache Software Foundation.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
|
||||
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
|
||||
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
||||
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
* ====================================================================
|
||||
*
|
||||
* This software consists of voluntary contributions made by many
|
||||
* individuals on behalf of the Apache Software Foundation. For more
|
||||
* information on the Apache Software Foundation, please see
|
||||
* <http://www.apache.org/>.
|
||||
*/
|
||||
package org.apache.poi.hpsf;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.LinkedList;
|
||||
import java.util.ListIterator;
|
||||
import org.apache.poi.util.LittleEndian;
|
||||
import org.apache.poi.util.LittleEndianConsts;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Adds writing support to the {@link PropertySet} class.</p>
|
||||
*
|
||||
* <p>Please be aware that this class' functionality will be merged into the
|
||||
* {@link PropertySet} class at a later time, so the API will change.</p>
|
||||
*
|
||||
* @author Rainer Klute <a
|
||||
* href="mailto:klute@rainer-klute.de"><klute@rainer-klute.de></a>
|
||||
* @version $Id$
|
||||
* @since 2003-02-19
|
||||
*/
|
||||
public class MutablePropertySet extends PropertySet
|
||||
{
|
||||
|
||||
/**
|
||||
* <p>Constructs a <code>MutablePropertySet</code> instance. Its
|
||||
* primary task is to initialize the immutable field with their proper
|
||||
* values. It also sets fields that might change to reasonable defaults.</p>
|
||||
*/
|
||||
public MutablePropertySet()
|
||||
{
|
||||
/* Initialize the "byteOrder" field. */
|
||||
byteOrder = LittleEndian.getUShort(BYTE_ORDER_ASSERTION);
|
||||
|
||||
/* Initialize the "format" field. */
|
||||
format = LittleEndian.getUShort(FORMAT_ASSERTION);
|
||||
|
||||
/* Initialize "osVersion" field as if the property has been created on
|
||||
* a Win32 platform, whether this is the case or not. */
|
||||
osVersion = (OS_WIN32 << 16) | 0x0A04;
|
||||
|
||||
/* Initailize the "classID" field. */
|
||||
classID = new ClassID();
|
||||
|
||||
/* Initialize the sections. Since property set must have at least
|
||||
* one section it is added right here. */
|
||||
sections = new LinkedList();
|
||||
sections.add(new MutableSection());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>The length of the property set stream header.</p>
|
||||
*/
|
||||
private final int OFFSET_HEADER =
|
||||
BYTE_ORDER_ASSERTION.length + /* Byte order */
|
||||
FORMAT_ASSERTION.length + /* Format */
|
||||
LittleEndianConsts.INT_SIZE + /* OS version */
|
||||
ClassID.LENGTH + /* Class ID */
|
||||
LittleEndianConsts.INT_SIZE; /* Section count */
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Sets the "byteOrder" property.</p>
|
||||
*
|
||||
* @param byteOrder the byteOrder value to set
|
||||
*/
|
||||
public void setByteOrder(final int byteOrder)
|
||||
{
|
||||
this.byteOrder = byteOrder;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Sets the "format" property.</p>
|
||||
*
|
||||
* @param format the format value to set
|
||||
*/
|
||||
public void setFormat(final int format)
|
||||
{
|
||||
this.format = format;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Sets the "osVersion" property.</p>
|
||||
*
|
||||
* @param osVersion the osVersion value to set
|
||||
*/
|
||||
public void setOSVersion(final int osVersion)
|
||||
{
|
||||
this.osVersion = osVersion;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Sets the property set stream's low-level "class ID"
|
||||
* field.</p>
|
||||
*
|
||||
* @param classID The property set stream's low-level "class ID" field.
|
||||
*
|
||||
* @see #getClassID
|
||||
*/
|
||||
public void setClassID(final ClassID classID)
|
||||
{
|
||||
this.classID = classID;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Removes all sections from this property set.</p>
|
||||
*/
|
||||
public void clearSections()
|
||||
{
|
||||
sections = null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Adds a section to this property set.</p>
|
||||
*
|
||||
* @param section The {@link Section} to add. It will be appended
|
||||
* after any sections that are already present in the property set
|
||||
* and thus become the last section.
|
||||
*/
|
||||
public void addSection(final Section section)
|
||||
{
|
||||
if (sections == null)
|
||||
sections = new LinkedList();
|
||||
sections.add(section);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Writes the property set to an output stream.</p>
|
||||
*
|
||||
* @param out the output stream to write the section to
|
||||
* @exception IOException if an error when writing to the output stream
|
||||
* occurs
|
||||
* @exception WritingNotSupportedException if HPSF does not yet support
|
||||
* writing a property's variant type.
|
||||
*/
|
||||
public void write(final OutputStream out)
|
||||
throws WritingNotSupportedException, IOException
|
||||
{
|
||||
/* Write the number of sections in this property set stream. */
|
||||
final int nrSections = sections.size();
|
||||
int length = 0;
|
||||
|
||||
/* 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, getClassID());
|
||||
length += TypeWriter.writeToStream(out, (int) nrSections);
|
||||
int offset = OFFSET_HEADER;
|
||||
|
||||
/* Write the section list, i.e. the references to the sections. Each
|
||||
* entry in the section list consist of a class ID and the offset to the
|
||||
* section's begin. */
|
||||
offset += nrSections * (ClassID.LENGTH + LittleEndian.INT_SIZE);
|
||||
final int sectionsBegin = offset;
|
||||
for (final ListIterator i = sections.listIterator(); i.hasNext();)
|
||||
{
|
||||
final MutableSection s = (MutableSection) i.next();
|
||||
length += TypeWriter.writeToStream(out, s.getFormatID());
|
||||
length += TypeWriter.writeUIntToStream(out, offset);
|
||||
offset += s.getSize();
|
||||
}
|
||||
|
||||
/* Write the sections themselves. */
|
||||
offset = sectionsBegin;
|
||||
for (final ListIterator i = sections.listIterator(); i.hasNext();)
|
||||
{
|
||||
final MutableSection s = (MutableSection) i.next();
|
||||
offset = s.write(out, offset);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
426
src/java/org/apache/poi/hpsf/MutableSection.java
Normal file
426
src/java/org/apache/poi/hpsf/MutableSection.java
Normal file
@ -0,0 +1,426 @@
|
||||
/*
|
||||
* ====================================================================
|
||||
* The Apache Software License, Version 1.1
|
||||
*
|
||||
* Copyright (c) 2000 The Apache Software Foundation. All rights
|
||||
* reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* 3. The end-user documentation included with the redistribution,
|
||||
* if any, must include the following acknowledgment:
|
||||
* "This product includes software developed by the
|
||||
* Apache Software Foundation (http://www.apache.org/)."
|
||||
* Alternately, this acknowledgment may appear in the software itself,
|
||||
* if and wherever such third-party acknowledgments normally appear.
|
||||
*
|
||||
* 4. The names "Apache" and "Apache Software Foundation" must
|
||||
* not be used to endorse or promote products derived from this
|
||||
* software without prior written permission. For written
|
||||
* permission, please contact apache@apache.org.
|
||||
*
|
||||
* 5. Products derived from this software may not be called "Apache",
|
||||
* nor may "Apache" appear in their name, without prior written
|
||||
* permission of the Apache Software Foundation.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
|
||||
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
|
||||
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
||||
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
* ====================================================================
|
||||
*
|
||||
* This software consists of voluntary contributions made by many
|
||||
* individuals on behalf of the Apache Software Foundation. For more
|
||||
* information on the Apache Software Foundation, please see
|
||||
* <http://www.apache.org/>.
|
||||
*/
|
||||
package org.apache.poi.hpsf;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.poi.util.LittleEndian;
|
||||
import org.apache.poi.util.LittleEndianConsts;
|
||||
|
||||
/**
|
||||
* <p>Adds writing capability to the {@link Section} class.</p>
|
||||
*
|
||||
* <p>Please be aware that this class' functionality will be merged into the
|
||||
* {@link Section} class at a later time, so the API will change.</p>
|
||||
*
|
||||
* @author Rainer Klute <a
|
||||
* href="mailto:klute@rainer-klute.de"><klute@rainer-klute.de></a>
|
||||
* @version $Id$
|
||||
* @since 2002-02-20
|
||||
*/
|
||||
public class MutableSection extends Section
|
||||
{
|
||||
|
||||
/**
|
||||
* <p>If the "dirty" flag is true, the section's size must be
|
||||
* (re-)calculated before the section is written.</p>
|
||||
*/
|
||||
private boolean dirty = true;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>List to assemble the properties. Unfortunately a wrong
|
||||
* decision has been taken when specifying the "properties" field
|
||||
* as an Property[]. It should have been a {@link java.util.List}.</p>
|
||||
*/
|
||||
private List preprops;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Creates an empty mutable section.</p>
|
||||
*/
|
||||
public MutableSection()
|
||||
{
|
||||
dirty = true;
|
||||
formatID = null;
|
||||
offset = -1;
|
||||
preprops = new LinkedList();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Sets the section's format ID.</p>
|
||||
*
|
||||
* @param formatID The section's format ID
|
||||
*
|
||||
* @see #setFormatID(byte[])
|
||||
* @see #getFormatID
|
||||
*/
|
||||
public void setFormatID(final ClassID formatID)
|
||||
{
|
||||
this.formatID = formatID;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Sets the section's format ID.</p>
|
||||
*
|
||||
* @param formatID The section's format ID as a byte array. It components
|
||||
* are in big-endian format.
|
||||
*
|
||||
* @see #setFormatID(ClassID)
|
||||
* @see #getFormatID
|
||||
*/
|
||||
public void setFormatID(final byte[] formatID)
|
||||
{
|
||||
setFormatID(new ClassID(formatID, 0));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Sets this section's properties. Any former values are overwritten.</p>
|
||||
*
|
||||
* @param properties This section's new properties.
|
||||
*/
|
||||
public void setProperties(final Property[] properties)
|
||||
{
|
||||
preprops = new LinkedList();
|
||||
for (int i = 0; i < properties.length; i++)
|
||||
preprops.add(properties[i]);
|
||||
dirty = true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <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>
|
||||
*
|
||||
* @param id The property's ID
|
||||
* @param value The property's value. It will be written as a Unicode
|
||||
* string.
|
||||
*
|
||||
* @see #setProperty(int, int, Object)
|
||||
* @see #getProperty
|
||||
*/
|
||||
public void setProperty(final int id, final String value)
|
||||
{
|
||||
setProperty(id, Variant.VT_LPWSTR, value);
|
||||
dirty = true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Sets the value and the variant type of the property with the
|
||||
* specified ID. If a property with this ID is not yet present in
|
||||
* the section, it will be added. An already present property with
|
||||
* the specified ID will be overwritten. A default mapping will be
|
||||
* used to choose the property's type.</p>
|
||||
*
|
||||
* @param id The property's ID.
|
||||
* @param variantType The property's variant type.
|
||||
* @param value The property's value.
|
||||
*
|
||||
* @see #setProperty(int, Object)
|
||||
* @see #getProperty
|
||||
* @see Variant
|
||||
*/
|
||||
public void setProperty(final int id, final long variantType,
|
||||
final Object value)
|
||||
{
|
||||
final MutableProperty p = new MutableProperty();
|
||||
p.setID(id);
|
||||
p.setType(variantType);
|
||||
p.setValue(value);
|
||||
setProperty(p);
|
||||
dirty = true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Sets a property. 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>
|
||||
*
|
||||
* @param p The property to be added to the section
|
||||
*
|
||||
* @see #setProperty(int, int, Object)
|
||||
* @see #setProperty(int, String)
|
||||
* @see #getProperty
|
||||
* @see Variant
|
||||
*/
|
||||
public void setProperty(final Property p)
|
||||
{
|
||||
final long id = p.getID();
|
||||
for (final Iterator i = preprops.iterator(); i.hasNext();)
|
||||
if (((Property) i.next()).getID() == id)
|
||||
{
|
||||
i.remove();
|
||||
break;
|
||||
}
|
||||
preprops.add(p);
|
||||
dirty = true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Sets the value of the boolean property with the specified
|
||||
* ID.</p>
|
||||
*
|
||||
* @param id The property's ID
|
||||
* @param value The property's value
|
||||
*
|
||||
* @see #setProperty(int, int, Object)
|
||||
* @see #getProperty
|
||||
* @see Variant
|
||||
*/
|
||||
protected void setPropertyBooleanValue(final int id, final boolean value)
|
||||
{
|
||||
setProperty(id, (long) Variant.VT_BOOL, new Boolean(value));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Returns the section's size.</p>
|
||||
*
|
||||
* @return the section's size.
|
||||
*/
|
||||
public int getSize()
|
||||
{
|
||||
if (dirty)
|
||||
{
|
||||
size = calcSize();
|
||||
dirty = false;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Calculates the section's size. It is the sum of the lengths of the
|
||||
* section's header (8), the properties list (16 times the number of
|
||||
* properties) and the properties themselves.</p>
|
||||
*
|
||||
* @return the section's length in bytes.
|
||||
*/
|
||||
private int calcSize()
|
||||
{
|
||||
int length = 0;
|
||||
|
||||
/* The section header. */
|
||||
length += LittleEndianConsts.INT_SIZE * 2;
|
||||
|
||||
/* The length of the property list. */
|
||||
Property[] psa = getProperties();
|
||||
if (psa == null)
|
||||
psa = new MutableProperty[0];
|
||||
length += psa.length * LittleEndianConsts.INT_SIZE * 3;
|
||||
|
||||
/* The sum of the lengths of the properties - it is calculated by simply
|
||||
* writing the properties to a temporary byte array output stream: */
|
||||
final ByteArrayOutputStream b = new ByteArrayOutputStream();
|
||||
for (int i = 0; i < psa.length; i++)
|
||||
{
|
||||
final MutableProperty mp = new MutableProperty();
|
||||
mp.setID(psa[i].getID());
|
||||
mp.setType(psa[i].getType());
|
||||
mp.setValue(psa[i].getValue());
|
||||
try
|
||||
{
|
||||
length += mp.write(b);
|
||||
}
|
||||
catch (WritingNotSupportedException ex)
|
||||
{
|
||||
/* It was not possible to write the property, not even as a
|
||||
* byte array. We cannot do anything about that. Instead of the
|
||||
* property we insert an empty one into the stream. */
|
||||
mp.setType(Variant.VT_EMPTY);
|
||||
mp.setValue(null);
|
||||
try
|
||||
{
|
||||
length += mp.write(b);
|
||||
}
|
||||
catch (Exception ex2)
|
||||
{
|
||||
/* Even writing an empty property went awfully wrong.
|
||||
* Let's give up. */
|
||||
throw new HPSFRuntimeException(ex2);
|
||||
}
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
/* Should never occur. */
|
||||
throw new HPSFRuntimeException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Writes this section into an output stream.</p>
|
||||
*
|
||||
* <p>Internally this is done by writing into three byte array output
|
||||
* streams: one for the properties, one for the property list and one for
|
||||
* the section as such. The two former are appended to the latter when they
|
||||
* have received all their data.</p>
|
||||
*
|
||||
* @param out The stream to write into
|
||||
* @param offset The offset from the beginning of the property set
|
||||
* stream this section begins at
|
||||
*
|
||||
* @return The offset of the first byte following this section in
|
||||
* the property set stream.
|
||||
* @exception IOException if an I/O error occurs
|
||||
* @exception WritingNotSupportedException if HPSF does not yet support
|
||||
* writing a property's variant type.
|
||||
*/
|
||||
public int write(final OutputStream out, final int offset)
|
||||
throws WritingNotSupportedException, IOException
|
||||
{
|
||||
/* The properties are written to this stream. */
|
||||
final ByteArrayOutputStream propertyStream =
|
||||
new ByteArrayOutputStream();
|
||||
|
||||
/* The property list is established here. After each property that has
|
||||
* been written to "propertyStream", a property list entry is written to
|
||||
* "propertyListStream". */
|
||||
final ByteArrayOutputStream propertyListStream =
|
||||
new ByteArrayOutputStream();
|
||||
|
||||
/* Maintain the current position in the list. */
|
||||
int position = 0;
|
||||
|
||||
/* Increase the position variable by the size of the property list so
|
||||
* that it points to the beginning of the properties themselves. */
|
||||
position += 2 * LittleEndian.INT_SIZE +
|
||||
getPropertyCount() * 2 * LittleEndian.INT_SIZE;
|
||||
|
||||
/* Write the properties and the property list into their respective
|
||||
* streams: */
|
||||
for (final Iterator i = preprops.iterator(); i.hasNext();)
|
||||
{
|
||||
final MutableProperty p = (MutableProperty) i.next();
|
||||
|
||||
/* Write the property list entry. */
|
||||
TypeWriter.writeUIntToStream(propertyListStream, p.getID());
|
||||
TypeWriter.writeUIntToStream(propertyListStream, position);
|
||||
|
||||
/* Write the property and update the position to the next
|
||||
* property. */
|
||||
position += p.write(propertyStream);
|
||||
}
|
||||
propertyStream.close();
|
||||
propertyListStream.close();
|
||||
|
||||
/* Write the section: */
|
||||
byte[] pb1 = propertyListStream.toByteArray();
|
||||
byte[] pb2 = propertyStream.toByteArray();
|
||||
TypeWriter.writeToStream(out, LittleEndian.INT_SIZE * 2 + pb1.length +
|
||||
pb2.length);
|
||||
TypeWriter.writeToStream(out, getPropertyCount());
|
||||
out.write(pb1);
|
||||
out.write(pb2);
|
||||
|
||||
return offset + position;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Overwrites the super class' method to cope with a redundancy:
|
||||
* the property count is maintained in a separate member variable, but
|
||||
* shouldn't.</p>
|
||||
*
|
||||
* @return The number of properties in this section
|
||||
*/
|
||||
public int getPropertyCount()
|
||||
{
|
||||
return preprops.size();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Returns this section's properties.</p>
|
||||
*
|
||||
* @return this section's properties.
|
||||
*/
|
||||
public Property[] getProperties()
|
||||
{
|
||||
return (Property[]) preprops.toArray(new Property[0]);
|
||||
}
|
||||
|
||||
}
|
@ -55,18 +55,16 @@
|
||||
package org.apache.poi.hpsf;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* <p>This exception is thrown if a format error in a property set stream is
|
||||
* detected or when the input data do not constitute a property set stream.</p>
|
||||
*
|
||||
* This exception is thrown if a format error in a property set stream is
|
||||
* detected or when the input data do not constitute a property set stream.</p>
|
||||
* <p>
|
||||
* <p>The constructors of this class are analogous to those of its superclass
|
||||
* and are documented there.</p>
|
||||
*
|
||||
* The constructors of this class are analogous to those of its superclass and
|
||||
* documented there.</p>
|
||||
*
|
||||
*@author Rainer Klute (klute@rainer-klute.de)
|
||||
*@version $Id$
|
||||
*@since 2002-02-09
|
||||
* @author Rainer Klute <a
|
||||
* href="mailto:klute@rainer-klute.de"><klute@rainer-klute.de></a>
|
||||
* @version $Id$
|
||||
* @since 2002-02-09
|
||||
*/
|
||||
public class NoPropertySetStreamException extends HPSFException
|
||||
{
|
||||
@ -80,6 +78,7 @@ public class NoPropertySetStreamException extends HPSFException
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Constructor</p>
|
||||
*
|
||||
@ -91,6 +90,7 @@ public class NoPropertySetStreamException extends HPSFException
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Constructor</p>
|
||||
*
|
||||
@ -102,6 +102,7 @@ public class NoPropertySetStreamException extends HPSFException
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Constructor</p>
|
||||
*
|
||||
|
@ -63,7 +63,8 @@ package org.apache.poi.hpsf;
|
||||
* <p>The constructors of this class are analogous to those of its
|
||||
* superclass and documented there.</p>
|
||||
*
|
||||
* @author Rainer Klute (klute@rainer-klute.de)
|
||||
* @author Rainer Klute <a
|
||||
* href="mailto:klute@rainer-klute.de"><klute@rainer-klute.de></a>
|
||||
* @version $Id$
|
||||
* @since 2002-02-09
|
||||
*/
|
||||
|
@ -63,8 +63,6 @@
|
||||
package org.apache.poi.hpsf;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.poi.util.LittleEndian;
|
||||
@ -85,11 +83,13 @@ import org.apache.poi.util.LittleEndian;
|
||||
* value, {@link Variant#VT_FILETIME} some date and time (of a
|
||||
* file).</p>
|
||||
*
|
||||
* <p><strong>FIXME:</strong> Reading is not implemented for all
|
||||
* {@link Variant} types yet. Feel free to submit error reports or
|
||||
* patches for the types you need.</p>
|
||||
* <p>Please note that not all {@link Variant} types yet. This might change
|
||||
* over time but largely depends on your feedback so that the POI team knows
|
||||
* which variant types are really needed. So please feel free to submit error
|
||||
* reports or patches for the types you need.</p>
|
||||
*
|
||||
* @author Rainer Klute (klute@rainer-klute.de)
|
||||
* @author Rainer Klute <a
|
||||
* href="mailto:klute@rainer-klute.de"><klute@rainer-klute.de></a>
|
||||
* @author Drew Varner (Drew.Varner InAndAround sc.edu)
|
||||
* @see Section
|
||||
* @see Variant
|
||||
@ -103,7 +103,7 @@ public class Property
|
||||
private static final int CP_UNICODE = 1200;
|
||||
|
||||
/** <p>The property's ID.</p> */
|
||||
protected int id;
|
||||
protected long id;
|
||||
|
||||
|
||||
/**
|
||||
@ -111,7 +111,7 @@ public class Property
|
||||
*
|
||||
* @return The ID value
|
||||
*/
|
||||
public int getID()
|
||||
public long getID()
|
||||
{
|
||||
return id;
|
||||
}
|
||||
@ -162,7 +162,7 @@ public class Property
|
||||
* @param codepage The section's and thus the property's
|
||||
* codepage. It is needed only when reading string values.
|
||||
*/
|
||||
public Property(final int id, final byte[] src, final long offset,
|
||||
public Property(final long id, final byte[] src, final long offset,
|
||||
final int length, final int codepage)
|
||||
{
|
||||
this.id = id;
|
||||
@ -187,7 +187,7 @@ public class Property
|
||||
}
|
||||
catch (UnsupportedVariantTypeException ex)
|
||||
{
|
||||
logUnsupported(ex);
|
||||
VariantSupport.writeUnsupportedTypeMessage(ex);
|
||||
value = ex.getValue();
|
||||
}
|
||||
}
|
||||
@ -281,15 +281,21 @@ public class Property
|
||||
* 4.</p>
|
||||
*
|
||||
* @return the property's size in bytes
|
||||
*
|
||||
* @exception WritingNotSupportedException if HPSF does not yet support the
|
||||
* property's variant type.
|
||||
*/
|
||||
protected int getSize()
|
||||
protected int getSize() throws WritingNotSupportedException
|
||||
{
|
||||
int length = LittleEndian.INT_SIZE;
|
||||
int length = VariantSupport.getVariantLength(type);
|
||||
if (length >= 0)
|
||||
return length; /* Fixed length */
|
||||
if (length == -2)
|
||||
/* Unknown length */
|
||||
throw new WritingNotSupportedException(type, null);
|
||||
|
||||
/* Variable length: */
|
||||
final int PADDING = 4; /* Pad to multiples of 4. */
|
||||
if (type > Integer.MAX_VALUE)
|
||||
throw new HPSFRuntimeException
|
||||
("Variant type " + type + " is greater than " +
|
||||
Integer.MAX_VALUE + ".");
|
||||
switch ((int) type)
|
||||
{
|
||||
case Variant.VT_LPSTR:
|
||||
@ -304,9 +310,7 @@ public class Property
|
||||
case Variant.VT_EMPTY:
|
||||
break;
|
||||
default:
|
||||
throw new HPSFRuntimeException
|
||||
("Writing is not yet implemented for variant type " +
|
||||
type + ". Please report this problem to the POI team!");
|
||||
throw new WritingNotSupportedException(type, value);
|
||||
}
|
||||
return length;
|
||||
}
|
||||
@ -318,27 +322,65 @@ public class Property
|
||||
*/
|
||||
public boolean equals(final Object o)
|
||||
{
|
||||
throw new UnsupportedOperationException("FIXME: Not yet implemented.");
|
||||
if (!(o instanceof Property))
|
||||
return false;
|
||||
final Property p = (Property) o;
|
||||
final Object pValue = p.getValue();
|
||||
if (id != p.getID() || type != p.getType())
|
||||
return false;
|
||||
if (value == null && pValue == null)
|
||||
return true;
|
||||
if (value == null || pValue == null)
|
||||
return false;
|
||||
|
||||
/* It's clear now that both values are non-null. */
|
||||
final Class valueClass = value.getClass();
|
||||
final Class pValueClass = pValue.getClass();
|
||||
if (!(valueClass.isAssignableFrom(pValueClass)) &&
|
||||
!(pValueClass.isAssignableFrom(valueClass)))
|
||||
return false;
|
||||
|
||||
if (value instanceof byte[])
|
||||
return Util.equal((byte[]) value, (byte[]) pValue);
|
||||
|
||||
return value.equals(pValue);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Keeps a list of those variant types for those an "unsupported" message
|
||||
* has already been issued.</p>
|
||||
* @see Object#hashCode()
|
||||
*/
|
||||
protected static List unsupportedMessage;
|
||||
|
||||
private static void logUnsupported(final UnsupportedVariantTypeException ex)
|
||||
public int hashCode()
|
||||
{
|
||||
if (unsupportedMessage == null)
|
||||
unsupportedMessage = new LinkedList();
|
||||
Long vt = new Long(ex.getVariantType());
|
||||
if (!unsupportedMessage.contains(vt))
|
||||
{
|
||||
System.err.println(ex.getMessage());
|
||||
unsupportedMessage.add(vt);
|
||||
}
|
||||
long hashCode = 0;
|
||||
hashCode += id;
|
||||
hashCode += type;
|
||||
if (value != null)
|
||||
hashCode += value.hashCode();
|
||||
final int returnHashCode = (int) (hashCode & 0x0ffffffffL );
|
||||
return returnHashCode;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @see Object#toString()
|
||||
*/
|
||||
public String toString()
|
||||
{
|
||||
final StringBuffer b = new StringBuffer();
|
||||
b.append(getClass().getName());
|
||||
b.append('[');
|
||||
b.append("id: ");
|
||||
b.append(getID());
|
||||
b.append(", type: ");
|
||||
b.append(getType());
|
||||
b.append(", value: ");
|
||||
b.append(getValue());
|
||||
b.append(']');
|
||||
return b.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -57,7 +57,6 @@ package org.apache.poi.hpsf;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.poi.hpsf.wellknown.SectionIDMap;
|
||||
@ -91,7 +90,8 @@ import org.apache.poi.util.LittleEndian;
|
||||
* NoSingleSectionException} if the {@link PropertySet} contains more
|
||||
* (or less) than exactly one {@link Section}).</p>
|
||||
*
|
||||
* @author Rainer Klute (klute@rainer-klute.de)
|
||||
* @author Rainer Klute <a
|
||||
* href="mailto:klute@rainer-klute.de"><klute@rainer-klute.de></a>
|
||||
* @author Drew Varner (Drew.Varner hanginIn sc.edu)
|
||||
* @version $Id$
|
||||
* @since 2002-02-09
|
||||
@ -397,7 +397,7 @@ public class PropertySet
|
||||
final int offset,
|
||||
final int length)
|
||||
{
|
||||
/* FIXME: Ensure that at most "length" bytes are read. */
|
||||
/* FIXME (3): Ensure that at most "length" bytes are read. */
|
||||
|
||||
/*
|
||||
* Read the header fields of the stream. They must always be
|
||||
@ -442,7 +442,7 @@ public class PropertySet
|
||||
*/
|
||||
private void init(final byte[] src, final int offset, final int length)
|
||||
{
|
||||
/* FIXME: Ensure that at most "length" bytes are read. */
|
||||
/* FIXME (3): Ensure that at most "length" bytes are read. */
|
||||
|
||||
/*
|
||||
* Read the stream's header fields.
|
||||
@ -645,6 +645,9 @@ public class PropertySet
|
||||
* to the specified parameter, else <code>false</code>.</p>
|
||||
*
|
||||
* @param o the object to compare this <code>PropertySet</code> with
|
||||
*
|
||||
* @return <code>true</code> if the objects are equal, <code>false</code>
|
||||
* if not
|
||||
*/
|
||||
public boolean equals(final Object o)
|
||||
{
|
||||
@ -672,4 +675,43 @@ public class PropertySet
|
||||
return Util.equals(getSections(), ps.getSections());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @see Object#hashCode()
|
||||
*/
|
||||
public int hashCode()
|
||||
{
|
||||
throw new UnsupportedOperationException("FIXME: Not yet implemented.");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @see Object#toString()
|
||||
*/
|
||||
public String toString()
|
||||
{
|
||||
final StringBuffer b = new StringBuffer();
|
||||
final int sectionCount = getSectionCount();
|
||||
b.append(getClass().getName());
|
||||
b.append('[');
|
||||
b.append("byteOrder: ");
|
||||
b.append(getByteOrder());
|
||||
b.append(", classID: ");
|
||||
b.append(getClassID());
|
||||
b.append(", format: ");
|
||||
b.append(getFormat());
|
||||
b.append(", OSVersion: ");
|
||||
b.append(getOSVersion());
|
||||
b.append(", sectionCount: ");
|
||||
b.append(sectionCount);
|
||||
b.append(", sections: [");
|
||||
final List sections = getSections();
|
||||
for (int i = 0; i < sectionCount; i++)
|
||||
b.append(((Section) sections.get(0)).toString());
|
||||
b.append(']');
|
||||
b.append(']');
|
||||
return b.toString();
|
||||
}
|
||||
}
|
||||
|
@ -61,7 +61,8 @@ import java.io.IOException;
|
||||
* <p>Factory class to create instances of {@link SummaryInformation},
|
||||
* {@link DocumentSummaryInformation} and {@link PropertySet}.</p>
|
||||
*
|
||||
* @author Rainer Klute (klute@rainer-klute.de)
|
||||
* @author Rainer Klute <a
|
||||
* href="mailto:klute@rainer-klute.de"><klute@rainer-klute.de></a>
|
||||
* @version $Id$
|
||||
* @since 2002-02-09
|
||||
*/
|
||||
|
@ -63,8 +63,11 @@
|
||||
package org.apache.poi.hpsf;
|
||||
|
||||
/**
|
||||
* <p>This exception is thrown when trying to read a (yet) unsupported variant
|
||||
* type.</p>
|
||||
* <p>This exception is thrown when HPSF tries to read a (yet) unsupported
|
||||
* variant type.</p>
|
||||
*
|
||||
* @see WritingNotSupportedException
|
||||
* @see UnsupportedVariantTypeException
|
||||
*
|
||||
* @author Rainer Klute <a
|
||||
* href="mailto:klute@rainer-klute.de"><klute@rainer-klute.de></a>
|
||||
@ -78,10 +81,11 @@ public class ReadingNotSupportedException
|
||||
/**
|
||||
* <p>Constructor</p>
|
||||
*
|
||||
* @param variantType
|
||||
* @param value
|
||||
* @param variantType The unsupported variant type.
|
||||
* @param value The value.
|
||||
*/
|
||||
public ReadingNotSupportedException(long variantType, Object value)
|
||||
public ReadingNotSupportedException(final long variantType,
|
||||
final Object value)
|
||||
{
|
||||
super(variantType, value);
|
||||
}
|
||||
|
@ -67,7 +67,8 @@ import org.apache.poi.util.LittleEndian;
|
||||
/**
|
||||
* <p>Represents a section in a {@link PropertySet}.</p>
|
||||
*
|
||||
* @author Rainer Klute (klute@rainer-klute.de)
|
||||
* @author Rainer Klute <a
|
||||
* href="mailto:klute@rainer-klute.de"><klute@rainer-klute.de></a>
|
||||
* @author Drew Varner (Drew.Varner allUpIn sc.edu)
|
||||
* @version $Id$
|
||||
* @since 2002-02-09
|
||||
@ -493,7 +494,7 @@ public class Section
|
||||
/**
|
||||
* <p>Checks whether this section is equal to another object.</p>
|
||||
*
|
||||
* @param o The object to cpmpare this section with
|
||||
* @param o The object to compare this section with
|
||||
* @return <code>true</code> if the objects are equal, <code>false</code> if
|
||||
* not
|
||||
*/
|
||||
@ -509,4 +510,50 @@ public class Section
|
||||
return Util.equals(s.getProperties(), getProperties());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @see Object#hashCode()
|
||||
*/
|
||||
public int hashCode()
|
||||
{
|
||||
long hashCode = 0;
|
||||
hashCode += getFormatID().hashCode();
|
||||
final Property[] pa = getProperties();
|
||||
for (int i = 0; i < pa.length; i++)
|
||||
hashCode += pa[i].hashCode();
|
||||
final int returnHashCode = (int) (hashCode & 0x0ffffffffL);
|
||||
return returnHashCode;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @see Object#toString()
|
||||
*/
|
||||
public String toString()
|
||||
{
|
||||
final StringBuffer b = new StringBuffer();
|
||||
final Property[] pa = getProperties();
|
||||
b.append(getClass().getName());
|
||||
b.append('[');
|
||||
b.append("formatID: ");
|
||||
b.append(getFormatID());
|
||||
b.append(", offset: ");
|
||||
b.append(getOffset());
|
||||
b.append(", propertyCount: ");
|
||||
b.append(getPropertyCount());
|
||||
b.append(", size: ");
|
||||
b.append(getSize());
|
||||
b.append(", properties: [\n");
|
||||
for (int i = 0; i < pa.length; i++)
|
||||
{
|
||||
b.append(pa[i].toString());
|
||||
b.append(",\n");
|
||||
}
|
||||
b.append(']');
|
||||
b.append(']');
|
||||
return b.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -82,7 +82,8 @@ import java.util.List;
|
||||
* went the other way round historically: the convenience classes came
|
||||
* only late to my mind.</p>
|
||||
*
|
||||
* @author Rainer Klute (klute@rainer-klute.de)
|
||||
* @author Rainer Klute <a
|
||||
* href="mailto:klute@rainer-klute.de"><klute@rainer-klute.de></a>
|
||||
* @version $Id$
|
||||
* @since 2002-02-09
|
||||
*/
|
||||
|
@ -69,7 +69,8 @@ import org.apache.poi.hpsf.wellknown.PropertyIDMap;
|
||||
* href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/com/stgu_8910.asp">http://msdn.microsoft.com/library/default.asp?url=/library/en-us/com/stgu_8910.asp</a>
|
||||
* for documentation from That Redmond Company.</p>
|
||||
*
|
||||
* @author Rainer Klute (klute@rainer-klute.de)
|
||||
* @author Rainer Klute <a
|
||||
* href="mailto:klute@rainer-klute.de"><klute@rainer-klute.de></a>
|
||||
* @see DocumentSummaryInformation
|
||||
* @version $Id$
|
||||
* @since 2002-02-09
|
||||
@ -297,7 +298,7 @@ public class SummaryInformation extends SpecialPropertySet
|
||||
* <strong>when this method is implemented. Please note that the
|
||||
* return type is likely to change!</strong></p>
|
||||
*
|
||||
* <p><strong>FIXME / Hint to developers:</strong> Drew Varner
|
||||
* <p><strong>FIXME (3) / Hint to developers:</strong> Drew Varner
|
||||
* <Drew.Varner -at- sc.edu> said that this is an image in
|
||||
* WMF or Clipboard (BMP?) format. He also provided two links that
|
||||
* might be helpful: <a
|
||||
|
@ -66,12 +66,12 @@ import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import org.apache.poi.util.LittleEndian;
|
||||
import org.apache.poi.util.LittleEndianConsts;
|
||||
|
||||
/**
|
||||
* <p>Class for writing little-endian data and more.</p>
|
||||
*
|
||||
* @author Rainer Klute (klute@rainer-klute.de)
|
||||
* @author Rainer Klute <a
|
||||
* href="mailto:klute@rainer-klute.de"><klute@rainer-klute.de></a>
|
||||
* @version $Id$
|
||||
* @since 2003-02-20
|
||||
*/
|
||||
@ -81,8 +81,9 @@ public class TypeWriter
|
||||
/**
|
||||
* <p>Writes a two-byte value (short) to an output stream.</p>
|
||||
*
|
||||
* @param out The stream to write to
|
||||
* @param n The value to write
|
||||
* @param out The stream to write to.
|
||||
* @param n The value to write.
|
||||
* @return The number of bytes that have been written.
|
||||
* @exception IOException if an I/O error occurs
|
||||
*/
|
||||
public static int writeToStream(final OutputStream out, final short n)
|
||||
@ -149,7 +150,7 @@ public class TypeWriter
|
||||
throws IOException
|
||||
{
|
||||
long high = n & 0xFFFFFFFF00000000L;
|
||||
if (high != 0)
|
||||
if (high != 0 && high != 0xFFFFFFFF00000000L)
|
||||
throw new IllegalPropertySetDataException
|
||||
("Value " + n + " cannot be represented by 4 bytes.");
|
||||
return writeToStream(out, (int) n);
|
||||
|
@ -62,7 +62,8 @@ package org.apache.poi.hpsf;
|
||||
* <p>The constructors of this class are analogous to those of its
|
||||
* superclass and documented there.</p>
|
||||
*
|
||||
* @author Rainer Klute (klute@rainer-klute.de)
|
||||
* @author Rainer Klute <a
|
||||
* href="mailto:klute@rainer-klute.de"><klute@rainer-klute.de></a>
|
||||
* @version $Id$
|
||||
* @since 2002-02-09
|
||||
*/
|
||||
|
@ -88,7 +88,7 @@ public abstract class UnsupportedVariantTypeException extends HPSFException
|
||||
{
|
||||
super("HPSF does not yet support the variant type " + variantType +
|
||||
" (" + Variant.getVariantName(variantType) + ", " +
|
||||
HexDump.toHex((int) variantType) + "). If you want support for " +
|
||||
HexDump.toHex(variantType) + "). If you want support for " +
|
||||
"this variant type in one of the next POI releases please " +
|
||||
"submit a request for enhancement (RFE) to " +
|
||||
"<http://nagoya.apache.org/bugzilla/>! Thank you!");
|
||||
|
@ -78,7 +78,7 @@ public class Util
|
||||
* <li><p>if for each <var>i</var> with
|
||||
* <var>i</var> >= 0 and
|
||||
* <var>i</var> < <var>a.length</var> holds
|
||||
* <var>a</var>[<var>i</var>] == <var>b</var>[<var>i</var>].</p></li>
|
||||
* <var>a</var>[<var>i</var>] == <var>b</var>[<var>i</var>].</p></li>
|
||||
*
|
||||
* </ul>
|
||||
*
|
||||
@ -191,6 +191,16 @@ public class Util
|
||||
return new Date(ms_since_19700101);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Converts a {@link Date} into a filetime.</p>
|
||||
*
|
||||
* @param date The date to be converted
|
||||
* @return The filetime
|
||||
*
|
||||
* @see #filetimeToDate
|
||||
*/
|
||||
public static long dateToFileTime(final Date date)
|
||||
{
|
||||
long ms_since_19700101 = date.getTime();
|
||||
@ -228,6 +238,17 @@ public class Util
|
||||
return internalEquals(o1, o2);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Compares to object arrays with regarding the objects' order. For
|
||||
* example, [1, 2, 3] and [2, 1, 3] are equal.</p>
|
||||
*
|
||||
* @param c1 The first object array.
|
||||
* @param c2 The second object array.
|
||||
* @return <code>true</code> if the object arrays are equal,
|
||||
* <code>false</code> if they are not.
|
||||
*/
|
||||
public static boolean equals(final Object[] c1, final Object[] c2)
|
||||
{
|
||||
final Object[] o1 = (Object[]) c1.clone();
|
||||
@ -252,4 +273,68 @@ public class Util
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Pads a byte array with 0x00 bytes so that its length is a multiple of
|
||||
* 4.</p>
|
||||
*
|
||||
* @param ba The byte array to pad.
|
||||
* @return The padded byte array.
|
||||
*/
|
||||
public static byte[] pad4(final byte[] ba)
|
||||
{
|
||||
final int PAD = 4;
|
||||
final byte[] result;
|
||||
int l = ba.length % PAD;
|
||||
if (l == 0)
|
||||
result = ba;
|
||||
else
|
||||
{
|
||||
l = PAD - l;
|
||||
result = new byte[ba.length + l];
|
||||
System.arraycopy(ba, 0, result, 0, ba.length);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Pads a character array with 0x0000 characters so that its length is a
|
||||
* multiple of 4.</p>
|
||||
*
|
||||
* @param ca The character array to pad.
|
||||
* @return The padded character array.
|
||||
*/
|
||||
public static char[] pad4(final char[] ca)
|
||||
{
|
||||
final int PAD = 4;
|
||||
final char[] result;
|
||||
int l = ca.length % PAD;
|
||||
if (l == 0)
|
||||
result = ca;
|
||||
else
|
||||
{
|
||||
l = PAD - l;
|
||||
result = new char[ca.length + l];
|
||||
System.arraycopy(ca, 0, result, 0, ca.length);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Pads a string with 0x0000 characters so that its length is a
|
||||
* multiple of 4.</p>
|
||||
*
|
||||
* @param s The string to pad.
|
||||
* @return The padded string as a character array.
|
||||
*/
|
||||
public static char[] pad4(final String s)
|
||||
{
|
||||
return pad4(s.toCharArray());
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -54,6 +54,7 @@
|
||||
*/
|
||||
package org.apache.poi.hpsf;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@ -357,79 +358,193 @@ public class Variant
|
||||
public static final int VT_BYREF = 0x4000;
|
||||
|
||||
/**
|
||||
* <p>FIXME: Document this!</p>
|
||||
* <p>FIXME (3): Document this!</p>
|
||||
*/
|
||||
public static final int VT_RESERVED = 0x8000;
|
||||
|
||||
/**
|
||||
* <p>FIXME: Document this!</p>
|
||||
* <p>FIXME (3): Document this!</p>
|
||||
*/
|
||||
public static final int VT_ILLEGAL = 0xFFFF;
|
||||
|
||||
/**
|
||||
* <p>FIXME: Document this!</p>
|
||||
* <p>FIXME (3): Document this!</p>
|
||||
*/
|
||||
public static final int VT_ILLEGALMASKED = 0xFFF;
|
||||
|
||||
/**
|
||||
* <p>FIXME: Document this!</p>
|
||||
* <p>FIXME (3): Document this!</p>
|
||||
*/
|
||||
public static final int VT_TYPEMASK = 0xFFF;
|
||||
|
||||
|
||||
|
||||
public static final Map m = new HashMap();
|
||||
/**
|
||||
* <p>Maps the numbers denoting the variant types to their corresponding
|
||||
* variant type names.</p>
|
||||
*/
|
||||
private static Map numberToName;
|
||||
|
||||
private static Map numberToLength;
|
||||
|
||||
/**
|
||||
* <p>Denotes a variant type with a length that is unknown to HPSF yet.</p>
|
||||
*/
|
||||
public static final Integer LENGTH_UNKNOWN = new Integer(-2);
|
||||
|
||||
/**
|
||||
* <p>Denotes a variant type with a variable length.</p>
|
||||
*/
|
||||
public static final Integer LENGTH_VARIABLE = new Integer(-1);
|
||||
|
||||
/**
|
||||
* <p>Denotes a variant type with a length of 0 bytes.</p>
|
||||
*/
|
||||
public static final Integer LENGTH_0 = new Integer(0);
|
||||
|
||||
/**
|
||||
* <p>Denotes a variant type with a length of 2 bytes.</p>
|
||||
*/
|
||||
public static final Integer LENGTH_2 = new Integer(2);
|
||||
|
||||
/**
|
||||
* <p>Denotes a variant type with a length of 4 bytes.</p>
|
||||
*/
|
||||
public static final Integer LENGTH_4 = new Integer(4);
|
||||
|
||||
/**
|
||||
* <p>Denotes a variant type with a length of 8 bytes.</p>
|
||||
*/
|
||||
public static final Integer LENGTH_8 = new Integer(8);
|
||||
|
||||
|
||||
|
||||
static
|
||||
{
|
||||
m.put(new Integer(0), "VT_EMPTY");
|
||||
m.put(new Integer(1), "VT_NULL");
|
||||
m.put(new Integer(2), "VT_I2");
|
||||
m.put(new Integer(3), "VT_I4");
|
||||
m.put(new Integer(4), "VT_R4");
|
||||
m.put(new Integer(5), "VT_R8");
|
||||
m.put(new Integer(6), "VT_CY");
|
||||
m.put(new Integer(7), "VT_DATE");
|
||||
m.put(new Integer(8), "VT_BSTR");
|
||||
m.put(new Integer(9), "VT_DISPATCH");
|
||||
m.put(new Integer(10), "VT_ERROR");
|
||||
m.put(new Integer(11), "VT_BOOL");
|
||||
m.put(new Integer(12), "VT_VARIANT");
|
||||
m.put(new Integer(13), "VT_UNKNOWN");
|
||||
m.put(new Integer(14), "VT_DECIMAL");
|
||||
m.put(new Integer(16), "VT_I1");
|
||||
m.put(new Integer(17), "VT_UI1");
|
||||
m.put(new Integer(18), "VT_UI2");
|
||||
m.put(new Integer(19), "VT_UI4");
|
||||
m.put(new Integer(20), "VT_I8");
|
||||
m.put(new Integer(21), "VT_UI8");
|
||||
m.put(new Integer(22), "VT_INT");
|
||||
m.put(new Integer(23), "VT_UINT");
|
||||
m.put(new Integer(24), "VT_VOID");
|
||||
m.put(new Integer(25), "VT_HRESULT");
|
||||
m.put(new Integer(26), "VT_PTR");
|
||||
m.put(new Integer(27), "VT_SAFEARRAY");
|
||||
m.put(new Integer(28), "VT_CARRAY");
|
||||
m.put(new Integer(29), "VT_USERDEFINED");
|
||||
m.put(new Integer(30), "VT_LPSTR");
|
||||
m.put(new Integer(31), "VT_LPWSTR");
|
||||
m.put(new Integer(64), "VT_FILETIME");
|
||||
m.put(new Integer(65), "VT_BLOB");
|
||||
m.put(new Integer(66), "VT_STREAM");
|
||||
m.put(new Integer(67), "VT_STORAGE");
|
||||
m.put(new Integer(68), "VT_STREAMED_OBJECT");
|
||||
m.put(new Integer(69), "VT_STORED_OBJECT");
|
||||
m.put(new Integer(70), "VT_BLOB_OBJECT");
|
||||
m.put(new Integer(71), "VT_CF");
|
||||
m.put(new Integer(72), "VT_CLSID");
|
||||
/* Initialize the number-to-name map: */
|
||||
Map tm1 = new HashMap();
|
||||
tm1.put(new Long(0), "VT_EMPTY");
|
||||
tm1.put(new Long(1), "VT_NULL");
|
||||
tm1.put(new Long(2), "VT_I2");
|
||||
tm1.put(new Long(3), "VT_I4");
|
||||
tm1.put(new Long(4), "VT_R4");
|
||||
tm1.put(new Long(5), "VT_R8");
|
||||
tm1.put(new Long(6), "VT_CY");
|
||||
tm1.put(new Long(7), "VT_DATE");
|
||||
tm1.put(new Long(8), "VT_BSTR");
|
||||
tm1.put(new Long(9), "VT_DISPATCH");
|
||||
tm1.put(new Long(10), "VT_ERROR");
|
||||
tm1.put(new Long(11), "VT_BOOL");
|
||||
tm1.put(new Long(12), "VT_VARIANT");
|
||||
tm1.put(new Long(13), "VT_UNKNOWN");
|
||||
tm1.put(new Long(14), "VT_DECIMAL");
|
||||
tm1.put(new Long(16), "VT_I1");
|
||||
tm1.put(new Long(17), "VT_UI1");
|
||||
tm1.put(new Long(18), "VT_UI2");
|
||||
tm1.put(new Long(19), "VT_UI4");
|
||||
tm1.put(new Long(20), "VT_I8");
|
||||
tm1.put(new Long(21), "VT_UI8");
|
||||
tm1.put(new Long(22), "VT_INT");
|
||||
tm1.put(new Long(23), "VT_UINT");
|
||||
tm1.put(new Long(24), "VT_VOID");
|
||||
tm1.put(new Long(25), "VT_HRESULT");
|
||||
tm1.put(new Long(26), "VT_PTR");
|
||||
tm1.put(new Long(27), "VT_SAFEARRAY");
|
||||
tm1.put(new Long(28), "VT_CARRAY");
|
||||
tm1.put(new Long(29), "VT_USERDEFINED");
|
||||
tm1.put(new Long(30), "VT_LPSTR");
|
||||
tm1.put(new Long(31), "VT_LPWSTR");
|
||||
tm1.put(new Long(64), "VT_FILETIME");
|
||||
tm1.put(new Long(65), "VT_BLOB");
|
||||
tm1.put(new Long(66), "VT_STREAM");
|
||||
tm1.put(new Long(67), "VT_STORAGE");
|
||||
tm1.put(new Long(68), "VT_STREAMED_OBJECT");
|
||||
tm1.put(new Long(69), "VT_STORED_OBJECT");
|
||||
tm1.put(new Long(70), "VT_BLOB_OBJECT");
|
||||
tm1.put(new Long(71), "VT_CF");
|
||||
tm1.put(new Long(72), "VT_CLSID");
|
||||
Map tm2 = new HashMap(tm1.size(), 1.0F);
|
||||
tm2.putAll(tm1);
|
||||
numberToName = Collections.unmodifiableMap(tm2);
|
||||
|
||||
/* Initialize the number-to-length map: */
|
||||
tm1.clear();
|
||||
tm1.put(new Long(0), LENGTH_0);
|
||||
tm1.put(new Long(1), LENGTH_UNKNOWN);
|
||||
tm1.put(new Long(2), LENGTH_2);
|
||||
tm1.put(new Long(3), LENGTH_4);
|
||||
tm1.put(new Long(4), LENGTH_4);
|
||||
tm1.put(new Long(5), LENGTH_8);
|
||||
tm1.put(new Long(6), LENGTH_UNKNOWN);
|
||||
tm1.put(new Long(7), LENGTH_UNKNOWN);
|
||||
tm1.put(new Long(8), LENGTH_UNKNOWN);
|
||||
tm1.put(new Long(9), LENGTH_UNKNOWN);
|
||||
tm1.put(new Long(10), LENGTH_UNKNOWN);
|
||||
tm1.put(new Long(11), LENGTH_UNKNOWN);
|
||||
tm1.put(new Long(12), LENGTH_UNKNOWN);
|
||||
tm1.put(new Long(13), LENGTH_UNKNOWN);
|
||||
tm1.put(new Long(14), LENGTH_UNKNOWN);
|
||||
tm1.put(new Long(16), LENGTH_UNKNOWN);
|
||||
tm1.put(new Long(17), LENGTH_UNKNOWN);
|
||||
tm1.put(new Long(18), LENGTH_UNKNOWN);
|
||||
tm1.put(new Long(19), LENGTH_UNKNOWN);
|
||||
tm1.put(new Long(20), LENGTH_UNKNOWN);
|
||||
tm1.put(new Long(21), LENGTH_UNKNOWN);
|
||||
tm1.put(new Long(22), LENGTH_UNKNOWN);
|
||||
tm1.put(new Long(23), LENGTH_UNKNOWN);
|
||||
tm1.put(new Long(24), LENGTH_UNKNOWN);
|
||||
tm1.put(new Long(25), LENGTH_UNKNOWN);
|
||||
tm1.put(new Long(26), LENGTH_UNKNOWN);
|
||||
tm1.put(new Long(27), LENGTH_UNKNOWN);
|
||||
tm1.put(new Long(28), LENGTH_UNKNOWN);
|
||||
tm1.put(new Long(29), LENGTH_UNKNOWN);
|
||||
tm1.put(new Long(30), LENGTH_VARIABLE);
|
||||
tm1.put(new Long(31), LENGTH_UNKNOWN);
|
||||
tm1.put(new Long(64), LENGTH_8);
|
||||
tm1.put(new Long(65), LENGTH_UNKNOWN);
|
||||
tm1.put(new Long(66), LENGTH_UNKNOWN);
|
||||
tm1.put(new Long(67), LENGTH_UNKNOWN);
|
||||
tm1.put(new Long(68), LENGTH_UNKNOWN);
|
||||
tm1.put(new Long(69), LENGTH_UNKNOWN);
|
||||
tm1.put(new Long(70), LENGTH_UNKNOWN);
|
||||
tm1.put(new Long(71), LENGTH_UNKNOWN);
|
||||
tm1.put(new Long(72), LENGTH_UNKNOWN);
|
||||
tm2 = new HashMap(tm1.size(), 1.0F);
|
||||
tm2.putAll(tm1);
|
||||
numberToLength = Collections.unmodifiableMap(tm2);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Returns the variant type name associated with a variant type
|
||||
* number.</p>
|
||||
*
|
||||
* @param variantType The variant type number
|
||||
* @return The variant type name or the string "unknown variant type"
|
||||
*/
|
||||
public static String getVariantName(final long variantType)
|
||||
{
|
||||
String name = (String) m.get(new Integer((int) variantType));
|
||||
final String name = (String) numberToName.get(new Long(variantType));
|
||||
return name != null ? name : "unknown variant type";
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Returns a variant type's length.</p>
|
||||
*
|
||||
* @param variantType The variant type number
|
||||
* @return The length of the variant type's data in bytes. If the length is
|
||||
* variable, i.e. the length of a string, -1 is returned. If HPSF does not
|
||||
* know the length, -2 is returned. The latter usually indicates an
|
||||
* unsupported variant type.
|
||||
*/
|
||||
public static int getVariantLength(final long variantType)
|
||||
{
|
||||
final Long key = new Long((int) variantType);
|
||||
final Long length = (Long) numberToLength.get(key);
|
||||
if (length == null)
|
||||
return -2;
|
||||
return length.intValue();
|
||||
}
|
||||
|
||||
}
|
@ -65,6 +65,8 @@ package org.apache.poi.hpsf;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Date;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.poi.util.LittleEndian;
|
||||
import org.apache.poi.util.LittleEndianConsts;
|
||||
@ -72,8 +74,8 @@ import org.apache.poi.util.LittleEndianConsts;
|
||||
/**
|
||||
* <p>Supports reading and writing of variant data.</p>
|
||||
*
|
||||
* <p><strong>FIXME:</strong> Reading and writing must be made more uniform than
|
||||
* it is now. The following items should be resolved:
|
||||
* <p><strong>FIXME (3):</strong> Reading and writing should be made more
|
||||
* uniform than it is now. The following items should be resolved:
|
||||
*
|
||||
* <ul>
|
||||
*
|
||||
@ -87,14 +89,73 @@ import org.apache.poi.util.LittleEndianConsts;
|
||||
*
|
||||
* @author Rainer Klute <a
|
||||
* href="mailto:klute@rainer-klute.de"><klute@rainer-klute.de></a>
|
||||
* @since 08.08.2003
|
||||
* @since 2003-08-08
|
||||
* @version $Id$
|
||||
*/
|
||||
public class VariantSupport extends Variant
|
||||
{
|
||||
|
||||
private static boolean logUnsupportedTypes = false;
|
||||
|
||||
/**
|
||||
* <p>Reads a variant data type from a byte array.</p>
|
||||
* <p>Specifies whether warnings about unsupported variant types are to be
|
||||
* written to <code>System.err</code> or not.</p>
|
||||
*
|
||||
* @param logUnsupportedTypes If <code>true</code> warnings will be written,
|
||||
* if <code>false</code> they won't.
|
||||
*/
|
||||
public static void setLogUnsupportedTypes(final boolean logUnsupportedTypes)
|
||||
{
|
||||
VariantSupport.logUnsupportedTypes = logUnsupportedTypes;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Checks whether logging of unsupported variant types warning is turned
|
||||
* on or off.</p>
|
||||
*
|
||||
* @return <code>true</code> if logging is turned on, else
|
||||
* <code>false</code>.
|
||||
*/
|
||||
public static boolean isLogUnsupportedTypes()
|
||||
{
|
||||
return logUnsupportedTypes;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Keeps a list of the variant types an "unsupported" message has already
|
||||
* been issued for.</p>
|
||||
*/
|
||||
protected static List unsupportedMessage;
|
||||
|
||||
/**
|
||||
* <p>Writes a warning to <code>System.err</code> that a variant type is
|
||||
* unsupported by HPSF. Such a warning is written only once for each variant
|
||||
* type. Log messages can be turned on or off by </p>
|
||||
*
|
||||
* @param ex The exception to log
|
||||
*/
|
||||
protected static void writeUnsupportedTypeMessage
|
||||
(final UnsupportedVariantTypeException ex)
|
||||
{
|
||||
if (isLogUnsupportedTypes())
|
||||
{
|
||||
if (unsupportedMessage == null)
|
||||
unsupportedMessage = new LinkedList();
|
||||
Long vt = new Long(ex.getVariantType());
|
||||
if (!unsupportedMessage.contains(vt))
|
||||
{
|
||||
System.err.println(ex.getMessage());
|
||||
unsupportedMessage.add(vt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Reads a variant type from a byte array.</p>
|
||||
*
|
||||
* @param src The byte array
|
||||
* @param offset The offset in the byte array where the variant
|
||||
@ -105,8 +166,8 @@ public class VariantSupport extends Variant
|
||||
* @return A Java object that corresponds best to the variant
|
||||
* field. For example, a VT_I4 is returned as a {@link Long}, a
|
||||
* VT_LPSTR as a {@link String}.
|
||||
* @exception UnsupportedVariantTypeException if HPSF does not (yet)
|
||||
* support the variant type which is to be read
|
||||
* @exception ReadingNotSupportedException if a property is to be written
|
||||
* who's variant type HPSF does not yet support
|
||||
*
|
||||
* @see Variant
|
||||
*/
|
||||
@ -161,7 +222,7 @@ public class VariantSupport extends Variant
|
||||
* String object. The 0x00 bytes at the end must be
|
||||
* stripped.
|
||||
*
|
||||
* FIXME: Reading an 8-bit string should pay attention
|
||||
* FIXME (2): Reading an 8-bit string should pay attention
|
||||
* to the codepage. Currently the byte making out the
|
||||
* property's value are interpreted according to the
|
||||
* platform's default character set.
|
||||
@ -238,51 +299,57 @@ public class VariantSupport extends Variant
|
||||
|
||||
|
||||
/**
|
||||
* <p>Writes a variant value to an output stream.</p>
|
||||
* <p>Writes a variant value to an output stream. This method ensures that
|
||||
* always a multiple of 4 bytes is written.</p>
|
||||
*
|
||||
* @param out The stream to write the value to.
|
||||
* @param type The variant's type.
|
||||
* @param value The variant's value.
|
||||
* @return The number of entities that have been written. In many cases an
|
||||
* "entity" is a byte but this is not always the case.
|
||||
* @exception IOException if an I/O exceptions occurs
|
||||
* @exception WritingNotSupportedException if a property is to be written
|
||||
* who's variant type HPSF does not yet support
|
||||
*/
|
||||
public static int write(final OutputStream out, final long type,
|
||||
final Object value)
|
||||
final Object value)
|
||||
throws IOException, WritingNotSupportedException
|
||||
{
|
||||
int length = 0;
|
||||
switch ((int) type)
|
||||
{
|
||||
case Variant.VT_BOOL:
|
||||
{
|
||||
int trueOrFalse;
|
||||
int length = 0;
|
||||
if (((Boolean) value).booleanValue())
|
||||
trueOrFalse = 1;
|
||||
else
|
||||
trueOrFalse = 0;
|
||||
length += TypeWriter.writeUIntToStream(out, trueOrFalse);
|
||||
return length;
|
||||
length = TypeWriter.writeUIntToStream(out, trueOrFalse);
|
||||
break;
|
||||
}
|
||||
case Variant.VT_LPSTR:
|
||||
{
|
||||
TypeWriter.writeUIntToStream
|
||||
length = TypeWriter.writeUIntToStream
|
||||
(out, ((String) value).length() + 1);
|
||||
char[] s = toPaddedCharArray((String) value);
|
||||
/* FIXME: The following line forces characters to bytes. This
|
||||
* is generally wrong and should only be done according to a
|
||||
* codepage. Alternatively Unicode could be written (see
|
||||
char[] s = Util.pad4((String) value);
|
||||
/* FIXME (2): The following line forces characters to bytes.
|
||||
* This is generally wrong and should only be done according to
|
||||
* a codepage. Alternatively Unicode could be written (see
|
||||
* Variant.VT_LPWSTR). */
|
||||
byte[] b = new byte[s.length];
|
||||
byte[] b = new byte[s.length + 1];
|
||||
for (int i = 0; i < s.length; i++)
|
||||
b[i] = (byte) s[i];
|
||||
b[b.length - 1] = 0x00;
|
||||
out.write(b);
|
||||
return b.length;
|
||||
length += b.length;
|
||||
break;
|
||||
}
|
||||
case Variant.VT_LPWSTR:
|
||||
{
|
||||
final int nrOfChars = ((String) value).length() + 1;
|
||||
TypeWriter.writeUIntToStream(out, nrOfChars);
|
||||
char[] s = toPaddedCharArray((String) value);
|
||||
char[] s = Util.pad4((String) value);
|
||||
for (int i = 0; i < s.length; i++)
|
||||
{
|
||||
final int high = (int) ((s[i] & 0xff00) >> 8);
|
||||
@ -292,79 +359,73 @@ public class VariantSupport extends Variant
|
||||
out.write(lowb);
|
||||
out.write(highb);
|
||||
}
|
||||
return nrOfChars * 2;
|
||||
length = nrOfChars * 2;
|
||||
out.write(0x00);
|
||||
out.write(0x00);
|
||||
length += 2;
|
||||
break;
|
||||
}
|
||||
case Variant.VT_CF:
|
||||
{
|
||||
final byte[] b = (byte[]) value;
|
||||
out.write(b);
|
||||
return b.length;
|
||||
length = b.length;
|
||||
break;
|
||||
}
|
||||
case Variant.VT_EMPTY:
|
||||
{
|
||||
TypeWriter.writeUIntToStream(out, Variant.VT_EMPTY);
|
||||
return LittleEndianConsts.INT_SIZE;
|
||||
length = LittleEndianConsts.INT_SIZE;
|
||||
break;
|
||||
}
|
||||
case Variant.VT_I2:
|
||||
{
|
||||
TypeWriter.writeToStream(out, ((Integer) value).shortValue());
|
||||
return LittleEndianConsts.SHORT_SIZE;
|
||||
length = LittleEndianConsts.SHORT_SIZE;
|
||||
break;
|
||||
}
|
||||
case Variant.VT_I4:
|
||||
{
|
||||
TypeWriter.writeToStream(out, ((Long) value).intValue());
|
||||
return LittleEndianConsts.INT_SIZE;
|
||||
length = LittleEndianConsts.INT_SIZE;
|
||||
break;
|
||||
}
|
||||
case Variant.VT_FILETIME:
|
||||
{
|
||||
int length = 0;
|
||||
long filetime = Util.dateToFileTime((Date) value);
|
||||
int high = (int) ((filetime >> 32) & 0xFFFFFFFFL);
|
||||
int low = (int) (filetime & 0x00000000FFFFFFFFL);
|
||||
length += TypeWriter.writeUIntToStream(out, 0x0000000FFFFFFFFL & low);
|
||||
length += TypeWriter.writeUIntToStream(out, 0x0000000FFFFFFFFL & high);
|
||||
return length;
|
||||
length += TypeWriter.writeUIntToStream
|
||||
(out, 0x0000000FFFFFFFFL & low);
|
||||
length += TypeWriter.writeUIntToStream
|
||||
(out, 0x0000000FFFFFFFFL & high);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
throw new WritingNotSupportedException(type, value);
|
||||
}
|
||||
/* The variant type is not supported yet. However, if the value
|
||||
* is a byte array we can write it nevertheless. */
|
||||
if (value instanceof byte[])
|
||||
{
|
||||
final byte[] b = (byte[]) value;
|
||||
out.write(b);
|
||||
length = b.length;
|
||||
writeUnsupportedTypeMessage
|
||||
(new WritingNotSupportedException(type, value));
|
||||
}
|
||||
else
|
||||
throw new WritingNotSupportedException(type, value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Converts a string into a 0x00-terminated character sequence padded
|
||||
* with 0x00 bytes to a multiple of 4.</p>
|
||||
*
|
||||
* @param value The string to convert
|
||||
* @return The padded character array
|
||||
*/
|
||||
private static char[] toPaddedCharArray(final String s)
|
||||
{
|
||||
final int PADDING = 4;
|
||||
int dl = s.length() + 1;
|
||||
final int r = dl % 4;
|
||||
if (r > 0)
|
||||
dl += PADDING - r;
|
||||
char[] buffer = new char[dl];
|
||||
s.getChars(0, s.length(), buffer, 0);
|
||||
for (int i = s.length(); i < dl; i++)
|
||||
buffer[i] = (char) 0;
|
||||
return buffer;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static int getLength(final long variantType, final int lengthInBytes)
|
||||
{
|
||||
switch ((int) variantType)
|
||||
/* Add 0x00 character to write a multiple of four bytes: */
|
||||
while (length % 4 != 0)
|
||||
{
|
||||
case VT_LPWSTR:
|
||||
return lengthInBytes / 2;
|
||||
default:
|
||||
return lengthInBytes;
|
||||
out.write(0);
|
||||
length++;
|
||||
}
|
||||
return length;
|
||||
}
|
||||
|
||||
}
|
@ -66,6 +66,9 @@ package org.apache.poi.hpsf;
|
||||
* <p>This exception is thrown when trying to write a (yet) unsupported variant
|
||||
* type.</p>
|
||||
*
|
||||
* @see ReadingNotSupportedException
|
||||
* @see UnsupportedVariantTypeException
|
||||
*
|
||||
* @author Rainer Klute <a
|
||||
* href="mailto:klute@rainer-klute.de"><klute@rainer-klute.de></a>
|
||||
* @since 2003-08-08
|
||||
@ -78,10 +81,11 @@ public class WritingNotSupportedException
|
||||
/**
|
||||
* <p>Constructor</p>
|
||||
*
|
||||
* @param variantType
|
||||
* @param value
|
||||
* @param variantType The unsupported varian type.
|
||||
* @param value The value.
|
||||
*/
|
||||
public WritingNotSupportedException(long variantType, Object value)
|
||||
public WritingNotSupportedException(final long variantType,
|
||||
final Object value)
|
||||
{
|
||||
super(variantType, value);
|
||||
}
|
||||
|
@ -66,9 +66,9 @@ import java.util.HashMap;
|
||||
* should treat them as unmodifiable, copy them and modifiy the
|
||||
* copies.</p>
|
||||
*
|
||||
* <p><strong>FIXME:</strong> Make the singletons unmodifiable. However,
|
||||
* <p><strong>FIXME (3):</strong> Make the singletons unmodifiable. However,
|
||||
* since this requires to use a {@link HashMap} delegate instead of
|
||||
* extending {@link HashMap} and thus requires a lot of stupid typing. I won't
|
||||
* extending {@link HashMap} and thus requires a lot of stupid typing, I won't
|
||||
* do that for the time being.</p>
|
||||
*
|
||||
* @author Rainer Klute (klute@rainer-klute.de)
|
||||
@ -141,7 +141,7 @@ public class PropertyIDMap extends HashMap
|
||||
* document</p> */
|
||||
public static final int PID_APPNAME = 18;
|
||||
|
||||
/** <p>ID of the property that denotes... FIXME</p> */
|
||||
/** <p>ID of the property that denotes... FIXME (2)</p> */
|
||||
public static final int PID_SECURITY = 19;
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user