2004-04-09 13:05:39 +00:00
|
|
|
/* ====================================================================
|
|
|
|
|
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.
|
|
|
|
|
==================================================================== */
|
|
|
|
|
|
2003-08-30 09:13:53 +00:00
|
|
|
package org.apache.poi.hpsf;
|
|
|
|
|
|
|
|
|
|
import java.io.ByteArrayOutputStream;
|
|
|
|
|
import java.io.IOException;
|
|
|
|
|
import java.io.OutputStream;
|
2004-06-09 17:51:51 +00:00
|
|
|
import java.util.Collections;
|
|
|
|
|
import java.util.Comparator;
|
2003-08-30 09:13:53 +00:00
|
|
|
import java.util.Iterator;
|
|
|
|
|
import java.util.LinkedList;
|
|
|
|
|
import java.util.List;
|
2004-06-09 17:51:51 +00:00
|
|
|
import java.util.ListIterator;
|
2003-09-18 18:56:35 +00:00
|
|
|
import java.util.Map;
|
2003-08-30 09:13:53 +00:00
|
|
|
|
2003-09-18 18:56:35 +00:00
|
|
|
import org.apache.poi.hpsf.wellknown.PropertyIDMap;
|
2003-08-30 09:13:53 +00:00
|
|
|
import org.apache.poi.util.LittleEndian;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* <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>
|
|
|
|
|
*
|
|
|
|
|
* @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;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2003-09-04 20:15:24 +00:00
|
|
|
/**
|
|
|
|
|
* <p>Contains the bytes making out the section. This byte array is
|
|
|
|
|
* established when the section's size is calculated and can be reused
|
|
|
|
|
* later. It is valid only if the "dirty" flag is false.</p>
|
|
|
|
|
*/
|
|
|
|
|
private byte[] sectionBytes;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2003-08-30 09:13:53 +00:00
|
|
|
/**
|
|
|
|
|
* <p>Creates an empty mutable section.</p>
|
|
|
|
|
*/
|
|
|
|
|
public MutableSection()
|
|
|
|
|
{
|
|
|
|
|
dirty = true;
|
|
|
|
|
formatID = null;
|
|
|
|
|
offset = -1;
|
|
|
|
|
preprops = new LinkedList();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2003-09-04 20:15:24 +00:00
|
|
|
/**
|
|
|
|
|
* <p>Constructs a <code>MutableSection</code> by doing a deep copy of an
|
|
|
|
|
* existing <code>Section</code>. All nested <code>Property</code>
|
|
|
|
|
* instances, will be their mutable counterparts in the new
|
|
|
|
|
* <code>MutableSection</code>.</p>
|
|
|
|
|
*
|
|
|
|
|
* @param s The section set to copy
|
|
|
|
|
*/
|
|
|
|
|
public MutableSection(final Section s)
|
|
|
|
|
{
|
|
|
|
|
setFormatID(s.getFormatID());
|
|
|
|
|
final Property[] pa = s.getProperties();
|
|
|
|
|
final MutableProperty[] mpa = new MutableProperty[pa.length];
|
|
|
|
|
for (int i = 0; i < pa.length; i++)
|
|
|
|
|
mpa[i] = new MutableProperty(pa[i]);
|
|
|
|
|
setProperties(mpa);
|
2003-09-18 18:56:35 +00:00
|
|
|
setDictionary(s.getDictionary());
|
2003-09-04 20:15:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2003-08-30 09:13:53 +00:00
|
|
|
/**
|
|
|
|
|
* <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)
|
|
|
|
|
{
|
2004-08-13 22:38:52 +00:00
|
|
|
ClassID fid = getFormatID();
|
|
|
|
|
if (fid == null)
|
|
|
|
|
{
|
|
|
|
|
fid = new ClassID();
|
|
|
|
|
setFormatID(fid);
|
|
|
|
|
}
|
|
|
|
|
fid.setBytes(formatID);
|
2003-08-30 09:13:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* <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)
|
|
|
|
|
{
|
2003-09-04 20:15:24 +00:00
|
|
|
this.properties = properties;
|
2003-08-30 09:13:53 +00:00
|
|
|
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.
|
|
|
|
|
*
|
2003-09-04 20:15:24 +00:00
|
|
|
* @see #setProperty(int, long, Object)
|
2003-08-30 09:13:53 +00:00
|
|
|
* @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.
|
|
|
|
|
*
|
2003-09-04 20:15:24 +00:00
|
|
|
* @see #setProperty(int, String)
|
2003-08-30 09:13:53 +00:00
|
|
|
* @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
|
|
|
|
|
*
|
2003-09-04 20:15:24 +00:00
|
|
|
* @see #setProperty(int, long, Object)
|
2003-08-30 09:13:53 +00:00
|
|
|
* @see #setProperty(int, String)
|
|
|
|
|
* @see #getProperty
|
|
|
|
|
* @see Variant
|
|
|
|
|
*/
|
|
|
|
|
public void setProperty(final Property p)
|
|
|
|
|
{
|
|
|
|
|
final long id = p.getID();
|
2003-09-18 18:56:35 +00:00
|
|
|
removeProperty(id);
|
|
|
|
|
preprops.add(p);
|
|
|
|
|
dirty = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* <p>Removes a property.</p>
|
|
|
|
|
*
|
|
|
|
|
* @param id The ID of the property to be removed
|
|
|
|
|
*/
|
|
|
|
|
public void removeProperty(final long id)
|
|
|
|
|
{
|
2003-08-30 09:13:53 +00:00
|
|
|
for (final Iterator i = preprops.iterator(); i.hasNext();)
|
|
|
|
|
if (((Property) i.next()).getID() == id)
|
|
|
|
|
{
|
|
|
|
|
i.remove();
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
dirty = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* <p>Sets the value of the boolean property with the specified
|
|
|
|
|
* ID.</p>
|
|
|
|
|
*
|
|
|
|
|
* @param id The property's ID
|
|
|
|
|
* @param value The property's value
|
|
|
|
|
*
|
2003-09-04 20:15:24 +00:00
|
|
|
* @see #setProperty(int, long, Object)
|
2003-08-30 09:13:53 +00:00
|
|
|
* @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)
|
|
|
|
|
{
|
2003-09-04 20:15:24 +00:00
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
size = calcSize();
|
|
|
|
|
dirty = false;
|
|
|
|
|
}
|
2003-09-18 18:56:35 +00:00
|
|
|
catch (HPSFRuntimeException ex)
|
|
|
|
|
{
|
|
|
|
|
throw ex;
|
|
|
|
|
}
|
2003-09-04 20:15:24 +00:00
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
throw new HPSFRuntimeException(ex);
|
|
|
|
|
}
|
2003-08-30 09:13:53 +00:00
|
|
|
}
|
|
|
|
|
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.
|
|
|
|
|
*/
|
2003-09-04 20:15:24 +00:00
|
|
|
private int calcSize() throws WritingNotSupportedException, IOException
|
2003-08-30 09:13:53 +00:00
|
|
|
{
|
2003-09-04 20:15:24 +00:00
|
|
|
final ByteArrayOutputStream out = new ByteArrayOutputStream();
|
|
|
|
|
write(out);
|
|
|
|
|
out.close();
|
|
|
|
|
sectionBytes = out.toByteArray();
|
|
|
|
|
return sectionBytes.length;
|
2003-08-30 09:13:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* <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>
|
|
|
|
|
*
|
2003-09-04 20:15:24 +00:00
|
|
|
* @param out The stream to write into.
|
2003-08-30 09:13:53 +00:00
|
|
|
*
|
2003-09-04 20:15:24 +00:00
|
|
|
* @return The number of bytes written, i.e. the section's size.
|
2003-08-30 09:13:53 +00:00
|
|
|
* @exception IOException if an I/O error occurs
|
|
|
|
|
* @exception WritingNotSupportedException if HPSF does not yet support
|
|
|
|
|
* writing a property's variant type.
|
|
|
|
|
*/
|
2003-09-04 20:15:24 +00:00
|
|
|
public int write(final OutputStream out)
|
2003-08-30 09:13:53 +00:00
|
|
|
throws WritingNotSupportedException, IOException
|
|
|
|
|
{
|
2003-09-04 20:15:24 +00:00
|
|
|
/* Check whether we have already generated the bytes making out the
|
|
|
|
|
* section. */
|
|
|
|
|
if (!dirty && sectionBytes != null)
|
|
|
|
|
{
|
|
|
|
|
out.write(sectionBytes);
|
|
|
|
|
return sectionBytes.length;
|
|
|
|
|
}
|
|
|
|
|
|
2003-08-30 09:13:53 +00:00
|
|
|
/* 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
|
2003-09-04 20:15:24 +00:00
|
|
|
* that it points behind the property list and to the beginning of the
|
|
|
|
|
* properties themselves. */
|
2003-08-30 09:13:53 +00:00
|
|
|
position += 2 * LittleEndian.INT_SIZE +
|
|
|
|
|
getPropertyCount() * 2 * LittleEndian.INT_SIZE;
|
|
|
|
|
|
2003-09-18 18:56:35 +00:00
|
|
|
/* Writing the section's dictionary it tricky. If there is a dictionary
|
|
|
|
|
* (property 0) the codepage property (property 1) has to be set, too.
|
|
|
|
|
* Since HPSF supports Unicode only, the codepage must be 1200. */
|
|
|
|
|
if (getProperty(PropertyIDMap.PID_DICTIONARY) != null)
|
|
|
|
|
{
|
|
|
|
|
final Object p1 = getProperty(PropertyIDMap.PID_CODEPAGE);
|
|
|
|
|
if (p1 != null)
|
|
|
|
|
{
|
|
|
|
|
if (!(p1 instanceof Integer))
|
|
|
|
|
throw new IllegalPropertySetDataException
|
|
|
|
|
("The codepage property (ID = 1) must be an " +
|
|
|
|
|
"Integer object.");
|
2004-06-22 16:11:39 +00:00
|
|
|
else if (((Integer) p1).intValue() != Constants.CP_UNICODE)
|
2003-09-18 18:56:35 +00:00
|
|
|
throw new IllegalPropertySetDataException
|
|
|
|
|
("The codepage property (ID = 1) must be " +
|
|
|
|
|
"1200 (Unicode).");
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
throw new IllegalPropertySetDataException
|
|
|
|
|
("The codepage property (ID = 1) must be set.");
|
|
|
|
|
}
|
|
|
|
|
|
2004-06-09 17:51:51 +00:00
|
|
|
/* Sort the property list by their property IDs: */
|
|
|
|
|
Collections.sort(preprops, new Comparator()
|
|
|
|
|
{
|
|
|
|
|
public int compare(final Object o1, final Object o2)
|
|
|
|
|
{
|
|
|
|
|
final Property p1 = (Property) o1;
|
|
|
|
|
final Property p2 = (Property) o2;
|
|
|
|
|
if (p1.getID() < p2.getID())
|
|
|
|
|
return -1;
|
|
|
|
|
else if (p1.getID() == p2.getID())
|
|
|
|
|
return 0;
|
|
|
|
|
else
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
2003-08-30 09:13:53 +00:00
|
|
|
/* Write the properties and the property list into their respective
|
|
|
|
|
* streams: */
|
2004-06-09 17:51:51 +00:00
|
|
|
for (final ListIterator i = preprops.listIterator(); i.hasNext();)
|
2003-08-30 09:13:53 +00:00
|
|
|
{
|
|
|
|
|
final MutableProperty p = (MutableProperty) i.next();
|
2003-09-18 18:56:35 +00:00
|
|
|
final long id = p.getID();
|
2003-08-30 09:13:53 +00:00
|
|
|
|
|
|
|
|
/* Write the property list entry. */
|
|
|
|
|
TypeWriter.writeUIntToStream(propertyListStream, p.getID());
|
|
|
|
|
TypeWriter.writeUIntToStream(propertyListStream, position);
|
2003-09-18 18:56:35 +00:00
|
|
|
|
|
|
|
|
/* If the property ID is not equal 0 we write the property and all
|
|
|
|
|
* is fine. However, if it equals 0 we have to write the section's
|
2003-12-02 17:46:01 +00:00
|
|
|
* dictionary which has an implicit type only and an explicit
|
|
|
|
|
* value. */
|
2003-09-18 18:56:35 +00:00
|
|
|
if (id != 0)
|
|
|
|
|
/* Write the property and update the position to the next
|
|
|
|
|
* property. */
|
2003-12-02 17:46:01 +00:00
|
|
|
position += p.write(propertyStream, getCodepage());
|
2003-09-18 18:56:35 +00:00
|
|
|
else
|
|
|
|
|
{
|
2003-12-02 17:46:01 +00:00
|
|
|
final int codepage = getCodepage();
|
|
|
|
|
if (codepage == -1)
|
2003-09-18 18:56:35 +00:00
|
|
|
throw new IllegalPropertySetDataException
|
|
|
|
|
("Codepage (property 1) is undefined.");
|
|
|
|
|
position += writeDictionary(propertyStream, dictionary);
|
|
|
|
|
}
|
2003-08-30 09:13:53 +00:00
|
|
|
}
|
|
|
|
|
propertyStream.close();
|
|
|
|
|
propertyListStream.close();
|
|
|
|
|
|
|
|
|
|
/* Write the section: */
|
|
|
|
|
byte[] pb1 = propertyListStream.toByteArray();
|
|
|
|
|
byte[] pb2 = propertyStream.toByteArray();
|
2003-09-04 20:15:24 +00:00
|
|
|
|
|
|
|
|
/* Write the section's length: */
|
|
|
|
|
TypeWriter.writeToStream(out, LittleEndian.INT_SIZE * 2 +
|
|
|
|
|
pb1.length + pb2.length);
|
|
|
|
|
|
|
|
|
|
/* Write the section's number of properties: */
|
2003-08-30 09:13:53 +00:00
|
|
|
TypeWriter.writeToStream(out, getPropertyCount());
|
2003-09-04 20:15:24 +00:00
|
|
|
|
|
|
|
|
/* Write the property list: */
|
2003-08-30 09:13:53 +00:00
|
|
|
out.write(pb1);
|
2003-09-04 20:15:24 +00:00
|
|
|
|
|
|
|
|
/* Write the properties: */
|
2003-08-30 09:13:53 +00:00
|
|
|
out.write(pb2);
|
|
|
|
|
|
2003-09-04 20:15:24 +00:00
|
|
|
int streamLength = LittleEndian.INT_SIZE * 2 + pb1.length + pb2.length;
|
|
|
|
|
return streamLength;
|
2003-08-30 09:13:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2003-09-18 18:56:35 +00:00
|
|
|
/**
|
|
|
|
|
* <p>Writes the section's dictionary.</p>
|
|
|
|
|
*
|
|
|
|
|
* @param out The output stream to write to.
|
|
|
|
|
* @param dictionary The dictionary.
|
|
|
|
|
* @return The number of bytes written
|
|
|
|
|
* @exception IOException if an I/O exception occurs.
|
|
|
|
|
*/
|
|
|
|
|
private static int writeDictionary(final OutputStream out,
|
|
|
|
|
final Map dictionary)
|
|
|
|
|
throws IOException
|
|
|
|
|
{
|
|
|
|
|
int length = 0;
|
|
|
|
|
length += TypeWriter.writeUIntToStream(out, dictionary.size());
|
|
|
|
|
for (final Iterator i = dictionary.keySet().iterator(); i.hasNext();)
|
|
|
|
|
{
|
|
|
|
|
final Long key = (Long) i.next();
|
|
|
|
|
final String value = (String) dictionary.get(key);
|
|
|
|
|
int sLength = value.length() + 1;
|
|
|
|
|
if (sLength % 2 == 1)
|
|
|
|
|
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++)
|
|
|
|
|
{
|
|
|
|
|
int high = (ca[j] & 0x0ff00) >> 8;
|
|
|
|
|
int low = (ca[j] & 0x000ff);
|
|
|
|
|
out.write(low);
|
|
|
|
|
out.write(high);
|
|
|
|
|
length += 2;
|
|
|
|
|
sLength--;
|
|
|
|
|
}
|
|
|
|
|
while (sLength > 0)
|
|
|
|
|
{
|
|
|
|
|
out.write(0x00);
|
|
|
|
|
out.write(0x00);
|
|
|
|
|
length += 2;
|
|
|
|
|
sLength--;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return length;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2003-08-30 09:13:53 +00:00
|
|
|
/**
|
|
|
|
|
* <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()
|
|
|
|
|
{
|
2003-09-18 18:56:35 +00:00
|
|
|
properties = (Property[]) preprops.toArray(new Property[0]);
|
|
|
|
|
return properties;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* <p>Gets a property.</p>
|
|
|
|
|
*
|
|
|
|
|
* <p><strong>FIXME (2):</strong> This method ensures that properties and
|
|
|
|
|
* preprops are in sync. Cleanup this awful stuff!</p>
|
|
|
|
|
*
|
|
|
|
|
* @param id The ID of the property to get
|
|
|
|
|
* @return The property or <code>null</code> if there is no such property
|
|
|
|
|
*/
|
|
|
|
|
public Object getProperty(final long id)
|
|
|
|
|
{
|
|
|
|
|
getProperties();
|
|
|
|
|
return super.getProperty(id);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* <p>Sets the section's dictionary. All keys in the dictionary must be
|
2003-10-23 20:44:24 +00:00
|
|
|
* {@link java.lang.Long} instances, all values must be
|
|
|
|
|
* {@link java.lang.String}s. This method overwrites the properties with IDs
|
2003-09-18 18:56:35 +00:00
|
|
|
* 0 and 1 since they are reserved for the dictionary and the dictionary's
|
|
|
|
|
* codepage. Setting these properties explicitly might have surprising
|
|
|
|
|
* effects. An application should never do this but always use this
|
|
|
|
|
* method.</p>
|
|
|
|
|
*
|
|
|
|
|
* @param dictionary The dictionary
|
|
|
|
|
*
|
|
|
|
|
* @exception IllegalPropertySetDataException if the dictionary's key and
|
|
|
|
|
* value types are not correct.
|
|
|
|
|
*
|
|
|
|
|
* @see Section#getDictionary()
|
|
|
|
|
*/
|
|
|
|
|
public void setDictionary(final Map dictionary)
|
|
|
|
|
throws IllegalPropertySetDataException
|
|
|
|
|
{
|
|
|
|
|
if (dictionary != null)
|
|
|
|
|
{
|
|
|
|
|
for (final Iterator i = dictionary.keySet().iterator();
|
|
|
|
|
i.hasNext();)
|
|
|
|
|
if (!(i.next() instanceof Long))
|
|
|
|
|
throw new IllegalPropertySetDataException
|
|
|
|
|
("Dictionary keys must be of type Long.");
|
|
|
|
|
for (final Iterator i = dictionary.values().iterator();
|
|
|
|
|
i.hasNext();)
|
|
|
|
|
if (!(i.next() instanceof String))
|
|
|
|
|
throw new IllegalPropertySetDataException
|
|
|
|
|
("Dictionary values must be of type String.");
|
|
|
|
|
this.dictionary = dictionary;
|
|
|
|
|
|
|
|
|
|
/* Set the dictionary property (ID 0). Please note that the second
|
|
|
|
|
* parameter in the method call below is unused because dictionaries
|
|
|
|
|
* don't have a type. */
|
|
|
|
|
setProperty(PropertyIDMap.PID_DICTIONARY, -1, dictionary);
|
|
|
|
|
|
|
|
|
|
/* Set the codepage property (ID 1) for the strings used in the
|
|
|
|
|
* dictionary. HPSF always writes Unicode strings to the
|
|
|
|
|
* dictionary. */
|
|
|
|
|
setProperty(PropertyIDMap.PID_CODEPAGE, Variant.VT_I2,
|
2004-06-22 16:11:39 +00:00
|
|
|
new Integer(Constants.CP_UNICODE));
|
2003-09-18 18:56:35 +00:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
/* Setting the dictionary to null means to remove property 0.
|
|
|
|
|
* However, it does not mean to remove property 1 (codepage). */
|
|
|
|
|
removeProperty(PropertyIDMap.PID_DICTIONARY);
|
2003-08-30 09:13:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|