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   package org.apache.poi.hpsf;
56   
57   import java.util.*;
58   import org.apache.poi.util.LittleEndian;
59   import org.apache.poi.hpsf.wellknown.*;
60   
61   /**
62    * <p>Represents a section in a {@link PropertySet}.</p>
63    *
64    * @author Rainer Klute (klute@rainer-klute.de)
65    * @author Drew Varner (Drew.Varner allUpIn sc.edu)
66    * @version $Id: Section.java,v 1.7 2002/07/17 16:23:22 klute Exp $
67    * @since 2002-02-09
68    */
69   public class Section
70   {
71   
72       /**
73        * <p>Maps property IDs to section-private PID strings. These
74        * strings can be found in the property with ID 0.</p>
75        */
76       protected Map dictionary;
77   
78       private ClassID formatID;
79   
80   
81       /**
82        * <p>Returns the format ID. The format ID is the "type" of the
83        * section.</p>
84        *
85        * @return The format ID
86        */
87       public ClassID getFormatID()
88       {
89           return formatID;
90       }
91   
92   
93   
94       private long offset;
95   
96   
97       /**
98        * <p>Returns the offset of the section in the stream.</p>
99        *
100       * @return The offset of the section in the stream.
101       */
102      public long getOffset()
103      {
104          return offset;
105      }
106  
107  
108  
109      private int size;
110  
111  
112      /**
113       * <p>Returns the section's size in bytes.</p>
114       *
115       * @return The section's size in bytes.
116       */
117      public int getSize()
118      {
119          return size;
120      }
121  
122  
123  
124      private int propertyCount;
125  
126  
127      /**
128       * <p>Returns the number of properties in this section.</p>
129       *
130       * @return The number of properties in this section.
131       */
132      public int getPropertyCount()
133      {
134          return propertyCount;
135      }
136  
137  
138  
139      private Property[] properties;
140  
141  
142      /**
143       * <p>Returns this section's properties.</p>
144       *
145       * @return This section's properties.
146       */
147      public Property[] getProperties()
148      {
149          return properties;
150      }
151  
152  
153  
154      /**
155       * <p>Creates a {@link Section} instance from a byte array.</p>
156       *
157       * @param src Contains the complete property set stream.
158       * @param offset The position in the stream that points to the
159       * section's format ID.
160       */
161      public Section(final byte[] src, int offset)
162      {
163          /*
164           *  Read the format ID.
165           */
166          formatID = new ClassID(src, offset);
167          offset += ClassID.LENGTH;
168  
169          /*
170           *  Read the offset from the stream's start and positions to
171           *  the section header.
172           */
173          this.offset = LittleEndian.getUInt(src, offset);
174          offset = (int)this.offset;
175  
176          /*
177           *  Read the section length.
178           */
179          size = (int)LittleEndian.getUInt(src, offset);
180          offset += LittleEndian.INT_SIZE;
181  
182          /*
183           *  Read the number of properties.
184           */
185          propertyCount = (int)LittleEndian.getUInt(src, offset);
186          offset += LittleEndian.INT_SIZE;
187  
188          /*
189           *  Read the properties. The offset is positioned at the first
190           *  entry of the property list.
191           */
192          properties = new Property[propertyCount];
193          for (int i = 0; i < properties.length; i++) {
194              final int id = (int)LittleEndian.getUInt(src, offset);
195              offset += LittleEndian.INT_SIZE;
196  
197              /*
198               *  Offset from the section.
199               */
200              final int sOffset = (int)LittleEndian.getUInt(src, offset);
201              offset += LittleEndian.INT_SIZE;
202  
203              /*
204               *  Calculate the length of the property.
205               */
206              int length;
207              if (i == properties.length - 1) {
208                  length = (int)(src.length - this.offset - sOffset);
209              } else {
210                  length = (int)
211                      LittleEndian.getUInt(src, offset + LittleEndian.INT_SIZE) -
212                      sOffset;
213              }
214  
215              /*
216               *  Create it.
217               */
218              properties[i] =
219                      new Property(id, src, this.offset + sOffset, length);
220          }
221  
222          /*
223           *  Extract the dictionary (if available).
224           */
225          dictionary = (Map) getProperty(0);
226      }
227  
228  
229  
230      /**
231       * <p>Returns the value of the property with the specified ID. If
232       * the property is not available, <code>null</code> is returned
233       * and a subsequent call to {@link #wasNull} will return
234       * <code>true</code>.</p>
235       *
236       * @param id The property's ID
237       *
238       * @return The property's value
239       */
240      protected Object getProperty(final int id)
241      {
242          wasNull = false;
243          for (int i = 0; i < properties.length; i++)
244              if (id == properties[i].getID())
245                  return properties[i].getValue();
246          wasNull = true;
247          return null;
248      }
249  
250  
251  
252      /**
253       * <p>Returns the value of the numeric property with the specified
254       * ID. If the property is not available, 0 is returned. A
255       * subsequent call to {@link #wasNull} will return
256       * <code>true</code> to let the caller distinguish that case from
257       * a real property value of 0.</p>
258       *
259       * @param id The property's ID
260       *
261       * @return The property's value
262       */
263      protected int getPropertyIntValue(final int id)
264      {
265          /* FIXME: Find out why the following is a Long instead of an
266           * Integer! */
267          final Long i = (Long) getProperty(id);
268          if (i != null)
269              return i.intValue();
270          else
271              return 0;
272      }
273  
274  
275  
276      /**
277       * <p>Returns the value of the boolean property with the specified
278       * ID. If the property is not available, <code>false</code> is
279       * returned. A subsequent call to {@link #wasNull} will return
280       * <code>true</code> to let the caller distinguish that case from
281       * a real property value of <code>false</code>.</p>
282       *
283       * @param id The property's ID
284       *
285       * @return The property's value
286       */
287      protected boolean getPropertyBooleanValue(final int id)
288      {
289          final Boolean b = (Boolean) getProperty(id);
290          if (b != null)
291              return b.booleanValue();
292          else
293              return false;
294          }
295  
296  
297  
298      private boolean wasNull;
299  
300  
301      /**
302       * <p>Checks whether the property which the last call to {@link
303       * #getPropertyIntValue} or {@link #getProperty} tried to access
304       * was available or not. This information might be important for
305       * callers of {@link #getPropertyIntValue} since the latter
306       * returns 0 if the property does not exist. Using {@link
307       * #wasNull} the caller can distiguish this case from a property's
308       * real value of 0.</p>
309       *
310       * @return <code>true</code> if the last call to {@link
311       * #getPropertyIntValue} or {@link #getProperty} tried to access a
312       * property that was not available, else <code>false</code>.
313       */
314      public boolean wasNull()
315      {
316          return wasNull;
317      }
318  
319  
320  
321      /**
322       * <p>Returns the PID string associated with a property ID. The ID
323       * is first looked up in the {@link Section}'s private
324       * dictionary. If it is not found there, the method calls {@link
325       * SectionIDMap#getPIDString}.</p>
326       *
327       * @param pid The property ID
328       *
329       * @return The property ID's string value
330       */
331      public String getPIDString(final int pid)
332      {
333          String s = null;
334          if (dictionary != null)
335              s = (String) dictionary.get(new Integer(pid));
336          if (s == null)
337              s = SectionIDMap.getPIDString(getFormatID().getBytes(), pid);
338          if (s == null)
339              s = SectionIDMap.UNDEFINED;
340          return s;
341      }
342  
343  }
344