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.model;
57   
58   import org.apache.poi.hssf.record.*;
59   import org.apache.poi.hssf.util.HSSFColor;
60   import org.apache.poi.hssf.util.SheetReferences;
61   import org.apache.poi.util.POILogFactory;
62   import org.apache.poi.util.POILogger;
63   
64   import java.util.ArrayList;
65   import java.util.Iterator;
66   import java.util.List;
67   import java.util.Locale;
68   
69   /**
70    * Low level model implementation of a Workbook.  Provides creational methods
71    * for settings and objects contained in the workbook object.
72    * <P>
73    * This file contains the low level binary records starting at the workbook's BOF and
74    * ending with the workbook's EOF.  Use HSSFWorkbook for a high level representation.
75    * <P>
76    * The structures of the highlevel API use references to this to perform most of their
77    * operations.  Its probably unwise to use these low level structures directly unless you
78    * really know what you're doing.  I recommend you read the Microsoft Excel 97 Developer's
79    * Kit (Microsoft Press) and the documentation at http://sc.openoffice.org/excelfileformat.pdf
80    * before even attempting to use this.
81    *
82    *
83    * @author  Shawn Laubach (slaubach at apache dot org) (Data Formats)
84    * @author  Andrew C. Oliver (acoliver at apache dot org)
85    * @author  Glen Stampoultzis (glens at apache.org)
86    * @author  Sergei Kozello (sergeikozello at mail.ru)
87    * @author  Luc Girardin (luc dot girardin at macrofocus dot com)
88    * @author  Dan Sherman (dsherman at isisph.com)
89    * @author  Brian Sanders (bsanders at risklabs dot com) - custom palette
90    * @see org.apache.poi.hssf.usermodel.HSSFWorkbook
91    * @version 1.0-pre
92    */
93   
94   public class Workbook implements Model {
95       private static final int   DEBUG       = POILogger.DEBUG;
96   
97   //    public static Workbook currentBook = null;
98   
99       /**
100       * constant used to set the "codepage" wherever "codepage" is set in records
101       * (which is duplciated in more than one record)
102       */
103  
104      private final static short CODEPAGE    = ( short ) 0x4b0;
105  
106      /**
107       * this contains the Worksheet record objects
108       */
109      protected WorkbookRecordList        records     = new WorkbookRecordList();
110  
111      /**
112       * this contains a reference to the SSTRecord so that new stings can be added
113       * to it.
114       */
115      protected SSTRecord        sst         = null;
116  
117      /**
118       * Holds the Extern Sheet with references to bound sheets
119       */
120      protected ExternSheetRecord externSheet= null;
121  
122      /**
123       * holds the "boundsheet" records (aka bundlesheet) so that they can have their
124       * reference to their "BOF" marker
125       */
126      protected ArrayList        boundsheets = new ArrayList();
127  
128      protected ArrayList        formats = new ArrayList();
129  
130      protected ArrayList        names = new ArrayList();
131  
132      protected int              numxfs      = 0;   // hold the number of extended format records
133      protected int              numfonts    = 0;   // hold the number of font records
134      private short              maxformatid  = -1;  // holds the max format id
135      private boolean            uses1904datewindowing  = false;  // whether 1904 date windowing is being used
136  
137      private static POILogger   log = POILogFactory.getLogger(Workbook.class);
138  
139      /**
140       * Creates new Workbook with no intitialization --useless right now
141       * @see #createWorkbook(List)
142       */
143      public Workbook() {
144      }
145  
146      /**
147       * read support  for low level
148       * API.  Pass in an array of Record objects, A Workbook
149       * object is constructed and passed back with all of its initialization set
150       * to the passed in records and references to those records held. Unlike Sheet
151       * workbook does not use an offset (its assumed to be 0) since its first in a file.
152       * If you need an offset then construct a new array with a 0 offset or write your
153       * own ;-p.
154       *
155       * @param recs an array of Record objects
156       * @return Workbook object
157       */
158      public static Workbook createWorkbook(List recs) {
159          log.log(DEBUG, "Workbook (readfile) created with reclen=",
160          new Integer(recs.size()));
161          Workbook  retval  = new Workbook();
162          ArrayList records = new ArrayList(recs.size() / 3);
163  
164          for (int k = 0; k < recs.size(); k++) {
165              Record rec = ( Record ) recs.get(k);
166  
167              if (rec.getSid() == EOFRecord.sid) {
168                  records.add(rec);
169                  log.log(DEBUG, "found workbook eof record at " + k);
170                  break;
171              }
172              switch (rec.getSid()) {
173  
174                  case BoundSheetRecord.sid :
175                      log.log(DEBUG, "found boundsheet record at " + k);
176                      retval.boundsheets.add(rec);
177                      retval.records.setBspos( k );
178                      break;
179  
180                  case SSTRecord.sid :
181                      log.log(DEBUG, "found sst record at " + k);
182                      retval.sst = ( SSTRecord ) rec;
183                      break;
184  
185                  case FontRecord.sid :
186                      log.log(DEBUG, "found font record at " + k);
187                      retval.records.setFontpos( k );
188                      retval.numfonts++;
189                      break;
190  
191                  case ExtendedFormatRecord.sid :
192                      log.log(DEBUG, "found XF record at " + k);
193                      retval.records.setXfpos( k );
194                      retval.numxfs++;
195                      break;
196  
197                  case TabIdRecord.sid :
198                      log.log(DEBUG, "found tabid record at " + k);
199                      retval.records.setTabpos( k );
200                      break;
201  
202                  case ProtectRecord.sid :
203                      log.log(DEBUG, "found protect record at " + k);
204                      retval.records.setProtpos( k );
205                      break;
206  
207                  case BackupRecord.sid :
208                      log.log(DEBUG, "found backup record at " + k);
209                      retval.records.setBackuppos( k );
210                      break;
211                  case ExternSheetRecord.sid :
212                      log.log(DEBUG, "found extern sheet record at " + k);
213                      retval.externSheet = ( ExternSheetRecord ) rec;
214                      break;
215                  case NameRecord.sid :
216                      log.log(DEBUG, "found name record at " + k);
217                      retval.names.add(rec);
218  //                    retval.records.namepos = k;
219                      break;
220                  case SupBookRecord.sid :
221                      log.log(DEBUG, "found SupBook record at " + k);
222  //                    retval.records.supbookpos = k;
223                      break;
224  	        case FormatRecord.sid :
225                  log.log(DEBUG, "found format record at " + k);
226                  retval.formats.add(rec);
227                  retval.maxformatid = retval.maxformatid >= ((FormatRecord)rec).getIndexCode() ? retval.maxformatid : ((FormatRecord)rec).getIndexCode();
228  		    break;
229                  case DateWindow1904Record.sid :
230                      log.log(DEBUG, "found datewindow1904 record at " + k);
231                      retval.uses1904datewindowing = ((DateWindow1904Record)rec).getWindowing() == 1;
232                      break;
233                  case PaletteRecord.sid:
234                      log.log(DEBUG, "found palette record at " + k);
235                      retval.records.setPalettepos( k );
236                  default :
237              }
238              records.add(rec);
239          }
240          //What if we dont have any ranges and supbooks
241  //        if (retval.records.supbookpos == 0) {
242  //            retval.records.supbookpos = retval.records.bspos + 1;
243  //            retval.records.namepos    = retval.records.supbookpos + 1;
244  //        }
245  
246          retval.records.setRecords(records);
247          log.log(DEBUG, "exit create workbook from existing file function");
248          return retval;
249      }
250  
251      /**
252       * Creates an empty workbook object with three blank sheets and all the empty
253       * fields.  Use this to create a workbook from scratch.
254       */
255      public static Workbook createWorkbook()
256      {
257          log.log( DEBUG, "creating new workbook from scratch" );
258          Workbook retval = new Workbook();
259          ArrayList records = new ArrayList( 30 );
260          ArrayList formats = new ArrayList( 8 );
261  
262          records.add( retval.createBOF() );
263          records.add( retval.createInterfaceHdr() );
264          records.add( retval.createMMS() );
265          records.add( retval.createInterfaceEnd() );
266          records.add( retval.createWriteAccess() );
267          records.add( retval.createCodepage() );
268          records.add( retval.createDSF() );
269          records.add( retval.createTabId() );
270          retval.records.setTabpos( records.size() - 1 );
271          records.add( retval.createFnGroupCount() );
272          records.add( retval.createWindowProtect() );
273          records.add( retval.createProtect() );
274          retval.records.setProtpos( records.size() - 1 );
275          records.add( retval.createPassword() );
276          records.add( retval.createProtectionRev4() );
277          records.add( retval.createPasswordRev4() );
278          records.add( retval.createWindowOne() );
279          records.add( retval.createBackup() );
280          retval.records.setBackuppos( records.size() - 1 );
281          records.add( retval.createHideObj() );
282          records.add( retval.createDateWindow1904() );
283          records.add( retval.createPrecision() );
284          records.add( retval.createRefreshAll() );
285          records.add( retval.createBookBool() );
286          records.add( retval.createFont() );
287          records.add( retval.createFont() );
288          records.add( retval.createFont() );
289          records.add( retval.createFont() );
290          retval.records.setFontpos( records.size() - 1 );   // last font record postion
291          retval.numfonts = 4;
292  
293          // set up format records
294          for ( int i = 0; i <= 7; i++ )
295          {
296              Record rec;
297              rec = retval.createFormat( i );
298              retval.maxformatid = retval.maxformatid >= ( (FormatRecord) rec ).getIndexCode() ? retval.maxformatid : ( (FormatRecord) rec ).getIndexCode();
299              formats.add( rec );
300              records.add( rec );
301          }
302          retval.formats = formats;
303  
304          for ( int k = 0; k < 21; k++ )
305          {
306              records.add( retval.createExtendedFormat( k ) );
307              retval.numxfs++;
308          }
309          retval.records.setXfpos( records.size() - 1 );
310          for ( int k = 0; k < 6; k++ )
311          {
312              records.add( retval.createStyle( k ) );
313          }
314          retval.records.setPalettepos( records.size() );
315          records.add( retval.createUseSelFS() );
316          for ( int k = 0; k < 1; k++ )
317          {   // now just do 1
318              BoundSheetRecord bsr =
319                      (BoundSheetRecord) retval.createBoundSheet( k );
320  
321              records.add( bsr );
322              retval.boundsheets.add( bsr );
323              retval.records.setBspos( records.size() - 1 );
324          }
325  //        retval.records.supbookpos = retval.records.bspos + 1;
326  //        retval.records.namepos = retval.records.supbookpos + 2;
327          records.add( retval.createCountry() );
328          retval.sst = (SSTRecord) retval.createSST();
329          records.add( retval.sst );
330          records.add( retval.createExtendedSST() );
331  
332          records.add( retval.createEOF() );
333          retval.records.setRecords(records);
334          log.log( DEBUG, "exit create new workbook from scratch" );
335          return retval;
336      }
337  
338      public int getNumRecords() {
339          return records.size();
340      }
341  
342      /**
343       * gets the font record at the given index in the font table.  Remember
344       * "There is No Four" (someone at M$ must have gone to Rocky Horror one too
345       * many times)
346       *
347       * @param idx the index to look at (0 or greater but NOT 4)
348       * @return FontRecord located at the given index
349       */
350  
351      public FontRecord getFontRecordAt(int idx) {
352          int index = idx;
353  
354          if (index > 4) {
355              index -= 1;   // adjust for "There is no 4"
356          }
357          if (index > (numfonts - 1)) {
358              throw new ArrayIndexOutOfBoundsException(
359              "There are only " + numfonts
360              + " font records, you asked for " + idx);
361          }
362          FontRecord retval =
363          ( FontRecord ) records.get((records.getFontpos() - (numfonts - 1)) + index);
364  
365          return retval;
366      }
367  
368      /**
369       * creates a new font record and adds it to the "font table".  This causes the
370       * boundsheets to move down one, extended formats to move down (so this function moves
371       * those pointers as well)
372       *
373       * @return FontRecord that was just created
374       */
375  
376      public FontRecord createNewFont() {
377          FontRecord rec = ( FontRecord ) createFont();
378  
379          records.add(records.getFontpos()+1, rec);
380          records.setFontpos( records.getFontpos() + 1 );
381          numfonts++;
382          return rec;
383      }
384  
385      /**
386       * gets the number of font records
387       *
388       * @return   number of font records in the "font table"
389       */
390  
391      public int getNumberOfFontRecords() {
392          return numfonts;
393      }
394  
395      /**
396       * Sets the BOF for a given sheet
397       *
398       * @param sheetnum the number of the sheet to set the positing of the bof for
399       * @param pos the actual bof position
400       */
401  
402      public void setSheetBof(int sheetnum, int pos) {
403          log.log(DEBUG, "setting bof for sheetnum =", new Integer(sheetnum),
404          " at pos=", new Integer(pos));
405          checkSheets(sheetnum);
406          (( BoundSheetRecord ) boundsheets.get(sheetnum))
407          .setPositionOfBof(pos);
408      }
409  
410      /**
411       * Returns the position of the backup record.
412       */
413  
414      public BackupRecord getBackupRecord() {
415          return ( BackupRecord ) records.get(records.getBackuppos());
416      }
417  
418  
419      /**
420       * sets the name for a given sheet.  If the boundsheet record doesn't exist and
421       * its only one more than we have, go ahead and create it.  If its > 1 more than
422       * we have, except
423       *
424       * @param sheetnum the sheet number (0 based)
425       * @param sheetname the name for the sheet
426       */
427  
428      // for compatibility
429      public void setSheetName(int sheetnum, String sheetname ) {
430          setSheetName( sheetnum, sheetname, (byte)0 );
431      }
432  
433      public void setSheetName(int sheetnum, String sheetname, short encoding ) {
434          checkSheets(sheetnum);
435          BoundSheetRecord sheet = (BoundSheetRecord)boundsheets.get( sheetnum );
436          sheet.setSheetname(sheetname);
437          sheet.setSheetnameLength( (byte)sheetname.length() );
438  		sheet.setCompressedUnicodeFlag( (byte)encoding );
439      }
440  
441      /**
442       * gets the name for a given sheet.
443       *
444       * @param sheetnum the sheet number (0 based)
445       * @return sheetname the name for the sheet
446       */
447  
448      public String getSheetName(int sheetnum) {
449          return (( BoundSheetRecord ) boundsheets.get(sheetnum))
450          .getSheetname();
451      }
452  
453      /**
454       * get the sheet's index
455       * @param name  sheet name
456       * @return sheet index or -1 if it was not found.
457       */
458  
459      public int getSheetIndex(String name) {
460          int retval = -1;
461  
462          for (int k = 0; k < boundsheets.size(); k++) {
463              String sheet = getSheetName(k);
464  
465              if (sheet.equals(name)) {
466                  retval = k;
467                  break;
468              }
469          }
470          return retval;
471      }
472  
473      /**
474       * if we're trying to address one more sheet than we have, go ahead and add it!  if we're
475       * trying to address >1 more than we have throw an exception!
476       */
477  
478      private void checkSheets(int sheetnum) {
479          if ((boundsheets.size()) <= sheetnum) {   // if we're short one add another..
480              if ((boundsheets.size() + 1) <= sheetnum) {
481                  throw new RuntimeException("Sheet number out of bounds!");
482              }
483              BoundSheetRecord bsr = (BoundSheetRecord ) createBoundSheet(sheetnum);
484  
485              records.add(records.getBspos()+1, bsr);
486              records.setBspos( records.getBspos() + 1 );
487              boundsheets.add(bsr);
488              fixTabIdRecord();
489          }
490      }
491  
492      public void removeSheet(int sheetnum) {
493          if (boundsheets.size() > sheetnum) {
494              records.remove(records.getBspos() - (boundsheets.size() - 1) + sheetnum);
495  //            records.bspos--;
496              boundsheets.remove(sheetnum);
497              fixTabIdRecord();
498          }
499      }
500  
501      /**
502       * make the tabid record look like the current situation.
503       *
504       */
505      private void fixTabIdRecord() {
506          TabIdRecord tir = ( TabIdRecord ) records.get(records.getTabpos());
507          short[]     tia = new short[ boundsheets.size() ];
508  
509          for (short k = 0; k < tia.length; k++) {
510              tia[ k ] = k;
511          }
512          tir.setTabIdArray(tia);
513      }
514  
515      /**
516       * returns the number of boundsheet objects contained in this workbook.
517       *
518       * @return number of BoundSheet records
519       */
520  
521      public int getNumSheets() {
522          log.log(DEBUG, "getNumSheets=", new Integer(boundsheets.size()));
523          return boundsheets.size();
524      }
525  
526      /**
527       * get the number of ExtendedFormat records contained in this workbook.
528       *
529       * @return int count of ExtendedFormat records
530       */
531  
532      public int getNumExFormats() {
533          log.log(DEBUG, "getXF=", new Integer(numxfs));
534          return numxfs;
535      }
536  
537      /**
538       * gets the ExtendedFormatRecord at the given 0-based index
539       *
540       * @param index of the Extended format record (0-based)
541       * @return ExtendedFormatRecord at the given index
542       */
543  
544      public ExtendedFormatRecord getExFormatAt(int index) {
545          int xfptr = records.getXfpos() - (numxfs - 1);
546  
547          xfptr += index;
548          ExtendedFormatRecord retval =
549          ( ExtendedFormatRecord ) records.get(xfptr);
550  
551          return retval;
552      }
553  
554      /**
555       * creates a new Cell-type Extneded Format Record and adds it to the end of
556       *  ExtendedFormatRecords collection
557       *
558       * @return ExtendedFormatRecord that was created
559       */
560  
561      public ExtendedFormatRecord createCellXF() {
562          ExtendedFormatRecord xf = createExtendedFormat();
563  
564          records.add(records.getXfpos()+1, xf);
565          records.setXfpos( records.getXfpos() + 1 );
566          numxfs++;
567          return xf;
568      }
569  
570      /**
571       * Adds a string to the SST table and returns its index (if its a duplicate
572       * just returns its index and update the counts)
573       *
574       * @param string the string to be added to the SSTRecord
575       * @param use16bits whether to use utf 16 or false for compressed unicode
576       * @return index of the string within the SSTRecord
577       */
578  
579      public int addSSTString(String string, boolean use16bits) {
580          log.log(DEBUG, "insert to sst string='", string, "' and use16bits= ",
581          new Boolean(use16bits));
582          if (sst == null) {
583              insertSST();
584          }
585          return sst.addString(string, use16bits);
586      }
587  
588      /**
589       * Adds a string to the SST table and returns its index (if its a duplicate
590       * just returns its index and update the counts) ASSUMES compressed unicode
591       * (meaning 8bit)
592       *
593       * @param string the string to be added to the SSTRecord
594       *
595       * @return index of the string within the SSTRecord
596       */
597  
598      public int addSSTString(String string) {
599          return addSSTString(string, false);
600      }
601  
602      /**
603       * given an index into the SST table, this function returns the corresponding String value
604       * @return String containing the SST String
605       */
606  
607      public String getSSTString(int str) {
608          if (sst == null) {
609              insertSST();
610          }
611          String retval = sst.getString(str);
612  
613          log.log(DEBUG, "Returning SST for index=", new Integer(str),
614          " String= ", retval);
615          return retval;
616      }
617  
618      /**
619       * use this function to add a Shared String Table to an existing sheet (say
620       * generated by a different java api) without an sst....
621       * @see #createSST()
622       * @see org.apache.poi.hssf.record.SSTRecord
623       */
624  
625      public void insertSST() {
626          log.log(DEBUG, "creating new SST via insertSST!");
627          sst = ( SSTRecord ) createSST();
628          records.add(records.size() - 1, createExtendedSST());
629          records.add(records.size() - 2, sst);
630      }
631  
632      /**
633       * Serializes all records int the worksheet section into a big byte array. Use
634       * this to write the Workbook out.
635       *
636       * @return byte array containing the HSSF-only portions of the POIFS file.
637       */
638  
639      public byte [] serialize() {
640          log.log(DEBUG, "Serializing Workbook!");
641          byte[] retval    = null;
642  
643          // ArrayList bytes     = new ArrayList(records.size());
644          int    arraysize = getSize();
645          int    pos       = 0;
646  
647          // for (int k = 0; k < records.size(); k++)
648          // {
649          // bytes.add((( Record ) records.get(k)).serialize());
650          //        }
651          // for (int k = 0; k < bytes.size(); k++)
652          // {
653          // arraysize += (( byte [] ) bytes.get(k)).length;
654          // }
655          retval = new byte[ arraysize ];
656          for (int k = 0; k < records.size(); k++) {
657  
658              // byte[] rec = (( byte [] ) bytes.get(k));
659              // System.arraycopy(rec, 0, retval, pos, rec.length);
660              Record record = records.get(k);
661              // Let's skip RECALCID records, as they are only use for optimization
662  	    if(record.getSid() != RecalcIdRecord.sid || ((RecalcIdRecord)record).isNeeded()) {
663                  pos += record.serialize(pos, retval);   // rec.length;
664  	    }
665          }
666          log.log(DEBUG, "Exiting serialize workbook");
667          return retval;
668      }
669  
670      /**
671       * Serializes all records int the worksheet section into a big byte array. Use
672       * this to write the Workbook out.
673       * @param offset of the data to be written
674       * @param data array of bytes to write this to
675       */
676  
677      public int serialize(int offset, byte [] data) {
678          log.log(DEBUG, "Serializing Workbook with offsets");
679  
680          // ArrayList bytes     = new ArrayList(records.size());
681          //        int arraysize = getSize();   // 0;
682          int pos       = 0;
683  
684          //        for (int k = 0; k < records.size(); k++)
685          //        {
686          //            bytes.add((( Record ) records.get(k)).serialize());
687          //
688          //        }
689          //        for (int k = 0; k < bytes.size(); k++)
690          //       {
691          //            arraysize += (( byte [] ) bytes.get(k)).length;
692          //        }
693          for (int k = 0; k < records.size(); k++) {
694  
695              // byte[] rec = (( byte [] ) bytes.get(k));
696              // System.arraycopy(rec, 0, data, offset + pos, rec.length);
697              Record record = records.get(k);
698              // Let's skip RECALCID records, as they are only use for optimization
699              if(record.getSid() != RecalcIdRecord.sid || ((RecalcIdRecord)record).isNeeded()) {
700  		pos += record.serialize(pos + offset, data);   // rec.length;
701              }
702          }
703          log.log(DEBUG, "Exiting serialize workbook");
704          return pos;
705      }
706  
707      public int getSize() {
708          int retval = 0;
709  
710          for (int k = 0; k < records.size(); k++) {
711              Record record = records.get(k);
712              // Let's skip RECALCID records, as they are only use for optimization
713              if(record.getSid() != RecalcIdRecord.sid || ((RecalcIdRecord)record).isNeeded()) {
714  		retval += record.getRecordSize();
715              }
716          }
717          return retval;
718      }
719  
720      /**
721       * creates the BOF record
722       * @see org.apache.poi.hssf.record.BOFRecord
723       * @see org.apache.poi.hssf.record.Record
724       * @return record containing a BOFRecord
725       */
726  
727      protected Record createBOF() {
728          BOFRecord retval = new BOFRecord();
729  
730          retval.setVersion(( short ) 0x600);
731          retval.setType(( short ) 5);
732          retval.setBuild(( short ) 0x10d3);
733  
734          //        retval.setBuild((short)0x0dbb);
735          retval.setBuildYear(( short ) 1996);
736          retval.setHistoryBitMask(0x41);   // was c1 before verify
737          retval.setRequiredVersion(0x6);
738          return retval;
739      }
740  
741      /**
742       * creates the InterfaceHdr record
743       * @see org.apache.poi.hssf.record.InterfaceHdrRecord
744       * @see org.apache.poi.hssf.record.Record
745       * @return record containing a InterfaceHdrRecord
746       */
747  
748      protected Record createInterfaceHdr() {
749          InterfaceHdrRecord retval = new InterfaceHdrRecord();
750  
751          retval.setCodepage(CODEPAGE);
752          return retval;
753      }
754  
755      /**
756       * creates an MMS record
757       * @see org.apache.poi.hssf.record.MMSRecord
758       * @see org.apache.poi.hssf.record.Record
759       * @return record containing a MMSRecord
760       */
761  
762      protected Record createMMS() {
763          MMSRecord retval = new MMSRecord();
764  
765          retval.setAddMenuCount(( byte ) 0);
766          retval.setDelMenuCount(( byte ) 0);
767          return retval;
768      }
769  
770      /**
771       * creates the InterfaceEnd record
772       * @see org.apache.poi.hssf.record.InterfaceEndRecord
773       * @see org.apache.poi.hssf.record.Record
774       * @return record containing a InterfaceEndRecord
775       */
776  
777      protected Record createInterfaceEnd() {
778          return new InterfaceEndRecord();
779      }
780  
781      /**
782       * creates the WriteAccess record containing the logged in user's name
783       * @see org.apache.poi.hssf.record.WriteAccessRecord
784       * @see org.apache.poi.hssf.record.Record
785       * @return record containing a WriteAccessRecord
786       */
787  
788      protected Record createWriteAccess() {
789          WriteAccessRecord retval = new WriteAccessRecord();
790  
791          retval.setUsername(System.getProperty("user.name"));
792          return retval;
793      }
794  
795      /**
796       * creates the Codepage record containing the constant stored in CODEPAGE
797       * @see org.apache.poi.hssf.record.CodepageRecord
798       * @see org.apache.poi.hssf.record.Record
799       * @return record containing a CodepageRecord
800       */
801  
802      protected Record createCodepage() {
803          CodepageRecord retval = new CodepageRecord();
804  
805          retval.setCodepage(CODEPAGE);
806          return retval;
807      }
808  
809      /**
810       * creates the DSF record containing a 0 since HSSF can't even create Dual Stream Files
811       * @see org.apache.poi.hssf.record.DSFRecord
812       * @see org.apache.poi.hssf.record.Record
813       * @return record containing a DSFRecord
814       */
815  
816      protected Record createDSF() {
817          DSFRecord retval = new DSFRecord();
818  
819          retval.setDsf(
820          ( short ) 0);   // we don't even support double stream files
821          return retval;
822      }
823  
824      /**
825       * creates the TabId record containing an array of 0,1,2.  This release of HSSF
826       * always has the default three sheets, no less, no more.
827       * @see org.apache.poi.hssf.record.TabIdRecord
828       * @see org.apache.poi.hssf.record.Record
829       * @return record containing a TabIdRecord
830       */
831  
832      protected Record createTabId() {
833          TabIdRecord retval     = new TabIdRecord();
834          short[]     tabidarray = {
835              0
836          };
837  
838          retval.setTabIdArray(tabidarray);
839          return retval;
840      }
841  
842      /**
843       * creates the FnGroupCount record containing the Magic number constant of 14.
844       * @see org.apache.poi.hssf.record.FnGroupCountRecord
845       * @see org.apache.poi.hssf.record.Record
846       * @return record containing a FnGroupCountRecord
847       */
848  
849      protected Record createFnGroupCount() {
850          FnGroupCountRecord retval = new FnGroupCountRecord();
851  
852          retval.setCount(( short ) 14);
853          return retval;
854      }
855  
856      /**
857       * creates the WindowProtect record with protect set to false.
858       * @see org.apache.poi.hssf.record.WindowProtectRecord
859       * @see org.apache.poi.hssf.record.Record
860       * @return record containing a WindowProtectRecord
861       */
862  
863      protected Record createWindowProtect() {
864          WindowProtectRecord retval = new WindowProtectRecord();
865  
866          retval.setProtect(
867          false);   // by default even when we support it we won't
868          return retval;   // want it to be protected
869      }
870  
871      /**
872       * creates the Protect record with protect set to false.
873       * @see org.apache.poi.hssf.record.ProtectRecord
874       * @see org.apache.poi.hssf.record.Record
875       * @return record containing a ProtectRecord
876       */
877  
878      protected Record createProtect() {
879          ProtectRecord retval = new ProtectRecord();
880  
881          retval.setProtect(
882          false);   // by default even when we support it we won't
883          return retval;   // want it to be protected
884      }
885  
886      /**
887       * creates the Password record with password set to 0.
888       * @see org.apache.poi.hssf.record.PasswordRecord
889       * @see org.apache.poi.hssf.record.Record
890       * @return record containing a PasswordRecord
891       */
892  
893      protected Record createPassword() {
894          PasswordRecord retval = new PasswordRecord();
895  
896          retval.setPassword(( short ) 0);   // no password by default!
897          return retval;
898      }
899  
900      /**
901       * creates the ProtectionRev4 record with protect set to false.
902       * @see org.apache.poi.hssf.record.ProtectionRev4Record
903       * @see org.apache.poi.hssf.record.Record
904       * @return record containing a ProtectionRev4Record
905       */
906  
907      protected Record createProtectionRev4() {
908          ProtectionRev4Record retval = new ProtectionRev4Record();
909  
910          retval.setProtect(false);
911          return retval;
912      }
913  
914      /**
915       * creates the PasswordRev4 record with password set to 0.
916       * @see org.apache.poi.hssf.record.PasswordRev4Record
917       * @see org.apache.poi.hssf.record.Record
918       * @return record containing a PasswordRev4Record
919       */
920  
921      protected Record createPasswordRev4() {
922          PasswordRev4Record retval = new PasswordRev4Record();
923  
924          retval.setPassword(( short ) 0);   // no password by default!
925          return retval;
926      }
927  
928      /**
929       * creates the WindowOne record with the following magic values: <P>
930       * horizontal hold - 0x168 <P>
931       * vertical hold   - 0x10e <P>
932       * width           - 0x3a5c <P>
933       * height          - 0x23be <P>
934       * options         - 0x38 <P>
935       * selected tab    - 0 <P>
936       * displayed tab   - 0 <P>
937       * num selected tab- 0 <P>
938       * tab width ratio - 0x258 <P>
939       * @see org.apache.poi.hssf.record.WindowOneRecord
940       * @see org.apache.poi.hssf.record.Record
941       * @return record containing a WindowOneRecord
942       */
943  
944      protected Record createWindowOne() {
945          WindowOneRecord retval = new WindowOneRecord();
946  
947          retval.setHorizontalHold(( short ) 0x168);
948          retval.setVerticalHold(( short ) 0x10e);
949          retval.setWidth(( short ) 0x3a5c);
950          retval.setHeight(( short ) 0x23be);
951          retval.setOptions(( short ) 0x38);
952          retval.setSelectedTab(( short ) 0x0);
953          retval.setDisplayedTab(( short ) 0x0);
954          retval.setNumSelectedTabs(( short ) 1);
955          retval.setTabWidthRatio(( short ) 0x258);
956          return retval;
957      }
958  
959      /**
960       * creates the Backup record with backup set to 0. (loose the data, who cares)
961       * @see org.apache.poi.hssf.record.BackupRecord
962       * @see org.apache.poi.hssf.record.Record
963       * @return record containing a BackupRecord
964       */
965  
966      protected Record createBackup() {
967          BackupRecord retval = new BackupRecord();
968  
969          retval.setBackup(
970          ( short ) 0);   // by default DONT save backups of files...just loose data
971          return retval;
972      }
973  
974      /**
975       * creates the HideObj record with hide object set to 0. (don't hide)
976       * @see org.apache.poi.hssf.record.HideObjRecord
977       * @see org.apache.poi.hssf.record.Record
978       * @return record containing a HideObjRecord
979       */
980  
981      protected Record createHideObj() {
982          HideObjRecord retval = new HideObjRecord();
983  
984          retval.setHideObj(( short ) 0);   // by default set hide object off
985          return retval;
986      }
987  
988      /**
989       * creates the DateWindow1904 record with windowing set to 0. (don't window)
990       * @see org.apache.poi.hssf.record.DateWindow1904Record
991       * @see org.apache.poi.hssf.record.Record
992       * @return record containing a DateWindow1904Record
993       */
994  
995      protected Record createDateWindow1904() {
996          DateWindow1904Record retval = new DateWindow1904Record();
997  
998          retval.setWindowing(
999          ( short ) 0);   // don't EVER use 1904 date windowing...tick tock..
1000         return retval;
1001     }
1002 
1003     /**
1004      * creates the Precision record with precision set to true. (full precision)
1005      * @see org.apache.poi.hssf.record.PrecisionRecord
1006      * @see org.apache.poi.hssf.record.Record
1007      * @return record containing a PrecisionRecord
1008      */
1009 
1010     protected Record createPrecision() {
1011         PrecisionRecord retval = new PrecisionRecord();
1012 
1013         retval.setFullPrecision(
1014         true);   // always use real numbers in calculations!
1015         return retval;
1016     }
1017 
1018     /**
1019      * creates the RefreshAll record with refreshAll set to true. (refresh all calcs)
1020      * @see org.apache.poi.hssf.record.RefreshAllRecord
1021      * @see org.apache.poi.hssf.record.Record
1022      * @return record containing a RefreshAllRecord
1023      */
1024 
1025     protected Record createRefreshAll() {
1026         RefreshAllRecord retval = new RefreshAllRecord();
1027 
1028         retval.setRefreshAll(false);
1029         return retval;
1030     }
1031 
1032     /**
1033      * creates the BookBool record with saveLinkValues set to 0. (don't save link values)
1034      * @see org.apache.poi.hssf.record.BookBoolRecord
1035      * @see org.apache.poi.hssf.record.Record
1036      * @return record containing a BookBoolRecord
1037      */
1038 
1039     protected Record createBookBool() {
1040         BookBoolRecord retval = new BookBoolRecord();
1041 
1042         retval.setSaveLinkValues(( short ) 0);
1043         return retval;
1044     }
1045 
1046     /**
1047      * creates a Font record with the following magic values: <P>
1048      * fontheight           = 0xc8<P>
1049      * attributes           = 0x0<P>
1050      * color palette index  = 0x7fff<P>
1051      * bold weight          = 0x190<P>
1052      * Font Name Length     = 5 <P>
1053      * Font Name            = Arial <P>
1054      *
1055      * @see org.apache.poi.hssf.record.FontRecord
1056      * @see org.apache.poi.hssf.record.Record
1057      * @return record containing a FontRecord
1058      */
1059 
1060     protected Record createFont() {
1061         FontRecord retval = new FontRecord();
1062 
1063         retval.setFontHeight(( short ) 0xc8);
1064         retval.setAttributes(( short ) 0x0);
1065         retval.setColorPaletteIndex(( short ) 0x7fff);
1066         retval.setBoldWeight(( short ) 0x190);
1067         retval.setFontNameLength(( byte ) 5);
1068         retval.setFontName("Arial");
1069         return retval;
1070     }
1071 
1072     /**
1073      * Creates a FormatRecord object
1074      * @param id    the number of the format record to create (meaning its position in
1075      *        a file as M$ Excel would create it.)
1076      * @return record containing a FormatRecord
1077      * @see org.apache.poi.hssf.record.FormatRecord
1078      * @see org.apache.poi.hssf.record.Record
1079      */
1080 
1081     protected Record createFormat(int id) {   // we'll need multiple editions for
1082         FormatRecord retval = new FormatRecord();   // the differnt formats
1083 
1084         switch (id) {
1085 
1086             case 0 :
1087                 retval.setIndexCode(( short ) 5);
1088                 retval.setFormatStringLength(( byte ) 0x17);
1089                 retval.setFormatString("\"$\"#,##0_);\\(\"$\"#,##0\\)");
1090                 break;
1091 
1092             case 1 :
1093                 retval.setIndexCode(( short ) 6);
1094                 retval.setFormatStringLength(( byte ) 0x1c);
1095                 retval.setFormatString("\"$\"#,##0_);[Red]\\(\"$\"#,##0\\)");
1096                 break;
1097 
1098             case 2 :
1099                 retval.setIndexCode(( short ) 7);
1100                 retval.setFormatStringLength(( byte ) 0x1d);
1101                 retval.setFormatString("\"$\"#,##0.00_);\\(\"$\"#,##0.00\\)");
1102                 break;
1103 
1104             case 3 :
1105                 retval.setIndexCode(( short ) 8);
1106                 retval.setFormatStringLength(( byte ) 0x22);
1107                 retval.setFormatString(
1108                 "\"$\"#,##0.00_);[Red]\\(\"$\"#,##0.00\\)");
1109                 break;
1110 
1111             case 4 :
1112                 retval.setIndexCode(( short ) 0x2a);
1113                 retval.setFormatStringLength(( byte ) 0x32);
1114                 retval.setFormatString(
1115                 "_(\"$\"* #,##0_);_(\"$\"* \\(#,##0\\);_(\"$\"* \"-\"_);_(@_)");
1116                 break;
1117 
1118             case 5 :
1119                 retval.setIndexCode(( short ) 0x29);
1120                 retval.setFormatStringLength(( byte ) 0x29);
1121                 retval.setFormatString(
1122                 "_(* #,##0_);_(* \\(#,##0\\);_(* \"-\"_);_(@_)");
1123                 break;
1124 
1125             case 6 :
1126                 retval.setIndexCode(( short ) 0x2c);
1127                 retval.setFormatStringLength(( byte ) 0x3a);
1128                 retval.setFormatString(
1129                 "_(\"$\"* #,##0.00_);_(\"$\"* \\(#,##0.00\\);_(\"$\"* \"-\"??_);_(@_)");
1130                 break;
1131 
1132             case 7 :
1133                 retval.setIndexCode(( short ) 0x2b);
1134                 retval.setFormatStringLength(( byte ) 0x31);
1135                 retval.setFormatString(
1136                 "_(* #,##0.00_);_(* \\(#,##0.00\\);_(* \"-\"??_);_(@_)");
1137                 break;
1138         }
1139         return retval;
1140     }
1141 
1142     /**
1143      * Creates an ExtendedFormatRecord object
1144      * @param id    the number of the extended format record to create (meaning its position in
1145      *        a file as MS Excel would create it.)
1146      *
1147      * @return record containing an ExtendedFormatRecord
1148      * @see org.apache.poi.hssf.record.ExtendedFormatRecord
1149      * @see org.apache.poi.hssf.record.Record
1150      */
1151 
1152     protected Record createExtendedFormat(int id) {   // we'll need multiple editions
1153         ExtendedFormatRecord retval = new ExtendedFormatRecord();
1154 
1155         switch (id) {
1156 
1157             case 0 :
1158                 retval.setFontIndex(( short ) 0);
1159                 retval.setFormatIndex(( short ) 0);
1160                 retval.setCellOptions(( short ) 0xfffffff5);
1161                 retval.setAlignmentOptions(( short ) 0x20);
1162                 retval.setIndentionOptions(( short ) 0);
1163                 retval.setBorderOptions(( short ) 0);
1164                 retval.setPaletteOptions(( short ) 0);
1165                 retval.setAdtlPaletteOptions(( short ) 0);
1166                 retval.setFillPaletteOptions(( short ) 0x20c0);
1167                 break;
1168 
1169             case 1 :
1170                 retval.setFontIndex(( short ) 1);
1171                 retval.setFormatIndex(( short ) 0);
1172                 retval.setCellOptions(( short ) 0xfffffff5);
1173                 retval.setAlignmentOptions(( short ) 0x20);
1174                 retval.setIndentionOptions(( short ) 0xfffff400);
1175                 retval.setBorderOptions(( short ) 0);
1176                 retval.setPaletteOptions(( short ) 0);
1177                 retval.setAdtlPaletteOptions(( short ) 0);
1178                 retval.setFillPaletteOptions(( short ) 0x20c0);
1179                 break;
1180 
1181             case 2 :
1182                 retval.setFontIndex(( short ) 1);
1183                 retval.setFormatIndex(( short ) 0);
1184                 retval.setCellOptions(( short ) 0xfffffff5);
1185                 retval.setAlignmentOptions(( short ) 0x20);
1186                 retval.setIndentionOptions(( short ) 0xfffff400);
1187                 retval.setBorderOptions(( short ) 0);
1188                 retval.setPaletteOptions(( short ) 0);
1189                 retval.setAdtlPaletteOptions(( short ) 0);
1190                 retval.setFillPaletteOptions(( short ) 0x20c0);
1191                 break;
1192 
1193             case 3 :
1194                 retval.setFontIndex(( short ) 2);
1195                 retval.setFormatIndex(( short ) 0);
1196                 retval.setCellOptions(( short ) 0xfffffff5);
1197                 retval.setAlignmentOptions(( short ) 0x20);
1198                 retval.setIndentionOptions(( short ) 0xfffff400);
1199                 retval.setBorderOptions(( short ) 0);
1200                 retval.setPaletteOptions(( short ) 0);
1201                 retval.setAdtlPaletteOptions(( short ) 0);
1202                 retval.setFillPaletteOptions(( short ) 0x20c0);
1203                 break;
1204 
1205             case 4 :
1206                 retval.setFontIndex(( short ) 2);
1207                 retval.setFormatIndex(( short ) 0);
1208                 retval.setCellOptions(( short ) 0xfffffff5);
1209                 retval.setAlignmentOptions(( short ) 0x20);
1210                 retval.setIndentionOptions(( short ) 0xfffff400);
1211                 retval.setBorderOptions(( short ) 0);
1212                 retval.setPaletteOptions(( short ) 0);
1213                 retval.setAdtlPaletteOptions(( short ) 0);
1214                 retval.setFillPaletteOptions(( short ) 0x20c0);
1215                 break;
1216 
1217             case 5 :
1218                 retval.setFontIndex(( short ) 0);
1219                 retval.setFormatIndex(( short ) 0);
1220                 retval.setCellOptions(( short ) 0xfffffff5);
1221                 retval.setAlignmentOptions(( short ) 0x20);
1222                 retval.setIndentionOptions(( short ) 0xfffff400);
1223                 retval.setBorderOptions(( short ) 0);
1224                 retval.setPaletteOptions(( short ) 0);
1225                 retval.setAdtlPaletteOptions(( short ) 0);
1226                 retval.setFillPaletteOptions(( short ) 0x20c0);
1227                 break;
1228 
1229             case 6 :
1230                 retval.setFontIndex(( short ) 0);
1231                 retval.setFormatIndex(( short ) 0);
1232                 retval.setCellOptions(( short ) 0xfffffff5);
1233                 retval.setAlignmentOptions(( short ) 0x20);
1234                 retval.setIndentionOptions(( short ) 0xfffff400);
1235                 retval.setBorderOptions(( short ) 0);
1236                 retval.setPaletteOptions(( short ) 0);
1237                 retval.setAdtlPaletteOptions(( short ) 0);
1238                 retval.setFillPaletteOptions(( short ) 0x20c0);
1239                 break;
1240 
1241             case 7 :
1242                 retval.setFontIndex(( short ) 0);
1243                 retval.setFormatIndex(( short ) 0);
1244                 retval.setCellOptions(( short ) 0xfffffff5);
1245                 retval.setAlignmentOptions(( short ) 0x20);
1246                 retval.setIndentionOptions(( short ) 0xfffff400);
1247                 retval.setBorderOptions(( short ) 0);
1248                 retval.setPaletteOptions(( short ) 0);
1249                 retval.setAdtlPaletteOptions(( short ) 0);
1250                 retval.setFillPaletteOptions(( short ) 0x20c0);
1251                 break;
1252 
1253             case 8 :
1254                 retval.setFontIndex(( short ) 0);
1255                 retval.setFormatIndex(( short ) 0);
1256                 retval.setCellOptions(( short ) 0xfffffff5);
1257                 retval.setAlignmentOptions(( short ) 0x20);
1258                 retval.setIndentionOptions(( short ) 0xfffff400);
1259                 retval.setBorderOptions(( short ) 0);
1260                 retval.setPaletteOptions(( short ) 0);
1261                 retval.setAdtlPaletteOptions(( short ) 0);
1262                 retval.setFillPaletteOptions(( short ) 0x20c0);
1263                 break;
1264 
1265             case 9 :
1266                 retval.setFontIndex(( short ) 0);
1267                 retval.setFormatIndex(( short ) 0);
1268                 retval.setCellOptions(( short ) 0xfffffff5);
1269                 retval.setAlignmentOptions(( short ) 0x20);
1270                 retval.setIndentionOptions(( short ) 0xfffff400);
1271                 retval.setBorderOptions(( short ) 0);
1272                 retval.setPaletteOptions(( short ) 0);
1273                 retval.setAdtlPaletteOptions(( short ) 0);
1274                 retval.setFillPaletteOptions(( short ) 0x20c0);
1275                 break;
1276 
1277             case 10 :
1278                 retval.setFontIndex(( short ) 0);
1279                 retval.setFormatIndex(( short ) 0);
1280                 retval.setCellOptions(( short ) 0xfffffff5);
1281                 retval.setAlignmentOptions(( short ) 0x20);
1282                 retval.setIndentionOptions(( short ) 0xfffff400);
1283                 retval.setBorderOptions(( short ) 0);
1284                 retval.setPaletteOptions(( short ) 0);
1285                 retval.setAdtlPaletteOptions(( short ) 0);
1286                 retval.setFillPaletteOptions(( short ) 0x20c0);
1287                 break;
1288 
1289             case 11 :
1290                 retval.setFontIndex(( short ) 0);
1291                 retval.setFormatIndex(( short ) 0);
1292                 retval.setCellOptions(( short ) 0xfffffff5);
1293                 retval.setAlignmentOptions(( short ) 0x20);
1294                 retval.setIndentionOptions(( short ) 0xfffff400);
1295                 retval.setBorderOptions(( short ) 0);
1296                 retval.setPaletteOptions(( short ) 0);
1297                 retval.setAdtlPaletteOptions(( short ) 0);
1298                 retval.setFillPaletteOptions(( short ) 0x20c0);
1299                 break;
1300 
1301             case 12 :
1302                 retval.setFontIndex(( short ) 0);
1303                 retval.setFormatIndex(( short ) 0);
1304                 retval.setCellOptions(( short ) 0xfffffff5);
1305                 retval.setAlignmentOptions(( short ) 0x20);
1306                 retval.setIndentionOptions(( short ) 0xfffff400);
1307                 retval.setBorderOptions(( short ) 0);
1308                 retval.setPaletteOptions(( short ) 0);
1309                 retval.setAdtlPaletteOptions(( short ) 0);
1310                 retval.setFillPaletteOptions(( short ) 0x20c0);
1311                 break;
1312 
1313             case 13 :
1314                 retval.setFontIndex(( short ) 0);
1315                 retval.setFormatIndex(( short ) 0);
1316                 retval.setCellOptions(( short ) 0xfffffff5);
1317                 retval.setAlignmentOptions(( short ) 0x20);
1318                 retval.setIndentionOptions(( short ) 0xfffff400);
1319                 retval.setBorderOptions(( short ) 0);
1320                 retval.setPaletteOptions(( short ) 0);
1321                 retval.setAdtlPaletteOptions(( short ) 0);
1322                 retval.setFillPaletteOptions(( short ) 0x20c0);
1323                 break;
1324 
1325             case 14 :
1326                 retval.setFontIndex(( short ) 0);
1327                 retval.setFormatIndex(( short ) 0);
1328                 retval.setCellOptions(( short ) 0xfffffff5);
1329                 retval.setAlignmentOptions(( short ) 0x20);
1330                 retval.setIndentionOptions(( short ) 0xfffff400);
1331                 retval.setBorderOptions(( short ) 0);
1332                 retval.setPaletteOptions(( short ) 0);
1333                 retval.setAdtlPaletteOptions(( short ) 0);
1334                 retval.setFillPaletteOptions(( short ) 0x20c0);
1335                 break;
1336 
1337                 // cell records
1338             case 15 :
1339                 retval.setFontIndex(( short ) 0);
1340                 retval.setFormatIndex(( short ) 0);
1341                 retval.setCellOptions(( short ) 0x1);
1342                 retval.setAlignmentOptions(( short ) 0x20);
1343                 retval.setIndentionOptions(( short ) 0x0);
1344                 retval.setBorderOptions(( short ) 0);
1345                 retval.setPaletteOptions(( short ) 0);
1346                 retval.setAdtlPaletteOptions(( short ) 0);
1347                 retval.setFillPaletteOptions(( short ) 0x20c0);
1348                 break;
1349 
1350                 // style
1351             case 16 :
1352                 retval.setFontIndex(( short ) 1);
1353                 retval.setFormatIndex(( short ) 0x2b);
1354                 retval.setCellOptions(( short ) 0xfffffff5);
1355                 retval.setAlignmentOptions(( short ) 0x20);
1356                 retval.setIndentionOptions(( short ) 0xfffff800);
1357                 retval.setBorderOptions(( short ) 0);
1358                 retval.setPaletteOptions(( short ) 0);
1359                 retval.setAdtlPaletteOptions(( short ) 0);
1360                 retval.setFillPaletteOptions(( short ) 0x20c0);
1361                 break;
1362 
1363             case 17 :
1364                 retval.setFontIndex(( short ) 1);
1365                 retval.setFormatIndex(( short ) 0x29);
1366                 retval.setCellOptions(( short ) 0xfffffff5);
1367                 retval.setAlignmentOptions(( short ) 0x20);
1368                 retval.setIndentionOptions(( short ) 0xfffff800);
1369                 retval.setBorderOptions(( short ) 0);
1370                 retval.setPaletteOptions(( short ) 0);
1371                 retval.setAdtlPaletteOptions(( short ) 0);
1372                 retval.setFillPaletteOptions(( short ) 0x20c0);
1373                 break;
1374 
1375             case 18 :
1376                 retval.setFontIndex(( short ) 1);
1377                 retval.setFormatIndex(( short ) 0x2c);
1378                 retval.setCellOptions(( short ) 0xfffffff5);
1379                 retval.setAlignmentOptions(( short ) 0x20);
1380                 retval.setIndentionOptions(( short ) 0xfffff800);
1381                 retval.setBorderOptions(( short ) 0);
1382                 retval.setPaletteOptions(( short ) 0);
1383                 retval.setAdtlPaletteOptions(( short ) 0);
1384                 retval.setFillPaletteOptions(( short ) 0x20c0);
1385                 break;
1386 
1387             case 19 :
1388                 retval.setFontIndex(( short ) 1);
1389                 retval.setFormatIndex(( short ) 0x2a);
1390                 retval.setCellOptions(( short ) 0xfffffff5);
1391                 retval.setAlignmentOptions(( short ) 0x20);
1392                 retval.setIndentionOptions(( short ) 0xfffff800);
1393                 retval.setBorderOptions(( short ) 0);
1394                 retval.setPaletteOptions(( short ) 0);
1395                 retval.setAdtlPaletteOptions(( short ) 0);
1396                 retval.setFillPaletteOptions(( short ) 0x20c0);
1397                 break;
1398 
1399             case 20 :
1400                 retval.setFontIndex(( short ) 1);
1401                 retval.setFormatIndex(( short ) 0x9);
1402                 retval.setCellOptions(( short ) 0xfffffff5);
1403                 retval.setAlignmentOptions(( short ) 0x20);
1404                 retval.setIndentionOptions(( short ) 0xfffff800);
1405                 retval.setBorderOptions(( short ) 0);
1406                 retval.setPaletteOptions(( short ) 0);
1407                 retval.setAdtlPaletteOptions(( short ) 0);
1408                 retval.setFillPaletteOptions(( short ) 0x20c0);
1409                 break;
1410 
1411                 // unused from this point down
1412             case 21 :
1413                 retval.setFontIndex(( short ) 5);
1414                 retval.setFormatIndex(( short ) 0x0);
1415                 retval.setCellOptions(( short ) 0x1);
1416                 retval.setAlignmentOptions(( short ) 0x20);
1417                 retval.setIndentionOptions(( short ) 0x800);
1418                 retval.setBorderOptions(( short ) 0);
1419                 retval.setPaletteOptions(( short ) 0);
1420                 retval.setAdtlPaletteOptions(( short ) 0);
1421                 retval.setFillPaletteOptions(( short ) 0x20c0);
1422                 break;
1423 
1424             case 22 :
1425                 retval.setFontIndex(( short ) 6);
1426                 retval.setFormatIndex(( short ) 0x0);
1427                 retval.setCellOptions(( short ) 0x1);
1428                 retval.setAlignmentOptions(( short ) 0x20);
1429                 retval.setIndentionOptions(( short ) 0x5c00);
1430                 retval.setBorderOptions(( short ) 0);
1431                 retval.setPaletteOptions(( short ) 0);
1432                 retval.setAdtlPaletteOptions(( short ) 0);
1433                 retval.setFillPaletteOptions(( short ) 0x20c0);
1434                 break;
1435 
1436             case 23 :
1437                 retval.setFontIndex(( short ) 0);
1438                 retval.setFormatIndex(( short ) 0x31);
1439                 retval.setCellOptions(( short ) 0x1);
1440                 retval.setAlignmentOptions(( short ) 0x20);
1441                 retval.setIndentionOptions(( short ) 0x5c00);
1442                 retval.setBorderOptions(( short ) 0);
1443                 retval.setPaletteOptions(( short ) 0);
1444                 retval.setAdtlPaletteOptions(( short ) 0);
1445                 retval.setFillPaletteOptions(( short ) 0x20c0);
1446                 break;
1447 
1448             case 24 :
1449                 retval.setFontIndex(( short ) 0);
1450                 retval.setFormatIndex(( short ) 0x8);
1451                 retval.setCellOptions(( short ) 0x1);
1452                 retval.setAlignmentOptions(( short ) 0x20);
1453                 retval.setIndentionOptions(( short ) 0x5c00);
1454                 retval.setBorderOptions(( short ) 0);
1455                 retval.setPaletteOptions(( short ) 0);
1456                 retval.setAdtlPaletteOptions(( short ) 0);
1457                 retval.setFillPaletteOptions(( short ) 0x20c0);
1458                 break;
1459 
1460             case 25 :
1461                 retval.setFontIndex(( short ) 6);
1462                 retval.setFormatIndex(( short ) 0x8);
1463                 retval.setCellOptions(( short ) 0x1);
1464                 retval.setAlignmentOptions(( short ) 0x20);
1465                 retval.setIndentionOptions(( short ) 0x5c00);
1466                 retval.setBorderOptions(( short ) 0);
1467                 retval.setPaletteOptions(( short ) 0);
1468                 retval.setAdtlPaletteOptions(( short ) 0);
1469                 retval.setFillPaletteOptions(( short ) 0x20c0);
1470                 break;
1471         }
1472         return retval;
1473     }
1474 
1475     /**
1476      * creates an default cell type ExtendedFormatRecord object.
1477      * @return ExtendedFormatRecord with intial defaults (cell-type)
1478      */
1479 
1480     protected ExtendedFormatRecord createExtendedFormat() {
1481         ExtendedFormatRecord retval = new ExtendedFormatRecord();
1482 
1483         retval.setFontIndex(( short ) 0);
1484         retval.setFormatIndex(( short ) 0x0);
1485         retval.setCellOptions(( short ) 0x1);
1486         retval.setAlignmentOptions(( short ) 0x20);
1487         retval.setIndentionOptions(( short ) 0);
1488         retval.setBorderOptions(( short ) 0);
1489         retval.setPaletteOptions(( short ) 0);
1490         retval.setAdtlPaletteOptions(( short ) 0);
1491         retval.setFillPaletteOptions(( short ) 0x20c0);
1492         retval.setTopBorderPaletteIdx(HSSFColor.BLACK.index);
1493         retval.setBottomBorderPaletteIdx(HSSFColor.BLACK.index);
1494         retval.setLeftBorderPaletteIdx(HSSFColor.BLACK.index);
1495         retval.setRightBorderPaletteIdx(HSSFColor.BLACK.index);
1496         return retval;
1497     }
1498 
1499     /**
1500      * Creates a StyleRecord object
1501      * @param id        the number of the style record to create (meaning its position in
1502      *                  a file as MS Excel would create it.
1503      * @return record containing a StyleRecord
1504      * @see org.apache.poi.hssf.record.StyleRecord
1505      * @see org.apache.poi.hssf.record.Record
1506      */
1507 
1508     protected Record createStyle(int id) {   // we'll need multiple editions
1509         StyleRecord retval = new StyleRecord();
1510 
1511         switch (id) {
1512 
1513             case 0 :
1514                 retval.setIndex(( short ) 0xffff8010);
1515                 retval.setBuiltin(( byte ) 3);
1516                 retval.setOutlineStyleLevel(( byte ) 0xffffffff);
1517                 break;
1518 
1519             case 1 :
1520                 retval.setIndex(( short ) 0xffff8011);
1521                 retval.setBuiltin(( byte ) 6);
1522                 retval.setOutlineStyleLevel(( byte ) 0xffffffff);
1523                 break;
1524 
1525             case 2 :
1526                 retval.setIndex(( short ) 0xffff8012);
1527                 retval.setBuiltin(( byte ) 4);
1528                 retval.setOutlineStyleLevel(( byte ) 0xffffffff);
1529                 break;
1530 
1531             case 3 :
1532                 retval.setIndex(( short ) 0xffff8013);
1533                 retval.setBuiltin(( byte ) 7);
1534                 retval.setOutlineStyleLevel(( byte ) 0xffffffff);
1535                 break;
1536 
1537             case 4 :
1538                 retval.setIndex(( short ) 0xffff8000);
1539                 retval.setBuiltin(( byte ) 0);
1540                 retval.setOutlineStyleLevel(( byte ) 0xffffffff);
1541                 break;
1542 
1543             case 5 :
1544                 retval.setIndex(( short ) 0xffff8014);
1545                 retval.setBuiltin(( byte ) 5);
1546                 retval.setOutlineStyleLevel(( byte ) 0xffffffff);
1547                 break;
1548         }
1549         return retval;
1550     }
1551 
1552     /**
1553      * Creates a palette record initialized to the default palette
1554      * @return a PaletteRecord instance populated with the default colors
1555      * @see org.apache.poi.hssf.record.PaletteRecord
1556      */
1557     protected PaletteRecord createPalette()
1558     {
1559         return new PaletteRecord(PaletteRecord.sid);
1560     }
1561     
1562     /**
1563      * Creates the UseSelFS object with the use natural language flag set to 0 (false)
1564      * @return record containing a UseSelFSRecord
1565      * @see org.apache.poi.hssf.record.UseSelFSRecord
1566      * @see org.apache.poi.hssf.record.Record
1567      */
1568 
1569     protected Record createUseSelFS() {
1570         UseSelFSRecord retval = new UseSelFSRecord();
1571 
1572         retval.setFlag(( short ) 0);
1573         return retval;
1574     }
1575 
1576     /**
1577      * create a "bound sheet" or "bundlesheet" (depending who you ask) record
1578      * Always sets the sheet's bof to 0.  You'll need to set that yourself.
1579      * @param id either sheet 0,1 or 2.
1580      * @return record containing a BoundSheetRecord
1581      * @see org.apache.poi.hssf.record.BoundSheetRecord
1582      * @see org.apache.poi.hssf.record.Record
1583      */
1584 
1585     protected Record createBoundSheet(int id) {   // 1,2,3 sheets
1586         BoundSheetRecord retval = new BoundSheetRecord();
1587 
1588         switch (id) {
1589 
1590             case 0 :
1591                 retval.setPositionOfBof(0x0);   // should be set later
1592                 retval.setOptionFlags(( short ) 0);
1593                 retval.setSheetnameLength(( byte ) 0x6);
1594                 retval.setCompressedUnicodeFlag(( byte ) 0);
1595                 retval.setSheetname("Sheet1");
1596                 break;
1597 
1598             case 1 :
1599                 retval.setPositionOfBof(0x0);   // should be set later
1600                 retval.setOptionFlags(( short ) 0);
1601                 retval.setSheetnameLength(( byte ) 0x6);
1602                 retval.setCompressedUnicodeFlag(( byte ) 0);
1603                 retval.setSheetname("Sheet2");
1604                 break;
1605 
1606             case 2 :
1607                 retval.setPositionOfBof(0x0);   // should be set later
1608                 retval.setOptionFlags(( short ) 0);
1609                 retval.setSheetnameLength(( byte ) 0x6);
1610                 retval.setCompressedUnicodeFlag(( byte ) 0);
1611                 retval.setSheetname("Sheet3");
1612                 break;
1613         }
1614         return retval;
1615     }
1616 
1617     /**
1618      * Creates the Country record with the default country set to 1
1619      * and current country set to 7 in case of russian locale ("ru_RU") and 1 otherwise
1620      * @return record containing a CountryRecord
1621      * @see org.apache.poi.hssf.record.CountryRecord
1622      * @see org.apache.poi.hssf.record.Record
1623      */
1624 
1625     protected Record createCountry() {   // what a novel idea, create your own!
1626         CountryRecord retval = new CountryRecord();
1627 
1628         retval.setDefaultCountry(( short ) 1);
1629 
1630         // from Russia with love ;)
1631         if ( Locale.getDefault().toString().equals( "ru_RU" ) ) {
1632 	        retval.setCurrentCountry(( short ) 7);
1633         }
1634         else {
1635 	        retval.setCurrentCountry(( short ) 1);
1636         }
1637 
1638         return retval;
1639     }
1640 
1641     /**
1642      * Creates the SST record with no strings and the unique/num string set to 0
1643      * @return record containing a SSTRecord
1644      * @see org.apache.poi.hssf.record.SSTRecord
1645      * @see org.apache.poi.hssf.record.Record
1646      */
1647 
1648     protected Record createSST() {
1649         return new SSTRecord();
1650     }
1651 
1652     /**
1653      * Creates the ExtendedSST record with numstrings per bucket set to 0x8.  HSSF
1654      * doesn't yet know what to do with this thing, but we create it with nothing in
1655      * it hardly just to make Excel happy and our sheets look like Excel's
1656      *
1657      * @return record containing an ExtSSTRecord
1658      * @see org.apache.poi.hssf.record.ExtSSTRecord
1659      * @see org.apache.poi.hssf.record.Record
1660      */
1661 
1662     protected Record createExtendedSST() {
1663         ExtSSTRecord retval = new ExtSSTRecord();
1664 
1665         retval.setNumStringsPerBucket(( short ) 0x8);
1666         return retval;
1667     }
1668 
1669     /**
1670      * creates the EOF record
1671      * @see org.apache.poi.hssf.record.EOFRecord
1672      * @see org.apache.poi.hssf.record.Record
1673      * @return record containing a EOFRecord
1674      */
1675 
1676     protected Record createEOF() {
1677         return new EOFRecord();
1678     }
1679 
1680     public SheetReferences getSheetReferences() {
1681        SheetReferences refs = new SheetReferences();
1682 
1683        if (externSheet != null) {
1684           for (int k = 0; k < externSheet.getNumOfREFStructures(); k++) {
1685               String sheetName = findSheetNameFromExternSheet((short)k);
1686               refs.addSheetReference(sheetName, k);
1687           }
1688        }
1689        return refs;
1690     }
1691 
1692     /** finds the sheet name by his extern sheet index
1693      * @param num extern sheet index
1694      * @return sheet name
1695      */
1696     public String findSheetNameFromExternSheet(short num){
1697         String result;
1698 
1699         short indexToSheet = externSheet.getREFRecordAt(num).getIndexToFirstSupBook();
1700         result = getSheetName(indexToSheet);
1701 
1702         return result;
1703     }
1704 
1705     /**
1706      * Finds the sheet index for a particular external sheet number.
1707      * @param externSheetNumber     The external sheet number to convert
1708      * @return  The index to the sheet found.
1709      */
1710     public int getSheetIndexFromExternSheetIndex(int externSheetNumber)
1711     {
1712         if (externSheetNumber >= externSheet.getNumOfREFStructures())
1713             return -1;
1714         else
1715             return externSheet.getREFRecordAt(externSheetNumber).getIndexToFirstSupBook();
1716     }
1717 
1718     /** returns the extern sheet number for specific sheet number ,
1719      *  if this sheet doesn't exist in extern sheet , add it
1720      * @param sheetNumber sheet number
1721      * @return index to extern sheet
1722      */
1723     public short checkExternSheet(int sheetNumber){
1724 
1725         int i = 0;
1726         boolean flag = false;
1727         short result = 0;
1728 
1729         if (externSheet == null) {
1730             externSheet = createExternSheet();
1731         }
1732 
1733         //Trying to find reference to this sheet
1734         while (i < externSheet.getNumOfREFStructures() && !flag){
1735             ExternSheetSubRecord record = externSheet.getREFRecordAt(i);
1736 
1737             if (record.getIndexToFirstSupBook() ==  sheetNumber &&
1738             record.getIndexToLastSupBook() == sheetNumber){
1739                 flag = true;
1740                 result = (short) i;
1741             }
1742 
1743             ++i;
1744         }
1745 
1746         //We Havent found reference to this sheet
1747         if (!flag) {
1748             result = addSheetIndexToExternSheet((short) sheetNumber);
1749         }
1750 
1751         return result;
1752     }
1753 
1754     private short addSheetIndexToExternSheet(short sheetNumber){
1755         short result;
1756 
1757         ExternSheetSubRecord record = new ExternSheetSubRecord();
1758         record.setIndexToFirstSupBook(sheetNumber);
1759         record.setIndexToLastSupBook(sheetNumber);
1760         externSheet.addREFRecord(record);
1761         externSheet.setNumOfREFStructures((short)(externSheet.getNumOfREFStructures() + 1));
1762         result = (short)(externSheet.getNumOfREFStructures() - 1);
1763 
1764         return result;
1765     }
1766 
1767 
1768 
1769     /** gets the total number of names
1770      * @return number of names
1771      */
1772     public int getNumNames(){
1773         int result = names.size();
1774 
1775         return result;
1776     }
1777 
1778     /** gets the name record
1779      * @param index name index
1780      * @return name record
1781      */
1782     public NameRecord getNameRecord(int index){
1783         NameRecord result = (NameRecord) names.get(index);
1784 
1785         return result;
1786 
1787     }
1788 
1789     /** creates new name
1790      * @return new name record
1791      */
1792     public NameRecord createName(){
1793 
1794         NameRecord name = new NameRecord();
1795 
1796         // Not the most efficient way but the other way was causing too many bugs
1797         int idx = findFirstRecordLocBySid(ExternSheetRecord.sid);
1798         if (idx == -1) idx = findFirstRecordLocBySid(SupBookRecord.sid);
1799         if (idx == -1) idx = findFirstRecordLocBySid(CountryRecord.sid);
1800 
1801         records.add(idx+names.size()+1, name);
1802         names.add(name);
1803 
1804         return name;
1805     }
1806 
1807     /** creates new name
1808      * @return new name record
1809      */
1810     public NameRecord addName(NameRecord name)
1811     {
1812         // Not the most efficient way but the other way was causing too many bugs
1813         int idx = findFirstRecordLocBySid(ExternSheetRecord.sid);
1814         if (idx == -1) idx = findFirstRecordLocBySid(SupBookRecord.sid);
1815         if (idx == -1) idx = findFirstRecordLocBySid(CountryRecord.sid);
1816         records.add(idx+names.size()+1, name);
1817         names.add(name);
1818 
1819         return name;
1820     }
1821 
1822     /** removes the name
1823      * @param namenum name index
1824      */
1825     public void removeName(int namenum){
1826         if (names.size() > namenum) {
1827             int idx = findFirstRecordLocBySid(NameRecord.sid);
1828             records.remove(idx + namenum);
1829             names.remove(namenum);
1830         }
1831 
1832     }
1833 
1834     /** creates a new extern sheet record
1835      * @return the new extern sheet record
1836      */
1837     protected ExternSheetRecord createExternSheet(){
1838         ExternSheetRecord externSheet = new ExternSheetRecord();
1839 
1840         int idx = findFirstRecordLocBySid(CountryRecord.sid);
1841 
1842         records.add(idx+1, externSheet);
1843 //        records.add(records.supbookpos + 1 , rec);
1844 
1845         //We also adds the supBook for internal reference
1846         SupBookRecord supbook = new SupBookRecord();
1847 
1848         supbook.setNumberOfSheets((short)getNumSheets());
1849         //supbook.setFlag();
1850 
1851         records.add(idx+1, supbook);
1852 //        records.add(records.supbookpos + 1 , supbook);
1853 
1854         return externSheet;
1855     }
1856 
1857     /**
1858      * Returns a format index that matches the passed in format.  It does not tie into HSSFDataFormat.
1859      * @param format the format string
1860      * @param createIfNotFound creates a new format if format not found
1861      * @return the format id of a format that matches or -1 if none found and createIfNotFound
1862      */
1863     public short getFormat(String format, boolean createIfNotFound) {
1864 	Iterator iterator;
1865 	for (iterator = formats.iterator(); iterator.hasNext();) {
1866 	    FormatRecord r = (FormatRecord)iterator.next();
1867 	    if (r.getFormatString().equals(format)) {
1868 		return r.getIndexCode();
1869 	    }
1870 	}
1871 
1872 	if (createIfNotFound) {
1873 	    return createFormat(format);
1874 	}
1875 
1876 	return -1;
1877     }
1878 
1879     /**
1880      * Returns the list of FormatRecords in the workbook.
1881      * @return ArrayList of FormatRecords in the notebook
1882      */
1883     public ArrayList getFormats() {
1884 	return formats;
1885     }
1886 
1887     /**
1888      * Creates a FormatRecord, inserts it, and returns the index code.
1889      * @param format the format string
1890      * @return the index code of the format record.
1891      * @see org.apache.poi.hssf.record.FormatRecord
1892      * @see org.apache.poi.hssf.record.Record
1893      */
1894     public short createFormat( String format )
1895     {
1896 //        ++xfpos;	//These are to ensure that positions are updated properly
1897 //        ++palettepos;
1898 //        ++bspos;
1899         FormatRecord rec = new FormatRecord();
1900         maxformatid = maxformatid >= (short) 0xa4 ? (short) ( maxformatid + 1 ) : (short) 0xa4; //Starting value from M$ empiracle study.
1901         rec.setIndexCode( maxformatid );
1902         rec.setFormatStringLength( (byte) format.length() );
1903         rec.setFormatString( format );
1904 
1905         int pos = 0;
1906         while ( pos < records.size() && records.get( pos ).getSid() != FormatRecord.sid )
1907             pos++;
1908         pos += formats.size();
1909         formats.add( rec );
1910         records.add( pos, rec );
1911         return maxformatid;
1912     }
1913 
1914 
1915     /**
1916      * Returns the first occurance of a record matching a particular sid.
1917      */
1918     public Record findFirstRecordBySid(short sid) {
1919         for (Iterator iterator = records.iterator(); iterator.hasNext(); ) {
1920             Record record = ( Record ) iterator.next();
1921             
1922             if (record.getSid() == sid) {
1923                 return record;
1924             }
1925         }
1926         return null;
1927     }
1928 
1929     /**
1930      * Returns the index of a record matching a particular sid.
1931      * @param sid   The sid of the record to match
1932      * @return      The index of -1 if no match made.
1933      */
1934     public int findFirstRecordLocBySid(short sid) {
1935         int index = 0;
1936         for (Iterator iterator = records.iterator(); iterator.hasNext(); ) {
1937             Record record = ( Record ) iterator.next();
1938 
1939             if (record.getSid() == sid) {
1940                 return index;
1941             }
1942             index ++;
1943         }
1944         return -1;
1945     }
1946 
1947     /**
1948      * Returns the next occurance of a record matching a particular sid.
1949      */
1950     public Record findNextRecordBySid(short sid, int pos) {
1951         int matches = 0;
1952         for (Iterator iterator = records.iterator(); iterator.hasNext(); ) {
1953             Record record = ( Record ) iterator.next();
1954 
1955             if (record.getSid() == sid) {
1956                 if (matches++ == pos)
1957                     return record;
1958             }
1959         }
1960         return null;
1961     }
1962 
1963     public List getRecords()
1964     {
1965         return records.getRecords();
1966     }
1967 
1968 //    public void insertChartRecords( List chartRecords )
1969 //    {
1970 //        backuppos += chartRecords.size();
1971 //        fontpos += chartRecords.size();
1972 //        palettepos += chartRecords.size();
1973 //        bspos += chartRecords.size();
1974 //        xfpos += chartRecords.size();
1975 //
1976 //        records.addAll(protpos, chartRecords);
1977 //    }
1978 
1979     /**
1980     * Whether date windowing is based on 1/2/1904 or 1/1/1900.
1981     * Some versions of Excel (Mac) can save workbooks using 1904 date windowing.
1982     *
1983     * @return true if using 1904 date windowing
1984     */
1985     public boolean isUsing1904DateWindowing() {
1986         return uses1904datewindowing;
1987     }
1988     
1989     /**
1990      * Returns the custom palette in use for this workbook; if a custom palette record
1991      * does not exist, then it is created.
1992      */
1993     public PaletteRecord getCustomPalette()
1994     {
1995         PaletteRecord palette;
1996         Record rec = records.get(records.getPalettepos());
1997         if (rec instanceof PaletteRecord)
1998         {
1999             palette = (PaletteRecord) rec;
2000         }
2001         else
2002         {
2003             palette = createPalette();
2004             records.add(records.getPalettepos(), palette);
2005         }
2006         return palette;
2007     }
2008 }
2009