1 2 17 18 19 package org.apache.poi.hssf.record; 20 21 import org.apache.poi.util.BinaryTree; 22 import org.apache.poi.util.LittleEndian; 23 import org.apache.poi.util.LittleEndianConsts; 24 25 31 class SSTDeserializer 32 { 33 34 private BinaryTree strings; 35 36 private int continuationReadChars; 37 38 private String unfinishedString; 39 40 private boolean wideChar; 41 42 private boolean richText; 43 44 private boolean extendedText; 45 46 private short runCount; 47 48 private int charCount; 49 private int extensionLength; 50 private int continueSkipBytes = 0; 51 52 53 public SSTDeserializer( BinaryTree strings ) 54 { 55 this.strings = strings; 56 initVars(); 57 } 58 59 private void initVars() 60 { 61 runCount = 0; 62 continuationReadChars = 0; 63 unfinishedString = ""; 64 wideChar = false; 67 richText = false; 68 extendedText = false; 69 continueSkipBytes = 0; 70 } 71 72 77 public void manufactureStrings( final byte[] data, final int initialOffset) 78 { 79 initVars(); 80 81 int offset = initialOffset; 82 final int dataSize = data.length; 83 while ( offset < dataSize ) 84 { 85 int remaining = dataSize - offset; 86 87 if ( ( remaining > 0 ) && ( remaining < LittleEndianConsts.SHORT_SIZE ) ) 88 { 89 throw new RecordFormatException( "Cannot get length of the last string in SSTRecord" ); 90 } 91 if ( remaining == LittleEndianConsts.SHORT_SIZE ) 92 { 93 setContinuationCharsRead( 0 ); unfinishedString = ""; 96 break; 97 } 98 charCount = LittleEndian.getUShort( data, offset ); 99 int charsRead = charCount; 100 readStringHeader( data, offset ); 101 boolean stringContinuesOverContinuation = remaining < totalStringSize(); 102 if ( stringContinuesOverContinuation ) 103 { 104 int remainingBytes = dataSize - offset - stringHeaderOverhead(); 105 charsRead = Math.min(charsRead, calculateCharCount( remainingBytes )); 108 setContinuationCharsRead( charsRead ); 109 if (charsRead == charCount) { 110 continueSkipBytes = offsetForContinuedRecord(0) - (remainingBytes - calculateByteCount(charsRead)); 113 } 114 } 115 processString( data, offset, charsRead ); 116 offset += totalStringSize(); 117 if ( stringContinuesOverContinuation ) 118 { 119 break; 120 } 121 } 122 } 123 124 142 147 private void readStringHeader( final byte[] data, final int index ) 148 { 149 150 byte optionFlag = data[index + LittleEndianConsts.SHORT_SIZE]; 151 152 wideChar = ( optionFlag & 1 ) == 1; 153 extendedText = ( optionFlag & 4 ) == 4; 154 richText = ( optionFlag & 8 ) == 8; 155 runCount = 0; 156 if ( richText ) 157 { 158 runCount = LittleEndian.getShort( data, index + SSTRecord.STRING_MINIMAL_OVERHEAD ); 159 } 160 extensionLength = 0; 161 if ( extendedText ) 162 { 163 extensionLength = LittleEndian.getInt( data, index + SSTRecord.STRING_MINIMAL_OVERHEAD 164 + (richText ? LittleEndianConsts.SHORT_SIZE : 0) ); 165 } 166 167 } 168 169 170 177 private int processString( final byte[] data, final int dataIndex, final int characters ) 178 { 179 180 int length = SSTRecord.STRING_MINIMAL_OVERHEAD + calculateByteCount( characters ); 182 byte[] unicodeStringBuffer = new byte[length]; 183 184 int offset = 0; 185 186 LittleEndian.putUShort( unicodeStringBuffer, offset, characters ); 188 offset += LittleEndianConsts.SHORT_SIZE; 189 unicodeStringBuffer[offset] = data[dataIndex + offset]; 191 int bytesRead = unicodeStringBuffer.length - SSTRecord.STRING_MINIMAL_OVERHEAD; 193 arraycopy( data, dataIndex + stringHeaderOverhead(), unicodeStringBuffer, SSTRecord.STRING_MINIMAL_OVERHEAD, bytesRead ); 194 UnicodeString string = new UnicodeString( UnicodeString.sid, 196 (short) unicodeStringBuffer.length, 197 unicodeStringBuffer ); 198 setContinuationCharsRead( calculateCharCount(bytesRead)); 199 200 if ( isStringFinished() ) 201 { 202 Integer integer = new Integer ( strings.size() ); 203 addToStringTable( strings, integer, string ); 204 } 205 else 206 { 207 unfinishedString = string.getString(); 208 } 209 210 return bytesRead; 211 } 212 213 private boolean isStringFinished() 214 { 215 return getContinuationCharsRead() == charCount; 216 } 217 218 224 static public void addToStringTable( BinaryTree strings, Integer integer, UnicodeString string ) 225 { 226 227 if ( string.isRichText() ) 228 string.setOptionFlags( (byte) ( string.getOptionFlags() & ( ~8 ) ) ); 229 if ( string.isExtendedText() ) 230 string.setOptionFlags( (byte) ( string.getOptionFlags() & ( ~4 ) ) ); 231 232 boolean added = false; 233 while ( added == false ) 234 { 235 try 236 { 237 strings.put( integer, string ); 238 added = true; 239 } 240 catch ( Exception ignore ) 241 { 242 string.setString( string.getString() + " " ); 243 } 244 } 245 246 } 247 248 249 private int calculateCharCount( final int byte_count ) 250 { 251 return byte_count / ( wideChar ? LittleEndianConsts.SHORT_SIZE : LittleEndianConsts.BYTE_SIZE ); 252 } 253 254 274 public void processContinueRecord( final byte[] record ) 275 { 276 if ( isStringFinished() ) 277 { 278 final int offset = continueSkipBytes; 279 initVars(); 280 manufactureStrings( record, offset); 281 } 282 else 283 { 284 wideChar = ( record[0] & 1 ) == 1; 288 289 if ( stringSpansContinuation( record.length - LittleEndianConsts.BYTE_SIZE ) ) 290 { 291 processEntireContinuation( record ); 292 } 293 else 294 { 295 readStringRemainder( record ); 296 } 297 } 298 299 } 300 301 306 private void readStringRemainder( final byte[] record ) 307 { 308 int stringRemainderSizeInBytes = calculateByteCount( charCount-getContinuationCharsRead() ); 309 byte[] unicodeStringData = new byte[SSTRecord.STRING_MINIMAL_OVERHEAD 310 + stringRemainderSizeInBytes]; 311 312 LittleEndian.putShort( unicodeStringData, 0, (short) (charCount-getContinuationCharsRead()) ); 314 315 unicodeStringData[LittleEndianConsts.SHORT_SIZE] = createOptionByte( wideChar, richText, extendedText ); 317 318 arraycopy( record, LittleEndianConsts.BYTE_SIZE, unicodeStringData, 321 SSTRecord.STRING_MINIMAL_OVERHEAD, 322 stringRemainderSizeInBytes ); 323 324 UnicodeString string = new UnicodeString( UnicodeString.sid, 326 (short) unicodeStringData.length, unicodeStringData, 327 unfinishedString ); 328 Integer integer = new Integer ( strings.size() ); 329 330 addToStringTable( strings, integer, string ); 331 332 int newOffset = offsetForContinuedRecord( stringRemainderSizeInBytes ); 333 manufactureStrings( record, newOffset); 334 } 335 336 339 private int stringSizeInBytes() 340 { 341 return calculateByteCount( charCount ); 342 } 343 344 348 private int totalStringSize() 349 { 350 return stringSizeInBytes() 351 + stringHeaderOverhead() 352 + LittleEndianConsts.INT_SIZE * runCount 353 + extensionLength; 354 } 355 356 private int stringHeaderOverhead() 357 { 358 return SSTRecord.STRING_MINIMAL_OVERHEAD 359 + ( richText ? LittleEndianConsts.SHORT_SIZE : 0 ) 360 + ( extendedText ? LittleEndianConsts.INT_SIZE : 0 ); 361 } 362 363 private int offsetForContinuedRecord( int stringRemainderSizeInBytes ) 364 { 365 int offset = stringRemainderSizeInBytes + runCount * LittleEndianConsts.INT_SIZE + extensionLength; 366 if (stringRemainderSizeInBytes != 0) 367 offset += + LittleEndianConsts.BYTE_SIZE; 370 return offset; 371 } 372 373 private byte createOptionByte( boolean wideChar, boolean richText, boolean farEast ) 374 { 375 return (byte) ( ( wideChar ? 1 : 0 ) + ( farEast ? 4 : 0 ) + ( richText ? 8 : 0 ) ); 376 } 377 378 384 private void processEntireContinuation( final byte[] record ) 385 { 386 int dataLengthInBytes = record.length - LittleEndianConsts.BYTE_SIZE; 388 byte[] unicodeStringData = new byte[record.length + LittleEndianConsts.SHORT_SIZE]; 389 390 int charsRead = calculateCharCount( dataLengthInBytes ); 391 LittleEndian.putShort( unicodeStringData, (byte) 0, (short) charsRead ); 392 arraycopy( record, 0, unicodeStringData, LittleEndianConsts.SHORT_SIZE, record.length ); 393 UnicodeString ucs = new UnicodeString( UnicodeString.sid, (short) unicodeStringData.length, unicodeStringData, unfinishedString); 394 395 unfinishedString = ucs.getString(); 396 setContinuationCharsRead( getContinuationCharsRead() + charsRead ); 397 if (getContinuationCharsRead() == charCount) { 398 Integer integer = new Integer ( strings.size() ); 399 addToStringTable( strings, integer, ucs ); 400 } 401 } 402 403 private boolean stringSpansContinuation( int continuationSizeInBytes ) 404 { 405 return calculateByteCount( charCount - getContinuationCharsRead() ) > continuationSizeInBytes; 406 } 407 408 412 413 int getContinuationCharsRead() 414 { 415 return continuationReadChars; 416 } 417 418 private void setContinuationCharsRead( final int count ) 419 { 420 continuationReadChars = count; 421 } 422 423 private int calculateByteCount( final int character_count ) 424 { 425 return character_count * ( wideChar ? LittleEndianConsts.SHORT_SIZE : LittleEndianConsts.BYTE_SIZE ); 426 } 427 428 429 521 private void arraycopy( byte[] src, int src_position, 522 byte[] dst, int dst_position, 523 int length ) 524 { 525 System.arraycopy( src, src_position, dst, dst_position, length ); 526 } 527 528 531 String getUnfinishedString() 532 { 533 return unfinishedString; 534 } 535 536 539 boolean isWideChar() 540 { 541 return wideChar; 542 } 543 544 545 } 546 | Popular Tags |