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 * Portions of this software are based upon public domain software 56 * originally written at the National Center for Supercomputing Applications, 57 * University of Illinois, Urbana-Champaign. 58 * 59 * Portions of this software are based upon public domain software 60 * originally written at the National Center for Supercomputing Applications, 61 * University of Illinois, Urbana-Champaign. 62 */ 63 package org.apache.poi.hpsf; 64 65 import java.util.*; 66 import org.apache.poi.util.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 * DWord telling how many bytes the string contains. The bytes follow 79 * 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 {@link 85 * Variant#VT_I4}, {@link Variant#VT_FILETIME}, {@link 86 * Variant#VT_LPSTR}, {@link Variant#VT_CF}, {@link Variant#VT_BOOL}, 87 * and reading the dictionary property is not yet implemented.</p> 88 * 89 * @author Rainer Klute (klute@rainer-klute.de) 90 * @author Drew Varner (Drew.Varner InAndAround sc.edu) 91 * @see Section 92 * @see Variant 93 * @version $Id: Property.java,v 1.9 2002/07/22 08:25:18 klute Exp $ 94 * @since 2002-02-09 95 */ 96 public class Property 97 { 98 99 private int id; 100 101 102 /** 103 * <p>Returns the property's ID.</p> 104 * 105 * @return The ID value 106 */ 107 public int getID() 108 { 109 return id; 110 } 111 112 113 114 private long type; 115 116 117 /** 118 * <p>Returns the property's type.</p> 119 * 120 * @return The type value 121 */ 122 public long getType() 123 { 124 return type; 125 } 126 127 128 129 private Object value; 130 131 132 /** 133 * <p>Returns the property's value.</p> 134 * 135 * @return The property's value 136 */ 137 public Object getValue() 138 { 139 return value; 140 } 141 142 143 144 /** 145 * <p>Creates a {@link Property} instance by reading its bytes 146 * from the property set stream.</p> 147 * 148 * @param id The property's ID. 149 * @param src The bytes the property set stream consists of. 150 * @param offset The property's type/value pair's offset in the 151 * section. 152 * @param length The property's type/value pair's length in bytes. 153 */ 154 public Property(final int id, final byte[] src, final long offset, 155 int length) 156 { 157 this.id = id; 158 159 /* 160 * ID 0 is a special case since it specifies a dictionary of 161 * property IDs and property names. 162 */ 163 if (id == 0) 164 { 165 value = readDictionary(src, offset, length); 166 return; 167 } 168 169 /* 170 * FIXME: Support this! 171 */ 172 // /* ID 1 is another special case: It denotes the code page of 173 // * byte strings in this section. */ 174 // if (id == 1) 175 // { 176 // value = readCodepage(src, offset); 177 // return; 178 // } 179 180 int o = (int) offset; 181 type = LittleEndian.getUInt(src, o); 182 o += LittleEndian.INT_SIZE; 183 184 /* 185 * FIXME: Support reading more types! 186 */ 187 switch ((int)type) { 188 case Variant.VT_I4: 189 { 190 /* 191 * Read a word. In Java it is represented as an 192 * Integer object. 193 */ 194 value = new Long(LittleEndian.getUInt(src, o)); 195 break; 196 } 197 case Variant.VT_FILETIME: 198 { 199 /* 200 * Read a FILETIME object. In Java it is represented 201 * as a Date. 202 */ 203 final long low = LittleEndian.getUInt(src, o); 204 o += LittleEndian.INT_SIZE; 205 final long high = LittleEndian.getUInt(src, o); 206 value = Util.filetimeToDate((int)high, (int)low); 207 break; 208 } 209 case Variant.VT_LPSTR: 210 { 211 /* 212 * Read a byte string. In Java it is represented as a 213 * String. The null bytes at the end of the byte 214 * strings must be stripped. 215 */ 216 final int first = o + LittleEndian.INT_SIZE; 217 long last = first + LittleEndian.getUInt(src, o) - 1; 218 o += LittleEndian.INT_SIZE; 219 while (src[(int)last] == 0 && first <= last) { 220 last--; 221 } 222 value = new String(src, (int)first, (int)(last - first + 1)); 223 break; 224 } 225 case Variant.VT_CF: 226 { 227 /* 228 * The first four bytes in src, from rc[offset] to 229 * src[offset + 3] contain the DWord for VT_CF, so 230 * skip it, we don't need it. 231 */ 232 /* 233 * Truncate the length of the return array by a DWord 234 * length (4 bytes). 235 */ 236 length = length - LittleEndian.INT_SIZE; 237 238 final byte[] v = new byte[length]; 239 for (int i = 0; i < length; i++) 240 v[i] = src[(int)(o + i)]; 241 value = v; 242 break; 243 } 244 case Variant.VT_BOOL: 245 { 246 /* 247 * The first four bytes in src, from src[offset] to 248 * src[offset + 3] contain the DWord for VT_BOOL, so 249 * skip it, we don't need it. 250 */ 251 final int first = o + LittleEndian.INT_SIZE; 252 long bool = LittleEndian.getUInt(src, o); 253 if (bool != 0) 254 value = new Boolean(true); 255 else 256 value = new Boolean(false); 257 break; 258 } 259 default: 260 { 261 final byte[] v = new byte[length]; 262 for (int i = 0; i < length; i++) 263 v[i] = src[(int)(offset + i)]; 264 value = v; 265 break; 266 } 267 } 268 } 269 270 271 272 /** 273 * <p>Reads a dictionary.</p> 274 * 275 * @param src The byte array containing the bytes making out the 276 * dictionary. 277 * @param offset At this offset within <var>src</var> the 278 * dictionary starts. 279 * @param length The dictionary contains at most this many bytes. 280 * @return The dictonary 281 */ 282 protected Map readDictionary(final byte[] src, final long offset, 283 final int length) 284 { 285 /* 286 * FIXME: Check the length! 287 */ 288 int o = (int)offset; 289 290 /* 291 * Read the number of dictionary entries. 292 */ 293 final long nrEntries = LittleEndian.getUInt(src, o); 294 o += LittleEndian.INT_SIZE; 295 296 final Map m = new HashMap((int)nrEntries, (float) 1.0); 297 for (int i = 0; i < nrEntries; i++) 298 { 299 /* 300 * The key 301 */ 302 final Long id = new Long(LittleEndian.getUInt(src, o)); 303 o += LittleEndian.INT_SIZE; 304 305 /* 306 * The value (a string) 307 */ 308 final long sLength = LittleEndian.getUInt(src, o); 309 o += LittleEndian.INT_SIZE; 310 311 /* 312 * Strip trailing 0x00 bytes. 313 */ 314 long l = sLength; 315 while (src[(int)(o + l - 1)] == 0x00) 316 l--; 317 final String s = new String(src, o, (int)l); 318 o += sLength; 319 m.put(id, s); 320 } 321 return m; 322 } 323 324 325 326 /** 327 * <p>Reads a code page.</p> 328 * 329 * @param src The byte array containing the bytes making out the 330 * code page. 331 * @param offset At this offset within <var>src</var> the code 332 * page starts. 333 * @return The code page. 334 */ 335 protected int readCodePage(final byte[] src, final long offset) 336 { 337 throw new UnsupportedOperationException("FIXME"); 338 } 339 340 } 341