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