/components/forks/poi/src/loci/poi/hpsf/Property.java
Java | 449 lines | 215 code | 59 blank | 175 comment | 47 complexity | 8fa7785e3864553983ffce8b9a33e916 MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.1, Apache-2.0, BSD-2-Clause, MPL-2.0-no-copyleft-exception
- /*
- * #%L
- * Fork of Apache Jakarta POI.
- * %%
- * Copyright (C) 2008 - 2013 Open Microscopy Environment:
- * - Board of Regents of the University of Wisconsin-Madison
- * - Glencoe Software, Inc.
- * - University of Dundee
- * %%
- * 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.
- * #L%
- */
- /* ====================================================================
- Licensed to the Apache Software Foundation (ASF) under one or more
- contributor license agreements. See the NOTICE file distributed with
- this work for additional information regarding copyright ownership.
- The ASF licenses this file to You under the Apache License, Version 2.0
- (the "License"); you may not use this file except in compliance with
- the License. You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- ==================================================================== */
-
- package loci.poi.hpsf;
- import java.io.UnsupportedEncodingException;
- import java.util.HashMap;
- import java.util.Map;
- import loci.poi.util.HexDump;
- import loci.poi.util.LittleEndian;
- /**
- * <p>A property in a {@link Section} of a {@link PropertySet}.</p>
- *
- * <p>The property's <strong>ID</strong> gives the property a meaning
- * in the context of its {@link Section}. Each {@link Section} spans
- * its own name space of property IDs.</p>
- *
- * <p>The property's <strong>type</strong> determines how its
- * <strong>value </strong> is interpreted. For example, if the type is
- * {@link Variant#VT_LPSTR} (byte string), the value consists of a
- * DWord telling how many bytes the string contains. The bytes follow
- * immediately, including any null bytes that terminate the
- * string. The type {@link Variant#VT_I4} denotes a four-byte integer
- * value, {@link Variant#VT_FILETIME} some date and time (of a
- * file).</p>
- *
- * <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>
- *
- * <p>Microsoft documentation: <a
- * href="http://msdn.microsoft.com/library/en-us/stg/stg/property_set_display_name_dictionary.asp?frame=true">
- * Property Set Display Name Dictionary</a>.
- *
- * @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
- * @version $Id: Property.java 489730 2006-12-22 19:18:16Z bayard $
- * @since 2002-02-09
- */
- public class Property
- {
- /** <p>The property's ID.</p> */
- protected long id;
- /**
- * <p>Returns the property's ID.</p>
- *
- * @return The ID value
- */
- public long getID()
- {
- return id;
- }
- /** <p>The property's type.</p> */
- protected long type;
- /**
- * <p>Returns the property's type.</p>
- *
- * @return The type value
- */
- public long getType()
- {
- return type;
- }
- /** <p>The property's value.</p> */
- protected Object value;
- /**
- * <p>Returns the property's value.</p>
- *
- * @return The property's value
- */
- public Object getValue()
- {
- return value;
- }
- /**
- * <p>Creates a property.</p>
- *
- * @param id the property's ID.
- * @param type the property's type, see {@link Variant}.
- * @param value the property's value. Only certain types are allowed, see {@link Variant}.
- */
- public Property(final long id, final long type, final Object value)
- {
- this.id = id;
- this.type = type;
- this.value = value;
- }
- /**
- * <p>Creates a {@link Property} instance by reading its bytes
- * from the property set stream.</p>
- *
- * @param id The property's ID.
- * @param src The bytes the property set stream consists of.
- * @param offset The property's type/value pair's offset in the
- * section.
- * @param length The property's type/value pair's length in bytes.
- * @param codepage The section's and thus the property's
- * codepage. It is needed only when reading string values.
- * @exception UnsupportedEncodingException if the specified codepage is not
- * supported.
- */
- public Property(final long id, final byte[] src, final long offset,
- final int length, final int codepage)
- throws UnsupportedEncodingException
- {
- this.id = id;
- /*
- * ID 0 is a special case since it specifies a dictionary of
- * property IDs and property names.
- */
- if (id == 0)
- {
- value = readDictionary(src, offset, length, codepage);
- return;
- }
- int o = (int) offset;
- type = LittleEndian.getUInt(src, o);
- o += LittleEndian.INT_SIZE;
- try
- {
- value = VariantSupport.read(src, o, length, (int) type, codepage);
- }
- catch (UnsupportedVariantTypeException ex)
- {
- VariantSupport.writeUnsupportedTypeMessage(ex);
- value = ex.getValue();
- }
- }
- /**
- * <p>Creates an empty property. It must be filled using the set method to
- * be usable.</p>
- */
- protected Property()
- { }
- /**
- * <p>Reads a dictionary.</p>
- *
- * @param src The byte array containing the bytes making out the dictionary.
- * @param offset At this offset within <var>src </var> the dictionary
- * starts.
- * @param length The dictionary contains at most this many bytes.
- * @param codepage The codepage of the string values.
- * @return The dictonary
- * @throws UnsupportedEncodingException if the dictionary's codepage is not
- * (yet) supported.
- */
- protected Map readDictionary(final byte[] src, final long offset,
- final int length, final int codepage)
- throws UnsupportedEncodingException
- {
- /* Check whether "offset" points into the "src" array". */
- if (offset < 0 || offset > src.length)
- throw new HPSFRuntimeException
- ("Illegal offset " + offset + " while HPSF stream contains " +
- length + " bytes.");
- int o = (int) offset;
- /*
- * Read the number of dictionary entries.
- */
- final long nrEntries = LittleEndian.getUInt(src, o);
- o += LittleEndian.INT_SIZE;
- final Map m = new HashMap((int) nrEntries, (float) 1.0);
- for (int i = 0; i < nrEntries; i++)
- {
- /* The key. */
- final Long id = new Long(LittleEndian.getUInt(src, o));
- o += LittleEndian.INT_SIZE;
- /* The value (a string). The length is the either the
- * number of (two-byte) characters if the character set is Unicode
- * or the number of bytes if the character set is not Unicode.
- * The length includes terminating 0x00 bytes which we have to strip
- * off to create a Java string. */
- long sLength = LittleEndian.getUInt(src, o);
- o += LittleEndian.INT_SIZE;
- /* Read the string. */
- final StringBuffer b = new StringBuffer();
- switch (codepage)
- {
- case -1:
- {
- /* Without a codepage the length is equal to the number of
- * bytes. */
- b.append(new String(src, o, (int) sLength));
- break;
- }
- case Constants.CP_UNICODE:
- {
- /* The length is the number of characters, i.e. the number
- * of bytes is twice the number of the characters. */
- final int nrBytes = (int) (sLength * 2);
- final byte[] h = new byte[nrBytes];
- for (int i2 = 0; i2 < nrBytes; i2 += 2)
- {
- h[i2] = src[o + i2 + 1];
- h[i2 + 1] = src[o + i2];
- }
- b.append(new String(h, 0, nrBytes,
- VariantSupport.codepageToEncoding(codepage)));
- break;
- }
- default:
- {
- /* For encodings other than Unicode the length is the number
- * of bytes. */
- b.append(new String(src, o, (int) sLength,
- VariantSupport.codepageToEncoding(codepage)));
- break;
- }
- }
- /* Strip 0x00 characters from the end of the string: */
- while (b.length() > 0 && b.charAt(b.length() - 1) == 0x00)
- b.setLength(b.length() - 1);
- if (codepage == Constants.CP_UNICODE)
- {
- if (sLength % 2 == 1)
- sLength++;
- o += (sLength + sLength);
- }
- else
- o += sLength;
- m.put(id, b.toString());
- }
- return m;
- }
- /**
- * <p>Returns the property's size in bytes. This is always a multiple of
- * 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() throws WritingNotSupportedException
- {
- 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. */
- switch ((int) type)
- {
- case Variant.VT_LPSTR:
- {
- int l = ((String) value).length() + 1;
- int r = l % PADDING;
- if (r > 0)
- l += PADDING - r;
- length += l;
- break;
- }
- case Variant.VT_EMPTY:
- break;
- default:
- throw new WritingNotSupportedException(type, value);
- }
- return length;
- }
- /**
- * <p>Compares two properties.</p>
- *
- * <p>Please beware that a property with ID == 0 is a special case: It does not have a type, and its value is the section's
- * dictionary. Another special case are strings: Two properties may have
- * the different types Variant.VT_LPSTR and Variant.VT_LPWSTR;</p>
- *
- * @see Object#equals(java.lang.Object)
- */
- public boolean equals(final Object o)
- {
- if (!(o instanceof Property))
- return false;
- final Property p = (Property) o;
- final Object pValue = p.getValue();
- final long pId = p.getID();
- if (id != pId || (id != 0 && !typesAreEqual(type, p.getType())))
- 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);
- }
- private boolean typesAreEqual(final long t1, final long t2)
- {
- if (t1 == t2 ||
- (t1 == Variant.VT_LPSTR && t2 == Variant.VT_LPWSTR) ||
- (t2 == Variant.VT_LPSTR && t1 == Variant.VT_LPWSTR))
- return true;
- else
- return false;
- }
- /**
- * @see Object#hashCode()
- */
- public int hashCode()
- {
- 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());
- final Object value = getValue();
- b.append(", value: ");
- b.append(value.toString());
- if (value instanceof String)
- {
- final String s = (String) value;
- final int l = s.length();
- final byte[] bytes = new byte[l * 2];
- for (int i = 0; i < l; i++)
- {
- final char c = s.charAt(i);
- final byte high = (byte) ((c & 0x00ff00) >> 8);
- final byte low = (byte) ((c & 0x0000ff) >> 0);
- bytes[i * 2] = high;
- bytes[i * 2 + 1] = low;
- }
- final String hex = HexDump.dump(bytes, 0L, 0);
- b.append(" [");
- b.append(hex);
- b.append("]");
- }
- b.append(']');
- return b.toString();
- }
- }