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