KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > poi > hssf > model > Workbook


1 /* ====================================================================
2    Copyright 2002-2004 Apache Software Foundation
3
4    Licensed under the Apache License, Version 2.0 (the "License");
5    you may not use this file except in compliance with the License.
6    You may obtain a copy of the License at
7
8        http://www.apache.org/licenses/LICENSE-2.0
9
10    Unless required by applicable law or agreed to in writing, software
11    distributed under the License is distributed on an "AS IS" BASIS,
12    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13    See the License for the specific language governing permissions and
14    limitations under the License.
15 ==================================================================== */

16
17
18 package org.apache.poi.hssf.model;
19
20 import org.apache.poi.ddf.*;
21 import org.apache.poi.hssf.record.*;
22 import org.apache.poi.hssf.util.HSSFColor;
23 import org.apache.poi.hssf.util.SheetReferences;
24 import org.apache.poi.util.POILogFactory;
25 import org.apache.poi.util.POILogger;
26
27 import java.util.ArrayList JavaDoc;
28 import java.util.Iterator JavaDoc;
29 import java.util.List JavaDoc;
30 import java.util.Locale JavaDoc;
31
32 /**
33  * Low level model implementation of a Workbook. Provides creational methods
34  * for settings and objects contained in the workbook object.
35  * <P>
36  * This file contains the low level binary records starting at the workbook's BOF and
37  * ending with the workbook's EOF. Use HSSFWorkbook for a high level representation.
38  * <P>
39  * The structures of the highlevel API use references to this to perform most of their
40  * operations. Its probably unwise to use these low level structures directly unless you
41  * really know what you're doing. I recommend you read the Microsoft Excel 97 Developer's
42  * Kit (Microsoft Press) and the documentation at http://sc.openoffice.org/excelfileformat.pdf
43  * before even attempting to use this.
44  *
45  *
46  * @author Shawn Laubach (slaubach at apache dot org) (Data Formats)
47  * @author Andrew C. Oliver (acoliver at apache dot org)
48  * @author Glen Stampoultzis (glens at apache.org)
49  * @author Sergei Kozello (sergeikozello at mail.ru)
50  * @author Luc Girardin (luc dot girardin at macrofocus dot com)
51  * @author Dan Sherman (dsherman at isisph.com)
52  * @author Brian Sanders (bsanders at risklabs dot com) - custom palette
53  * @see org.apache.poi.hssf.usermodel.HSSFWorkbook
54  * @version 1.0-pre
55  */

56
57 public class Workbook implements Model
58 {
59     private static final int DEBUG = POILogger.DEBUG;
60
61 // public static Workbook currentBook = null;
62

63     /**
64      * constant used to set the "codepage" wherever "codepage" is set in records
65      * (which is duplciated in more than one record)
66      */

67
68     private final static short CODEPAGE = ( short ) 0x4b0;
69
70     /**
71      * this contains the Worksheet record objects
72      */

73     protected WorkbookRecordList records = new WorkbookRecordList();
74
75     /**
76      * this contains a reference to the SSTRecord so that new stings can be added
77      * to it.
78      */

79     protected SSTRecord sst = null;
80
81     /**
82      * Holds the Extern Sheet with references to bound sheets
83      */

84     protected ExternSheetRecord externSheet= null;
85
86     /**
87      * holds the "boundsheet" records (aka bundlesheet) so that they can have their
88      * reference to their "BOF" marker
89      */

90     protected ArrayList JavaDoc boundsheets = new ArrayList JavaDoc();
91
92     protected ArrayList JavaDoc formats = new ArrayList JavaDoc();
93
94     protected ArrayList JavaDoc names = new ArrayList JavaDoc();
95
96     protected int numxfs = 0; // hold the number of extended format records
97
protected int numfonts = 0; // hold the number of font records
98
private short maxformatid = -1; // holds the max format id
99
private boolean uses1904datewindowing = false; // whether 1904 date windowing is being used
100
private DrawingManager drawingManager;
101
102     private static POILogger log = POILogFactory.getLogger(Workbook.class);
103
104     /**
105      * Creates new Workbook with no intitialization --useless right now
106      * @see #createWorkbook(List)
107      */

108     public Workbook() {
109     }
110
111     /**
112      * read support for low level
113      * API. Pass in an array of Record objects, A Workbook
114      * object is constructed and passed back with all of its initialization set
115      * to the passed in records and references to those records held. Unlike Sheet
116      * workbook does not use an offset (its assumed to be 0) since its first in a file.
117      * If you need an offset then construct a new array with a 0 offset or write your
118      * own ;-p.
119      *
120      * @param recs an array of Record objects
121      * @return Workbook object
122      */

123     public static Workbook createWorkbook(List JavaDoc recs) {
124         if (log.check( POILogger.DEBUG ))
125             log.log(DEBUG, "Workbook (readfile) created with reclen=",
126                     new Integer JavaDoc(recs.size()));
127         Workbook retval = new Workbook();
128         ArrayList JavaDoc records = new ArrayList JavaDoc(recs.size() / 3);
129
130         for (int k = 0; k < recs.size(); k++) {
131             Record rec = ( Record ) recs.get(k);
132
133             if (rec.getSid() == EOFRecord.sid) {
134                 records.add(rec);
135                 if (log.check( POILogger.DEBUG ))
136                     log.log(DEBUG, "found workbook eof record at " + k);
137                 break;
138             }
139             switch (rec.getSid()) {
140
141                 case BoundSheetRecord.sid :
142                     if (log.check( POILogger.DEBUG ))
143                         log.log(DEBUG, "found boundsheet record at " + k);
144                     retval.boundsheets.add(rec);
145                     retval.records.setBspos( k );
146                     break;
147
148                 case SSTRecord.sid :
149                     if (log.check( POILogger.DEBUG ))
150                         log.log(DEBUG, "found sst record at " + k);
151                     retval.sst = ( SSTRecord ) rec;
152                     break;
153
154                 case FontRecord.sid :
155                     if (log.check( POILogger.DEBUG ))
156                         log.log(DEBUG, "found font record at " + k);
157                     retval.records.setFontpos( k );
158                     retval.numfonts++;
159                     break;
160
161                 case ExtendedFormatRecord.sid :
162                     if (log.check( POILogger.DEBUG ))
163                         log.log(DEBUG, "found XF record at " + k);
164                     retval.records.setXfpos( k );
165                     retval.numxfs++;
166                     break;
167
168                 case TabIdRecord.sid :
169                     if (log.check( POILogger.DEBUG ))
170                         log.log(DEBUG, "found tabid record at " + k);
171                     retval.records.setTabpos( k );
172                     break;
173
174                 case ProtectRecord.sid :
175                     if (log.check( POILogger.DEBUG ))
176                         log.log(DEBUG, "found protect record at " + k);
177                     retval.records.setProtpos( k );
178                     break;
179
180                 case BackupRecord.sid :
181                     if (log.check( POILogger.DEBUG ))
182                         log.log(DEBUG, "found backup record at " + k);
183                     retval.records.setBackuppos( k );
184                     break;
185                 case ExternSheetRecord.sid :
186                     if (log.check( POILogger.DEBUG ))
187                         log.log(DEBUG, "found extern sheet record at " + k);
188                     retval.externSheet = ( ExternSheetRecord ) rec;
189                     break;
190                 case NameRecord.sid :
191                     if (log.check( POILogger.DEBUG ))
192                         log.log(DEBUG, "found name record at " + k);
193                     retval.names.add(rec);
194                     // retval.records.namepos = k;
195
break;
196                 case SupBookRecord.sid :
197                     if (log.check( POILogger.DEBUG ))
198                         log.log(DEBUG, "found SupBook record at " + k);
199                     // retval.records.supbookpos = k;
200
break;
201                 case FormatRecord.sid :
202                     if (log.check( POILogger.DEBUG ))
203                         log.log(DEBUG, "found format record at " + k);
204                     retval.formats.add(rec);
205                     retval.maxformatid = retval.maxformatid >= ((FormatRecord)rec).getIndexCode() ? retval.maxformatid : ((FormatRecord)rec).getIndexCode();
206                     break;
207                 case DateWindow1904Record.sid :
208                     if (log.check( POILogger.DEBUG ))
209                         log.log(DEBUG, "found datewindow1904 record at " + k);
210                     retval.uses1904datewindowing = ((DateWindow1904Record)rec).getWindowing() == 1;
211                     break;
212                 case PaletteRecord.sid:
213                     if (log.check( POILogger.DEBUG ))
214                         log.log(DEBUG, "found palette record at " + k);
215                     retval.records.setPalettepos( k );
216                 default :
217             }
218             records.add(rec);
219         }
220         //What if we dont have any ranges and supbooks
221
// if (retval.records.supbookpos == 0) {
222
// retval.records.supbookpos = retval.records.bspos + 1;
223
// retval.records.namepos = retval.records.supbookpos + 1;
224
// }
225

226         retval.records.setRecords(records);
227         if (log.check( POILogger.DEBUG ))
228             log.log(DEBUG, "exit create workbook from existing file function");
229         return retval;
230     }
231
232     /**
233      * Creates an empty workbook object with three blank sheets and all the empty
234      * fields. Use this to create a workbook from scratch.
235      */

236     public static Workbook createWorkbook()
237     {
238         if (log.check( POILogger.DEBUG ))
239             log.log( DEBUG, "creating new workbook from scratch" );
240         Workbook retval = new Workbook();
241         ArrayList JavaDoc records = new ArrayList JavaDoc( 30 );
242         ArrayList JavaDoc formats = new ArrayList JavaDoc( 8 );
243
244         records.add( retval.createBOF() );
245         records.add( retval.createInterfaceHdr() );
246         records.add( retval.createMMS() );
247         records.add( retval.createInterfaceEnd() );
248         records.add( retval.createWriteAccess() );
249         records.add( retval.createCodepage() );
250         records.add( retval.createDSF() );
251         records.add( retval.createTabId() );
252         retval.records.setTabpos( records.size() - 1 );
253         records.add( retval.createFnGroupCount() );
254         records.add( retval.createWindowProtect() );
255         records.add( retval.createProtect() );
256         retval.records.setProtpos( records.size() - 1 );
257         records.add( retval.createPassword() );
258         records.add( retval.createProtectionRev4() );
259         records.add( retval.createPasswordRev4() );
260         records.add( retval.createWindowOne() );
261         records.add( retval.createBackup() );
262         retval.records.setBackuppos( records.size() - 1 );
263         records.add( retval.createHideObj() );
264         records.add( retval.createDateWindow1904() );
265         records.add( retval.createPrecision() );
266         records.add( retval.createRefreshAll() );
267         records.add( retval.createBookBool() );
268         records.add( retval.createFont() );
269         records.add( retval.createFont() );
270         records.add( retval.createFont() );
271         records.add( retval.createFont() );
272         retval.records.setFontpos( records.size() - 1 ); // last font record postion
273
retval.numfonts = 4;
274
275         // set up format records
276
for ( int i = 0; i <= 7; i++ )
277         {
278             Record rec;
279             rec = retval.createFormat( i );
280             retval.maxformatid = retval.maxformatid >= ( (FormatRecord) rec ).getIndexCode() ? retval.maxformatid : ( (FormatRecord) rec ).getIndexCode();
281             formats.add( rec );
282             records.add( rec );
283         }
284         retval.formats = formats;
285
286         for ( int k = 0; k < 21; k++ )
287         {
288             records.add( retval.createExtendedFormat( k ) );
289             retval.numxfs++;
290         }
291         retval.records.setXfpos( records.size() - 1 );
292         for ( int k = 0; k < 6; k++ )
293         {
294             records.add( retval.createStyle( k ) );
295         }
296         records.add( retval.createUseSelFS() );
297         for ( int k = 0; k < 1; k++ )
298         { // now just do 1
299
BoundSheetRecord bsr =
300                     (BoundSheetRecord) retval.createBoundSheet( k );
301
302             records.add( bsr );
303             retval.boundsheets.add( bsr );
304             retval.records.setBspos( records.size() - 1 );
305         }
306 // retval.records.supbookpos = retval.records.bspos + 1;
307
// retval.records.namepos = retval.records.supbookpos + 2;
308
records.add( retval.createCountry() );
309         retval.sst = (SSTRecord) retval.createSST();
310         records.add( retval.sst );
311         records.add( retval.createExtendedSST() );
312
313         records.add( retval.createEOF() );
314         retval.records.setRecords(records);
315         if (log.check( POILogger.DEBUG ))
316             log.log( DEBUG, "exit create new workbook from scratch" );
317         return retval;
318     }
319
320
321     /**Retrieves the Builtin NameRecord that matches the name and index
322      * There shouldn't be too many names to make the sequential search too slow
323      * @param name byte representation of the builtin name to match
324      * @param sheetIndex Index to match
325      * @return null if no builtin NameRecord matches
326      */

327     public NameRecord getSpecificBuiltinRecord(byte name, int sheetIndex)
328     {
329         Iterator JavaDoc iterator = names.iterator();
330         while (iterator.hasNext()) {
331             NameRecord record = ( NameRecord ) iterator.next();
332     
333             //print areas are one based
334
if (record.getBuiltInName() == name && record.getIndexToSheet() == sheetIndex) {
335                 return record;
336             }
337         }
338         
339         return null;
340         
341     }
342
343     /**
344      * Removes the specified Builtin NameRecord that matches the name and index
345      * @param name byte representation of the builtin to match
346      * @param sheetIndex zero-based sheet reference
347      */

348     public void removeBuiltinRecord(byte name, int sheetIndex) {
349         //the name array is smaller so searching through it should be faster than
350
//using the findFirstXXXX methods
351
NameRecord record = getSpecificBuiltinRecord(name, sheetIndex);
352         if (record != null) {
353             names.remove(record);
354         }
355         
356     }
357
358     public int getNumRecords() {
359         return records.size();
360     }
361
362     /**
363      * gets the font record at the given index in the font table. Remember
364      * "There is No Four" (someone at M$ must have gone to Rocky Horror one too
365      * many times)
366      *
367      * @param idx the index to look at (0 or greater but NOT 4)
368      * @return FontRecord located at the given index
369      */

370
371     public FontRecord getFontRecordAt(int idx) {
372         int index = idx;
373
374         if (index > 4) {
375             index -= 1; // adjust for "There is no 4"
376
}
377         if (index > (numfonts - 1)) {
378             throw new ArrayIndexOutOfBoundsException JavaDoc(
379             "There are only " + numfonts
380             + " font records, you asked for " + idx);
381         }
382         FontRecord retval =
383         ( FontRecord ) records.get((records.getFontpos() - (numfonts - 1)) + index);
384
385         return retval;
386     }
387
388     /**
389      * creates a new font record and adds it to the "font table". This causes the
390      * boundsheets to move down one, extended formats to move down (so this function moves
391      * those pointers as well)
392      *
393      * @return FontRecord that was just created
394      */

395
396     public FontRecord createNewFont() {
397         FontRecord rec = ( FontRecord ) createFont();
398
399         records.add(records.getFontpos()+1, rec);
400         records.setFontpos( records.getFontpos() + 1 );
401         numfonts++;
402         return rec;
403     }
404
405     /**
406      * gets the number of font records
407      *
408      * @return number of font records in the "font table"
409      */

410
411     public int getNumberOfFontRecords() {
412         return numfonts;
413     }
414
415     /**
416      * Sets the BOF for a given sheet
417      *
418      * @param sheetnum the number of the sheet to set the positing of the bof for
419      * @param pos the actual bof position
420      */

421
422     public void setSheetBof(int sheetnum, int pos) {
423         if (log.check( POILogger.DEBUG ))
424             log.log(DEBUG, "setting bof for sheetnum =", new Integer JavaDoc(sheetnum),
425                 " at pos=", new Integer JavaDoc(pos));
426         checkSheets(sheetnum);
427         (( BoundSheetRecord ) boundsheets.get(sheetnum))
428         .setPositionOfBof(pos);
429     }
430
431     /**
432      * Returns the position of the backup record.
433      */

434
435     public BackupRecord getBackupRecord() {
436         return ( BackupRecord ) records.get(records.getBackuppos());
437     }
438
439
440     /**
441      * sets the name for a given sheet. If the boundsheet record doesn't exist and
442      * its only one more than we have, go ahead and create it. If its > 1 more than
443      * we have, except
444      *
445      * @param sheetnum the sheet number (0 based)
446      * @param sheetname the name for the sheet
447      */

448
449     // for compatibility
450
public void setSheetName(int sheetnum, String JavaDoc sheetname ) {
451         setSheetName( sheetnum, sheetname, (byte)0 );
452     }
453
454     /**
455      * Determines whether a workbook contains the privided sheet name.
456      *
457      * @param name the name to test
458      * @param excludeSheetIdx the sheet to exclude from the check or -1 to include all sheets in the check.
459      * @return true if the sheet contains the name, false otherwise.
460      */

461     public boolean doesContainsSheetName( String JavaDoc name, int excludeSheetIdx )
462     {
463         for ( int i = 0; i < boundsheets.size(); i++ )
464         {
465             BoundSheetRecord boundSheetRecord = (BoundSheetRecord) boundsheets.get( i );
466             if (excludeSheetIdx != i && name.equals(boundSheetRecord.getSheetname()))
467                 return true;
468         }
469         return false;
470     }
471
472     public void setSheetName(int sheetnum, String JavaDoc sheetname, short encoding ) {
473         checkSheets(sheetnum);
474         BoundSheetRecord sheet = (BoundSheetRecord)boundsheets.get( sheetnum );
475         sheet.setSheetname(sheetname);
476         sheet.setSheetnameLength( (byte)sheetname.length() );
477         sheet.setCompressedUnicodeFlag( (byte)encoding );
478     }
479     
480     /**
481      * sets the order of appearance for a given sheet.
482      *
483      * @param sheetname the name of the sheet to reorder
484      * @param pos the position that we want to insert the sheet into (0 based)
485      */

486     
487     public void setSheetOrder(String JavaDoc sheetname, int pos ) {
488     int sheetNumber = getSheetIndex(sheetname);
489     //remove the sheet that needs to be reordered and place it in the spot we want
490
boundsheets.add(pos, boundsheets.remove(sheetNumber));
491     }
492
493     /**
494      * gets the name for a given sheet.
495      *
496      * @param sheetnum the sheet number (0 based)
497      * @return sheetname the name for the sheet
498      */

499
500     public String JavaDoc getSheetName(int sheetnum) {
501         return (( BoundSheetRecord ) boundsheets.get(sheetnum))
502         .getSheetname();
503     }
504
505     /**
506      * get the sheet's index
507      * @param name sheet name
508      * @return sheet index or -1 if it was not found.
509      */

510
511     public int getSheetIndex(String JavaDoc name) {
512         int retval = -1;
513
514         for (int k = 0; k < boundsheets.size(); k++) {
515             String JavaDoc sheet = getSheetName(k);
516
517             if (sheet.equalsIgnoreCase(name)) {
518                 retval = k;
519                 break;
520             }
521         }
522         return retval;
523     }
524
525     /**
526      * if we're trying to address one more sheet than we have, go ahead and add it! if we're
527      * trying to address >1 more than we have throw an exception!
528      */

529
530     private void checkSheets(int sheetnum) {
531         if ((boundsheets.size()) <= sheetnum) { // if we're short one add another..
532
if ((boundsheets.size() + 1) <= sheetnum) {
533                 throw new RuntimeException JavaDoc("Sheet number out of bounds!");
534             }
535             BoundSheetRecord bsr = (BoundSheetRecord ) createBoundSheet(sheetnum);
536
537             records.add(records.getBspos()+1, bsr);
538             records.setBspos( records.getBspos() + 1 );
539             boundsheets.add(bsr);
540             fixTabIdRecord();
541         }
542     }
543
544     public void removeSheet(int sheetnum) {
545         if (boundsheets.size() > sheetnum) {
546             records.remove(records.getBspos() - (boundsheets.size() - 1) + sheetnum);
547 // records.bspos--;
548
boundsheets.remove(sheetnum);
549             fixTabIdRecord();
550         }
551     }
552
553     /**
554      * make the tabid record look like the current situation.
555      *
556      */

557     private void fixTabIdRecord() {
558         TabIdRecord tir = ( TabIdRecord ) records.get(records.getTabpos());
559         short[] tia = new short[ boundsheets.size() ];
560
561         for (short k = 0; k < tia.length; k++) {
562             tia[ k ] = k;
563         }
564         tir.setTabIdArray(tia);
565     }
566
567     /**
568      * returns the number of boundsheet objects contained in this workbook.
569      *
570      * @return number of BoundSheet records
571      */

572
573     public int getNumSheets() {
574         if (log.check( POILogger.DEBUG ))
575             log.log(DEBUG, "getNumSheets=", new Integer JavaDoc(boundsheets.size()));
576         return boundsheets.size();
577     }
578
579     /**
580      * get the number of ExtendedFormat records contained in this workbook.
581      *
582      * @return int count of ExtendedFormat records
583      */

584
585     public int getNumExFormats() {
586         if (log.check( POILogger.DEBUG ))
587             log.log(DEBUG, "getXF=", new Integer JavaDoc(numxfs));
588         return numxfs;
589     }
590
591     /**
592      * gets the ExtendedFormatRecord at the given 0-based index
593      *
594      * @param index of the Extended format record (0-based)
595      * @return ExtendedFormatRecord at the given index
596      */

597
598     public ExtendedFormatRecord getExFormatAt(int index) {
599         int xfptr = records.getXfpos() - (numxfs - 1);
600
601         xfptr += index;
602         ExtendedFormatRecord retval =
603         ( ExtendedFormatRecord ) records.get(xfptr);
604
605         return retval;
606     }
607
608     /**
609      * creates a new Cell-type Extneded Format Record and adds it to the end of
610      * ExtendedFormatRecords collection
611      *
612      * @return ExtendedFormatRecord that was created
613      */

614
615     public ExtendedFormatRecord createCellXF() {
616         ExtendedFormatRecord xf = createExtendedFormat();
617
618         records.add(records.getXfpos()+1, xf);
619         records.setXfpos( records.getXfpos() + 1 );
620         numxfs++;
621         return xf;
622     }
623
624     /**
625      * Adds a string to the SST table and returns its index (if its a duplicate
626      * just returns its index and update the counts)
627      *
628      * @param string the string to be added to the SSTRecord
629      * @param use16bits whether to use utf 16 or false for compressed unicode
630      * @return index of the string within the SSTRecord
631      */

632
633     public int addSSTString(String JavaDoc string, boolean use16bits) {
634         if (log.check( POILogger.DEBUG ))
635             log.log(DEBUG, "insert to sst string='", string, "' and use16bits= ",
636         new Boolean JavaDoc(use16bits));
637         if (sst == null) {
638             insertSST();
639         }
640         return sst.addString(string, use16bits);
641     }
642
643     /**
644      * Adds a string to the SST table and returns its index (if its a duplicate
645      * just returns its index and update the counts) ASSUMES compressed unicode
646      * (meaning 8bit)
647      *
648      * @param string the string to be added to the SSTRecord
649      *
650      * @return index of the string within the SSTRecord
651      */

652
653     public int addSSTString(String JavaDoc string) {
654         return addSSTString(string, false);
655     }
656
657     /**
658      * given an index into the SST table, this function returns the corresponding String value
659      * @return String containing the SST String
660      */

661
662     public String JavaDoc getSSTString(int str) {
663         if (sst == null) {
664             insertSST();
665         }
666         String JavaDoc retval = sst.getString(str);
667
668         if (log.check( POILogger.DEBUG ))
669             log.log(DEBUG, "Returning SST for index=", new Integer JavaDoc(str),
670                 " String= ", retval);
671         return retval;
672     }
673
674     /**
675      * use this function to add a Shared String Table to an existing sheet (say
676      * generated by a different java api) without an sst....
677      * @see #createSST()
678      * @see org.apache.poi.hssf.record.SSTRecord
679      */

680
681     public void insertSST() {
682         if (log.check( POILogger.DEBUG ))
683             log.log(DEBUG, "creating new SST via insertSST!");
684         sst = ( SSTRecord ) createSST();
685         records.add(records.size() - 1, createExtendedSST());
686         records.add(records.size() - 2, sst);
687     }
688
689     /**
690      * Serializes all records int the worksheet section into a big byte array. Use
691      * this to write the Workbook out.
692      *
693      * @return byte array containing the HSSF-only portions of the POIFS file.
694      */

695      // GJS: Not used so why keep it.
696
// public byte [] serialize() {
697
// log.log(DEBUG, "Serializing Workbook!");
698
// byte[] retval = null;
699
//
700
//// ArrayList bytes = new ArrayList(records.size());
701
// int arraysize = getSize();
702
// int pos = 0;
703
//
704
// retval = new byte[ arraysize ];
705
// for (int k = 0; k < records.size(); k++) {
706
//
707
// Record record = records.get(k);
708
//// Let's skip RECALCID records, as they are only use for optimization
709
// if(record.getSid() != RecalcIdRecord.sid || ((RecalcIdRecord)record).isNeeded()) {
710
// pos += record.serialize(pos, retval); // rec.length;
711
// }
712
// }
713
// log.log(DEBUG, "Exiting serialize workbook");
714
// return retval;
715
// }
716

717     /**
718      * Serializes all records int the worksheet section into a big byte array. Use
719      * this to write the Workbook out.
720      * @param offset of the data to be written
721      * @param data array of bytes to write this to
722      */

723
724     public int serialize( int offset, byte[] data )
725     {
726         if (log.check( POILogger.DEBUG ))
727             log.log( DEBUG, "Serializing Workbook with offsets" );
728
729         int pos = 0;
730
731         SSTRecord sst = null;
732         int sstPos = 0;
733         for ( int k = 0; k < records.size(); k++ )
734         {
735
736             Record record = records.get( k );
737             // Let's skip RECALCID records, as they are only use for optimization
738
if ( record.getSid() != RecalcIdRecord.sid || ( (RecalcIdRecord) record ).isNeeded() )
739             {
740                 if (record instanceof SSTRecord)
741                 {
742                     sst = (SSTRecord)record;
743                     sstPos = pos;
744                 }
745                 if (record.getSid() == ExtSSTRecord.sid && sst != null)
746                 {
747                     record = sst.createExtSSTRecord(sstPos + offset);
748                 }
749                 pos += record.serialize( pos + offset, data ); // rec.length;
750
}
751         }
752         if (log.check( POILogger.DEBUG ))
753             log.log( DEBUG, "Exiting serialize workbook" );
754         return pos;
755     }
756
757     public int getSize()
758     {
759         int retval = 0;
760
761         SSTRecord sst = null;
762         for ( int k = 0; k < records.size(); k++ )
763         {
764             Record record = records.get( k );
765             // Let's skip RECALCID records, as they are only use for optimization
766
if ( record.getSid() != RecalcIdRecord.sid || ( (RecalcIdRecord) record ).isNeeded() )
767             {
768                 if (record instanceof SSTRecord)
769                     sst = (SSTRecord)record;
770                 if (record.getSid() == ExtSSTRecord.sid && sst != null)
771                     retval += sst.calcExtSSTRecordSize();
772                 else
773                     retval += record.getRecordSize();
774             }
775         }
776         return retval;
777     }
778
779     /**
780      * creates the BOF record
781      * @see org.apache.poi.hssf.record.BOFRecord
782      * @see org.apache.poi.hssf.record.Record
783      * @return record containing a BOFRecord
784      */

785
786     protected Record createBOF() {
787         BOFRecord retval = new BOFRecord();
788
789         retval.setVersion(( short ) 0x600);
790         retval.setType(( short ) 5);
791         retval.setBuild(( short ) 0x10d3);
792
793         // retval.setBuild((short)0x0dbb);
794
retval.setBuildYear(( short ) 1996);
795         retval.setHistoryBitMask(0x41); // was c1 before verify
796
retval.setRequiredVersion(0x6);
797         return retval;
798     }
799
800     /**
801      * creates the InterfaceHdr record
802      * @see org.apache.poi.hssf.record.InterfaceHdrRecord
803      * @see org.apache.poi.hssf.record.Record
804      * @return record containing a InterfaceHdrRecord
805      */

806
807     protected Record createInterfaceHdr() {
808         InterfaceHdrRecord retval = new InterfaceHdrRecord();
809
810         retval.setCodepage(CODEPAGE);
811         return retval;
812     }
813
814     /**
815      * creates an MMS record
816      * @see org.apache.poi.hssf.record.MMSRecord
817      * @see org.apache.poi.hssf.record.Record
818      * @return record containing a MMSRecord
819      */

820
821     protected Record createMMS() {
822         MMSRecord retval = new MMSRecord();
823
824         retval.setAddMenuCount(( byte ) 0);
825         retval.setDelMenuCount(( byte ) 0);
826         return retval;
827     }
828
829     /**
830      * creates the InterfaceEnd record
831      * @see org.apache.poi.hssf.record.InterfaceEndRecord
832      * @see org.apache.poi.hssf.record.Record
833      * @return record containing a InterfaceEndRecord
834      */

835
836     protected Record createInterfaceEnd() {
837         return new InterfaceEndRecord();
838     }
839
840     /**
841      * creates the WriteAccess record containing the logged in user's name
842      * @see org.apache.poi.hssf.record.WriteAccessRecord
843      * @see org.apache.poi.hssf.record.Record
844      * @return record containing a WriteAccessRecord
845      */

846
847     protected Record createWriteAccess() {
848         WriteAccessRecord retval = new WriteAccessRecord();
849
850         try
851         {
852             retval.setUsername(System.getProperty("user.name"));
853         }
854         catch (java.security.AccessControlException JavaDoc e)
855         {
856                 // AccessControlException can occur in a restricted context
857
// (client applet/jws application or restricted security server)
858
retval.setUsername("POI");
859         }
860         return retval;
861     }
862
863     /**
864      * creates the Codepage record containing the constant stored in CODEPAGE
865      * @see org.apache.poi.hssf.record.CodepageRecord
866      * @see org.apache.poi.hssf.record.Record
867      * @return record containing a CodepageRecord
868      */

869
870     protected Record createCodepage() {
871         CodepageRecord retval = new CodepageRecord();
872
873         retval.setCodepage(CODEPAGE);
874         return retval;
875     }
876
877     /**
878      * creates the DSF record containing a 0 since HSSF can't even create Dual Stream Files
879      * @see org.apache.poi.hssf.record.DSFRecord
880      * @see org.apache.poi.hssf.record.Record
881      * @return record containing a DSFRecord
882      */

883
884     protected Record createDSF() {
885         DSFRecord retval = new DSFRecord();
886
887         retval.setDsf(
888         ( short ) 0); // we don't even support double stream files
889
return retval;
890     }
891
892     /**
893      * creates the TabId record containing an array of 0,1,2. This release of HSSF
894      * always has the default three sheets, no less, no more.
895      * @see org.apache.poi.hssf.record.TabIdRecord
896      * @see org.apache.poi.hssf.record.Record
897      * @return record containing a TabIdRecord
898      */

899
900     protected Record createTabId() {
901         TabIdRecord retval = new TabIdRecord();
902         short[] tabidarray = {
903             0
904         };
905
906         retval.setTabIdArray(tabidarray);
907         return retval;
908     }
909
910     /**
911      * creates the FnGroupCount record containing the Magic number constant of 14.
912      * @see org.apache.poi.hssf.record.FnGroupCountRecord
913      * @see org.apache.poi.hssf.record.Record
914      * @return record containing a FnGroupCountRecord
915      */

916
917     protected Record createFnGroupCount() {
918         FnGroupCountRecord retval = new FnGroupCountRecord();
919
920         retval.setCount(( short ) 14);
921         return retval;
922     }
923
924     /**
925      * creates the WindowProtect record with protect set to false.
926      * @see org.apache.poi.hssf.record.WindowProtectRecord
927      * @see org.apache.poi.hssf.record.Record
928      * @return record containing a WindowProtectRecord
929      */

930
931     protected Record createWindowProtect() {
932         WindowProtectRecord retval = new WindowProtectRecord();
933
934         retval.setProtect(
935         false); // by default even when we support it we won't
936
return retval; // want it to be protected
937
}
938
939     /**
940