1
54
55 package org.apache.poi.hssf.record;
56
57 import org.apache.poi.util.BinaryTree;
58 import org.apache.poi.util.LittleEndianConsts;
59
60 import java.util.List;
61 import java.util.ArrayList;
62
63
69 class SSTSerializer
70 {
71
72 private List recordLengths;
73 private BinaryTree strings;
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
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 private int calculateUnicodeSize()
111 {
112 int retval = 0;
113
114 for ( int k = 0; k < strings.size(); k++ )
115 {
116 retval += getUnicodeString(k).getRecordSize();
117 }
118 return retval;
119 }
120
121
122
123 public int getRecordSize()
124 {
125 recordLengths = new ArrayList();
126 int retval = 0;
127 int unicodesize = calculateUnicodeSize();
128
129 if ( unicodesize > SSTRecord.MAX_DATA_SPACE )
130 {
131 retval = calcRecordSizesForLongStrings( unicodesize );
132 }
133 else
134 {
135
136 retval = SSTRecord.SST_RECORD_OVERHEAD + unicodesize;
137 recordLengths.add( new Integer( unicodesize ) );
138 }
139 return retval;
140 }
141
142 private int calcRecordSizesForLongStrings( int unicodesize )
143 {
144 int retval;
145 UnicodeString unistr = null;
146 int stringreminant = 0;
147 int unipos = 0;
148 boolean lastneedcontinue = false;
149 int stringbyteswritten = 0;
150 boolean finished = false;
151 boolean first_record = true;
152 int totalWritten = 0;
153
154 while ( !finished )
155 {
156 int record = 0;
157 int pos = 0;
158
159 if ( first_record )
160 {
161
162
163 record = SSTRecord.MAX_RECORD_SIZE;
164 pos = 12;
165 first_record = false;
166 recordLengths.add( new Integer( record - SSTRecord.STD_RECORD_OVERHEAD ) );
167 }
168 else
169 {
170
171
172 pos = 0;
173 int to_be_written = ( unicodesize - stringbyteswritten ) + ( lastneedcontinue ? 1 : 0 );
174 int size = Math.min( SSTRecord.MAX_RECORD_SIZE - SSTRecord.STD_RECORD_OVERHEAD, to_be_written );
175
176 if ( size == to_be_written )
177 {
178 finished = true;
179 }
180 record = size + SSTRecord.STD_RECORD_OVERHEAD;
181 recordLengths.add( new Integer( size ) );
182 pos = 4;
183 }
184 if ( lastneedcontinue )
185 {
186 int available = SSTRecord.MAX_RECORD_SIZE - pos;
187
188 if ( stringreminant <= available )
189 {
190
191
192 stringbyteswritten += stringreminant - 1;
193 pos += stringreminant;
194 lastneedcontinue = false;
195 }
196 else
197 {
198
199
200 int toBeWritten = unistr.maxBrokenLength( available );
201
202 if ( available != toBeWritten )
203 {
204 int shortrecord = record - ( available - toBeWritten );
205 recordLengths.set( recordLengths.size() - 1,
206 new Integer( shortrecord - SSTRecord.STD_RECORD_OVERHEAD ) );
207 record = shortrecord;
208 }
209 stringbyteswritten += toBeWritten - 1;
210 pos += toBeWritten;
211 stringreminant -= toBeWritten - 1;
212 lastneedcontinue = true;
213 }
214 }
215 for ( ; unipos < strings.size(); unipos++ )
216 {
217 int available = SSTRecord.MAX_RECORD_SIZE - pos;
218 Integer intunipos = new Integer( unipos );
219
220 unistr = ( (UnicodeString) strings.get( intunipos ) );
221 if ( unistr.getRecordSize() <= available )
222 {
223 stringbyteswritten += unistr.getRecordSize();
224 pos += unistr.getRecordSize();
225 }
226 else
227 {
228 if ( available >= SSTRecord.STRING_MINIMAL_OVERHEAD )
229 {
230 int toBeWritten =
231 unistr.maxBrokenLength( available );
232
233 stringbyteswritten += toBeWritten;
234 stringreminant =
235 ( unistr.getRecordSize() - toBeWritten )
236 + LittleEndianConsts.BYTE_SIZE;
237 if ( available != toBeWritten )
238 {
239 int shortrecord = record
240 - ( available - toBeWritten );
241
242 recordLengths.set(
243 recordLengths.size() - 1,
244 new Integer(
245 shortrecord - SSTRecord.STD_RECORD_OVERHEAD ) );
246 record = shortrecord;
247 }
248 lastneedcontinue = true;
249 unipos++;
250 }
251 else
252 {
253 int shortrecord = record - available;
254
255 recordLengths.set( recordLengths.size() - 1,
256 new Integer( shortrecord - SSTRecord.STD_RECORD_OVERHEAD ) );
257 record = shortrecord;
258 }
259 break;
260 }
261 }
262 totalWritten += record;
263 }
264 retval = totalWritten;
265
266 return retval;
267 }
268
269
270 private void serializeSingleSSTRecord( byte[] data, int offset, int record_length_index )
271 {
272
273
274 int len = ( (Integer) recordLengths.get( record_length_index++ ) ).intValue();
275 int recordSize = SSTRecord.SST_RECORD_OVERHEAD + len - SSTRecord.STD_RECORD_OVERHEAD;
276 sstRecordHeader.writeSSTHeader( data, 0 + offset, recordSize );
277 int pos = SSTRecord.SST_RECORD_OVERHEAD;
278
279 for ( int k = 0; k < strings.size(); k++ )
280 {
281
282 System.arraycopy( getUnicodeString(k).serialize(), 0, data, pos + offset, getUnicodeString(k).getRecordSize() );
283 pos += getUnicodeString(k).getRecordSize();
284 }
285 }
286
287
292 private void serializeLargeRecord( int record_size, int record_length_index, byte[] buffer, int offset )
293 {
294
295 byte[] stringReminant = null;
296 int stringIndex = 0;
297 boolean lastneedcontinue = false;
298 boolean first_record = true;
299 int totalWritten = 0;
300
301 while ( totalWritten != record_size )
302 {
303 int recordLength = ( (Integer) recordLengths.get( record_length_index++ ) ).intValue();
304 RecordProcessor recordProcessor = new RecordProcessor( buffer,
305 recordLength, numStrings, numUniqueStrings );
306
307
308 recordProcessor.writeRecordHeader( offset, totalWritten, recordLength, first_record );
309 first_record = false;
310
311
312
313 if ( lastneedcontinue )
314 {
315 lastneedcontinue = stringReminant.length > recordProcessor.getAvailable();
316
317 stringReminant = recordProcessor.writeStringRemainder( lastneedcontinue,
318 stringReminant, offset, totalWritten );
319 }
320
321
322
323 for ( ; stringIndex < strings.size(); stringIndex++ )
324 {
325 UnicodeString unistr = getUnicodeString( stringIndex );
326
327 if ( unistr.getRecordSize() <= recordProcessor.getAvailable() )
328 {
329 recordProcessor.writeWholeString( unistr, offset, totalWritten );
330 }
331 else
332 {
333
334
335 if ( recordProcessor.getAvailable() >= SSTRecord.STRING_MINIMAL_OVERHEAD )
336 {
337
338
339 stringReminant = recordProcessor.writePartString( unistr, offset, totalWritten );
340 lastneedcontinue = true;
341 stringIndex++;
342 }
343 break;
344 }
345 }
346 totalWritten += recordLength + SSTRecord.STD_RECORD_OVERHEAD;
347 }
348 }
349
350 private UnicodeString getUnicodeString( int index )
351 {
352 Integer intunipos = new Integer( index );
353 return ( (UnicodeString) strings.get( intunipos ) );
354 }
355
356 }
357