KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > poi > hpsf > Section


1
2 /* ====================================================================
3    Copyright 2002-2004 Apache Software Foundation
4
5    Licensed under the Apache License, Version 2.0 (the "License");
6    you may not use this file except in compliance with the License.
7    You may obtain a copy of the License at
8
9        http://www.apache.org/licenses/LICENSE-2.0
10
11    Unless required by applicable law or agreed to in writing, software
12    distributed under the License is distributed on an "AS IS" BASIS,
13    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14    See the License for the specific language governing permissions and
15    limitations under the License.
16 ==================================================================== */

17         
18 package org.apache.poi.hpsf;
19
20 import java.io.UnsupportedEncodingException JavaDoc;
21 import java.util.ArrayList JavaDoc;
22 import java.util.Collections JavaDoc;
23 import java.util.Iterator JavaDoc;
24 import java.util.List JavaDoc;
25 import java.util.Map JavaDoc;
26
27 import org.apache.poi.hpsf.wellknown.PropertyIDMap;
28 import org.apache.poi.hpsf.wellknown.SectionIDMap;
29 import org.apache.poi.util.LittleEndian;
30
31 /**
32  * <p>Represents a section in a {@link PropertySet}.</p>
33  *
34  * @author Rainer Klute <a
35  * HREF="mailto:klute@rainer-klute.de">&lt;klute@rainer-klute.de&gt;</a>
36  * @author Drew Varner (Drew.Varner allUpIn sc.edu)
37  * @version $Id: Section.java,v 1.24 2004/08/15 13:43:35 klute Exp $
38  * @since 2002-02-09
39  */

40 public class Section
41 {
42
43     /**
44      * <p>Maps property IDs to section-private PID strings. These
45      * strings can be found in the property with ID 0.</p>
46      */

47     protected Map JavaDoc dictionary;
48
49     /**
50      * <p>The section's format ID, {@link #getFormatID}.</p>
51      */

52     protected ClassID formatID;
53
54
55     /**
56      * <p>Returns the format ID. The format ID is the "type" of the
57      * section. For example, if the format ID of the first {@link
58      * Section} contains the bytes specified by
59      * <code>org.apache.poi.hpsf.wellknown.SectionIDMap.SUMMARY_INFORMATION_ID</code>
60      * the section (and thus the property set) is a SummaryInformation.</p>
61      *
62      * @return The format ID
63      */

64     public ClassID getFormatID()
65     {
66         return formatID;
67     }
68
69
70
71     /**
72      * @see #getOffset
73      */

74     protected long offset;
75
76
77     /**
78      * <p>Returns the offset of the section in the stream.</p>
79      *
80      * @return The offset of the section in the stream.
81      */

82     public long getOffset()
83     {
84         return offset;
85     }
86
87
88
89     /**
90      * @see #getSize
91      */

92     protected int size;
93
94
95     /**
96      * <p>Returns the section's size in bytes.</p>
97      *
98      * @return The section's size in bytes.
99      */

100     public int getSize()
101     {
102         return size;
103     }
104
105
106
107     /**
108      * <p>Returns the number of properties in this section.</p>
109      *
110      * @return The number of properties in this section.
111      */

112     public int getPropertyCount()
113     {
114         return properties.length;
115     }
116
117
118
119     /**
120      * @see #getProperties
121      */

122     protected Property[] properties;
123
124
125     /**
126      * <p>Returns this section's properties.</p>
127      *
128      * @return This section's properties.
129      */

130     public Property[] getProperties()
131     {
132         return properties;
133     }
134
135
136
137     /**
138      * <p>Creates an empty and uninitialized {@link Section}.
139      */

140     protected Section()
141     { }
142
143
144
145     /**
146      * <p>Creates a {@link Section} instance from a byte array.</p>
147      *
148      * @param src Contains the complete property set stream.
149      * @param offset The position in the stream that points to the
150      * section's format ID.
151      *
152      * @exception UnsupportedEncodingException if the section's codepage is not
153      * supported.
154      */

155     public Section(final byte[] src, final int offset)
156     throws UnsupportedEncodingException JavaDoc
157     {
158         int o1 = offset;
159
160         /*
161          * Read the format ID.
162          */

163         formatID = new ClassID(src, o1);
164         o1 += ClassID.LENGTH;
165
166         /*
167          * Read the offset from the stream's start and positions to
168          * the section header.
169          */

170         this.offset = LittleEndian.getUInt(src, o1);
171         o1 = (int) this.offset;
172
173         /*
174          * Read the section length.
175          */

176         size = (int) LittleEndian.getUInt(src, o1);
177         o1 += LittleEndian.INT_SIZE;
178
179         /*
180          * Read the number of properties.
181          */

182         final int propertyCount = (int) LittleEndian.getUInt(src, o1);
183         o1 += LittleEndian.INT_SIZE;
184
185         /*
186          * Read the properties. The offset is positioned at the first
187          * entry of the property list. There are two problems:
188          *
189          * 1. For each property we have to find out its length. In the
190          * property list we find each property's ID and its offset relative
191          * to the section's beginning. Unfortunately the properties in the
192          * property list need not to be in ascending order, so it is not
193          * possible to calculate the length as
194          * (offset of property(i+1) - offset of property(i)). Before we can
195          * that we first have to sort the property list by ascending offsets.
196          *
197          * 2. We have to read the property with ID 1 before we read other
198          * properties, at least before other properties containing strings.
199          * The reason is that property 1 specifies the codepage. If it is
200          * 1200, all strings are in Unicode. In other words: Before we can
201          * read any strings we have to know whether they are in Unicode or
202          * not. Unfortunately property 1 is not guaranteed to be the first in
203          * a section.
204          *
205          * The algorithm below reads the properties in two passes: The first
206          * one looks for property ID 1 and extracts the codepage number. The
207          * seconds pass reads the other properties.
208          */

209         properties = new Property[propertyCount];
210         
211         /* Pass 1: Read the property list. */
212         int pass1Offset = o1;
213         List JavaDoc propertyList = new ArrayList JavaDoc(propertyCount);
214         PropertyListEntry ple;
215         for (int i = 0; i < properties.length; i++)
216         {
217             ple = new PropertyListEntry();
218
219             /* Read the property ID. */
220             ple.id = (int) LittleEndian.getUInt(src, pass1Offset);
221             pass1Offset += LittleEndian.INT_SIZE;
222
223             /* Offset from the section's start. */
224             ple.offset = (int) LittleEndian.getUInt(src, pass1Offset);
225             pass1Offset += LittleEndian.INT_SIZE;
226
227             /* Add the entry to the property list. */
228             propertyList.add(ple);
229         }
230
231         /* Sort the property list by ascending offsets: */
232         Collections.sort(propertyList);
233
234         /* Calculate the properties' lengths. */
235         for (int i = 0; i < propertyCount - 1; i++)
236         {
237             final PropertyListEntry ple1 =
238                 (PropertyListEntry) propertyList.get(i);
239             final PropertyListEntry ple2 =
240                 (PropertyListEntry) propertyList.get(i + 1);
241             ple1.length = ple2.offset - ple1.offset;
242         }
243         if (propertyCount > 0)
244         {
245             ple = (PropertyListEntry) propertyList.get(propertyCount - 1);
246             ple.length = size - ple.offset;
247         }
248
249         /* Look for the codepage. */
250         int codepage = -1;
251         for (final Iterator JavaDoc i = propertyList.iterator();
252              codepage == -1 && i.hasNext();)
253         {
254             ple = (PropertyListEntry) i.next();
255
256             /* Read the codepage if the property ID is 1. */
257             if (ple.id == PropertyIDMap.PID_CODEPAGE)
258             {
259                 /* Read the property's value type. It must be
260                  * VT_I2. */

261                 int o = (int) (this.offset + ple.offset);
262                 final long type = LittleEndian.getUInt(src, o);
263                 o += LittleEndian.INT_SIZE;
264
265                 if (type != Variant.VT_I2)
266                     throw new HPSFRuntimeException
267                         ("Value type of property ID 1 is not VT_I2 but " +
268                          type + ".");
269
270                 /* Read the codepage number. */
271                 codepage = LittleEndian.getUShort(src, o);
272             }
273         }
274
275         /* Pass 2: Read all properties - including the codepage property,
276          * if available. */

277         int i1 = 0;
278         for (final Iterator JavaDoc i = propertyList.iterator(); i.hasNext();)
279         {
280             ple = (PropertyListEntry) i.next();
281             properties[i1++] = new Property(ple.id, src,
282                                             this.offset + ple.offset,
283                                             ple.length, codepage);
284         }
285
286         /*
287          * Extract the dictionary (if available).
288          */

289         dictionary = (Map JavaDoc) getProperty(0);
290     }
291
292
293
294     /**
295      * <p>Represents an entry in the property list and holds a property's ID and
296      * its offset from the section's beginning.</p>
297      */

298     class PropertyListEntry implements Comparable JavaDoc
299     {
300         int id;
301         int offset;
302         int length;
303
304         /**
305          * <p>Compares this {@link PropertyListEntry} with another one by their
306          * offsets. A {@link PropertyListEntry} is "smaller" than another one if
307          * its offset from the section's begin is smaller.</p>
308          *
309          * @see Comparable#compareTo(java.lang.Object)
310          */

311         public int compareTo(final Object JavaDoc o)
312         {
313             if (!(o instanceof PropertyListEntry))
314                 throw new ClassCastException JavaDoc(o.toString());
315             final int otherOffset = ((PropertyListEntry) o).offset;
316             if (offset < otherOffset)
317                 return -1;
318             else if (offset == otherOffset)
319                 return 0;
320             else
321                 return 1;
322         }
323     }
324
325
326
327     /**
328      * <p>Returns the value of the property with the specified ID. If
329      * the property is not available, <code>null</code> is returned
330      * and a subsequent call to {@link #wasNull} will return
331      * <code>true</code>.</p>
332      *
333      * @param id The property's ID
334      *
335      * @return The property's value
336      */

337     public Object JavaDoc getProperty(final long id)
338     {
339         wasNull = false;
340         for (int i = 0; i < properties.length; i++)
341             if (id == properties[i].getID())
342                 return properties[i].getValue();
343         wasNull = true;
344         return null;
345     }
346
347
348
349     /**
350      * <p>Returns the value of the numeric property with the specified
351      * ID. If the property is not available, 0 is returned. A
352      * subsequent call to {@link #wasNull} will return
353      * <code>true</code> to let the caller distinguish that case from
354      * a real property value of 0.</p>
355      *
356      * @param id The property's ID
357      *
358      * @return The property's value
359      */

360     protected int getPropertyIntValue(final long id)
361     {
362         final Long JavaDoc i;
363         final Object JavaDoc o = getProperty(id);
364         if (o == null)
365             return 0;
366         if (!(o instanceof Long JavaDoc))
367             throw new HPSFRuntimeException
368                 ("This property is not an integer type, but " +
369                  o.getClass().getName() + ".");
370         i = (Long JavaDoc) o;
371         return i.intValue();
372     }
373
374
375
376     /**
377      * <p>Returns the value of the boolean property with the specified
378      * ID. If the property is not available, <code>false</code> is
379      * returned. A subsequent call to {@link #wasNull} will return
380      * <code>true</code> to let the caller distinguish that case from
381      * a real property value of <code>false</code>.</p>
382      *
383      * @param id The property's ID
384      *
385      * @return The property's value
386      */

387     protected boolean getPropertyBooleanValue(final int id)
388     {
389         final Boolean JavaDoc b = (Boolean JavaDoc) getProperty(id);
390         if (b != null)
391             return b.booleanValue();
392         else
393             return false;
394         }
395
396
397
398     /**
399      * <p>This member is <code>true</code> if the last call to {@link
400      * #getPropertyIntValue} or {@link #getProperty} tried to access a
401      * property that was not available, else <code>false</code>.</p>
402      */

403     private boolean wasNull;
404
405
406     /**
407      * <p>Checks whether the property which the last call to {@link
408      * #getPropertyIntValue} or {@link #getProperty} tried to access
409      * was available or not. This information might be important for
410      * callers of {@link #getPropertyIntValue} since the latter
411      * returns 0 if the property does not exist. Using {@link
412      * #wasNull} the caller can distiguish this case from a property's
413      * real value of 0.</p>
414      *
415      * @return <code>true</code> if the last call to {@link
416      * #getPropertyIntValue} or {@link #getProperty} tried to access a
417      * property that was not available, else <code>false</code>.
418      */

419     public boolean wasNull()
420     {
421         return wasNull;
422     }
423
424
425
426     /**
427      * <p>Returns the PID string associated with a property ID. The ID
428      * is first looked up in the {@link Section}'s private
429      * dictionary. If it is not found there, the method calls {@link
430      * SectionIDMap#getPIDString}.</p>
431      *
432      * @param pid The property ID
433      *
434      * @return The property ID's string value
435      */

436     public String JavaDoc getPIDString(final long pid)
437     {
438         String JavaDoc s = null;
439         if (dictionary != null)
440             s = (String JavaDoc) dictionary.get(new Long JavaDoc(pid));
441         if (s == null)
442             s = SectionIDMap.getPIDString(getFormatID().getBytes(), pid);
443         if (s == null)
444             s = SectionIDMap.UNDEFINED;
445         return s;
446     }
447
448
449
450     /**
451      * <p>Checks whether this section is equal to another object. The result is
452      * <code>false</code> if one of the the following conditions holds:</p>
453      *
454      * <ul>
455      *
456      * <li><p>The other object is not a {@link Section}.</p></li>
457      *
458      * <li><p>The format IDs of the two sections are not equal.</p></li>
459      *
460      * <li><p>The sections have a different number of properties. However,
461      * properties with ID 1 (codepage) are not counted.</p></li>
462      *
463      * <li><p>The other object is not a {@link Section}.</p></li>
464      *
465      * <li><p>The properties have different values. The order of the properties
466      * is irrelevant.</p></li>
467      *
468      * </ul>
469      *
470      * @param o The object to compare this section with
471      * @return <code>true</code> if the objects are equal, <code>false</code> if
472      * not
473      */

474     public boolean equals(final Object JavaDoc o)
475     {
476         if (o == null || !(o instanceof Section))
477             return false;
478         final Section s = (Section) o;
479         if (!s.getFormatID().equals(getFormatID()))
480             return false;
481
482         /* Compare all properties except 0 and 1 as they must be handled
483          * specially. */

484         Property[] pa1 = new Property[getProperties().length];
485         Property[] pa2 = new Property[s.getProperties().length];
486         System.arraycopy(getProperties(), 0, pa1, 0, pa1.length);
487         System.arraycopy(s.getProperties(), 0, pa2, 0, pa2.length);
488
489         /* Extract properties 0 and 1 and remove them from the copy of the
490          * arrays. */

491         Property p10 = null;
492         Property p20 = null;
493         for (int i = 0; i < pa1.length; i++)
494         {
495             final long id = pa1[i].getID();
496             if (id == 0)
497             {
498                 p10 = pa1[i];
499                 pa1 = remove(pa1, i);
500                 i--;
501             }
502             if (id == 1)
503             {
504                 // p11 = pa1[i];
505
pa1 = remove(pa1, i);
506                 i--;
507             }
508         }
509         for (int i = 0; i < pa2.length; i++)
510         {
511             final long id = pa2[i].getID();
512             if (id == 0)
513             {
514                 p20 = pa2[i];
515                 pa2 = remove(pa2, i);
516                 i--;
517             }
518             if (id == 1)
519             {
520                 // p21 = pa2[i];
521
pa2 = remove(pa2, i);
522                 i--;
523             }
524         }
525
526         /* If the number of properties (not counting property 1) is unequal the
527          * sections are unequal. */

528         if (pa1.length != pa2.length)
529             return false;
530
531         /* If the dictionaries are unequal the sections are unequal. */
532         boolean dictionaryEqual = true;
533         if (p10 != null && p20 != null)
534             dictionaryEqual = p10.getValue().equals(p20.getValue());
535         else if (p10 != null || p20 != null)
536             dictionaryEqual = false;
537         if (!dictionaryEqual)
538             return false;
539         else
540             return Util.equals(pa1, pa2);
541     }
542
543
544
545     /**
546      * <p>Removes a field from a property array. The resulting array is
547      * compactified and returned.</p>
548      */

549     private Property[] remove(final Property[] pa, final int i)
550     {
551         final Property[] h = new Property[pa.length - 1];
552         if (i > 0)
553             System.arraycopy(pa, 0, h, 0, i);
554         System.arraycopy(pa, i + 1, h, i, h.length - i);
555         return h;
556     }
557
558
559
560     /**
561      * @see Object#hashCode()
562      */

563     public int hashCode()
564     {
565         long hashCode = 0;
566         hashCode += getFormatID().hashCode();
567         final Property[] pa = getProperties();
568         for (int i = 0; i < pa.length; i++)
569             hashCode += pa[i].hashCode();
570         final int returnHashCode = (int) (hashCode & 0x0ffffffffL);
571         return returnHashCode;
572     }
573
574
575
576     /**
577      * @see Object#toString()
578      */

579     public String JavaDoc toString()
580     {
581         final StringBuffer JavaDoc b = new StringBuffer JavaDoc();
582         final Property[] pa = getProperties();
583         b.append(getClass().getName());
584         b.append('[');
585         b.append("formatID: ");
586         b.append(getFormatID());
587         b.append(", offset: ");
588         b.append(getOffset());
589         b.append(", propertyCount: ");
590         b.append(getPropertyCount());
591         b.append(", size: ");
592         b.append(getSize());
593         b.append(", properties: [\n");
594         for (int i = 0; i < pa.length; i++)
595         {
596             b.append(pa[i].toString());
597             b.append(",\n");
598         }
599         b.append(']');
600         b.append(']');
601         return b.toString();
602     }
603
604
605
606     /**
607      * <p>Gets the section's dictionary. A dictionary allows an application to
608      * use human-readable property names instead of numeric property IDs. It
609      * contains mappings from property IDs to their associated string
610      * values. The dictionary is stored as the property with ID 0. The codepage
611      * for the strings in the dictionary is defined by property with ID 1.</p>
612      *
613      * @return the dictionary or <code>null</code> if the section does not have
614      * a dictionary.
615      */

616     public Map JavaDoc getDictionary()
617     {
618         return dictionary;
619     }
620
621
622
623     /**
624      * <p>Gets the section's codepage, if any.</p>
625      *
626      * @return The section's codepage if one is defined, else -1.
627      */

628     public int getCodepage()
629     {
630         final Integer JavaDoc codepage =
631             (Integer JavaDoc) getProperty(PropertyIDMap.PID_CODEPAGE);
632         return codepage != null ? codepage.intValue() : -1;
633     }
634
635 }
636
Popular Tags