1 2 /* ==================================================================== 3 * The Apache Software License, Version 1.1 4 * 5 * Copyright (c) 2002 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" and 28 * "Apache POI" must not be used to endorse or promote products 29 * derived from this software without prior written permission. For 30 * written permission, please contact apache@apache.org. 31 * 32 * 5. Products derived from this software may not be called "Apache", 33 * "Apache POI", nor may "Apache" appear in their name, without 34 * prior written 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 56 package org.apache.poi.hssf.record; 57 58 import org.apache.poi.util.LittleEndian; 59 import org.apache.poi.util.StringUtil; 60 61 /** 62 * Title: Unicode String<P> 63 * Description: Unicode String record. We implement these as a record, although 64 * they are really just standard fields that are in several records. 65 * It is considered more desirable then repeating it in all of them.<P> 66 * REFERENCE: PG 264 Microsoft Excel 97 Developer's Kit (ISBN: 1-57231-498-2)<P> 67 * @author Andrew C. Oliver 68 * @author Marc Johnson (mjohnson at apache dot org) 69 * @version 2.0-pre 70 */ 71 72 public class UnicodeString 73 extends Record 74 implements Comparable 75 { 76 public final static short sid = 0xFFF; 77 private short field_1_charCount; // = 0; 78 private byte field_2_optionflags; // = 0; 79 private String field_3_string; // = null; 80 81 public int hashCode() 82 { 83 return field_1_charCount; 84 } 85 86 public boolean equals(Object o) 87 { 88 if ((o == null) || (o.getClass() != this.getClass())) 89 { 90 return false; 91 } 92 UnicodeString other = ( UnicodeString ) o; 93 94 return ((field_1_charCount == other.field_1_charCount) 95 && (field_2_optionflags == other.field_2_optionflags) 96 && field_3_string.equals(other.field_3_string)); 97 } 98 99 public UnicodeString() 100 { 101 } 102 103 /** 104 * construct a unicode string record and fill its fields, ID is ignored 105 * @param id - ignored 106 * @param size - size of the data 107 * @param data - the bytes of the string/fields 108 */ 109 110 public UnicodeString(short id, short size, byte [] data) 111 { 112 super(id, size, data); 113 } 114 115 /** 116 * construct a unicode string from a string fragment + data 117 */ 118 119 public UnicodeString(short id, short size, byte [] data, String prefix) 120 { 121 this(id, size, data); 122 field_3_string = prefix + field_3_string; 123 setCharCount(); 124 } 125 126 /** 127 * NO OP 128 */ 129 130 protected void validateSid(short id) 131 { 132 133 // included only for interface compliance 134 } 135 136 protected void fillFields(byte [] data, short size) 137 { 138 field_1_charCount = LittleEndian.getShort(data, 0); 139 field_2_optionflags = data[ 2 ]; 140 if ((field_2_optionflags & 1) == 0) 141 { 142 field_3_string = new String(data, 3, getCharCount()); 143 } 144 else 145 { 146 char[] array = new char[ getCharCount() ]; 147 148 for (int j = 0; j < array.length; j++) 149 { 150 array[ j ] = ( char ) LittleEndian.getShort(data, 151 3 + (j * 2)); 152 } 153 field_3_string = new String(array); 154 } 155 } 156 157 /** 158 * get the number of characters in the string 159 * 160 * 161 * @return number of characters 162 * 163 */ 164 165 public short getCharCount() 166 { 167 return field_1_charCount; 168 } 169 170 /** 171 * set the number of characters in the string 172 * @param cc - number of characters 173 */ 174 175 public void setCharCount(short cc) 176 { 177 field_1_charCount = cc; 178 } 179 180 /** 181 * sets the number of characters to whaterver number of characters is in the string 182 * currently. effectively setCharCount(getString.length()). 183 * @see #setString(String) 184 * @see #getString() 185 */ 186 187 public void setCharCount() 188 { 189 field_1_charCount = ( short ) field_3_string.length(); 190 } 191 192 /** 193 * get the option flags which among other things return if this is a 16-bit or 194 * 8 bit string 195 * 196 * @return optionflags bitmask 197 * 198 */ 199 200 public byte getOptionFlags() 201 { 202 return field_2_optionflags; 203 } 204 205 /** 206 * set the option flags which among other things return if this is a 16-bit or 207 * 8 bit string 208 * 209 * @param of optionflags bitmask 210 * 211 */ 212 213 public void setOptionFlags(byte of) 214 { 215 field_2_optionflags = of; 216 } 217 218 /** 219 * get the actual string this contains as a java String object 220 * 221 * 222 * @return String 223 * 224 */ 225 226 public String getString() 227 { 228 return field_3_string; 229 } 230 231 /** 232 * set the actual string this contains 233 * @param string the text 234 */ 235 236 public void setString(String string) 237 { 238 field_3_string = string; 239 if (getCharCount() < field_3_string.length()) 240 { 241 setCharCount(); 242 } 243 } 244 245 /** 246 * unlike the real records we return the same as "getString()" rather than debug info 247 * @see #getDebugInfo() 248 * @return String value of the record 249 */ 250 251 public String toString() 252 { 253 return getString(); 254 } 255 256 /** 257 * return a character representation of the fields of this record 258 * 259 * 260 * @return String of output for biffviewer etc. 261 * 262 */ 263 264 public String getDebugInfo() 265 { 266 StringBuffer buffer = new StringBuffer(); 267 268 buffer.append("[UNICODESTRING]\n"); 269 buffer.append(" .charcount = ") 270 .append(Integer.toHexString(getCharCount())).append("\n"); 271 buffer.append(" .optionflags = ") 272 .append(Integer.toHexString(getOptionFlags())).append("\n"); 273 buffer.append(" .string = ").append(getString()) 274 .append("\n"); 275 buffer.append("[/UNICODESTRING]\n"); 276 return buffer.toString(); 277 } 278 279 public int serialize(int offset, byte [] data) 280 { 281 int charsize = 1; 282 283 if (getOptionFlags() == 1) 284 { 285 charsize = 2; 286 } 287 288 // byte[] retval = new byte[ 3 + (getString().length() * charsize) ]; 289 LittleEndian.putShort(data, 0 + offset, getCharCount()); 290 data[ 2 + offset ] = getOptionFlags(); 291 292 // System.out.println("Unicode: We've got "+retval[2]+" for our option flag"); 293 if (getOptionFlags() == 0) 294 { 295 StringUtil.putCompressedUnicode(getString(), data, 0x3 + offset); 296 } 297 else 298 { 299 StringUtil.putUncompressedUnicode(getString(), data, 300 0x3 + offset); 301 } 302 return getRecordSize(); 303 } 304 305 public int getRecordSize() 306 { 307 int charsize = 1; 308 309 if (getOptionFlags() == 1) 310 { 311 charsize = 2; 312 } 313 return 3 + (getString().length() * charsize); 314 } 315 316 public short getSid() 317 { 318 return this.sid; 319 } 320 321 /** 322 * called by the constructor, should set class level fields. Should throw 323 * runtime exception for bad/icomplete data. 324 * 325 * @param data raw data 326 * @param size size of data 327 * @param offset of the records data (provided a big array of the file) 328 */ 329 330 protected void fillFields(byte [] data, short size, int offset) 331 { 332 } 333 334 public int compareTo(Object obj) 335 { 336 UnicodeString str = ( UnicodeString ) obj; 337 338 return this.getString().compareTo(str.getString()); 339 } 340 341 int maxBrokenLength(final int proposedBrokenLength) 342 { 343 int rval = proposedBrokenLength; 344 345 if ((field_2_optionflags & 1) == 1) 346 { 347 int proposedStringLength = proposedBrokenLength - 3; 348 349 if ((proposedStringLength % 2) == 1) 350 { 351 proposedStringLength--; 352 } 353 rval = proposedStringLength + 3; 354 } 355 return rval; 356 } 357 358 // public boolean equals(Object obj) { 359 // if (!(obj instanceof UnicodeString)) return false; 360 // 361 // UnicodeString str = (UnicodeString)obj; 362 // 363 // 364 // return this.getString().equals(str.getString()); 365 // } 366 } 367