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.BinaryTree;
59   import org.apache.poi.util.LittleEndian;
60   import org.apache.poi.util.LittleEndianConsts;
61   
62   import java.util.*;
63   
64   /**
65    * Title:        Static String Table Record
66    * <P>
67    * Description:  This holds all the strings for LabelSSTRecords.
68    * <P>
69    * REFERENCE:    PG 389 Microsoft Excel 97 Developer's Kit (ISBN:
70    *               1-57231-498-2)
71    * <P>
72    * @author Andrew C. Oliver (acoliver at apache dot org)
73    * @author Marc Johnson (mjohnson at apache dot org)
74    * @version 2.0-pre
75    * @see org.apache.poi.hssf.record.LabelSSTRecord
76    * @see org.apache.poi.hssf.record.ContinueRecord
77    */
78   
79   public class SSTRecord
80       extends Record
81   {
82   
83       // how big can an SST record be? As big as any record can be: 8228
84       // bytes
85       private static final int  _max                     = 8228;
86   
87       // standard record overhead: two shorts (record id plus data space
88       // size)
89       private static final int  _std_record_overhead     =
90           2 * LittleEndianConsts.SHORT_SIZE;
91   
92       // SST overhead: the standard record overhead, plus the number of
93       // strings and the number of unique strings -- two ints
94       private static final int  _sst_record_overhead     =
95           (_std_record_overhead + (2 * LittleEndianConsts.INT_SIZE));
96   
97       // how much data can we stuff into an SST record? That would be
98       // _max minus the standard SST record overhead
99       private static final int  _max_data_space          =
100          _max - _sst_record_overhead;
101  
102      // overhead for each string includes the string's character count
103      // (a short) and the flag describing its characteristics (a byte)
104      private static final int  _string_minimal_overhead =
105          LittleEndianConsts.SHORT_SIZE + LittleEndianConsts.BYTE_SIZE;
106      public static final short sid                      = 0xfc;
107  
108      // union of strings in the SST and EXTSST
109      private int               field_1_num_strings;
110  
111      // according to docs ONLY SST
112      private int               field_2_num_unique_strings;
113      private BinaryTree        field_3_strings;
114  
115      // this is the number of characters we expect in the first
116      // sub-record in a subsequent continuation record
117      private int               __expected_chars;
118  
119      // this is the string we were working on before hitting the end of
120      // the current record. This string is NOT finished.
121      private String            _unfinished_string;
122  
123      // this is the total length of the current string being handled
124      private int               _total_length_bytes;
125  
126      // this is the offset into a string field of the actual string
127      // data
128      private int               _string_data_offset;
129  
130      // this is true if the string uses wide characters
131      private boolean           _wide_char;
132      private List              _record_lengths = null;
133  
134      /**
135       * default constructor
136       */
137  
138      public SSTRecord()
139      {
140          field_1_num_strings        = 0;
141          field_2_num_unique_strings = 0;
142          field_3_strings            = new BinaryTree();
143          setExpectedChars(0);
144          _unfinished_string  = "";
145          _total_length_bytes = 0;
146          _string_data_offset = 0;
147          _wide_char          = false;
148      }
149  
150      /**
151       * Constructs an SST record and sets its fields appropriately.
152       *
153       * @param id must be 0xfc or an exception will be throw upon
154       *           validation
155       * @param size the size of the data area of the record
156       * @param data of the record (should not contain sid/len)
157       */
158  
159      public SSTRecord(final short id, final short size, final byte [] data)
160      {
161          super(id, size, data);
162      }
163  
164      /**
165       * Constructs an SST record and sets its fields appropriately.
166       *
167       * @param id must be 0xfc or an exception will be throw upon
168       *           validation
169       * @param size the size of the data area of the record
170       * @param data of the record (should not contain sid/len)
171       * @param offset of the record
172       */
173  
174      public SSTRecord(final short id, final short size, final byte [] data,
175                       int offset)
176      {
177          super(id, size, data, offset);
178      }
179  
180      /**
181       * Add a string. Determines whether 8-bit encoding can be used, or
182       * whether 16-bit encoding must be used.
183       * <p>
184       * THIS IS THE PREFERRED METHOD OF ADDING A STRING. IF YOU USE THE
185       * OTHER ,code>addString</code> METHOD AND FORCE 8-BIT ENCODING ON
186       * A STRING THAT SHOULD USE 16-BIT ENCODING, YOU WILL CORRUPT THE
187       * STRING; IF YOU USE THAT METHOD AND FORCE 16-BIT ENCODING, YOU
188       * ARE WASTING SPACE WHEN THE WORKBOOK IS WRITTEN OUT.
189       *
190       * @param string string to be added
191       *
192       * @return the index of that string in the table
193       */
194  
195      public int addString(final String string)
196      {
197          int rval;
198  
199          if (string == null)
200          {
201              rval = addString("", false);
202          }
203          else
204          {
205  
206              // scan for characters greater than 255 ... if any are
207              // present, we have to use 16-bit encoding. Otherwise, we
208              // can use 8-bit encoding
209              boolean useUTF16 = false;
210              int     strlen   = string.length();
211  
212              for (int j = 0; j < strlen; j++)
213              {
214                  if (string.charAt(j) > 255)
215                  {
216                      useUTF16 = true;
217                      break;
218                  }
219              }
220              rval = addString(string, useUTF16);
221          }
222          return rval;
223      }
224  
225      /**
226       * Add a string and assert the encoding (8-bit or 16-bit) to be
227       * used.
228       * <P>
229       * USE THIS METHOD AT YOUR OWN RISK. IF YOU FORCE 8-BIT ENCODING,
230       * YOU MAY CORRUPT YOUR STRING. IF YOU FORCE 16-BIT ENCODING AND
231       * IT ISN'T NECESSARY, YOU WILL WASTE SPACE WHEN THIS RECORD IS
232       * WRITTEN OUT.
233       *
234       * @param string string to be added
235       * @param useUTF16 if true, forces 16-bit encoding. If false,
236       *                 forces 8-bit encoding
237       *
238       * @return the index of that string in the table
239       */
240  
241      public int addString(final String string, final boolean useUTF16)
242      {
243          field_1_num_strings++;
244          String        str  = (string == null) ? ""
245                                                : string;
246          int           rval = -1;
247          UnicodeString ucs  = new UnicodeString();
248  
249          ucs.setString(str);
250          ucs.setCharCount(( short ) str.length());
251          ucs.setOptionFlags(( byte ) (useUTF16 ? 1
252                                                : 0));
253          Integer integer = ( Integer ) field_3_strings.getKeyForValue(ucs);
254  
255          if (integer != null)
256          {
257              rval = integer.intValue();
258          }
259          else
260          {
261  
262              // This is a new string -- we didn't see it among the
263              // strings we've already collected
264              rval = field_3_strings.size();
265              field_2_num_unique_strings++;
266              integer = new Integer(rval);
267              field_3_strings.put(integer, ucs);
268          }
269          return rval;
270      }
271  
272      /**
273       * @return number of strings
274       */
275  
276      public int getNumStrings()
277      {
278          return field_1_num_strings;
279      }
280  
281      /**
282       * @return number of unique strings
283       */
284  
285      public int getNumUniqueStrings()
286      {
287          return field_2_num_unique_strings;
288      }
289  
290      /**
291       * USE THIS METHOD AT YOUR OWN PERIL: THE <code>addString</code>
292       * METHODS MANIPULATE THE NUMBER OF STRINGS AS A SIDE EFFECT; YOUR
293       * ATTEMPTS AT MANIPULATING THE STRING COUNT IS LIKELY TO BE VERY
294       * WRONG AND WILL RESULT IN BAD BEHAVIOR WHEN THIS RECORD IS
295       * WRITTEN OUT AND ANOTHER PROCESS ATTEMPTS TO READ THE RECORD
296       *
297       * @param count  number of strings
298       *
299       */
300  
301      public void setNumStrings(final int count)
302      {
303          field_1_num_strings = count;
304      }
305  
306      /**
307       * USE THIS METHOD AT YOUR OWN PERIL: THE <code>addString</code>
308       * METHODS MANIPULATE THE NUMBER OF UNIQUE STRINGS AS A SIDE
309       * EFFECT; YOUR ATTEMPTS AT MANIPULATING THE UNIQUE STRING COUNT
310       * IS LIKELY TO BE VERY WRONG AND WILL RESULT IN BAD BEHAVIOR WHEN
311       * THIS RECORD IS WRITTEN OUT AND ANOTHER PROCESS ATTEMPTS TO READ
312       * THE RECORD
313       *
314       * @param count  number of strings
315       */
316  
317      public void getNumUniqueStrings(final int count)
318      {
319          field_2_num_unique_strings = count;
320      }
321  
322      /**
323       * Get a particular string by its index
324       *
325       * @param id index into the array of strings
326       *
327       * @return the desired string
328       */
329  
330      public String getString(final int id)
331      {
332          return (( UnicodeString ) field_3_strings.get(new Integer(id)))
333              .getString();
334      }
335  
336      public boolean getString16bit(final int id)
337      {
338          return ((( UnicodeString ) field_3_strings.get(new Integer(id)))
339              .getOptionFlags() == 1);
340      }
341  
342      /**
343       * Return a debugging string representation
344       *
345       * @return string representation
346       */
347  
348      public String toString()
349      {
350          StringBuffer buffer = new StringBuffer();
351  
352          buffer.append("[SST]\n");
353          buffer.append("    .numstrings     = ")
354              .append(Integer.toHexString(getNumStrings())).append("\n");
355          buffer.append("    .uniquestrings  = ")
356              .append(Integer.toHexString(getNumUniqueStrings())).append("\n");
357          for (int k = 0; k < field_3_strings.size(); k++)
358          {
359              buffer.append("    .string_" + k + "      = ")
360                  .getend((( UnicodeString ) field_3_strings
361                  .get(new Integer(k))).toString()).append("\n");
362          }
363          buffer.append("[/SST]\n");
364          return buffer.toString();
365      }
366  
367      /**
368       * Create a byte array consisting of an SST record and any
369       * required Continue records, ready to be written out.
370       * <p>
371       * If an SST record and any subsequent Continue records are read
372       * in to create this instance, this method should produce a byte
373       * array that is identical to the byte array produced by
374       * concatenating the input records' data.
375       *
376       * @return the byte array
377       */
378  
379      public int serialize(int offset, byte [] data)
380      {
381          int rval                = getRecordSize();
382          int record_length_index = 0;
383  
384          // get the linear size of that array
385          int unicodesize         = calculateUnicodeSize();
386  
387          if (unicodesize > _max_data_space)
388          {
389              byte[]  stringreminant     = null;
390              int     unipos             = 0;
391              boolean lastneedcontinue   = false;
392              int     stringbyteswritten = 0;
393              boolean first_record       = true;
394              int     totalWritten       = 0;
395              int     size               = 0;
396  
397              while (totalWritten != rval)
398              {
399                  int pos = 0;
400  
401                  // write the appropriate header
402                  int available;
403  
404                  if (first_record)
405                  {
406                      size         =
407                          (( Integer ) _record_lengths
408                              .get(record_length_index++)).intValue();
409                      available    = size - 8;
410                      pos          = writeSSTHeader(data,
411                                                    pos + offset
412                                                    + totalWritten, size);
413                      size         += _std_record_overhead;
414                      first_record = false;
415                  }
416                  else
417                  {
418                      pos = 0;
419                      int to_be_written = (unicodesize - stringbyteswritten)
420                                          + (lastneedcontinue ? 1
421                                                              : 0);           // not used?
422  
423                      size      =
424                          (( Integer ) _record_lengths
425                              .get(record_length_index++)).intValue();
426                      available = size;
427                      pos       = writeContinueHeader(data,
428                                                      pos + offset
429                                                      + totalWritten, size);
430                      size      = size + _std_record_overhead;
431                  }
432  
433                  // now, write the rest of the data into the current
434                  // record space
435                  if (lastneedcontinue)
436                  {
437  
438                      // the last string in the previous record was not
439                      // written out completely
440                      if (stringreminant.length <= available)
441                      {
442  
443                          // write reminant -- it'll all fit neatly
444                          System.arraycopy(stringreminant, 0, data,
445                                           pos + offset + totalWritten,
446                                           stringreminant.length);
447                          stringbyteswritten += stringreminant.length - 1;
448                          pos                += stringreminant.length;
449                          lastneedcontinue   = false;
450                          available          -= stringreminant.length;
451                      }
452                      else
453                      {
454  
455                          // write as much of the remnant as possible
456                          System.arraycopy(stringreminant, 0, data,
457                                           pos + offset + totalWritten,
458                                           available);
459                          stringbyteswritten += available - 1;
460                          pos                += available;
461                          byte[] leftover =
462                              new byte[ (stringreminant.length - available) + LittleEndianConsts.BYTE_SIZE ];
463  
464                          System.arraycopy(stringreminant, available, leftover,
465                                           LittleEndianConsts.BYTE_SIZE,
466                                           stringreminant.length - available);
467                          leftover[ 0 ]    = stringreminant[ 0 ];
468                          stringreminant   = leftover;
469                          available        = 0;
470                          lastneedcontinue = true;
471                      }
472                  }
473  
474                  // last string's remnant, if any, is cleaned up as
475                  // best as can be done ... now let's try and write
476                  // some more strings
477                  for (; unipos < field_3_strings.size(); unipos++)
478                  {
479                      Integer       intunipos = new Integer(unipos);
480                      UnicodeString unistr    =
481                          (( UnicodeString ) field_3_strings.get(intunipos));
482  
483                      if (unistr.getRecordSize() <= available)
484                      {
485                          unistr.serialize(pos + offset + totalWritten, data);
486                          int rsize = unistr.getRecordSize();
487  
488                          stringbyteswritten += rsize;
489                          pos                += rsize;
490                          available          -= rsize;
491                      }
492                      else
493                      {
494  
495                          // can't write the entire string out
496                          if (available >= _string_minimal_overhead)
497                          {
498  
499                              // we can write some of it
500                              byte[] ucs = unistr.serialize();
501  
502                              System.arraycopy(ucs, 0, data,
503                                               pos + offset + totalWritten,
504                                               available);
505                              stringbyteswritten += available;
506                              stringreminant     =
507                                  new byte[ (ucs.length - available) + LittleEndianConsts.BYTE_SIZE ];
508                              System.arraycopy(ucs, available, stringreminant,
509                                               LittleEndianConsts.BYTE_SIZE,
510                                               ucs.length - available);
511                              stringreminant[ 0 ] =
512                                  ucs[ LittleEndianConsts.SHORT_SIZE ];
513                              available           = 0;
514                              lastneedcontinue    = true;
515                              unipos++;
516                          }
517                          break;
518                      }
519                  }
520                  totalWritten += size;
521              }
522          }
523          else
524          {
525  
526              // short data: write one simple SST record
527              int datasize = _sst_record_overhead + unicodesize;           // not used?
528  
529              writeSSTHeader(
530                  data, 0 + offset,
531                  _sst_record_overhead
532                  + (( Integer ) _record_lengths.get(
533                  record_length_index++)).intValue() - _std_record_overhead);
534              int pos = _sst_record_overhead;
535  
536              for (int k = 0; k < field_3_strings.size(); k++)
537              {
538                  UnicodeString unistr =
539                      (( UnicodeString ) field_3_strings.get(new Integer(k)));
540  
541                  System.arraycopy(unistr.serialize(), 0, data, pos + offset,
542                                   unistr.getRecordSize());
543                  pos += unistr.getRecordSize();
544              }
545          }
546          return rval;
547      }
548  
549      // not used: remove?
550      private int calculateStringsize()
551      {
552          int retval = 0;
553  
554          for (int k = 0; k < field_3_strings.size(); k++)
555          {
556              retval +=
557                  (( UnicodeString ) field_3_strings.get(new Integer(k)))
558                      .getRecordSize();
559          }
560          return retval;
561      }
562  
563      /**
564       * Process a Continue record. A Continue record for an SST record
565       * contains the same kind of data that the SST record contains,
566       * with the following exceptions:
567       * <P>
568       * <OL>
569       * <LI>The string counts at the beginning of the SST record are
570       *     not in the Continue record
571       * <LI>The first string in the Continue record might NOT begin
572       *     with a size. If the last string in the previous record is
573       *     continued in this record, the size is determined by that
574       *     last string in the previous record; the first string will
575       *     begin with a flag byte, followed by the remaining bytes (or
576       *     words) of the last string from the previous
577       *     record. Otherwise, the first string in the record will
578       *     begin with a string length
579       * </OL>
580       *
581       * @param record the Continue record's byte data
582       */
583  
584      public void processContinueRecord(final byte [] record)
585      {
586          if (getExpectedChars() == 0)
587          {
588              _unfinished_string  = "";
589              _total_length_bytes = 0;
590              _string_data_offset = 0;
591              _wide_char          = false;
592              manufactureStrings(record, 0, ( short ) record.length);
593          }
594          else
595          {
596              int data_length = record.length - LittleEndianConsts.BYTE_SIZE;
597  
598              if (calculateByteCount(getExpectedChars()) > data_length)
599              {
600  
601                  // create artificial data to create a UnicodeString
602                  byte[] input =
603                      new byte[ record.length + LittleEndianConsts.SHORT_SIZE ];
604                  short  size  = ( short ) (((record[ 0 ] & 1) == 1)
605                                            ? (data_length
606                                               / LittleEndianConsts.SHORT_SIZE)
607                                            : (data_length
608                                               / LittleEndianConsts.BYTE_SIZE));
609  
610                  LittleEndian.putShort(input, ( byte ) 0, size);
611                  System.arraycopy(record, 0, input,
612                                   LittleEndianConsts.SHORT_SIZE,
613                                   record.length);
614                  UnicodeString ucs = new UnicodeString(UnicodeString.sid,
615                                                        ( short ) input.length,
616                                                        input);
617  
618                  _unfinished_string = _unfinished_string + ucs.getString();
619                  setExpectedChars(getExpectedChars() - size);
620              }
621              else
622              {
623                  setupStringParameters(record, -LittleEndianConsts.SHORT_SIZE,
624                                        getExpectedChars());
625                  byte[] str_data = new byte[ _total_length_bytes ];
626                  int    length   = _string_minimal_overhead
627                                    + (calculateByteCount(getExpectedChars()));
628                  byte[] bstring  = new byte[ length ];
629  
630                  // Copy data from the record into the string
631                  // buffer. Copy skips the length of a short in the
632                  // string buffer, to leave room for the string length.
633                  System.arraycopy(record, 0, str_data,
634                                   LittleEndianConsts.SHORT_SIZE,
635                                   str_data.length
636                                   - LittleEndianConsts.SHORT_SIZE);
637  
638                  // write the string length
639                  LittleEndian.putShort(bstring, 0,
640                                        ( short ) getExpectedChars());
641  
642                  // write the options flag
643                  bstring[ LittleEndianConsts.SHORT_SIZE ] =
644                      str_data[ LittleEndianConsts.SHORT_SIZE ];
645  
646                  // copy the bytes/words making up the string; skipping
647                  // past all the overhead of the str_data array
648                  System.arraycopy(str_data, _string_data_offset, bstring,
649                                   _string_minimal_overhead,
650                                   bstring.length - _string_minimal_overhead);
651  
652                  // use special constructor to create the final string
653                  UnicodeString string  =
654                      new UnicodeString(UnicodeString.sid,
655                                        ( short ) bstring.length, bstring,
656                                        _unfinished_string);
657                  Integer       integer = new Integer(field_3_strings.size());
658  
659                  field_3_strings.put(integer, string);
660                  manufactureStrings(record,
661                                     _total_length_bytes
662                                     - LittleEndianConsts
663                                         .SHORT_SIZE, ( short ) record.length);
664              }
665          }
666      }
667  
668      /**
669       * @return sid
670       */
671  
672      public short getSid()
673      {
674          return sid;
675      }
676  
677      /**
678       * @return hashcode
679       */
680  
681      public int hashCode()
682      {
683          return field_2_num_unique_strings;
684      }
685  
686      /**
687       *
688       * @param o
689       * @return true if equal
690       */
691  
692      public boolean equals(Object o)
693      {
694          if ((o == null) || (o.getClass() != this.getClass()))
695          {
696              return false;
697          }
698          SSTRecord other = ( SSTRecord ) o;
699  
700          return ((field_1_num_strings == other
701              .field_1_num_strings) && (field_2_num_unique_strings == other
702                  .field_2_num_unique_strings) && field_3_strings
703                      .equals(other.field_3_strings));
704      }
705  
706      /**
707       * validate SID
708       *
709       * @param id the alleged SID
710       *
711       * @exception RecordFormatException if validation fails
712       */
713  
714      protected void validateSid(final short id)
715          throws RecordFormatException
716      {
717          if (id != sid)
718          {
719              throw new RecordFormatException("NOT An SST RECORD");
720          }
721      }
722  
723      /**
724       * Fill the fields from the data
725       * <P>
726       * The data consists of sets of string data. This string data is
727       * arranged as follows:
728       * <P>
729       * <CODE>
730       * short  string_length;   // length of string data
731       * byte   string_flag;     // flag specifying special string
732       *                         // handling
733       * short  run_count;       // optional count of formatting runs
734       * int    extend_length;   // optional extension length
735       * char[] string_data;     // string data, can be byte[] or
736       *                         // short[] (length of array is
737       *                         // string_length)
738       * int[]  formatting_runs; // optional formatting runs (length of
739       *                         // array is run_count)
740       * byte[] extension;       // optional extension (length of array
741       *                         // is extend_length)
742       * </CODE>
743       * <P>
744       * The string_flag is bit mapped as follows:
745       * <P>
746       * <TABLE>
747       *   <TR>
748       *      <TH>Bit number</TH>
749       *      <TH>Meaning if 0</TH>
750       *      <TH>Meaning if 1</TH>
751       *   <TR>
752       *   <TR>
753       *      <TD>0</TD>
754       *      <TD>string_data is byte[]</TD>
755       *      <TD>string_data is short[]</TH>
756       *   <TR>
757       *   <TR>
758       *      <TD>1</TD>
759       *      <TD>Should always be 0</TD>
760       *      <TD>string_flag is defective</TH>
761       *   <TR>
762       *   <TR>
763       *      <TD>2</TD>
764       *      <TD>extension is not included</TD>
765       *      <TD>extension is included</TH>
766       *   <TR>
767       *   <TR>
768       *      <TD>3</TD>
769       *      <TD>formatting run data is not included</TD>
770       *      <TD>formatting run data is included</TH>
771       *   <TR>
772       *   <TR>
773       *      <TD>4</TD>
774       *      <TD>Should always be 0</TD>
775       *      <TD>string_flag is defective</TH>
776       *   <TR>
777       *   <TR>
778       *      <TD>5</TD>
779       *      <TD>Should always be 0</TD>
780       *      <TD>string_flag is defective</TH>
781       *   <TR>
782       *   <TR>
783       *      <TD>6</TD>
784       *      <TD>Should always be 0</TD>
785       *      <TD>string_flag is defective</TH>
786       *   <TR>
787       *   <TR>
788       *      <TD>7</TD>
789       *      <TD>Should always be 0</TD>
790       *      <TD>string_flag is defective</TH>
791       *   <TR>
792       * </TABLE>
793       * <P>
794       * We can handle eating the overhead associated with bits 2 or 3
795       * (or both) being set, but we have no idea what to do with the
796       * associated data. The UnicodeString class can handle the byte[]
797       * vs short[] nature of the actual string data
798       *
799       * @param data raw data
800       * @param size size of the raw data
801       */
802  
803      protected void fillFields(final byte [] data, final short size,
804                                int offset)
805      {
806  
807          // this method is ALWAYS called after construction -- using
808          // the nontrivial constructor, of course -- so this is where
809          // we initialize our fields
810          field_1_num_strings        = LittleEndian.getInt(data, 0 + offset);
811          field_2_num_unique_strings = LittleEndian.getInt(data, 4 + offset);
812          field_3_strings            = new BinaryTree();
813          setExpectedChars(0);
814          _unfinished_string  = "";
815          _total_length_bytes = 0;
816          _string_data_offset = 0;
817          _wide_char          = false;
818          manufactureStrings(data, 8 + offset, size);
819      }
820  
821      /**
822       * @return the number of characters we expect in the first
823       *         sub-record in a subsequent continuation record
824       */
825  
826      int getExpectedChars()
827      {
828          return __expected_chars;
829      }
830  
831      /**
832       * @return an iterator of the strings we hold. All instances are
833       *         UnicodeStrings
834       */
835  
836      Iterator getStrings()
837      {
838          return field_3_strings.values().iterator();
839      }
840  
841      /**
842       * @return count of the strings we hold.
843       */
844  
845      int countStrings()
846      {
847          return field_3_strings.size();
848      }
849  
850      /**
851       * @return the unfinished string
852       */
853  
854      String getUnfinishedString()
855      {
856          return _unfinished_string;
857      }
858  
859      /**
860       * @return the total length of the current string
861       */
862  
863      int getTotalLength()
864      {
865          return _total_length_bytes;
866      }
867  
868      /**
869       * @return offset into current string data
870       */
871  
872      int getStringDataOffset()
873      {
874          return _string_data_offset;
875      }
876  
877      /**
878       * @return true if current string uses wide characters
879       */
880  
881      boolean isWideChar()
882      {
883          return _wide_char;
884      }
885  
886      private int writeSSTHeader(final byte [] data, final int pos,
887                                 final int recsize)
888      {
889          int offset = pos;
890  
891          LittleEndian.putShort(data, offset, sid);
892          offset += LittleEndianConsts.SHORT_SIZE;
893          LittleEndian.putShort(data, offset, ( short ) (recsize));
894          offset += LittleEndianConsts.SHORT_SIZE;
895          LittleEndian.putInt(data, offset, getNumStrings());
896          offset += LittleEndianConsts.INT_SIZE;
897          LittleEndian.putInt(data, offset, getNumUniqueStrings());
898          offset += LittleEndianConsts.INT_SIZE;
899          return offset - pos;
900      }
901  
902      private int writeContinueHeader(final byte [] data, final int pos,
903                                      final int recsize)
904      {
905          int offset = pos;
906  
907          LittleEndian.putShort(data, offset, ContinueRecord.sid);
908          offset += LittleEndianConsts.SHORT_SIZE;
909          LittleEndian.putShort(data, offset, ( short ) (recsize));
910          offset += LittleEndianConsts.SHORT_SIZE;
911          return offset - pos;
912      }
913  
914      private int calculateUCArrayLength(final byte [][] ucarray)
915      {
916          int retval = 0;
917  
918          for (int k = 0; k < ucarray.length; k++)
919          {
920              retval += ucarray[ k ].length;
921          }
922          return retval;
923      }
924  
925      private void manufactureStrings(final byte [] data, final int index,
926                                      short size)
927      {
928          int offset = index;
929  
930          while (offset < size)
931          {
932              int remaining = size - offset;
933  
934              if ((remaining > 0)
935                      && (remaining < LittleEndianConsts.SHORT_SIZE))
936              {
937                  throw new RecordFormatException(
938                      "Cannot get length of the last string in SSTRecord");
939              }
940              if (remaining == LittleEndianConsts.SHORT_SIZE)
941              {
942                  setExpectedChars(LittleEndian.getShort(data, offset));
943                  _unfinished_string = "";
944                  break;
945              }
946              short char_count = LittleEndian.getShort(data, offset);
947  
948              setupStringParameters(data, offset, char_count);
949              if (remaining < _total_length_bytes)
950              {
951                  setExpectedChars(calculateCharCount(_total_length_bytes
952                                                      - remaining));
953                  char_count          -= getExpectedChars();
954                  _total_length_bytes = remaining;
955              }
956              else
957              {
958                  setExpectedChars(0);
959              }
960              processString(data, offset, char_count);
961              offset += _total_length_bytes;
962              if (getExpectedChars() != 0)
963              {
964                  break;
965              }
966          }
967      }
968  
969      private void setupStringParameters(final byte [] data, final int index,
970                                         final int char_count)
971      {
972          byte flag = data[ index + LittleEndianConsts.SHORT_SIZE ];
973  
974          _wide_char = (flag & 1) == 1;
975          boolean extended      = (flag & 4) == 4;
976          boolean formatted_run = (flag & 8) == 8;
977  
978          _total_length_bytes = _string_minimal_overhead
979                                + calculateByteCount(char_count);
980          _string_data_offset = _string_minimal_overhead;
981          if (formatted_run)
982          {
983              short run_count = LittleEndian.getShort(data,
984                                                      index
985                                                      + _string_data_offset);
986  
987              _string_data_offset += LittleEndianConsts.SHORT_SIZE;
988              _total_length_bytes += LittleEndianConsts.SHORT_SIZE
989                                     + (LittleEndianConsts.INT_SIZE
990                                        * run_count);
991          }
992          if (extended)
993          {
994              int extension_length = LittleEndian.getInt(data,
995                                                         index
996                                                         + _string_data_offset);
997  
998              _string_data_offset += LittleEndianConsts.INT_SIZE;
999              _total_length_bytes += LittleEndianConsts.INT_SIZE
1000                                    + extension_length;
1001         }
1002     }
1003 
1004     private void processString(final byte [] data, final int index,
1005                                final short char_count)
1006     {
1007         byte[] str_data = new byte[ _total_length_bytes ];
1008         int    length   = _string_minimal_overhead
1009                           + calculateByteCount(char_count);
1010         byte[] bstring  = new byte[ length ];
1011 
1012         System.arraycopy(data, index, str_data, 0, str_data.length);
1013         int offset = 0;
1014 
1015         LittleEndian.putShort(bstring, offset, char_count);
1016         offset            += LittleEndianConsts.SHORT_SIZE;
1017         bstring[ offset ] = str_data[ offset ];
1018         System.arraycopy(str_data, _string_data_offset, bstring,
1019                          _string_minimal_overhead,
1020                          bstring.length - _string_minimal_overhead);
1021         UnicodeString string = new UnicodeString(UnicodeString.sid,
1022                                                  ( short ) bstring.length,
1023                                                  bstring);
1024 
1025         if (getExpectedChars() != 0)
1026         {
1027             _unfinished_string = string.getString();
1028         }
1029         else
1030         {
1031             Integer integer = new Integer(field_3_strings.size());
1032 
1033             field_3_strings.put(integer, string);
1034         }
1035     }
1036 
1037     private void setExpectedChars(final int count)
1038     {
1039         __expected_chars = count;
1040     }
1041 
1042     private int calculateByteCount(final int character_count)
1043     {
1044         return character_count * (_wide_char ? LittleEndianConsts.SHORT_SIZE
1045                                              : LittleEndianConsts.BYTE_SIZE);
1046     }
1047 
1048     private int calculateCharCount(final int byte_count)
1049     {
1050         return byte_count / (_wide_char ? LittleEndianConsts.SHORT_SIZE
1051                                         : LittleEndianConsts.BYTE_SIZE);
1052     }
1053 
1054     // we can probably simplify this later...this calculates the size
1055     // w/o serializing but still is a bit slow
1056     public int getRecordSize()
1057     {
1058         _record_lengths = new ArrayList();
1059         int retval      = 0;
1060         int unicodesize = calculateUnicodeSize();
1061 
1062         if (unicodesize > _max_data_space)
1063         {
1064             UnicodeString unistr             = null;
1065             int           stringreminant     = 0;
1066             int           unipos             = 0;
1067             boolean       lastneedcontinue   = false;
1068             int           stringbyteswritten = 0;
1069             boolean       finished           = false;
1070             boolean       first_record       = true;
1071             int           totalWritten       = 0;
1072 
1073             while (!finished)
1074             {
1075                 int record = 0;
1076                 int pos    = 0;
1077 
1078                 if (first_record)
1079                 {
1080 
1081                     // writing SST record
1082                     record       = _max;
1083                     pos          = 12;
1084                     first_record = false;
1085                     _record_lengths.add(new Integer(record
1086                                                     - _std_record_overhead));
1087                 }
1088                 else
1089                 {
1090 
1091                     // writing continue record
1092                     pos = 0;
1093                     int to_be_written = (unicodesize - stringbyteswritten)
1094                                         + (lastneedcontinue ? 1
1095                                                             : 0);
1096                     int size          = Math.min(_max - _std_record_overhead,
1097                                                  to_be_written);
1098 
1099                     if (size == to_be_written)
1100                     {
1101                         finished = true;
1102                     }
1103                     record = size + _std_record_overhead;
1104                     _record_lengths.add(new Integer(size));
1105                     pos = 4;
1106                 }
1107                 if (lastneedcontinue)
1108                 {
1109                     int available = _max - pos;
1110 
1111                     if (stringreminant <= available)
1112                     {
1113 
1114                         // write reminant
1115                         stringbyteswritten += stringreminant - 1;
1116                         pos                += stringreminant;
1117                         lastneedcontinue   = false;
1118                     }
1119                     else
1120                     {
1121 
1122                         // write as much of the remnant as possible
1123                         int toBeWritten = unistr.maxBrokenLength(available);
1124 
1125                         if (available != toBeWritten)
1126                         {
1127                             int shortrecord = record
1128                                               - (available - toBeWritten);
1129 
1130                             _record_lengths.set(
1131                                 _record_lengths.size() - 1,
1132                                 new Integer(
1133                                     shortrecord - _std_record_overhead));
1134                             record = shortrecord;
1135                         }
1136                         stringbyteswritten += toBeWritten - 1;
1137                         pos                += toBeWritten;
1138                         stringreminant     -= toBeWritten - 1;
1139                         lastneedcontinue   = true;
1140                     }
1141                 }
1142                 for (; unipos < field_3_strings.size(); unipos++)
1143                 {
1144                     int     available = _max - pos;
1145                     Integer intunipos = new Integer(unipos);
1146 
1147                     unistr =
1148                         (( UnicodeString ) field_3_strings.get(intunipos));
1149                     if (unistr.getRecordSize() <= available)
1150                     {
1151                         stringbyteswritten += unistr.getRecordSize();
1152                         pos                += unistr.getRecordSize();
1153                     }
1154                     else
1155                     {
1156                         if (available >= _string_minimal_overhead)
1157                         {
1158                             int toBeWritten =
1159                                 unistr.maxBrokenLength(available);
1160 
1161                             stringbyteswritten += toBeWritten;
1162                             stringreminant     =
1163                                 (unistr.getRecordSize() - toBeWritten)
1164                                 + LittleEndianConsts.BYTE_SIZE;
1165                             if (available != toBeWritten)
1166                             {
1167                                 int shortrecord = record
1168                                                   - (available - toBeWritten);
1169 
1170                                 _record_lengths.set(
1171                                     _record_lengths.size() - 1,
1172                                     new Integer(
1173                                         shortrecord - _std_record_overhead));
1174                                 record = shortrecord;
1175                             }
1176                             lastneedcontinue = true;
1177                             unipos++;
1178                         }
1179                         else
1180                         {
1181                             int shortrecord = record - available;
1182 
1183                             _record_lengths.set(
1184                                 _record_lengths.size() - 1,
1185                                 new Integer(
1186                                     shortrecord - _std_record_overhead));
1187                             record = shortrecord;
1188                         }
1189                         break;
1190                     }
1191                 }
1192                 totalWritten += record;
1193             }
1194             retval = totalWritten;
1195         }
1196         else
1197         {
1198 
1199             // short data: write one simple SST record
1200             retval = _sst_record_overhead + unicodesize;
1201             _record_lengths.add(new Integer(unicodesize));
1202         }
1203         return retval;
1204     }
1205 
1206     private int calculateUnicodeSize()
1207     {
1208         int retval = 0;
1209 
1210         for (int k = 0; k < field_3_strings.size(); k++)
1211         {
1212             UnicodeString string =
1213                 ( UnicodeString ) field_3_strings.get(new Integer(k));
1214 
1215             retval += string.getRecordSize();
1216         }
1217         return retval;
1218     }
1219 }
1220 ?????????????????????????????????????????SHORT_SIZE???????????????????????????????????????????????????????????????record?????????????????????????????????????????????????????getSid????????????????sid????????????????????????????????????????????????????????hashCode????????????????field_2_num_unique_strings????????????????????????????????????????????????????????????????????????????????????????equals??????????????o?????????????????????????????o?????????SSTRecord?????????????????????????????????????????o??????????????field_1_num_strings??????????????????field_1_num_strings?????????????????????????????????????????other??????????????????field_2_num_unique_strings???????????????????????????????????????field_2_num_unique_strings?????????????????????????????????????????????????????????????????????other?????????????????????????????????????????????????field_3_strings?????????????????????????????other???????????????????????????????????field_3_strings???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????validateSid????????????????RecordFormatException?????????????id???????????????????sid?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????fillFields??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????field_1_num_strings??????????????????????????????????????LittleEndian???????????????????????????????????????????????????getInt??????????????????????????????????????????????????????????data????????????????????????????????????????????????????????????????????offset?????????field_2_num_unique_strings??????????????????????????????????????LittleEndian???????????????????????????????????????????????????getInt??????????????????????????????????????????????????????????data????????????????????????????????????????????????????????????????????offset?????????field_3_strings??????????????????????????????????????????BinaryTree?????????setExpectedChars?????????_unfinished_string?????????_total_length_bytes?????????_string_data_offset?????????_wide_char?????????manufactureStrings????????????????????????????data??????????????????????????????????????offset??????????????????????????????????????????????size??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????getExpectedChars????????????????__expected_chars?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????getStrings????????????????field_3_strings????????????????????????????????values??????????????????????????????????????????????????????????????????????countStrings????????????????field_3_strings????????????????????????????????size?????????????????????????????????????????????????????????????????getUnfinishedString????????????????_unfinished_string???????????????????????????????????????????????????????????????????????????????getTotalLength????????????????_total_length_bytes????????????????????????????????????????????????????????????????????????getStringDataOffset????????????????_string_data_offset????????????????????????????????????????????????????????????????????????????????????????isWideChar????????????????_wide_char?????????????????writeSSTHeader??????????????????????pos?????????LittleEndian??????????????????????putShort???????????????????????????????data?????????????????????????????????????offset?????????????????????????????????????????????sid?????????offset???????????????????LittleEndianConsts??????????????????????????????????????SHORT_SIZE?????????LittleEndian??????????????????????putShort???????????????????????????????data?????????????????????????????????????offset????????????????????????????????????????????????????????recsize?????????offset???????????????????LittleEndianConsts??????????????????????????????????????SHORT_SIZE?????????LittleEndian??????????????????????putInt?????????????????????????????data???????????????????????????????????offset???????????????????????????????????????????getNumStrings?????????offset???????????????????LittleEndianConsts??????????????????????????????????????INT_SIZE?????????LittleEndian??????????????????????putInt?????????????????????????????data???????????????????????????????????offset???????????????????????????????????????????getNumUniqueStrings?????????offset???????????????????LittleEndianConsts??????????????????????????????????????INT_SIZE????????????????offset?????????????????????????pos?????????????????writeContinueHeader??????????????????????pos?????????LittleEndian??????????????????????putShort???????????????????????????????data?????????????????????????????????????offset?????????offset???????????????????LittleEndianConsts??????????????????????????????????????SHORT_SIZE?????????LittleEndian??????????????????????putShort???????????????????????????????data?????????????????????????????????????offset????????????????????????????????????????????????????????recsize?????????offset???????????????????LittleEndianConsts??????????????????????????????????????SHORT_SIZE????????????????offset?????????????????????????pos?????????????????calculateUCArrayLength?????????????????????????k?????????????????????????????ucarray?????????????????????????????????????????????k?????????????retval???????????????????????ucarray????????????????????????????????k????????????????retval??????????????????manufactureStrings??????????????????????index????????????????offset?????????????????????????size?????????????????????????????size????????????????????????????????????offset??????????????????remaining?????????????????????????remaining?????????????????????????????????????LittleEndianConsts????????????????????????????????????????????????????????SHORT_SIZE?????????????????remaining??????????????????????????????LittleEndianConsts?????????????????????????????????????????????????SHORT_SIZE?????????????????setExpectedChars??????????????????????????????????LittleEndian???????????????????????????????????????????????getShort????????????????????????????????????????????????????????data??????????????????????????????????????????????????????????????offset?????????????????_unfinished_string????????????????????????????????LittleEndian?????????????????????????????????????????????getShort??????????????????????????????????????????????????????data????????????????????????????????????????????????????????????offset?????????????setupStringParameters???????????????????????????????????data?????????????????????????????????????????offset?????????????????????????????????????????????????char_count?????????????????remaining?????????????????????????????_total_length_bytes?????????????????setExpectedChars??????????????????????????????????calculateCharCount?????????????????????????????????????????????????????_total_length_bytes???????????????????????????????????????????????????????remaining?????????????????char_count????????????????????????????????????????getExpectedChars?????????????????_total_length_bytes???????????????????????????????????????remaining?????????????????setExpectedChars?????????????processString???????????????????????????data?????????????????????????????????offset?????????????????????????????????????????char_count?????????????offset???????????????????????_total_length_bytes?????????????????getExpectedChars??????????????????setupStringParameters?????????????????????data???????????????????????????index???????????????????????????????????LittleEndianConsts??????????????????????????????????????????????????????SHORT_SIZE?????????_wide_char???????????????????????flag??????????????????????????????????flag??????????????????????????????????flag?????????_total_length_bytes???????????????????????????????_string_minimal_overhead?????????????????????????????????calculateByteCount????????????????????????????????????????????????????char_count?????????_string_data_offset???????????????????????????????_string_minimal_overhead?????????????formatted_run???????????????????????????????LittleEndian????????????????????????????????????????????getShort?????????????????????????????????????????????????????data?????????????????????????????????????????????????????index???????????????????????????????????????????????????????_string_data_offset?????????????_string_data_offset????????????????????????????????????LittleEndianConsts???????????????????????????????????????????????????????SHORT_SIZE?????????????_total_length_bytes????????????????????????????????????LittleEndianConsts???????????????????????????????????????????????????????SHORT_SIZE???????????????????????????????????????LittleEndianConsts??????????????????????????????????????????????????????????INT_SIZE?????????????????????????????????????????run_count?????????????extended????????????????????????????????????LittleEndian?????????????????????????????????????????????????getInt????????????????????????????????????????????????????????data????????????????????????????????????????????????????????index??????????????????????????????????????????????????????????_string_data_offset?????????????_string_data_offset????????????????????????????????????LittleEndianConsts???????????????????????????????????????????????????????INT_SIZE?????????????_total_length_bytes????????????????????????????????????LittleEndianConsts???????????????????????????????????????????????????????INT_SIZE??????????????????????????????????????extension_length??????????????????processString???????????????????????????????byte?????????????????????????????????????_total_length_bytes???????????????????????????_string_minimal_overhead?????????????????????????????calculateByteCount????????????????????????????????????????????????char_count???????????????????????????????byte?????????????????????????????????????length??????????????????????????data????????????????????????????????index???????????????????????????????????????str_data????????????????????????????????????????????????????str_data?????????LittleEndian??????????????????????putShort???????????????????????????????bstring????????????????????????????????????????offset????????????????????????????????????????????????char_count?????????offset??????????????????????????????LittleEndianConsts?????????????????????????????????????????????????SHORT_SIZE?????????bstring??????????????????offset?????????????????????????????str_data???????????????????????????????????????offset??????????????????????????str_data????????????????????????????????????_string_data_offset?????????????????????????????????????????????????????????bstring??????????????????????????_string_minimal_overhead??????????????????????????bstring???????????????????????????????????????????_string_minimal_overhead?????????UnicodeString????????????????????????????????????????????????????????????bstring??????????????????????????????????????????????????bstring?????????????getExpectedChars?????????????_unfinished_string??????????????????????????????????string?????????????????????????????????????????getString???????????????????????????????????????????field_3_strings???????????????????????????????????????????????????????????size?????????????field_3_strings?????????????????????????????put?????????????????????????????????integer??????????????????????????????????????????string??????????????????setExpectedChars?????????__expected_chars????????????????????????????count?????????????????calculateByteCount????????????????character_count???????????????????????????????????_wide_char????????????????????????????????????????????????LittleEndianConsts???????????????????????????????????????????????????????????????????SHORT_SIZE????????????????????????????????????????????????LittleEndianConsts???????????????????????????????????????????????????????????????????BYTE_SIZE?????????????????calculateCharCount????????????????byte_count??????????????????????????????_wide_char???????????????????????????????????????????LittleEndianConsts??????????????????????????????????????????????????????????????SHORT_SIZE???????????????????????????????????????????LittleEndianConsts??????????????????????????????????????????????????????????????BYTE_SIZE?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????getRecordSize?????????_record_lengths???????????????????????????calculateUnicodeSize?????????????unicodesize???????????????????????????_max_data_space?????????????UnicodeString?????????????????????finished?????????????????????first_record???????????????????????????????????????????????????????????????record????????????????????????????????????_max?????????????????????pos?????????????????????first_record?????????????????????_record_lengths?????????????????????????????????????????????????????record???????????????????????????????????????????????????????_std_record_overhead????????????????????????????????????????????????????????????????????pos??????????????????????????????????????????unicodesize????????????????????????????????????????????????????????stringbyteswritten????????????????????????????????????????????lastneedcontinue??????????????????????????????????????????????????_max?????????????????????????????????????????????????????????_std_record_overhead??????????????????????????????????????????????????to_be_written?????????????????????????size?????????????????????????????????to_be_written?????????????????????????finished?????????????????????record??????????????????????????????size?????????????????????????????????????_std_record_overhead?????????????????????_record_lengths?????????????????????????????????????????????????????size?????????????????????pos?????????????????????lastneedcontinue?????????????????????????????????????_max????????????????????????????????????????????pos?????????????????????????stringreminant???????????????????????????????????????????available???????????????????????????????????????????????????????????????????stringbyteswritten???????????????????????????????????????????????stringreminant?????????????????????????pos???????????????????????????????????????????????stringreminant?????????????????????????lastneedcontinue???????????????????????????????????????????????????????????????????????????????????????????????????????????????unistr??????????????????????????????????????????????????maxBrokenLength??????????????????????????????????????????????????????????????????available?????????????????????????????available??????????????????????????????????????????toBeWritten???????????????????????????????????????????????record??????????????????????????????????????????????????available??????????????????????????????????????????????????????????????toBeWritten?????????????????????????????_record_lengths?????????????????????????????????_record_lengths?????????????????????????????????????shortrecord???????????????????????????????????????????????????_std_record_overhead?????????????????????????????record??????????????????????????????????????shortrecord?????????????????????????stringbyteswritten???????????????????????????????????????????????toBeWritten?????????????????????????pos???????????????????????????????????????????????toBeWritten?????????????????????????stringreminant???????????????????????????????????????????????toBeWritten?????????????????????????lastneedcontinue????????????????????????unipos?????????????????????????????????field_3_strings?????????????????????????????????????????????????size?????????????????????????????????????????????????????????unipos?????????????????????????????????????????_max????????????????????????????????????????????????pos?????????????????????????????????????????????????????unipos?????????????????????unistr????????????????????????????????????????????field_3_strings????????????????????????????????????????????????????????????get????????????????????????????????????????????????????????????????intunipos?????????????????????????unistr????????????????????????????????getRecordSize???????????????????????????????????????????????????available?????????????????????????stringbyteswritten???????????????????????????????????????????????unistr??????????????????????????????????????????????????????getRecordSize?????????????????????????pos???????????????????????????????????????????????unistr??????????????????????????????????????????????????????getRecordSize?????????????????????????????available??????????????????????????????????????????_string_minimal_overhead?????????????????????????????????unistr????????????????????????????????????????maxBrokenLength????????????????????????????????????????????????????????available?????????????????????????????stringbyteswritten???????????????????????????????????????????????????toBeWritten?????????????????????????????stringreminant??????????????????????????????????unistr?????????????????????????????????????????getRecordSize???????????????????????????????????????????????????????????toBeWritten???????????????????????????????????LittleEndianConsts??????????????????????????????????????????????????????BYTE_SIZE?????????????????????????????????available??????????????????????????????????????????????toBeWritten???????????????????????????????????????????????????record??????????????????????????????????????????????????????available??????????????????????????????????????????????????????????????????toBeWritten?????????????????????????????????_record_lengths?????????????????????????????????????_record_lengths?????????????????????????????????????????shortrecord???????????????????????????????????????????????????????_std_record_overhead?????????????????????????????????record??????????????????????????????????????????shortrecord?????????????????????????????lastneedcontinue?????????????????????????????unipos???????????????????????????????????????????????record????????????????????????????????????????????????????????available?????????????????????????????_record_lengths?????????????????????????????????_record_lengths?????????????????????????????????????shortrecord???????????????????????????????????????????????????_std_record_overhead?????????????????????????????record??????????????????????????????????????shortrecord?????????????????totalWritten?????????????????????????????????record?????????????retval??????????????????????totalWritten????????????????????????????????????????????????????????????????????retval??????????????????????_sst_record_overhead?????????????????????????????????????????????unicodesize?????????????_record_lengths?????????????????????????????????????????????unicodesize????????????????retval?????????????????calculateUnicodeSize?????????????????????????k?????????????????????????????field_3_strings?????????????????????????????????????????????size?????????????????????????????????????????????????????k?????????????UnicodeString???????????????????????????????????field_3_strings???????????????????????????????????????????????????get???????????????????????????????????????????????????????????????????k?????????????retval???????????????????????string??????????????????????????????getRecordSize????????????????retval