KickJava   Java API By Example, From Geeks To Geeks.

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


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.HashMap JavaDoc;
22 import java.util.Map JavaDoc;
23
24 import org.apache.poi.util.HexDump;
25 import org.apache.poi.util.LittleEndian;
26
27 /**
28  * <p>A property in a {@link Section} of a {@link PropertySet}.</p>
29  *
30  * <p>The property's <strong>ID</strong> gives the property a meaning
31  * in the context of its {@link Section}. Each {@link Section} spans
32  * its own name space of property IDs.</p>
33  *
34  * <p>The property's <strong>type</strong> determines how its
35  * <strong>value </strong> is interpreted. For example, if the type is
36  * {@link Variant#VT_LPSTR} (byte string), the value consists of a
37  * DWord telling how many bytes the string contains. The bytes follow
38  * immediately, including any null bytes that terminate the
39  * string. The type {@link Variant#VT_I4} denotes a four-byte integer
40  * value, {@link Variant#VT_FILETIME} some date and time (of a
41  * file).</p>
42  *
43  * <p>Please note that not all {@link Variant} types yet. This might change
44  * over time but largely depends on your feedback so that the POI team knows
45  * which variant types are really needed. So please feel free to submit error
46  * reports or patches for the types you need.</p>
47  *
48  * @author Rainer Klute <a
49  * HREF="mailto:klute@rainer-klute.de">&lt;klute@rainer-klute.de&gt;</a>
50  * @author Drew Varner (Drew.Varner InAndAround sc.edu)
51  * @see Section
52  * @see Variant
53  * @version $Id: Property.java,v 1.20 2004/08/31 20:45:00 klute Exp $
54  * @since 2002-02-09
55  */

56 public class Property
57 {
58
59     /** <p>The property's ID.</p> */
60     protected long id;
61
62
63     /**
64      * <p>Returns the property's ID.</p>
65      *
66      * @return The ID value
67      */

68     public long getID()
69     {
70         return id;
71     }
72
73
74
75     /** <p>The property's type.</p> */
76     protected long type;
77
78
79     /**
80      * <p>Returns the property's type.</p>
81      *
82      * @return The type value
83      */

84     public long getType()
85     {
86         return type;
87     }
88
89
90
91     /** <p>The property's value.</p> */
92     protected Object JavaDoc value;
93
94
95     /**
96      * <p>Returns the property's value.</p>
97      *
98      * @return The property's value
99      */

100     public Object JavaDoc getValue()
101     {
102         return value;
103     }
104
105
106
107     /**
108      * <p>Creates a {@link Property} instance by reading its bytes
109      * from the property set stream.</p>
110      *
111      * @param id The property's ID.
112      * @param src The bytes the property set stream consists of.
113      * @param offset The property's type/value pair's offset in the
114      * section.
115      * @param length The property's type/value pair's length in bytes.
116      * @param codepage The section's and thus the property's
117      * codepage. It is needed only when reading string values.
118      * @exception UnsupportedEncodingException if the specified codepage is not
119      * supported.
120      */

121     public Property(final long id, final byte[] src, final long offset,
122                     final int length, final int codepage)
123     throws UnsupportedEncodingException JavaDoc
124     {
125         this.id = id;
126
127         /*
128          * ID 0 is a special case since it specifies a dictionary of
129          * property IDs and property names.
130          */

131         if (id == 0)
132         {
133             value = readDictionary(src, offset, length, codepage);
134             return;
135         }
136
137         int o = (int) offset;
138         type = LittleEndian.getUInt(src, o);
139         o += LittleEndian.INT_SIZE;
140
141         try
142         {
143             value = VariantSupport.read(src, o, length, (int) type, codepage);
144         }
145         catch (UnsupportedVariantTypeException ex)
146         {
147             VariantSupport.writeUnsupportedTypeMessage(ex);
148             value = ex.getValue();
149         }
150     }
151
152
153
154     /**
155      * <p>Creates an empty property. It must be filled using the set method to
156      * be usable.</p>
157      */

158     protected Property()
159     { }
160
161
162
163     /**
164      * <p>Reads a dictionary.</p>
165      *
166      * @param src The byte array containing the bytes making out the
167      * dictionary.
168      * @param offset At this offset within <var>src</var> the
169      * dictionary starts.
170      * @param length The dictionary contains at most this many bytes.
171      * @param codepage The codepage of the string values.
172      * @return The dictonary
173      */

174     protected Map JavaDoc readDictionary(final byte[] src, final long offset,
175                                  final int length, final int codepage)
176     {
177         /* Check whether "offset" points into the "src" array". */
178         if (offset < 0 || offset > src.length)
179             throw new HPSFRuntimeException
180                 ("Illegal offset " + offset + " while HPSF stream contains " +
181                  length + " bytes.");
182         int o = (int) offset;
183
184         /*
185          * Read the number of dictionary entries.
186          */

187         final long nrEntries = LittleEndian.getUInt(src, o);
188         o += LittleEndian.INT_SIZE;
189
190         final Map JavaDoc m = new HashMap JavaDoc((int) nrEntries, (float) 1.0);
191         for (int i = 0; i < nrEntries; i++)
192         {
193             /* The key. */
194             final Long JavaDoc id = new Long JavaDoc(LittleEndian.getUInt(src, o));
195             o += LittleEndian.INT_SIZE;
196
197             /* The value (a string). The length is the either the
198              * number of characters if the character set is Unicode or
199              * else the number of bytes. The length includes
200              * terminating 0x00 bytes which we have to strip off to
201              * create a Java string. */

202             long sLength = LittleEndian.getUInt(src, o);
203             o += LittleEndian.INT_SIZE;
204
205             /* Read the bytes or characters depending on whether the
206              * character set is Unicode or not. */

207             StringBuffer JavaDoc b = new StringBuffer JavaDoc((int) sLength);
208             for (int j = 0; j < sLength; j++)
209                 if (codepage == Constants.CP_UNICODE)
210                 {
211                     final int i1 = o + (j * 2);
212                     final int i2 = i1 + 1;
213                     b.append((char) ((src[i2] << 8) + src[i1]));
214                 }
215                 else
216                     b.append((char) src[o + j]);
217
218             /* Strip 0x00 characters from the end of the string: */
219             while (b.length() > 0 && b.charAt(b.length() - 1) == 0x00)
220                 b.setLength(b.length() - 1);
221             if (codepage == Constants.CP_UNICODE)
222             {
223                 if (sLength % 2 == 1)
224                     sLength++;
225                 o += (sLength + sLength);
226             }
227             else
228                 o += sLength;
229             m.put(id, b.toString());
230         }
231         return m;
232     }
233
234
235
236     /**
237      * <p>Returns the property's size in bytes. This is always a multiple of
238      * 4.</p>
239      *
240      * @return the property's size in bytes
241      *
242      * @exception WritingNotSupportedException if HPSF does not yet support the
243      * property's variant type.
244      */

245     protected int getSize() throws WritingNotSupportedException
246     {
247         int length = VariantSupport.getVariantLength(type);
248         if (length >= 0)
249             return length; /* Fixed length */
250         if (length == -2)
251             /* Unknown length */
252             throw new WritingNotSupportedException(type, null);
253
254         /* Variable length: */
255         final int PADDING = 4; /* Pad to multiples of 4. */
256         switch ((int) type)
257         {
258             case Variant.VT_LPSTR:
259             {
260                 int l = ((String JavaDoc) value).length() + 1;
261                 int r = l % PADDING;
262                 if (r > 0)
263                     l += PADDING - r;
264                 length += l;
265                 break;
266             }
267             case Variant.VT_EMPTY:
268                 break;
269             default:
270                 throw new WritingNotSupportedException(type, value);
271         }
272         return length;
273     }
274
275
276
277     /**
278      * <p>Compares two properties.</p>
279      *
280      * <p>Please beware that a property with ID == 0 is a special case: It does not have a type, and its value is the section's
281      * dictionary. Another special case are strings: Two properties may have
282      * the different types Variant.VT_LPSTR and Variant.VT_LPWSTR;</p>
283      *
284      * @see Object#equals(java.lang.Object)
285      */

286     public boolean equals(final Object JavaDoc o)
287     {
288         if (!(o instanceof Property))
289             return false;
290         final Property p = (Property) o;
291         final Object JavaDoc pValue = p.getValue();
292         final long pId = p.getID();
293         if (id != pId || (id != 0 && !typesAreEqual(type, p.getType())))
294             return false;
295         if (value == null && pValue == null)
296             return true;
297         if (value == null || pValue == null)
298             return false;
299
300         /* It's clear now that both values are non-null. */
301         final Class JavaDoc valueClass = value.getClass();
302         final Class JavaDoc pValueClass = pValue.getClass();
303         if (!(valueClass.isAssignableFrom(pValueClass)) &&
304             !(pValueClass.isAssignableFrom(valueClass)))
305             return false;
306
307         if (value instanceof byte[])
308             return Util.equal((byte[]) value, (byte[]) pValue);
309
310         return value.equals(pValue);
311     }
312
313
314
315     private boolean typesAreEqual(final long t1, final long t2)
316     {
317         if (t1 == t2 ||
318             (t1 == Variant.VT_LPSTR && t2 == Variant.VT_LPWSTR) ||
319             (t2 == Variant.VT_LPSTR && t1 == Variant.VT_LPWSTR))
320             return true;
321         else
322             return false;
323     }
324
325
326
327     /**
328      * @see Object#hashCode()
329      */

330     public int hashCode()
331     {
332         long hashCode = 0;
333         hashCode += id;
334         hashCode += type;
335         if (value != null)
336             hashCode += value.hashCode();
337         final int returnHashCode = (int) (hashCode & 0x0ffffffffL );
338         return returnHashCode;
339
340     }
341
342
343
344     /**
345      * @see Object#toString()
346      */

347     public String JavaDoc toString()
348     {
349         final StringBuffer JavaDoc b = new StringBuffer JavaDoc();
350         b.append(getClass().getName());
351         b.append('[');
352         b.append("id: ");
353         b.append(getID());
354         b.append(", type: ");
355         b.append(getType());
356         final Object JavaDoc value = getValue();
357         b.append(", value: ");
358         b.append(value.toString());
359         if (value instanceof String JavaDoc)
360         {
361             final String JavaDoc s = (String JavaDoc) value;
362             final int l = s.length();
363             final byte[] bytes = new byte[l * 2];
364             for (int i = 0; i < l; i++)
365             {
366                 final char c = s.charAt(i);
367                 final byte high = (byte) ((c & 0x00ff00) >> 8);
368                 final byte low = (byte) ((c & 0x0000ff) >> 0);
369                 bytes[i * 2] = high;
370                 bytes[i * 2 + 1] = low;
371             }
372             final String JavaDoc hex = HexDump.dump(bytes, 0L, 0);
373             b.append(" [");
374             b.append(hex);
375             b.append("]");
376         }
377         b.append(']');
378         return b.toString();
379     }
380
381 }
382
Popular Tags