1 /* ==================================================================== 2 * The Apache Software License, Version 1.1 3 * 4 * Copyright (c) 2002 The Apache Software Foundation. All rights 5 * reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in 16 * the documentation and/or other materials provided with the 17 * distribution. 18 * 19 * 3. The end-user documentation included with the redistribution, 20 * if any, must include the following acknowledgment: 21 * "This product includes software developed by the 22 * Apache Software Foundation (http://www.apache.org/)." 23 * Alternately, this acknowledgment may appear in the software itself, 24 * if and wherever such third-party acknowledgments normally appear. 25 * 26 * 4. The names "Apache" and "Apache Software Foundation" and 27 * "Apache POI" must not be used to endorse or promote products 28 * derived from this software without prior written permission. For 29 * written permission, please contact apache@apache.org. 30 * 31 * 5. Products derived from this software may not be called "Apache", 32 * "Apache POI", nor may "Apache" appear in their name, without 33 * prior written permission of the Apache Software Foundation. 34 * 35 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED 36 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 37 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 38 * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR 39 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 40 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 41 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 42 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 43 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 44 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 45 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 46 * SUCH DAMAGE. 47 * ==================================================================== 48 * 49 * This software consists of voluntary contributions made by many 50 * individuals on behalf of the Apache Software Foundation. For more 51 * information on the Apache Software Foundation, please see 52 * <http://www.apache.org/>. 53 */ 54 55 package org.apache.poi.hssf.record; 56 57 import org.apache.poi.util.BinaryTree; 58 59 import java.util.List; 60 61 /** 62 * This class handles serialization of SST records. It utilizes the record processor 63 * class write individual records. This has been refactored from the SSTRecord class. 64 * 65 * @author Glen Stampoultzis (glens at apache.org) 66 */ 67 class SSTSerializer 68 { 69 70 // todo: make private again 71 List recordLengths; 72 BinaryTree strings; 73 74 private int numStrings; 75 private int numUniqueStrings; 76 private SSTRecordHeader sstRecordHeader; 77 78 public SSTSerializer( List recordLengths, BinaryTree strings, int numStrings, int numUniqueStrings ) 79 { 80 this.recordLengths = recordLengths; 81 this.strings = strings; 82 this.numStrings = numStrings; 83 this.numUniqueStrings = numUniqueStrings; 84 this.sstRecordHeader = new SSTRecordHeader( numStrings, numUniqueStrings ); 85 } 86 87 /** 88 * Create a byte array consisting of an SST record and any 89 * required Continue records, ready to be written out. 90 * <p> 91 * If an SST record and any subsequent Continue records are read 92 * in to create this instance, this method should produce a byte 93 * array that is identical to the byte array produced by 94 * concatenating the input records' data. 95 * 96 * @return the byte array 97 */ 98 public int serialize( int offset, byte[] data ) 99 { 100 int record_size = getRecordSize(); 101 int record_length_index = 0; 102 103 if ( calculateUnicodeSize() > SSTRecord.MAX_DATA_SPACE ) 104 serializeLargeRecord( record_size, record_length_index, data, offset ); 105 else 106 serializeSingleSSTRecord( data, offset, record_length_index ); 107 return record_size; 108 } 109 110 111 112 // todo: make private again 113 /** 114 * Calculates the total unicode size for all the strings. 115 * 116 * @return the total size. 117 */ 118 int calculateUnicodeSize() 119 { 120 int retval = 0; 121 122 for ( int k = 0; k < strings.size(); k++ ) 123 { 124 retval += getUnicodeString( k ).getRecordSize(); 125 } 126 return retval; 127 } 128 129 /** 130 * This case is chosen when an SST record does not span over to a continue record. 131 * 132 */ 133 private void serializeSingleSSTRecord( byte[] data, int offset, int record_length_index ) 134 { 135 int len = ( (Integer) recordLengths.get( record_length_index++ ) ).intValue(); 136 int recordSize = SSTRecord.SST_RECORD_OVERHEAD + len - SSTRecord.STD_RECORD_OVERHEAD; 137 sstRecordHeader.writeSSTHeader( data, 0 + offset, recordSize ); 138 int pos = SSTRecord.SST_RECORD_OVERHEAD; 139 140 for ( int k = 0; k < strings.size(); k++ ) 141 { 142 System.arraycopy( getUnicodeString( k ).serialize(), 0, data, pos + offset, getUnicodeString( k ).getRecordSize() ); 143 pos += getUnicodeString( k ).getRecordSize(); 144 } 145 } 146 147 /** 148 * Large records are serialized to an SST and to one or more CONTINUE records. Joy. They have the special 149 * characteristic that they can change the option field when a single string is split across to a 150 * CONTINUE record. 151 */ 152 private void serializeLargeRecord( int record_size, int record_length_index, byte[] buffer, int offset ) 153 { 154 155 byte[] stringReminant = null; 156 int stringIndex = 0; 157 boolean lastneedcontinue = false; 158 boolean first_record = true; 159 int totalWritten = 0; 160 161 while ( totalWritten != record_size ) 162 { 163 int recordLength = ( (Integer) recordLengths.get( record_length_index++ ) ).intValue(); 164 RecordProcessor recordProcessor = new RecordProcessor( buffer, 165 recordLength, numStrings, numUniqueStrings ); 166 167 // write the appropriate header 168 recordProcessor.writeRecordHeader( offset, totalWritten, recordLength, first_record ); 169 first_record = false; 170 171 // now, write the rest of the data into the current 172 // record space 173 if ( lastneedcontinue ) 174 { 175 lastneedcontinue = stringReminant.length > recordProcessor.getAvailable(); 176 // the last string in the previous record was not written out completely 177 stringReminant = recordProcessor.writeStringRemainder( lastneedcontinue, 178 stringReminant, offset, totalWritten ); 179 } 180 181 // last string's remnant, if any, is cleaned up as best as can be done ... now let's try and write 182 // some more strings 183 for ( ; stringIndex < strings.size(); stringIndex++ ) 184 { 185 UnicodeString unistr = getUnicodeString( stringIndex ); 186 187 if ( unistr.getRecordSize() <= recordProcessor.getAvailable() ) 188 { 189 recordProcessor.writeWholeString( unistr, offset, totalWritten ); 190 } 191 else 192 { 193 194 // can't write the entire string out 195 if ( recordProcessor.getAvailable() >= SSTRecord.STRING_MINIMAL_OVERHEAD ) 196 { 197 198 // we can write some of it 199 stringReminant = recordProcessor.writePartString( unistr, offset, totalWritten ); 200 lastneedcontinue = true; 201 stringIndex++; 202 } 203 break; 204 } 205 } 206 totalWritten += recordLength + SSTRecord.STD_RECORD_OVERHEAD; 207 } 208 } 209 210 private UnicodeString getUnicodeString( int index ) 211 { 212 Integer intunipos = new Integer( index ); 213 return ( (UnicodeString) strings.get( intunipos ) ); 214 } 215 216 public int getRecordSize() 217 { 218 return new SSTRecordSizeCalculator(this).getRecordSize(); 219 } 220 221 } 222