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 is not implemented for all 85 * {@link Variant} types yet. Feel free to submit error reports or 86 * patches for the types you need.</p> 87 * 88 * @author Rainer Klute (klute@rainer-klute.de) 89 * @author Drew Varner (Drew.Varner InAndAround sc.edu) 90 * @see Section 91 * @see Variant 92 * @version $Id: Property.java,v 1.10 2002/12/10 06:15:19 klute Exp $ 93 * @since 2002-02-09 94 */ 95 public class Property 96 { 97 98 /* Codepage 1200 denotes Unicode. */ 99 private static int CP_UNICODE = 1200; 100 101 private int id; 102 103 104 /** 105 * <p>Returns the property's ID.</p> 106 * 107 * @return The ID value 108 */ 109 public int getID() 110 { 111 return id; 112 } 113 114 115 116 private long type; 117 118 119 /** 120 * <p>Returns the property's type.</p> 121 * 122 * @return The type value 123 */ 124 public long getType() 125 { 126 return type; 127 } 128 129 130 131 private Object value; 132 133 134 /** 135 * <p>Returns the property's value.</p> 136 * 137 * @return The property's value 138 */ 139 public Object getValue() 140 { 141 return value; 142 } 143 144 145 146 /** 147 * <p>Creates a {@link Property} instance by reading its bytes 148 * from the property set stream.</p> 149 * 150 * @param id The property's ID. 151 * @param src The bytes the property set stream consists of. 152 * @param offset The property's type/value pair's offset in the 153 * section. 154 * @param length The property's type/value pair's length in bytes. 155 * @param codepage The section's and thus the property's 156 * codepage. It is needed only when reading string values. 157 */ 158 public Property(final int id, final byte[] src, final long offset, 159 int length, int codepage) 160 { 161 this.id = id; 162 163 /* 164 * ID 0 is a special case since it specifies a dictionary of 165 * property IDs and property names. 166 */ 167 if (id == 0) 168 { 169 value = readDictionary(src, offset, length, codepage); 170 return; 171 } 172 173 int o = (int) offset; 174 type = LittleEndian.getUInt(src, o); 175 o += LittleEndian.INT_SIZE; 176 177 try 178 { 179 value = TypeReader.read(src, o, length, (int) type); 180 } 181 catch (Throwable t) 182 { 183 t.printStackTrace(); 184 value = "*** null ***"; 185 } 186 } 187 188 189 190 /** 191 * <p>Reads a dictionary.</p> 192 * 193 * @param src The byte array containing the bytes making out the 194 * dictionary. 195 * @param offset At this offset within <var>src</var> the 196 * dictionary starts. 197 * @param length The dictionary contains at most this many bytes. 198 * @param codepage The codepage of the string values. 199 * @return The dictonary 200 */ 201 protected Map readDictionary(final byte[] src, final long offset, 202 final int length, final int codepage) 203 { 204 /* Check whether "offset" points into the "src" array". */ 205 if (offset < 0 || offset > src.length) 206 throw new HPSFRuntimeException 207 ("Illegal offset " + offset + " while HPSF stream contains " + 208 length + " bytes."); 209 int o = (int) offset; 210 211 /* 212 * Read the number of dictionary entries. 213 */ 214 final long nrEntries = LittleEndian.getUInt(src, o); 215 o += LittleEndian.INT_SIZE; 216 217 final Map m = new HashMap((int) nrEntries, (float) 1.0); 218 for (int i = 0; i < nrEntries; i++) 219 { 220 /* The key. */ 221 final Long id = new Long(LittleEndian.getUInt(src, o)); 222 o += LittleEndian.INT_SIZE; 223 224 /* The value (a string). The length is the either the 225 * number of characters if the character set is Unicode or 226 * else the number of bytes. The length includes 227 * terminating 0x00 bytes which we have to strip off to 228 * create a Java string. */ 229 long sLength = LittleEndian.getUInt(src, o); 230 o += LittleEndian.INT_SIZE; 231 232 /* Read the bytes or characters depending on whether the 233 * character set is Unicode or not. */ 234 StringBuffer b = new StringBuffer((int) sLength); 235 for (int j = 0; j < sLength; j++) 236 if (codepage == CP_UNICODE) 237 { 238 final int i1 = o + (j * 2); 239 final int i2 = i1 + 1; 240 b.append((char) ((src[i2] << 8) + src[i1])); 241 } 242 else 243 b.append((char) src[o + j]); 244 245 /* Strip 0x00 characters from the end of the string: */ 246 while (b.charAt(b.length() - 1) == 0x00) 247 b.setLength(b.length() - 1); 248 if (codepage == CP_UNICODE) 249 { 250 if (sLength % 2 == 1) 251 sLength++; 252 o += (sLength + sLength); 253 } 254 else 255 o += sLength; 256 m.put(id, b.toString()); 257 } 258 return m; 259 } 260 261 } 262