1    /* ====================================================================
2     * The Apache Software License, Version 1.1
3     *
4     * Copyright (c) 2000 The Apache Software Foundation.  All rights
5     * reserved.
6     *
7     * Redistribution and use in source and binary forms, with or without
8     * modification, are permitted provided that the following conditions
9     * are met:
10    *
11    * 1. Redistributions of source code must retain the above copyright
12    *    notice, this list of conditions and the following disclaimer.
13    *
14    * 2. Redistributions in binary form must reproduce the above copyright
15    *    notice, this list of conditions and the following disclaimer in
16    *    the documentation and/or other materials provided with the
17    *    distribution.
18    *
19    * 3. The end-user documentation included with the redistribution,
20    *    if any, must include the following acknowledgment:
21    *       "This product includes software developed by the
22    *        Apache Software Foundation (http://www.apache.org/)."
23    *    Alternately, this acknowledgment may appear in the software itself,
24    *    if and wherever such third-party acknowledgments normally appear.
25    *
26    * 4. The names "Apache" and "Apache Software Foundation" must
27    *    not be used to endorse or promote products derived from this
28    *    software without prior written permission. For written
29    *    permission, please contact apache@apache.org.
30    *
31    * 5. Products derived from this software may not be called "Apache",
32    *    nor may "Apache" appear in their name, without prior written
33    *    permission of the Apache Software Foundation.
34    *
35    * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
36    * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
37    * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
38    * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
39    * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
40    * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
41    * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
42    * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
43    * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
44    * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
45    * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
46    * SUCH DAMAGE.
47    * ====================================================================
48    *
49    * This software consists of voluntary contributions made by many
50    * individuals on behalf of the Apache Software Foundation.  For more
51    * information on the Apache Software Foundation, please see
52    * <http://www.apache.org/>.
53   *
54   * Portions of this software are based upon public domain software
55   * originally written at the National Center for Supercomputing Applications,
56   * University of Illinois, Urbana-Champaign.
57    *
58    * Portions of this software are based upon public domain software
59    * originally written at the National Center for Supercomputing Applications,
60    * University of Illinois, Urbana-Champaign.
61    */
62   
63   package org.apache.poi.hpsf;
64   
65   import java.util.*;
66   import org.apache.poi.hpsf.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    * {@link DWord} telling how many bytes the string contains. The bytes
79    * follow 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 those
85    * mentioned above and the dictionary property is not yet
86    * implemented.</p>
87    *
88    * @see Section
89    * @see Variant
90    *
91    * @author Rainer Klute (klute@rainer-klute.de)
92    * @author Drew Varner (Drew.Varner InAndAround sc.edu)
93    *
94    * @version $Id: Property.java,v 1.5 2002/05/06 23:25:35 acoliver Exp $
95    * @since 2002-02-09
96    */
97   public class Property
98   {
99   
100      private int id;
101  
102      /**
103       * <p>Returns the property's ID.</p>
104       */
105      public int getID()
106      {
107          return id;
108      }
109  
110  
111  
112      private int type;
113  
114      /**
115       * <p>Returns the property's type.</p>
116       */
117      public int getType()
118      {
119          return type;
120      }
121  
122  
123  
124      private Object value;
125  
126      /**
127       * <p>Returns the property value's.</p>
128       */
129      public Object getValue()
130      {
131          return value;
132      }
133  
134  
135  
136      /**
137       * <p>Creates a {@link Property} instance by reading its bytes
138       * from the property set stream.</p>
139       *
140       * @param id The property's ID.
141       *
142       * @param src The bytes the property set stream consists of.
143       *
144       * @param offset The property's type/value pair's offset in the
145       * section.
146       *
147       * @param length The property's type/value pair's length in bytes.
148       * list.
149       */
150      public Property(final int id, final byte[] src, final int offset,
151                      int length)
152      {
153          this.id = id;
154  
155          /* ID 0 is a special case since it specifies a dictionary of
156           * property IDs and property names. */
157          if (id == 0)
158          {
159              value = readDictionary(src, offset, length);
160              return;
161          }
162  
163          /* FIXME: Support this! */
164  //        /* ID 1 is another special case: It denotes the code page of
165  //         * byte strings in this section. */
166  //        if (id == 1)
167  //        {
168  //            value = readCodepage(src, offset);
169  //            return;
170  //        }
171  
172          int o = offset;
173          type = new DWord(src, o).intValue();
174          o += DWord.LENGTH;
175  
176          /* FIXME: Support reading more types! */
177          switch (type)
178          {
179              case Variant.VT_I4:
180              {
181                  /* Read a word. In Java it is represented as an
182                     Integer object. */
183                  value = new Integer(new DWord(src, o).intValue());
184                  break;
185              }
186              case Variant.VT_FILETIME:
187              {
188                  /* Read a FILETIME object. In Java it is represented
189                     as a Date. */
190                  final int low = new DWord(src, o).intValue();
191                  o += DWord.LENGTH;
192                  final int high = new DWord(src, o).intValue();
193                  value = Util.filetimeToDate(high, low);
194                  break;
195              }
196              case Variant.VT_LPSTR:
197              {
198                  /* Read a byte string. In Java it is represented as a
199                     String. The null bytes at the end of the byte
200                     strings must be stripped. */
201                  final int first = o + DWord.LENGTH;
202                  int last = first + new DWord(src, o).intValue() - 1;
203                  o += DWord.LENGTH;
204                  while (src[last] == 0 && first <= last)
205                      last--;
206                  value = new String(src, first, last - first + 1);
207                  break;
208              }
209              case Variant.VT_CF:
210              {
211                  /* The first four bytes in src, from rc[offset] to
212                   * src[offset + 3] contain the DWord for VT_CF, so
213                   * skip it, we don't need it. */
214  
215                  /* Truncate the length of the return array by a DWord
216                   * length (4 bytes). */
217                  length = length - DWord.LENGTH;
218  
219                  final byte[] v = new byte[length];
220                  for (int i = 0; i < length; i++)
221                      v[i] = src[o + i];
222                  value = v;
223                  break;
224              }
225              case Variant.VT_BOOL:
226              {
227                  /* The first four bytes in src, from src[offset] to
228                   * src[offset + 3] contain the DWord for VT_BOOL, so
229                   * skip it, we don't need it. */
230                  final int first = o + DWord.LENGTH;
231                  DWord bool = new DWord(src,o);
232                  if (bool.intValue() == -1)
233                  {
234                      value = new Boolean(true);
235                  }
236                  else if (bool.intValue() == 0)
237                  {
238                      value = new Boolean(false);
239                  }
240                  else
241                      /* FIXME: Someone might invent a new
242                       * HPSFRuntimeException subclass
243                       * IllegalPropertySetDataException for this and
244                       * similar cases. */
245                      throw new HPSFRuntimeException
246                          ("Illegal property set data: A boolean must be " +
247                           "either -1 (true) or 0 (false).");
248                  break;
249              }
250              default:
251              {
252                  final byte[] v = new byte[length];
253                  for (int i = 0; i < length; i++)
254                      v[i] = src[offset + i];
255                  value = v;
256                  break;
257              }
258          }
259      }
260  
261  
262  
263      /**
264       * <p>Reads a dictionary.</p>
265       *
266       * @param src The byte array containing the bytes making out the
267       * dictionary.
268       *
269       * @param offset At this offset within <var>src</var> the
270       * dictionary starts.
271       *
272       * @param length The dictionary contains at most this many bytes.
273       */
274      protected Map readDictionary(final byte[] src, final int offset,
275                                   final int length)
276      {
277          /* FIXME: Check the length! */
278          int o = offset;
279  
280          /* Read the number of dictionary entries. */
281          final int nrEntries = new DWord(src, o).intValue();
282          o += DWord.LENGTH;
283  
284          final Map m = new HashMap(nrEntries, (float) 1.0);
285          for (int i = 0; i < nrEntries; i++)
286          {
287              /* The key */
288              final Integer id = new Integer(new DWord(src, o).intValue());
289              o += DWord.LENGTH;
290  
291              /* The value (a string) */
292              final int sLength = new DWord(src, o).intValue();
293              o += DWord.LENGTH;
294              /* Strip trailing 0x00 bytes. */
295              int l = sLength;
296              while (src[o + l - 1] == 0x00)
297                  l--;
298              final String s = new String(src, o, l);
299              o += sLength;
300              m.put(id, s);
301          }
302          return m;
303      }
304  
305  
306  
307      /**
308       * <p>Reads a code page.</p>
309       *
310       * @param src The byte array containing the bytes making out the
311       * code page.
312       *
313       * @param offset At this offset within <var>src</var> the code
314       * page starts.
315       */
316      protected int readCodePage(final byte[] src, final int offset)
317      {
318          throw new UnsupportedOperationException("FIXME");
319      }
320  
321  }
322