1    /*
2     *  ====================================================================
3     *  The Apache Software License, Version 1.1
4     *
5     *  Copyright (c) 2000 The Apache Software Foundation.  All rights
6     *  reserved.
7     *
8     *  Redistribution and use in source and binary forms, with or without
9     *  modification, are permitted provided that the following conditions
10    *  are met:
11    *
12    *  1. Redistributions of source code must retain the above copyright
13    *  notice, this list of conditions and the following disclaimer.
14    *
15    *  2. Redistributions in binary form must reproduce the above copyright
16    *  notice, this list of conditions and the following disclaimer in
17    *  the documentation and/or other materials provided with the
18    *  distribution.
19    *
20    *  3. The end-user documentation included with the redistribution,
21    *  if any, must include the following acknowledgment:
22    *  "This product includes software developed by the
23    *  Apache Software Foundation (http://www.apache.org/)."
24    *  Alternately, this acknowledgment may appear in the software itself,
25    *  if and wherever such third-party acknowledgments normally appear.
26    *
27    *  4. The names "Apache" and "Apache Software Foundation" must
28    *  not be used to endorse or promote products derived from this
29    *  software without prior written permission. For written
30    *  permission, please contact apache@apache.org.
31    *
32    *  5. Products derived from this software may not be called "Apache",
33    *  nor may "Apache" appear in their name, without prior written
34    *  permission of the Apache Software Foundation.
35    *
36    *  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
37    *  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
38    *  OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
39    *  DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
40    *  ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
41    *  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
42    *  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
43    *  USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
44    *  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
45    *  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
46    *  OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
47    *  SUCH DAMAGE.
48    *  ====================================================================
49    *
50    *  This software consists of voluntary contributions made by many
51    *  individuals on behalf of the Apache Software Foundation.  For more
52    *  information on the Apache Software Foundation, please see
53    *  <http://www.apache.org/>.
54    *
55    *  Portions of this software are based upon public domain software
56    *  originally written at the National Center for Supercomputing Applications,
57    *  University of Illinois, Urbana-Champaign.
58    *
59    *  Portions of this software are based upon public domain software
60    *  originally written at the National Center for Supercomputing Applications,
61    *  University of Illinois, Urbana-Champaign.
62    */
63   package org.apache.poi.hpsf;
64   
65   import java.util.*;
66   import org.apache.poi.util.LittleEndian;
67   
68   /**
69    * <p>A property in a {@link Section} of a {@link PropertySet}.</p>
70    *
71    * <p>The property's <strong>ID</strong> gives the property a meaning
72    * in the context of its {@link Section}. Each {@link Section} spans
73    * its own name space of property IDs.</p>
74    *
75    * <p>The property's <strong>type</strong> determines how its
76    * <strong>value </strong> is interpreted. For example, if the type is
77    * {@link Variant#VT_LPSTR} (byte string), the value consists of a
78    * DWord telling how many bytes the string contains. The bytes follow
79    * immediately, including any null bytes that terminate the
80    * string. The type {@link Variant#VT_I4} denotes a four-byte integer
81    * value, {@link Variant#VT_FILETIME} some date and time (of a
82    * file).</p>
83    *
84    * <p><strong>FIXME:</strong> Reading is not implemented for all
85    * {@link Variant} types yet. Feel free to submit error reports or
86    * patches for the types you need.</p>
87    *
88    * @author Rainer Klute (klute@rainer-klute.de)
89    * @author Drew Varner (Drew.Varner InAndAround sc.edu)
90    * @see Section
91    * @see Variant
92    * @version $Id: Property.java,v 1.10 2002/12/10 06:15:19 klute Exp $
93    * @since 2002-02-09
94    */
95   public class Property
96   {
97   
98       /* Codepage 1200 denotes Unicode. */
99       private static int CP_UNICODE = 1200;
100  
101      private int id;
102  
103  
104      /**
105       * <p>Returns the property's ID.</p>
106       *
107       * @return The ID value
108       */
109      public int getID()
110      {
111          return id;
112      }
113  
114  
115  
116      private long type;
117  
118  
119      /**
120       * <p>Returns the property's type.</p>
121       *
122       * @return The type value
123       */
124      public long getType()
125      {
126          return type;
127      }
128  
129  
130  
131      private Object value;
132  
133  
134      /**
135       * <p>Returns the property's value.</p>
136       *
137       * @return The property's value
138       */
139      public Object getValue()
140      {
141          return value;
142      }
143  
144  
145  
146      /**
147       * <p>Creates a {@link Property} instance by reading its bytes
148       * from the property set stream.</p>
149       *
150       * @param id The property's ID.
151       * @param src The bytes the property set stream consists of.
152       * @param offset The property's type/value pair's offset in the
153       * section.
154       * @param length The property's type/value pair's length in bytes.
155       * @param codepage The section's and thus the property's
156       * codepage. It is needed only when reading string values.
157       */
158      public Property(final int id, final byte[] src, final long offset,
159  		    int length, int codepage)
160      {
161          this.id = id;
162  
163          /*
164           * ID 0 is a special case since it specifies a dictionary of
165           * property IDs and property names.
166           */
167          if (id == 0)
168  	{
169              value = readDictionary(src, offset, length, codepage);
170              return;
171          }
172  
173          int o = (int) offset;
174          type = LittleEndian.getUInt(src, o);
175          o += LittleEndian.INT_SIZE;
176  
177  	try
178  	{
179  	    value = TypeReader.read(src, o, length, (int) type);
180  	}
181  	catch (Throwable t)
182  	{
183  	    t.printStackTrace();
184  	    value = "*** null ***";
185  	}
186      }
187  
188  
189  
190      /**
191       * <p>Reads a dictionary.</p>
192       *
193       * @param src The byte array containing the bytes making out the
194       * dictionary.
195       * @param offset At this offset within <var>src</var> the
196       * dictionary starts.
197       * @param length The dictionary contains at most this many bytes.
198       * @param codepage The codepage of the string values.
199       * @return The dictonary
200       */
201      protected Map readDictionary(final byte[] src, final long offset,
202  				 final int length, final int codepage)
203      {
204  	/* Check whether "offset" points into the "src" array". */
205  	if (offset < 0 || offset > src.length)
206  	    throw new HPSFRuntimeException
207  		("Illegal offset " + offset + " while HPSF stream contains " +
208  		 length + " bytes.");
209          int o = (int) offset;
210  
211          /*
212           * Read the number of dictionary entries.
213           */
214          final long nrEntries = LittleEndian.getUInt(src, o);
215          o += LittleEndian.INT_SIZE;
216  
217          final Map m = new HashMap((int) nrEntries, (float) 1.0);
218          for (int i = 0; i < nrEntries; i++)
219  	{
220              /* The key. */
221              final Long id = new Long(LittleEndian.getUInt(src, o));
222              o += LittleEndian.INT_SIZE;
223  
224              /* The value (a string). The length is the either the
225               * number of characters if the character set is Unicode or
226               * else the number of bytes. The length includes
227               * terminating 0x00 bytes which we have to strip off to
228               * create a Java string. */
229              long sLength = LittleEndian.getUInt(src, o);
230              o += LittleEndian.INT_SIZE;
231  
232              /* Read the bytes or characters depending on whether the
233               * character set is Unicode or not. */
234  	    StringBuffer b = new StringBuffer((int) sLength);
235  	    for (int j = 0; j < sLength; j++)
236  		if (codepage == CP_UNICODE)
237  		{
238  		    final int i1 = o + (j * 2);
239  		    final int i2 = i1 + 1;
240  		    b.append((char) ((src[i2] << 8) + src[i1]));
241  		}
242  		else
243  		    b.append((char) src[o + j]);
244  
245  	    /* Strip 0x00 characters from the end of the string: */
246  	    while (b.charAt(b.length() - 1) == 0x00)
247  		b.setLength(b.length() - 1);
248  	    if (codepage == CP_UNICODE)
249  	    {
250  		if (sLength % 2 == 1)
251  		    sLength++;
252  		o += (sLength + sLength);
253  	    }
254  	    else
255  		o += sLength;
256              m.put(id, b.toString());
257          }
258          return m;
259      }
260  
261  }
262