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 of other types than {@link
85    * Variant#VT_I4}, {@link Variant#VT_FILETIME}, {@link
86    * Variant#VT_LPSTR}, {@link Variant#VT_CF}, {@link Variant#VT_BOOL},
87    * and reading the dictionary property is not yet implemented.</p>
88    *
89    * @author Rainer Klute (klute@rainer-klute.de)
90    * @author Drew Varner (Drew.Varner InAndAround sc.edu)
91    * @see Section
92    * @see Variant
93    * @version $Id: Property.java,v 1.9 2002/07/22 08:25:18 klute Exp $
94    * @since 2002-02-09
95    */
96   public class Property
97   {
98   
99       private int id;
100  
101  
102      /**
103       * <p>Returns the property's ID.</p>
104       *
105       * @return The ID value
106       */
107      public int getID()
108      {
109          return id;
110      }
111  
112  
113  
114      private long type;
115  
116  
117      /**
118       * <p>Returns the property's type.</p>
119       *
120       * @return The type value
121       */
122      public long getType()
123      {
124          return type;
125      }
126  
127  
128  
129      private Object value;
130  
131  
132      /**
133       * <p>Returns the property's value.</p>
134       *
135       * @return The property's value
136       */
137      public Object getValue()
138      {
139          return value;
140      }
141  
142  
143  
144      /**
145       * <p>Creates a {@link Property} instance by reading its bytes
146       * from the property set stream.</p>
147       *
148       * @param id The property's ID.
149       * @param src The bytes the property set stream consists of.
150       * @param offset The property's type/value pair's offset in the
151       * section.
152       * @param length The property's type/value pair's length in bytes.
153       */
154      public Property(final int id, final byte[] src, final long offset,
155  		    int length)
156      {
157          this.id = id;
158  
159          /*
160           *  ID 0 is a special case since it specifies a dictionary of
161           *  property IDs and property names.
162           */
163          if (id == 0)
164  	{
165              value = readDictionary(src, offset, length);
166              return;
167          }
168  
169          /*
170           *  FIXME: Support this!
171           */
172  //        /* ID 1 is another special case: It denotes the code page of
173  //         * byte strings in this section. */
174  //        if (id == 1)
175  //        {
176  //            value = readCodepage(src, offset);
177  //            return;
178  //        }
179  
180          int o = (int) offset;
181          type = LittleEndian.getUInt(src, o);
182          o += LittleEndian.INT_SIZE;
183  
184          /*
185           *  FIXME: Support reading more types!
186           */
187          switch ((int)type) {
188              case Variant.VT_I4:
189              {
190                  /*
191                   *  Read a word. In Java it is represented as an
192                   *  Integer object.
193                   */
194                  value = new Long(LittleEndian.getUInt(src, o));
195                  break;
196              }
197              case Variant.VT_FILETIME:
198              {
199                  /*
200                   *  Read a FILETIME object. In Java it is represented
201                   *  as a Date.
202                   */
203                  final long low = LittleEndian.getUInt(src, o);
204                  o += LittleEndian.INT_SIZE;
205                  final long high = LittleEndian.getUInt(src, o);
206                  value = Util.filetimeToDate((int)high, (int)low);
207                  break;
208              }
209              case Variant.VT_LPSTR:
210              {
211                  /*
212                   *  Read a byte string. In Java it is represented as a
213                   *  String. The null bytes at the end of the byte
214                   *  strings must be stripped.
215                   */
216                  final int first = o + LittleEndian.INT_SIZE;
217                  long last = first + LittleEndian.getUInt(src, o) - 1;
218                  o += LittleEndian.INT_SIZE;
219                  while (src[(int)last] == 0 && first <= last) {
220                      last--;
221                  }
222                  value = new String(src, (int)first, (int)(last - first + 1));
223                  break;
224              }
225              case Variant.VT_CF:
226              {
227                  /*
228                   *  The first four bytes in src, from rc[offset] to
229                   *  src[offset + 3] contain the DWord for VT_CF, so
230                   *  skip it, we don't need it.
231                   */
232                  /*
233                   *  Truncate the length of the return array by a DWord
234                   *  length (4 bytes).
235                   */
236                  length = length - LittleEndian.INT_SIZE;
237  
238                  final byte[] v = new byte[length];
239                  for (int i = 0; i < length; i++)
240                      v[i] = src[(int)(o + i)];
241  		value = v;
242                  break;
243              }
244              case Variant.VT_BOOL:
245              {
246                  /*
247                   *  The first four bytes in src, from src[offset] to
248                   *  src[offset + 3] contain the DWord for VT_BOOL, so
249                   *  skip it, we don't need it.
250                   */
251                  final int first = o + LittleEndian.INT_SIZE;
252                  long bool = LittleEndian.getUInt(src, o);
253                  if (bool != 0)
254                      value = new Boolean(true);
255                  else
256                      value = new Boolean(false);
257  		break;
258              }
259              default:
260              {
261                  final byte[] v = new byte[length];
262                  for (int i = 0; i < length; i++)
263                      v[i] = src[(int)(offset + i)];
264  		value = v;
265                  break;
266              }
267          }
268      }
269  
270  
271  
272      /**
273       * <p>Reads a dictionary.</p>
274       *
275       * @param src The byte array containing the bytes making out the
276       * dictionary.
277       * @param offset At this offset within <var>src</var> the
278       * dictionary starts.
279       * @param length The dictionary contains at most this many bytes.
280       * @return The dictonary
281       */
282      protected Map readDictionary(final byte[] src, final long offset,
283  				 final int length)
284      {
285          /*
286           *  FIXME: Check the length!
287           */
288          int o = (int)offset;
289  
290          /*
291           *  Read the number of dictionary entries.
292           */
293          final long nrEntries = LittleEndian.getUInt(src, o);
294          o += LittleEndian.INT_SIZE;
295  
296          final Map m = new HashMap((int)nrEntries, (float) 1.0);
297          for (int i = 0; i < nrEntries; i++)
298  	{
299              /*
300               *  The key
301               */
302              final Long id = new Long(LittleEndian.getUInt(src, o));
303              o += LittleEndian.INT_SIZE;
304  
305              /*
306               *  The value (a string)
307               */
308              final long sLength = LittleEndian.getUInt(src, o);
309              o += LittleEndian.INT_SIZE;
310  
311              /*
312               *  Strip trailing 0x00 bytes.
313               */
314              long l = sLength;
315              while (src[(int)(o + l - 1)] == 0x00)
316                  l--;
317              final String s = new String(src, o, (int)l);
318              o += sLength;
319              m.put(id, s);
320          }
321          return m;
322      }
323  
324  
325  
326      /**
327       * <p>Reads a code page.</p>
328       *
329       * @param src The byte array containing the bytes making out the
330       * code page.
331       * @param offset At this offset within <var>src</var> the code
332       * page starts.
333       * @return The code page.
334       */
335      protected int readCodePage(final byte[] src, final long offset)
336      {
337          throw new UnsupportedOperationException("FIXME");
338      }
339  
340  }
341