KickJava   Java API By Example, From Geeks To Geeks.

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


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

16
17 package org.apache.poi.hpsf;
18
19 import java.io.IOException JavaDoc;
20 import java.io.OutputStream JavaDoc;
21 import java.io.UnsupportedEncodingException JavaDoc;
22 import java.util.Date JavaDoc;
23 import java.util.LinkedList JavaDoc;
24 import java.util.List JavaDoc;
25
26 import org.apache.poi.util.LittleEndian;
27 import org.apache.poi.util.LittleEndianConsts;
28
29 /**
30  * <p>Supports reading and writing of variant data.</p>
31  *
32  * <p><strong>FIXME (3):</strong> Reading and writing should be made more
33  * uniform than it is now. The following items should be resolved:
34  *
35  * <ul>
36  *
37  * <li><p>Reading requires a length parameter that is 4 byte greater than the
38  * actual data, because the variant type field is included. </p></li>
39  *
40  * <li><p>Reading reads from a byte array while writing writes to an byte array
41  * output stream.</p></li>
42  *
43  * </ul>
44  *
45  * @author Rainer Klute <a
46  * HREF="mailto:klute@rainer-klute.de">&lt;klute@rainer-klute.de&gt;</a>
47  * @since 2003-08-08
48  * @version $Id: VariantSupport.java,v 1.12 2005/02/13 18:35:09 klute Exp $
49  */

50 public class VariantSupport extends Variant
51 {
52
53     private static boolean logUnsupportedTypes = false;
54
55     /**
56      * <p>Specifies whether warnings about unsupported variant types are to be
57      * written to <code>System.err</code> or not.</p>
58      *
59      * @param logUnsupportedTypes If <code>true</code> warnings will be written,
60      * if <code>false</code> they won't.
61      */

62     public static void setLogUnsupportedTypes(final boolean logUnsupportedTypes)
63     {
64         VariantSupport.logUnsupportedTypes = logUnsupportedTypes;
65     }
66
67     /**
68      * <p>Checks whether logging of unsupported variant types warning is turned
69      * on or off.</p>
70      *
71      * @return <code>true</code> if logging is turned on, else
72      * <code>false</code>.
73      */

74     public static boolean isLogUnsupportedTypes()
75     {
76         return logUnsupportedTypes;
77     }
78
79
80
81     /**
82      * <p>Keeps a list of the variant types an "unsupported" message has already
83      * been issued for.</p>
84      */

85     protected static List JavaDoc unsupportedMessage;
86
87     /**
88      * <p>Writes a warning to <code>System.err</code> that a variant type is
89      * unsupported by HPSF. Such a warning is written only once for each variant
90      * type. Log messages can be turned on or off by </p>
91      *
92      * @param ex The exception to log
93      */

94     protected static void writeUnsupportedTypeMessage
95         (final UnsupportedVariantTypeException ex)
96     {
97         if (isLogUnsupportedTypes())
98         {
99             if (unsupportedMessage == null)
100                 unsupportedMessage = new LinkedList JavaDoc();
101             Long JavaDoc vt = new Long JavaDoc(ex.getVariantType());
102             if (!unsupportedMessage.contains(vt))
103             {
104                 System.err.println(ex.getMessage());
105                 unsupportedMessage.add(vt);
106             }
107         }
108     }
109
110
111
112     /**
113      * <p>Reads a variant type from a byte array.</p>
114      *
115      * @param src The byte array
116      * @param offset The offset in the byte array where the variant
117      * starts
118      * @param length The length of the variant including the variant
119      * type field
120      * @param type The variant type to read
121      * @param codepage The codepage to use to write non-wide strings
122      * @return A Java object that corresponds best to the variant
123      * field. For example, a VT_I4 is returned as a {@link Long}, a
124      * VT_LPSTR as a {@link String}.
125      * @exception ReadingNotSupportedException if a property is to be written
126      * who's variant type HPSF does not yet support
127      * @exception UnsupportedEncodingException if the specified codepage is not
128      * supported.
129      *
130      * @see Variant
131      */

132     public static Object JavaDoc read(final byte[] src, final int offset,
133                               final int length, final long type,
134                               final int codepage)
135     throws ReadingNotSupportedException, UnsupportedEncodingException JavaDoc
136     {
137         Object JavaDoc value;
138         int o1 = offset;
139         int l1 = length - LittleEndian.INT_SIZE;
140         long lType = type;
141
142         /* Instead of trying to read 8-bit characters from a Unicode string,
143          * read 16-bit characters. */

144         if (codepage == Constants.CP_UNICODE && type == Variant.VT_LPSTR)
145             lType = Variant.VT_LPWSTR;
146
147         switch ((int) lType)
148         {
149             case Variant.VT_EMPTY:
150             {
151                 value = null;
152                 break;
153             }
154             case Variant.VT_I2:
155             {
156                 /*
157                  * Read a short. In Java it is represented as an
158                  * Integer object.
159                  */

160                 value = new Integer JavaDoc(LittleEndian.getUShort(src, o1));
161                 break;
162             }
163             case Variant.VT_I4:
164             {
165                 /*
166                  * Read a word. In Java it is represented as a
167                  * Long object.
168                  */

169                 value = new Long JavaDoc(LittleEndian.getUInt(src, o1));
170                 break;
171             }
172             case Variant.VT_FILETIME:
173             {
174                 /*
175                  * Read a FILETIME object. In Java it is represented
176                  * as a Date object.
177                  */

178                 final long low = LittleEndian.getUInt(src, o1);
179                 o1 += LittleEndian.INT_SIZE;
180                 final long high = LittleEndian.getUInt(src, o1);
181                 value = Util.filetimeToDate((int) high, (int) low);
182                 break;
183             }
184             case Variant.VT_LPSTR:
185             {
186                 /*
187                  * Read a byte string. In Java it is represented as a
188                  * String object. The 0x00 bytes at the end must be
189                  * stripped.
190                  */

191                 final int first = o1 + LittleEndian.INT_SIZE;
192                 long last = first + LittleEndian.getUInt(src, o1) - 1;
193                 o1 += LittleEndian.INT_SIZE;
194                 while (src[(int) last] == 0 && first <= last)
195                     last--;
196                 final int l = (int) (last - first + 1);
197                 value = codepage != -1 ?
198                     new String JavaDoc(src, (int) first, l,
199                                codepageToEncoding(codepage)) :
200                     new String JavaDoc(src, (int) first, l);
201                 break;
202             }
203             case Variant.VT_LPWSTR:
204             {
205                 /*
206                  * Read a Unicode string. In Java it is represented as
207                  * a String object. The 0x00 bytes at the end must be
208                  * stripped.
209                  */

210                 final int first = o1 + LittleEndian.INT_SIZE;
211                 long last = first + LittleEndian.getUInt(src, o1) - 1;
212                 long l = last - first;
213                 o1 += LittleEndian.INT_SIZE;
214                 StringBuffer JavaDoc b = new StringBuffer JavaDoc((int) (last - first));
215                 for (int i = 0; i <= l; i++)
216                 {
217                     final int i1 = o1 + (i * 2);
218                     final int i2 = i1 + 1;
219                     final int high = src[i2] << 8;
220                     final int low = src[i1] & 0x00ff;
221                     final char c = (char) (high | low);
222                     b.append(c);
223                 }
224                 /* Strip 0x00 characters from the end of the string: */
225                 while (b.length() > 0 && b.charAt(b.length() - 1) == 0x00)
226                     b.setLength(b.length() - 1);
227                 value = b.toString();
228                 break;
229             }
230             case Variant.VT_CF:
231             {
232                 final byte[] v = new byte[l1];
233                 for (int i = 0; i < l1; i++)
234                     v[i] = src[(int) (o1 + i)];
235                 value = v;
236                 break;
237             }
238             case Variant.VT_BOOL:
239             {
240                 /*
241                  * The first four bytes in src, from src[offset] to
242                  * src[offset + 3] contain the DWord for VT_BOOL, so
243                  * skip it, we don't need it.
244                  */

245                 // final int first = offset + LittleEndian.INT_SIZE;
246
long bool = LittleEndian.getUInt(src, o1);
247                 if (bool != 0)
248                     value = Boolean.TRUE;
249                 else
250                     value = Boolean.FALSE;
251                 break;
252             }
253             default:
254             {
255                 final byte[] v = new byte[l1];
256                 for (int i = 0; i < l1; i++)
257                     v[i] = src[(int) (o1 + i)];
258                 throw new ReadingNotSupportedException(type, v);
259             }
260         }
261         return value;
262     }
263
264
265
266     /**
267      * <p>Turns a codepage number into the equivalent character encoding's
268      * name.</p>
269      *
270      * @param codepage The codepage number
271      *
272      * @return The character encoding's name. If the codepage number is 65001,
273      * the encoding name is "UTF-8". All other positive numbers are mapped to
274      * "cp" followed by the number, e.g. if the codepage number is 1252 the
275      * returned character encoding name will be "cp1252".
276      *
277      * @exception UnsupportedEncodingException if the specified codepage is
278      * less than zero.
279      */

280     public static String JavaDoc codepageToEncoding(final int codepage)
281     throws UnsupportedEncodingException JavaDoc
282     {
283         if (codepage <= 0)
284             throw new UnsupportedEncodingException JavaDoc
285                 ("Codepage number may not be " + codepage);
286         switch (codepage)
287         {
288             case Constants.CP_MACROMAN:
289                 return "MacRoman";
290             case Constants.CP_SJIS:
291                 return "SJIS";
292             case Constants.CP_UTF16:
293                 return "UTF-16";
294             case Constants.CP_UTF8:
295                 return "UTF-8";
296             default:
297                 return "cp" + codepage;
298         }
299     }
300
301
302     /**
303      * <p>Writes a variant value to an output stream. This method ensures that
304      * always a multiple of 4 bytes is written.</p>
305      *
306      * <p>If the codepage is UTF-16, which is encouraged, strings
307      * <strong>must</strong> always be written as {@link Variant#VT_LPWSTR}
308      * strings, not as {@link Variant#VT_LPSTR} strings. This method ensure this
309      * by converting strings appropriately, if needed.</p>
310      *
311      * @param out The stream to write the value to.
312      * @param type The variant's type.
313      * @param value The variant's value.
314      * @param codepage The codepage to use to write non-wide strings
315      * @return The number of entities that have been written. In many cases an
316      * "entity" is a byte but this is not always the case.
317      * @exception IOException if an I/O exceptions occurs
318      * @exception WritingNotSupportedException if a property is to be written
319      * who's variant type HPSF does not yet support
320      */

321     public static int write(final OutputStream JavaDoc out, final long type,
322                             final Object JavaDoc value, final int codepage)
323         throws IOException JavaDoc, WritingNotSupportedException
324     {
325         int length = 0;
326         switch ((int) type)
327         {
328             case Variant.VT_BOOL:
329             {
330                 int trueOrFalse;
331                 if (((Boolean JavaDoc) value).booleanValue())
332                     trueOrFalse = 1;
333                 else
334                     trueOrFalse = 0;
335                 length = TypeWriter.writeUIntToStream(out, trueOrFalse);
336                 break;
337             }
338             case Variant.VT_LPSTR:
339             {
340                 final byte[] bytes =
341                     (codepage == -1 ?
342                     ((String JavaDoc) value).getBytes() :
343                     ((String JavaDoc) value).getBytes(codepageToEncoding(codepage)));
344                 length = TypeWriter.writeUIntToStream(out, bytes.length + 1);
345                 final byte[] b = new byte[bytes.length + 1];
346                 System.arraycopy(bytes, 0, b, 0, bytes.length);
347                 b[b.length - 1] = 0x00;
348                 out.write(b);
349                 length += b.length;
350                 break;
351             }
352             case Variant.VT_LPWSTR:
353             {
354                 final int nrOfChars = ((String JavaDoc) value).length() + 1;
355                 length += TypeWriter.writeUIntToStream(out, nrOfChars);
356                 char[] s = Util.pad4((String JavaDoc) value);
357                 for (int i = 0; i < s.length; i++)
358                 {
359                     final int high = (int) ((s[i] & 0x0000ff00) >> 8);
360                     final int low = (int) (s[i] & 0x000000ff);
361                     final byte highb = (byte) high;
362                     final byte lowb = (byte) low;
363                     out.write(lowb);
364                     out.write(highb);
365                     length += 2;
366                 }
367                 out.write(0x00);
368                 out.write(0x00);
369                 length += 2;
370                 break;
371             }
372             case Variant.VT_CF:
373             {
374                 final byte[] b = (byte[]) value;
375                 out.write(b);
376                 length = b.length;
377                 break;
378             }
379             case Variant.VT_EMPTY:
380             {
381                 TypeWriter.writeUIntToStream(out, Variant.VT_EMPTY);
382                 length = LittleEndianConsts.INT_SIZE;
383                 break;
384             }
385             case Variant.VT_I2:
386             {
387                 TypeWriter.writeToStream(out, ((Integer JavaDoc) value).shortValue());
388                 length = LittleEndianConsts.SHORT_SIZE;
389                 break;
390             }
391             case Variant.VT_I4:
392             {
393                 length += TypeWriter.writeToStream(out,
394                           ((Long JavaDoc) value).intValue());
395                 break;
396             }
397             case Variant.VT_FILETIME:
398             {
399                 long filetime = Util.dateToFileTime((Date JavaDoc) value);
400                 int high = (int) ((filetime >> 32) & 0x00000000FFFFFFFFL);
401                 int low = (int) (filetime & 0x00000000FFFFFFFFL);
402                 length += TypeWriter.writeUIntToStream
403                     (out, 0x0000000FFFFFFFFL & low);
404                 length += TypeWriter.writeUIntToStream
405                     (out, 0x0000000FFFFFFFFL & high);
406                 break;
407             }
408             default:
409             {
410                 /* The variant type is not supported yet. However, if the value
411                  * is a byte array we can write it nevertheless. */

412                 if (value instanceof byte[])
413                 {
414                     final byte[] b = (byte[]) value;
415                     out.write(b);
416                     length = b.length;
417                     writeUnsupportedTypeMessage
418                         (new WritingNotSupportedException(type, value));
419                 }
420                 else
421                     throw new WritingNotSupportedException(type, value);
422                 break;
423             }
424         }
425
426         return length;
427     }
428
429 }
430
Popular Tags