1 /* ====================================================================
2 Licensed to the Apache Software Foundation (ASF) under one or more
3 contributor license agreements. See the NOTICE file distributed with
4 this work for additional information regarding copyright ownership.
5 The ASF licenses this file to You under the Apache License, Version 2.0
6 (the "License"); you may not use this file except in compliance with
7 the License. You may obtain a copy of the License at
8
9 http://www.apache.org/licenses/LICENSE-2.0
10
11 Unless required by applicable law or agreed to in writing, software
12 distributed under the License is distributed on an "AS IS" BASIS,
13 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 See the License for the specific language governing permissions and
15 limitations under the License.
16 ==================================================================== */
17
18
19 package org.apache.poi.hssf.model;
20
21 import org.apache.poi.ddf;
22 import org.apache.poi.hssf.record;
23 import org.apache.poi.hssf.util.HSSFColor;
24 import org.apache.poi.hssf.util.SheetReferences;
25 import org.apache.poi.util.POILogFactory;
26 import org.apache.poi.util.POILogger;
27
28 import java.util.ArrayList;
29 import java.util.Iterator;
30 import java.util.List;
31 import java.util.Locale;
32
33 /**
34 * Low level model implementation of a Workbook. Provides creational methods
35 * for settings and objects contained in the workbook object.
36 * <P>
37 * This file contains the low level binary records starting at the workbook's BOF and
38 * ending with the workbook's EOF. Use HSSFWorkbook for a high level representation.
39 * <P>
40 * The structures of the highlevel API use references to this to perform most of their
41 * operations. Its probably unwise to use these low level structures directly unless you
42 * really know what you're doing. I recommend you read the Microsoft Excel 97 Developer's
43 * Kit (Microsoft Press) and the documentation at http://sc.openoffice.org/excelfileformat.pdf
44 * before even attempting to use this.
45 *
46 *
47 * @author Luc Girardin (luc dot girardin at macrofocus dot com)
48 * @author Sergei Kozello (sergeikozello at mail.ru)
49 * @author Shawn Laubach (slaubach at apache dot org) (Data Formats)
50 * @author Andrew C. Oliver (acoliver at apache dot org)
51 * @author Brian Sanders (bsanders at risklabs dot com) - custom palette
52 * @author Dan Sherman (dsherman at isisph.com)
53 * @author Glen Stampoultzis (glens at apache.org)
54 * @see org.apache.poi.hssf.usermodel.HSSFWorkbook
55 * @version 1.0-pre
56 */
57
58 public class Workbook implements Model
59 {
60 private static final int DEBUG = POILogger.DEBUG;
61
62 // public static Workbook currentBook = null;
63
64 /**
65 * constant used to set the "codepage" wherever "codepage" is set in records
66 * (which is duplciated in more than one record)
67 */
68
69 private final static short CODEPAGE = ( short ) 0x4b0;
70
71 /**
72 * this contains the Worksheet record objects
73 */
74 protected WorkbookRecordList records = new WorkbookRecordList();
75
76 /**
77 * this contains a reference to the SSTRecord so that new stings can be added
78 * to it.
79 */
80 protected SSTRecord sst = null;
81
82
83 private LinkTable linkTable; // optionally occurs if there are references in the document. (4.10.3)
84
85 /**
86 * holds the "boundsheet" records (aka bundlesheet) so that they can have their
87 * reference to their "BOF" marker
88 */
89 protected ArrayList boundsheets = new ArrayList();
90
91 protected ArrayList formats = new ArrayList();
92
93 protected ArrayList hyperlinks = new ArrayList();
94
95 protected int numxfs = 0; // hold the number of extended format records
96 protected int numfonts = 0; // hold the number of font records
97 private short maxformatid = -1; // holds the max format id
98 private boolean uses1904datewindowing = false; // whether 1904 date windowing is being used
99 private DrawingManager2 drawingManager;
100 private List escherBSERecords = new ArrayList(); // EscherBSERecord
101 private WindowOneRecord windowOne;
102 private FileSharingRecord fileShare;
103 private WriteAccessRecord writeAccess;
104 private WriteProtectRecord writeProtect;
105
106 private static POILogger log = POILogFactory.getLogger(Workbook.class);
107
108 /**
109 * Creates new Workbook with no intitialization --useless right now
110 * @see #createWorkbook(List)
111 */
112 public Workbook() {
113 }
114
115 /**
116 * read support for low level
117 * API. Pass in an array of Record objects, A Workbook
118 * object is constructed and passed back with all of its initialization set
119 * to the passed in records and references to those records held. Unlike Sheet
120 * workbook does not use an offset (its assumed to be 0) since its first in a file.
121 * If you need an offset then construct a new array with a 0 offset or write your
122 * own ;-p.
123 *
124 * @param recs an array of Record objects
125 * @return Workbook object
126 */
127 public static Workbook createWorkbook(List recs) {
128 if (log.check( POILogger.DEBUG ))
129 log.log(DEBUG, "Workbook (readfile) created with reclen=",
130 new Integer(recs.size()));
131 Workbook retval = new Workbook();
132 ArrayList records = new ArrayList(recs.size() / 3);
133 retval.records.setRecords(records);
134
135 int k;
136 for (k = 0; k < recs.size(); k++) {
137 Record rec = ( Record ) recs.get(k);
138
139 if (rec.getSid() == EOFRecord.sid) {
140 records.add(rec);
141 if (log.check( POILogger.DEBUG ))
142 log.log(DEBUG, "found workbook eof record at " + k);
143 break;
144 }
145 switch (rec.getSid()) {
146
147 case BoundSheetRecord.sid :
148 if (log.check( POILogger.DEBUG ))
149 log.log(DEBUG, "found boundsheet record at " + k);
150 retval.boundsheets.add(rec);
151 retval.records.setBspos( k );
152 break;
153
154 case SSTRecord.sid :
155 if (log.check( POILogger.DEBUG ))
156 log.log(DEBUG, "found sst record at " + k);
157 retval.sst = ( SSTRecord ) rec;
158 break;
159
160 case FontRecord.sid :
161 if (log.check( POILogger.DEBUG ))
162 log.log(DEBUG, "found font record at " + k);
163 retval.records.setFontpos( k );
164 retval.numfonts++;
165 break;
166
167 case ExtendedFormatRecord.sid :
168 if (log.check( POILogger.DEBUG ))
169 log.log(DEBUG, "found XF record at " + k);
170 retval.records.setXfpos( k );
171 retval.numxfs++;
172 break;
173
174 case TabIdRecord.sid :
175 if (log.check( POILogger.DEBUG ))
176 log.log(DEBUG, "found tabid record at " + k);
177 retval.records.setTabpos( k );
178 break;
179
180 case ProtectRecord.sid :
181 if (log.check( POILogger.DEBUG ))
182 log.log(DEBUG, "found protect record at " + k);
183 retval.records.setProtpos( k );
184 break;
185
186 case BackupRecord.sid :
187 if (log.check( POILogger.DEBUG ))
188 log.log(DEBUG, "found backup record at " + k);
189 retval.records.setBackuppos( k );
190 break;
191 case ExternSheetRecord.sid :
192 throw new RuntimeException("Extern sheet is part of LinkTable");
193 case NameRecord.sid :
194 case SupBookRecord.sid :
195 // LinkTable can start with either of these
196 if (log.check( POILogger.DEBUG ))
197 log.log(DEBUG, "found SupBook record at " + k);
198 retval.linkTable = new LinkTable(recs, k, retval.records);
199 k+=retval.linkTable.getRecordCount() - 1;
200 continue;
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 break;
217 case WindowOneRecord.sid:
218 if (log.check( POILogger.DEBUG ))
219 log.log(DEBUG, "found WindowOneRecord at " + k);
220 retval.windowOne = (WindowOneRecord) rec;
221 break;
222 case WriteAccessRecord.sid:
223 if (log.check( POILogger.DEBUG ))
224 log.log(DEBUG, "found WriteAccess at " + k);
225 retval.writeAccess = (WriteAccessRecord) rec;
226 break;
227 case WriteProtectRecord.sid:
228 if (log.check( POILogger.DEBUG ))
229 log.log(DEBUG, "found WriteProtect at " + k);
230 retval.writeProtect = (WriteProtectRecord) rec;
231 break;
232 case FileSharingRecord.sid:
233 if (log.check( POILogger.DEBUG ))
234 log.log(DEBUG, "found FileSharing at " + k);
235 retval.fileShare = (FileSharingRecord) rec;
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 // Look for other interesting values that
247 // follow the EOFRecord
248 for ( ; k < recs.size(); k++) {
249 Record rec = ( Record ) recs.get(k);
250 switch (rec.getSid()) {
251 case HyperlinkRecord.sid:
252 retval.hyperlinks.add(rec);
253 break;
254 }
255 }
256
257 if (retval.windowOne == null) {
258 retval.windowOne = (WindowOneRecord) retval.createWindowOne();
259 }
260 if (log.check( POILogger.DEBUG ))
261 log.log(DEBUG, "exit create workbook from existing file function");
262 return retval;
263 }
264
265 /**
266 * Creates an empty workbook object with three blank sheets and all the empty
267 * fields. Use this to create a workbook from scratch.
268 */
269 public static Workbook createWorkbook()
270 {
271 if (log.check( POILogger.DEBUG ))
272 log.log( DEBUG, "creating new workbook from scratch" );
273 Workbook retval = new Workbook();
274 ArrayList records = new ArrayList( 30 );
275 retval.records.setRecords(records);
276 ArrayList formats = new ArrayList( 8 );
277
278 records.add( retval.createBOF() );
279 records.add( retval.createInterfaceHdr() );
280 records.add( retval.createMMS() );
281 records.add( retval.createInterfaceEnd() );
282 records.add( retval.createWriteAccess() );
283 records.add( retval.createCodepage() );
284 records.add( retval.createDSF() );
285 records.add( retval.createTabId() );
286 retval.records.setTabpos( records.size() - 1 );
287 records.add( retval.createFnGroupCount() );
288 records.add( retval.createWindowProtect() );
289 records.add( retval.createProtect() );
290 retval.records.setProtpos( records.size() - 1 );
291 records.add( retval.createPassword() );
292 records.add( retval.createProtectionRev4() );
293 records.add( retval.createPasswordRev4() );
294 retval.windowOne = (WindowOneRecord) retval.createWindowOne();
295 records.add( retval.windowOne );
296 records.add( retval.createBackup() );
297 retval.records.setBackuppos( records.size() - 1 );
298 records.add( retval.createHideObj() );
299 records.add( retval.createDateWindow1904() );
300 records.add( retval.createPrecision() );
301 records.add( retval.createRefreshAll() );
302 records.add( retval.createBookBool() );
303 records.add( retval.createFont() );
304 records.add( retval.createFont() );
305 records.add( retval.createFont() );
306 records.add( retval.createFont() );
307 retval.records.setFontpos( records.size() - 1 ); // last font record postion
308 retval.numfonts = 4;
309
310 // set up format records
311 for ( int i = 0; i <= 7; i++ )
312 {
313 Record rec;
314 rec = retval.createFormat( i );
315 retval.maxformatid = retval.maxformatid >= ( (FormatRecord) rec ).getIndexCode() ? retval.maxformatid : ( (FormatRecord) rec ).getIndexCode();
316 formats.add( rec );
317 records.add( rec );
318 }
319 retval.formats = formats;
320
321 for ( int k = 0; k < 21; k++ )
322 {
323 records.add( retval.createExtendedFormat( k ) );
324 retval.numxfs++;
325 }
326 retval.records.setXfpos( records.size() - 1 );
327 for ( int k = 0; k < 6; k++ )
328 {
329 records.add( retval.createStyle( k ) );
330 }
331 records.add( retval.createUseSelFS() );
332
333 int nBoundSheets = 1; // now just do 1
334 for ( int k = 0; k < nBoundSheets; k++ ) {
335 BoundSheetRecord bsr =
336 (BoundSheetRecord) retval.createBoundSheet( k );
337
338 records.add( bsr );
339 retval.boundsheets.add( bsr );
340 retval.records.setBspos( records.size() - 1 );
341 }
342 // retval.records.supbookpos = retval.records.bspos + 1;
343 // retval.records.namepos = retval.records.supbookpos + 2;
344 records.add( retval.createCountry() );
345 for ( int k = 0; k < nBoundSheets; k++ ) {
346 retval.getOrCreateLinkTable().checkExternSheet(k);
347 }
348 retval.sst = (SSTRecord) retval.createSST();
349 records.add( retval.sst );
350 records.add( retval.createExtendedSST() );
351
352 records.add( retval.createEOF() );
353 if (log.check( POILogger.DEBUG ))
354 log.log( DEBUG, "exit create new workbook from scratch" );
355 return retval;
356 }
357
358
359 /**Retrieves the Builtin NameRecord that matches the name and index
360 * There shouldn't be too many names to make the sequential search too slow
361 * @param name byte representation of the builtin name to match
362 * @param sheetIndex Index to match
363 * @return null if no builtin NameRecord matches
364 */
365 public NameRecord getSpecificBuiltinRecord(byte name, int sheetIndex)
366 {
367 return getOrCreateLinkTable().getSpecificBuiltinRecord(name, sheetIndex);
368 }
369
370 /**
371 * Removes the specified Builtin NameRecord that matches the name and index
372 * @param name byte representation of the builtin to match
373 * @param sheetIndex zero-based sheet reference
374 */
375 public void removeBuiltinRecord(byte name, int sheetIndex) {
376 linkTable.removeBuiltinRecord(name, sheetIndex);
377 // TODO - do we need "this.records.remove(...);" similar to that in this.removeName(int namenum) {}?
378 }
379
380 public int getNumRecords() {
381 return records.size();
382 }
383
384 /**
385 * gets the font record at the given index in the font table. Remember
386 * "There is No Four" (someone at M$ must have gone to Rocky Horror one too
387 * many times)
388 *
389 * @param idx the index to look at (0 or greater but NOT 4)
390 * @return FontRecord located at the given index
391 */
392
393 public FontRecord getFontRecordAt(int idx) {
394 int index = idx;
395
396 if (index > 4) {
397 index -= 1; // adjust for "There is no 4"
398 }
399 if (index > (numfonts - 1)) {
400 throw new ArrayIndexOutOfBoundsException(
401 "There are only " + numfonts
402 + " font records, you asked for " + idx);
403 }
404 FontRecord retval =
405 ( FontRecord ) records.get((records.getFontpos() - (numfonts - 1)) + index);
406
407 return retval;
408 }
409
410 /**
411 * creates a new font record and adds it to the "font table". This causes the
412 * boundsheets to move down one, extended formats to move down (so this function moves
413 * those pointers as well)
414 *
415 * @return FontRecord that was just created
416 */
417
418 public FontRecord createNewFont() {
419 FontRecord rec = ( FontRecord ) createFont();
420
421 records.add(records.getFontpos()+1, rec);
422 records.setFontpos( records.getFontpos() + 1 );
423 numfonts++;
424 return rec;
425 }
426
427 /**
428 * gets the number of font records
429 *
430 * @return number of font records in the "font table"
431 */
432
433 public int getNumberOfFontRecords() {
434 return numfonts;
435 }
436
437 /**
438 * Sets the BOF for a given sheet
439 *
440 * @param sheetnum the number of the sheet to set the positing of the bof for
441 * @param pos the actual bof position
442 */
443
444 public void setSheetBof(int sheetnum, int pos) {
445 if (log.check( POILogger.DEBUG ))
446 log.log(DEBUG, "setting bof for sheetnum =", new Integer(sheetnum),
447 " at pos=", new Integer(pos));
448 checkSheets(sheetnum);
449 (( BoundSheetRecord ) boundsheets.get(sheetnum))
450 .setPositionOfBof(pos);
451 }
452
453 /**
454 * Returns the position of the backup record.
455 */
456
457 public BackupRecord getBackupRecord() {
458 return ( BackupRecord ) records.get(records.getBackuppos());
459 }
460
461
462 /**
463 * sets the name for a given sheet. If the boundsheet record doesn't exist and
464 * its only one more than we have, go ahead and create it. If its > 1 more than
465 * we have, except
466 *
467 * @param sheetnum the sheet number (0 based)
468 * @param sheetname the name for the sheet
469 */
470 public void setSheetName(int sheetnum, String sheetname ) {
471 checkSheets(sheetnum);
472 BoundSheetRecord sheet = (BoundSheetRecord)boundsheets.get( sheetnum );
473 sheet.setSheetname(sheetname);
474 sheet.setSheetnameLength( (byte)sheetname.length() );
475 }
476
477 /**
478 * Determines whether a workbook contains the provided sheet name.
479 *
480 * @param name the name to test (case insensitive match)
481 * @param excludeSheetIdx the sheet to exclude from the check or -1 to include all sheets in the check.
482 * @return true if the sheet contains the name, false otherwise.
483 */
484 public boolean doesContainsSheetName( String name, int excludeSheetIdx )
485 {
486 for ( int i = 0; i < boundsheets.size(); i++ )
487 {
488 BoundSheetRecord boundSheetRecord = (BoundSheetRecord) boundsheets.get( i );
489 if (excludeSheetIdx != i && name.equalsIgnoreCase(boundSheetRecord.getSheetname()))
490 return true;
491 }
492 return false;
493 }
494
495 /**
496 * sets the name for a given sheet forcing the encoding. This is STILL A BAD IDEA.
497 * Poi now automatically detects unicode
498 *
499 *@deprecated 3-Jan-06 Simply use setSheetNam e(int sheetnum, String sheetname)
500 * @param sheetnum the sheet number (0 based)
501 * @param sheetname the name for the sheet
502 */
503 public void setSheetName(int sheetnum, String sheetname, short encoding ) {
504 checkSheets(sheetnum);
505 BoundSheetRecord sheet = (BoundSheetRecord)boundsheets.get( sheetnum );
506 sheet.setSheetname(sheetname);
507 sheet.setSheetnameLength( (byte)sheetname.length() );
508 sheet.setCompressedUnicodeFlag( (byte)encoding );
509 }
510
511 /**
512 * sets the order of appearance for a given sheet.
513 *
514 * @param sheetname the name of the sheet to reorder
515 * @param pos the position that we want to insert the sheet into (0 based)
516 */
517
518 public void setSheetOrder(String sheetname, int pos ) {
519 int sheetNumber = getSheetIndex(sheetname);
520 //remove the sheet that needs to be reordered and place it in the spot we want
521 boundsheets.add(pos, boundsheets.remove(sheetNumber));
522 }
523
524 /**
525 * gets the name for a given sheet.
526 *
527 * @param sheetnum the sheet number (0 based)
528 * @return sheetname the name for the sheet
529 */
530
531 public String getSheetName(int sheetnum) {
532 return (( BoundSheetRecord ) boundsheets.get(sheetnum))
533 .getSheetname();
534 }
535
536 /**
537 * gets the hidden flag for a given sheet.
538 *
539 * @param sheetnum the sheet number (0 based)
540 * @return True if sheet is hidden
541 */
542
543 public boolean isSheetHidden(int sheetnum) {
544 BoundSheetRecord bsr = ( BoundSheetRecord ) boundsheets.get(sheetnum);
545 return bsr.isHidden();
546 }
547
548 /**
549 * Hide or unhide a sheet
550 *
551 * @param sheetnum The sheet number
552 * @param hidden True to mark the sheet as hidden, false otherwise
553 */
554
555 public void setSheetHidden(int sheetnum, boolean hidden) {
556 BoundSheetRecord bsr = ( BoundSheetRecord ) boundsheets.get(sheetnum);
557 bsr.setHidden(hidden);
558 }
559 /**
560 * get the sheet's index
561 * @param name sheet name
562 * @return sheet index or -1 if it was not found.
563 */
564
565 public int getSheetIndex(String name) {
566 int retval = -1;
567
568 for (int k = 0; k < boundsheets.size(); k++) {
569 String sheet = getSheetName(k);
570
571 if (sheet.equalsIgnoreCase(name)) {
572 retval = k;
573 break;
574 }
575 }
576 return retval;
577 }
578
579 /**
580 * if we're trying to address one more sheet than we have, go ahead and add it! if we're
581 * trying to address >1 more than we have throw an exception!
582 */
583
584 private void checkSheets(int sheetnum) {
585 if ((boundsheets.size()) <= sheetnum) { // if we're short one add another..
586 if ((boundsheets.size() + 1) <= sheetnum) {
587 throw new RuntimeException("Sheet number out of bounds!");
588 }
589 BoundSheetRecord bsr = (BoundSheetRecord ) createBoundSheet(sheetnum);
590
591 records.add(records.getBspos()+1, bsr);
592 records.setBspos( records.getBspos() + 1 );
593 boundsheets.add(bsr);
594 getOrCreateLinkTable().checkExternSheet(sheetnum);
595 fixTabIdRecord();
596 }
597 }
598
599 public void removeSheet(int sheetnum) {
600 if (boundsheets.size() > sheetnum) {
601 records.remove(records.getBspos() - (boundsheets.size() - 1) + sheetnum);
602 // records.bspos--;
603 boundsheets.remove(sheetnum);
604 fixTabIdRecord();
605 }
606
607 // Within NameRecords, it's ok to have the formula
608 // part point at deleted sheets. It's also ok to
609 // have the ExternSheetNumber point at deleted
610 // sheets.
611 // However, the sheet index must be adjusted, or
612 // excel will break. (Sheet index is either 0 for
613 // global, or 1 based index to sheet)
614 int sheetNum1Based = sheetnum + 1;
615 for(int i=0; i<getNumNames(); i++) {
616 NameRecord nr = getNameRecord(i);
617
618 if(nr.getIndexToSheet() == sheetNum1Based) {
619 // Excel re-writes these to point to no sheet
620 nr.setEqualsToIndexToSheet((short)0);
621 } else if(nr.getIndexToSheet() > sheetNum1Based) {
622 // Bump down by one, so still points
623 // at the same sheet
624 nr.setEqualsToIndexToSheet((short)(
625 nr.getEqualsToIndexToSheet()-1
626 ));
627 }
628 }
629 }
630
631 /**
632 * make the tabid record look like the current situation.
633 *
634 */
635 private void fixTabIdRecord() {
636 TabIdRecord tir = ( TabIdRecord ) records.get(records.getTabpos());
637 short[] tia = new short[ boundsheets.size() ];
638
639 for (short k = 0; k < tia.length; k++) {
640 tia[ k ] = k;
641 }
642 tir.setTabIdArray(tia);
643 }
644
645 /**
646 * returns the number of boundsheet objects contained in this workbook.
647 *
648 * @return number of BoundSheet records
649 */
650
651 public int getNumSheets() {
652 if (log.check( POILogger.DEBUG ))
653 log.log(DEBUG, "getNumSheets=", new Integer(boundsheets.size()));
654 return boundsheets.size();
655 }
656
657 /**
658 * get the number of ExtendedFormat records contained in this workbook.
659 *
660 * @return int count of ExtendedFormat records
661 */
662
663 public int getNumExFormats() {
664 if (log.check( POILogger.DEBUG ))
665 log.log(DEBUG, "getXF=", new Integer(numxfs));
666 return numxfs;
667 }
668
669 /**
670 * gets the ExtendedFormatRecord at the given 0-based index
671 *
672 * @param index of the Extended format record (0-based)
673 * @return ExtendedFormatRecord at the given index
674 */
675
676 public ExtendedFormatRecord getExFormatAt(int index) {
677 int xfptr = records.getXfpos() - (numxfs - 1);
678
679 xfptr += index;
680 ExtendedFormatRecord retval =
681 ( ExtendedFormatRecord ) records.get(xfptr);
682
683 return retval;
684 }
685
686 /**
687 * creates a new Cell-type Extneded Format Record and adds it to the end of
688 * ExtendedFormatRecords collection
689 *
690 * @return ExtendedFormatRecord that was created
691 */
692
693 public ExtendedFormatRecord createCellXF() {
694 ExtendedFormatRecord xf = createExtendedFormat();
695
696 records.add(records.getXfpos()+1, xf);
697 records.setXfpos( records.getXfpos() + 1 );
698 numxfs++;
699 return xf;
700 }
701
702 /**
703 * Adds a string to the SST table and returns its index (if its a duplicate
704 * just returns its index and update the counts) ASSUMES compressed unicode
705 * (meaning 8bit)
706 *
707 * @param string the string to be added to the SSTRecord
708 *
709 * @return index of the string within the SSTRecord
710 */
711
712 public int addSSTString(UnicodeString string) {
713 if (log.check( POILogger.DEBUG ))
714 log.log(DEBUG, "insert to sst string='", string);
715 if (sst == null) {
716 insertSST();
717 }
718 return sst.addString(string);
719 }
720
721 /**
722 * given an index into the SST table, this function returns the corresponding String value
723 * @return String containing the SST String
724 */
725
726 public UnicodeString getSSTString(int str) {
727 if (sst == null) {
728 insertSST();
729 }
730 UnicodeString retval = sst.getString(str);
731
732 if (log.check( POILogger.DEBUG ))
733 log.log(DEBUG, "Returning SST for index=", new Integer(str),
734 " String= ", retval);
735 return retval;
736 }
737
738 /**
739 * use this function to add a Shared String Table to an existing sheet (say
740 * generated by a different java api) without an sst....
741 * @see #createSST()
742 * @see org.apache.poi.hssf.record.SSTRecord
743 */
744
745 public void insertSST() {
746 if (log.check( POILogger.DEBUG ))
747 log.log(DEBUG, "creating new SST via insertSST!");
748 sst = ( SSTRecord ) createSST();
749 records.add(records.size() - 1, createExtendedSST());
750 records.add(records.size() - 2, sst);
751 }
752
753 /**
754 * Serializes all records int the worksheet section into a big byte array. Use
755 * this to write the Workbook out.
756 *
757 * @return byte array containing the HSSF-only portions of the POIFS file.
758 */
759 // GJS: Not used so why keep it.
760 // public byte [] serialize() {
761 // log.log(DEBUG, "Serializing Workbook!");
762 // byte[] retval = null;
763 //
764 //// ArrayList bytes = new ArrayList(records.size());
765 // int arraysize = getSize();
766 // int pos = 0;
767 //
768 // retval = new byte[ arraysize ];
769 // for (int k = 0; k < records.size(); k++) {
770 //
771 // Record record = records.get(k);
772 //// Let's skip RECALCID records, as they are only use for optimization
773 // if(record.getSid() != RecalcIdRecord.sid || ((RecalcIdRecord)record).isNeeded()) {
774 // pos += record.serialize(pos, retval); // rec.length;
775 // }
776 // }
777 // log.log(DEBUG, "Exiting serialize workbook");
778 // return retval;
779 // }
780
781 /**
782 * Serializes all records int the worksheet section into a big byte array. Use
783 * this to write the Workbook out.
784 * @param offset of the data to be written
785 * @param data array of bytes to write this to
786 */
787
788 public int serialize( int offset, byte[] data )
789 {
790 if (log.check( POILogger.DEBUG ))
791 log.log( DEBUG, "Serializing Workbook with offsets" );
792
793 int pos = 0;
794
795 SSTRecord sst = null;
796 int sstPos = 0;
797 boolean wroteBoundSheets = false;
798 for ( int k = 0; k < records.size(); k++ )
799 {
800
801 Record record = records.get( k );
802 // Let's skip RECALCID records, as they are only use for optimization
803 if ( record.getSid() != RecalcIdRecord.sid || ( (RecalcIdRecord) record ).isNeeded() )
804 {
805 int len = 0;
806 if (record instanceof SSTRecord)
807 {
808 sst = (SSTRecord)record;
809 sstPos = pos;
810 }
811 if (record.getSid() == ExtSSTRecord.sid && sst != null)
812 {
813 record = sst.createExtSSTRecord(sstPos + offset);
814 }
815 if (record instanceof BoundSheetRecord) {
816 if(!wroteBoundSheets) {
817 for (int i = 0; i < boundsheets.size(); i++) {
818 len+= ((BoundSheetRecord)boundsheets.get(i))
819 .serialize(pos+offset+len, data);
820 }
821 wroteBoundSheets = true;
822 }
823 } else {
824 len = record.serialize( pos + offset, data );
825 }
826 ///// DEBUG BEGIN /////
827 // if (len != record.getRecordSize())
828 // throw new IllegalStateException("Record size does not match serialized bytes. Serialized size = " + len + " but getRecordSize() returns " + record.getRecordSize());
829 ///// DEBUG END /////
830 pos += len; // rec.length;
831 }
832 }
833 if (log.check( POILogger.DEBUG ))
834 log.log( DEBUG, "Exiting serialize workbook" );
835 return pos;
836 }
837
838 public int getSize()
839 {
840 int retval = 0;
841
842 SSTRecord sst = null;
843 for ( int k = 0; k < records.size(); k++ )
844 {
845 Record record = records.get( k );
846 // Let's skip RECALCID records, as they are only use for optimization
847 if ( record.getSid() != RecalcIdRecord.sid || ( (RecalcIdRecord) record ).isNeeded() )
848 {
849 if (record instanceof SSTRecord)
850 sst = (SSTRecord)record;
851 if (record.getSid() == ExtSSTRecord.sid && sst != null)
852 retval += sst.calcExtSSTRecordSize();
853 else
854 retval += record.getRecordSize();
855 }
856 }
857 return retval;
858 }
859
860 /**
861 * creates the BOF record
862 * @see org.apache.poi.hssf.record.BOFRecord
863 * @see org.apache.poi.hssf.record.Record
864 * @return record containing a BOFRecord
865 */
866
867 protected Record createBOF() {
868 BOFRecord retval = new BOFRecord();
869
870 retval.setVersion(( short ) 0x600);
871 retval.setType(( short ) 5);
872 retval.setBuild(( short ) 0x10d3);
873
874 // retval.setBuild((short)0x0dbb);
875 retval.setBuildYear(( short ) 1996);
876 retval.setHistoryBitMask(0x41); // was c1 before verify
877 retval.setRequiredVersion(0x6);
878 return retval;
879 }
880
881 /**
882 * creates the InterfaceHdr record
883 * @see org.apache.poi.hssf.record.InterfaceHdrRecord
884 * @see org.apache.poi.hssf.record.Record
885 * @return record containing a InterfaceHdrRecord
886 */
887
888 protected Record createInterfaceHdr() {
889 InterfaceHdrRecord retval = new InterfaceHdrRecord();
890
891 retval.setCodepage(CODEPAGE);
892 return retval;
893 }
894
895 /**
896 * creates an MMS record
897 * @see org.apache.poi.hssf.record.MMSRecord
898 * @see org.apache.poi.hssf.record.Record
899 * @return record containing a MMSRecord
900 */
901
902 protected Record createMMS() {
903 MMSRecord retval = new MMSRecord();
904
905 retval.setAddMenuCount(( byte ) 0);
906 retval.setDelMenuCount(( byte ) 0);
907 return retval;
908 }
909
910 /**
911 * creates the InterfaceEnd record
912 * @see org.apache.poi.hssf.record.InterfaceEndRecord
913 * @see org.apache.poi.hssf.record.Record
914 * @return record containing a InterfaceEndRecord
915 */
916
917 protected Record createInterfaceEnd() {
918 return new InterfaceEndRecord();
919 }
920
921 /**
922 * creates the WriteAccess record containing the logged in user's name
923 * @see org.apache.poi.hssf.record.WriteAccessRecord
924 * @see org.apache.poi.hssf.record.Record
925 * @return record containing a WriteAccessRecord
926 */
927
928 protected Record createWriteAccess() {
929 WriteAccessRecord retval = new WriteAccessRecord();
930
931 try
932 {
933 retval.setUsername(System.getProperty("user.name"));
934 }
935 catch (java.security.AccessControlException e)
936 {
937 // AccessControlException can occur in a restricted context
938 // (client applet/jws application or restricted security server)
939 retval.setUsername("POI");
940 }
941 return retval;
942 }
943
944 /**
945 * creates the Codepage record containing the constant stored in CODEPAGE
946 * @see org.apache.poi.hssf.record.CodepageRecord
947 * @see org.apache.poi.hssf.record.Record
948 * @return record containing a CodepageRecord
949 */
950
951 protected Record createCodepage() {
952 CodepageRecord retval = new CodepageRecord();
953
954 retval.setCodepage(CODEPAGE);
955 return retval;
956 }
957
958 /**
959 * creates the DSF record containing a 0 since HSSF can't even create Dual Stream Files
960 * @see org.apache.poi.hssf.record.DSFRecord
961 * @see org.apache.poi.hssf.record.Record
962 * @return record containing a DSFRecord
963 */
964
965 protected Record createDSF() {
966 DSFRecord retval = new DSFRecord();
967
968 retval.setDsf(
969 ( short ) 0); // we don't even support double stream files
970 return retval;
971 }
972
973 /**
974 * creates the TabId record containing an array of 0,1,2. This release of HSSF
975 * always has the default three sheets, no less, no more.
976 * @see org.apache.poi.hssf.record.TabIdRecord
977 * @see org.apache.poi.hssf.record.Record
978 * @return record containing a TabIdRecord
979 */
980
981 protected Record createTabId() {
982 TabIdRecord retval = new TabIdRecord();
983 short[] tabidarray = {
984 0
985 };
986
987 retval.setTabIdArray(tabidarray);
988 return retval;
989 }
990
991 /**
992 * creates the FnGroupCount record containing the Magic number constant of 14.
993 * @see org.apache.poi.hssf.record.FnGroupCountRecord
994 * @see org.apache.poi.hssf.record.Record
995 * @return record containing a FnGroupCountRecord
996 */
997
998 protected Record createFnGroupCount() {
999 FnGroupCountRecord retval = new FnGroupCountRecord();
1000
1001 retval.setCount(( short ) 14);
1002 return retval;
1003 }
1004
1005 /**
1006 * creates the WindowProtect record with protect set to false.
1007 * @see org.apache.poi.hssf.record.WindowProtectRecord
1008 * @see org.apache.poi.hssf.record.Record
1009 * @return record containing a WindowProtectRecord
1010 */
1011
1012 protected Record createWindowProtect() {
1013 WindowProtectRecord retval = new WindowProtectRecord();
1014
1015 retval.setProtect(
1016 false); // by default even when we support it we won't
1017 return retval; // want it to be protected
1018 }
1019
1020 /**
1021 * creates the Protect record with protect set to false.
1022 * @see org.apache.poi.hssf.record.ProtectRecord
1023 * @see org.apache.poi.hssf.record.Record
1024 * @return record containing a ProtectRecord
1025 */
1026
1027 protected Record createProtect() {
1028 ProtectRecord retval = new ProtectRecord();
1029
1030 retval.setProtect(
1031 false); // by default even when we support it we won't
1032 return retval; // want it to be protected
1033 }
1034
1035 /**
1036 * creates the Password record with password set to 0.
1037 * @see org.apache.poi.hssf.record.PasswordRecord
1038 * @see org.apache.poi.hssf.record.Record
1039 * @return record containing a PasswordRecord
1040 */
1041
1042 protected Record createPassword() {
1043 PasswordRecord retval = new PasswordRecord();
1044
1045 retval.setPassword(( short ) 0); // no password by default!
1046 return retval;
1047 }
1048
1049 /**
1050 * creates the ProtectionRev4 record with protect set to false.
1051 * @see org.apache.poi.hssf.record.ProtectionRev4Record
1052 * @see org.apache.poi.hssf.record.Record
1053 * @return record containing a ProtectionRev4Record
1054 */
1055
1056 protected Record createProtectionRev4() {
1057 ProtectionRev4Record retval = new ProtectionRev4Record();
1058
1059 retval.setProtect(false);
1060 return retval;
1061 }
1062
1063 /**
1064 * creates the PasswordRev4 record with password set to 0.
1065 * @see org.apache.poi.hssf.record.PasswordRev4Record
1066 * @see org.apache.poi.hssf.record.Record
1067 * @return record containing a PasswordRev4Record
1068 */
1069
1070 protected Record createPasswordRev4() {
1071 PasswordRev4Record retval = new PasswordRev4Record();
1072
1073 retval.setPassword(( short ) 0); // no password by default!
1074 return retval;
1075 }
1076
1077 /**
1078 * creates the WindowOne record with the following magic values: <P>
1079 * horizontal hold - 0x168 <P>
1080 * vertical hold - 0x10e <P>
1081 * width - 0x3a5c <P>
1082 * height - 0x23be <P>
1083 * options - 0x38 <P>
1084 * selected tab - 0 <P>
1085 * displayed tab - 0 <P>
1086 * num selected tab- 0 <P>
1087 * tab width ratio - 0x258 <P>
1088 * @see org.apache.poi.hssf.record.WindowOneRecord
1089 * @see org.apache.poi.hssf.record.Record
1090 * @return record containing a WindowOneRecord
1091 */
1092
1093 protected Record createWindowOne() {
1094 WindowOneRecord retval = new WindowOneRecord();
1095
1096 retval.setHorizontalHold(( short ) 0x168);
1097 retval.setVerticalHold(( short ) 0x10e);
1098 retval.setWidth(( short ) 0x3a5c);
1099 retval.setHeight(( short ) 0x23be);
1100 retval.setOptions(( short ) 0x38);
1101 retval.setSelectedTab(( short ) 0x0);
1102 retval.setDisplayedTab(( short ) 0x0);
1103 retval.setNumSelectedTabs(( short ) 1);
1104 retval.setTabWidthRatio(( short ) 0x258);
1105 return retval;
1106 }
1107
1108 /**
1109 * creates the Backup record with backup set to 0. (loose the data, who cares)
1110 * @see org.apache.poi.hssf.record.BackupRecord
1111 * @see org.apache.poi.hssf.record.Record
1112 * @return record containing a BackupRecord
1113 */
1114
1115 protected Record createBackup() {
1116 BackupRecord retval = new BackupRecord();
1117
1118 retval.setBackup(
1119 ( short ) 0); // by default DONT save backups of files...just loose data
1120 return retval;
1121 }
1122
1123 /**
1124 * creates the HideObj record with hide object set to 0. (don't hide)
1125 * @see org.apache.poi.hssf.record.HideObjRecord
1126 * @see org.apache.poi.hssf.record.Record
1127 * @return record containing a HideObjRecord
1128 */
1129
1130 protected Record createHideObj() {
1131 HideObjRecord retval = new HideObjRecord();
1132
1133 retval.setHideObj(( short ) 0); // by default set hide object off
1134 return retval;
1135 }
1136
1137 /**
1138 * creates the DateWindow1904 record with windowing set to 0. (don't window)
1139 * @see org.apache.poi.hssf.record.DateWindow1904Record
1140 * @see org.apache.poi.hssf.record.Record
1141 * @return record containing a DateWindow1904Record
1142 */
1143
1144 protected Record createDateWindow1904() {
1145 DateWindow1904Record retval = new DateWindow1904Record();
1146
1147 retval.setWindowing(
1148 ( short ) 0); // don't EVER use 1904 date windowing...tick tock..
1149 return retval;
1150 }
1151
1152 /**
1153 * creates the Precision record with precision set to true. (full precision)
1154 * @see org.apache.poi.hssf.record.PrecisionRecord
1155 * @see org.apache.poi.hssf.record.Record
1156 * @return record containing a PrecisionRecord
1157 */
1158
1159 protected Record createPrecision() {
1160 PrecisionRecord retval = new PrecisionRecord();
1161
1162 retval.setFullPrecision(
1163 true); // always use real numbers in calculations!
1164 return retval;
1165 }
1166
1167 /**
1168 * creates the RefreshAll record with refreshAll set to true. (refresh all calcs)
1169 * @see org.apache.poi.hssf.record.RefreshAllRecord
1170 * @see org.apache.poi.hssf.record.Record
1171 * @return record containing a RefreshAllRecord
1172 */
1173
1174 protected Record createRefreshAll() {
1175 RefreshAllRecord retval = new RefreshAllRecord();
1176
1177 retval.setRefreshAll(false);
1178 return retval;
1179 }
1180
1181 /**
1182 * creates the BookBool record with saveLinkValues set to 0. (don't save link values)
1183 * @see org.apache.poi.hssf.record.BookBoolRecord
1184 * @see org.apache.poi.hssf.record.Record
1185 * @return record containing a BookBoolRecord
1186 */
1187
1188 protected Record createBookBool() {
1189 BookBoolRecord retval = new BookBoolRecord();
1190
1191 retval.setSaveLinkValues(( short ) 0);
1192 return retval;
1193 }
1194
1195 /**
1196 * creates a Font record with the following magic values: <P>
1197 * fontheight = 0xc8<P>
1198 * attributes = 0x0<P>
1199 * color palette index = 0x7fff<P>
1200 * bold weight = 0x190<P>
1201 * Font Name Length = 5 <P>
1202 * Font Name = Arial <P>
1203 *
1204 * @see org.apache.poi.hssf.record.FontRecord
1205 * @see org.apache.poi.hssf.record.Record
1206 * @return record containing a FontRecord
1207 */
1208
1209 protected Record createFont() {
1210 FontRecord retval = new FontRecord();
1211
1212 retval.setFontHeight(( short ) 0xc8);
1213 retval.setAttributes(( short ) 0x0);
1214 retval.setColorPaletteIndex(( short ) 0x7fff);
1215 retval.setBoldWeight(( short ) 0x190);
1216 retval.setFontNameLength(( byte ) 5);
1217 retval.setFontName("Arial");
1218 return retval;
1219 }
1220
1221 /**
1222 * Creates a FormatRecord object
1223 * @param id the number of the format record to create (meaning its position in
1224 * a file as M$ Excel would create it.)
1225 * @return record containing a FormatRecord
1226 * @see org.apache.poi.hssf.record.FormatRecord
1227 * @see org.apache.poi.hssf.record.Record
1228 */
1229
1230 protected Record createFormat(int id) { // we'll need multiple editions for
1231 FormatRecord retval = new FormatRecord(); // the differnt formats
1232
1233 switch (id) {
1234
1235 case 0 :
1236 retval.setIndexCode(( short ) 5);
1237 retval.setFormatStringLength(( byte ) 0x17);
1238 retval.setFormatString("\"$\"#,##0_);\\(\"$\"#,##0\\)");
1239 break;
1240
1241 case 1 :
1242 retval.setIndexCode(( short ) 6);
1243 retval.setFormatStringLength(( byte ) 0x1c);
1244 retval.setFormatString("\"$\"#,##0_);[Red]\\(\"$\"#,##0\\)");
1245 break;
1246
1247 case 2 :
1248 retval.setIndexCode(( short ) 7);
1249 retval.setFormatStringLength(( byte ) 0x1d);
1250 retval.setFormatString("\"$\"#,##0.00_);\\(\"$\"#,##0.00\\)");
1251 break;
1252
1253 case 3 :
1254 retval.setIndexCode(( short ) 8);
1255 retval.setFormatStringLength(( byte ) 0x22);
1256 retval.setFormatString(
1257 "\"$\"#,##0.00_);[Red]\\(\"$\"#,##0.00\\)");
1258 break;
1259
1260 case 4 :
1261 retval.setIndexCode(( short ) 0x2a);
1262 retval.setFormatStringLength(( byte ) 0x32);
1263 retval.setFormatString(
1264 "_(\"$\"* #,##0_);_(\"$\"* \\(#,##0\\);_(\"$\"* \"-\"_);_(@_)");
1265 break;
1266
1267 case 5 :
1268 retval.setIndexCode(( short ) 0x29);
1269 retval.setFormatStringLength(( byte ) 0x29);
1270 retval.setFormatString(
1271 "_(* #,##0_);_(* \\(#,##0\\);_(* \"-\"_);_(@_)");
1272 break;
1273
1274 case 6 :
1275 retval.setIndexCode(( short ) 0x2c);
1276 retval.setFormatStringLength(( byte ) 0x3a);
1277 retval.setFormatString(
1278 "_(\"$\"* #,##0.00_);_(\"$\"* \\(#,##0.00\\);_(\"$\"* \"-\"??_);_(@_)");
1279 break;
1280
1281 case 7 :
1282 retval.setIndexCode(( short ) 0x2b);
1283 retval.setFormatStringLength(( byte ) 0x31);
1284 retval.setFormatString(
1285 "_(* #,##0.00_);_(* \\(#,##0.00\\);_(* \"-\"??_);_(@_)");
1286 break;
1287 }
1288 return retval;
1289 }
1290
1291 /**
1292 * Creates an ExtendedFormatRecord object
1293 * @param id the number of the extended format record to create (meaning its position in
1294 * a file as MS Excel would create it.)
1295 *
1296 * @return record containing an ExtendedFormatRecord
1297 * @see org.apache.poi.hssf.record.ExtendedFormatRecord
1298 * @see org.apache.poi.hssf.record.Record
1299 */
1300
1301 protected Record createExtendedFormat(int id) { // we'll need multiple editions
1302 ExtendedFormatRecord retval = new ExtendedFormatRecord();
1303
1304 switch (id) {
1305
1306 case 0 :
1307 retval.setFontIndex(( short ) 0);
1308 retval.setFormatIndex(( short ) 0);
1309 retval.setCellOptions(( short ) 0xfffffff5);
1310 retval.setAlignmentOptions(( short ) 0x20);
1311 retval.setIndentionOptions(( short ) 0);
1312 retval.setBorderOptions(( short ) 0);
1313 retval.setPaletteOptions(( short ) 0);
1314 retval.setAdtlPaletteOptions(( short ) 0);
1315 retval.setFillPaletteOptions(( short ) 0x20c0);
1316 break;
1317
1318 case 1 :
1319 retval.setFontIndex(( short ) 1);
1320 retval.setFormatIndex(( short ) 0);
1321 retval.setCellOptions(( short ) 0xfffffff5);
1322 retval.setAlignmentOptions(( short ) 0x20);
1323 retval.setIndentionOptions(( short ) 0xfffff400);
1324 retval.setBorderOptions(( short ) 0);
1325 retval.setPaletteOptions(( short ) 0);
1326 retval.setAdtlPaletteOptions(( short ) 0);
1327 retval.setFillPaletteOptions(( short ) 0x20c0);
1328 break;
1329
1330 case 2 :
1331 retval.setFontIndex(( short ) 1);
1332 retval.setFormatIndex(( short ) 0);
1333 retval.setCellOptions(( short ) 0xfffffff5);
1334 retval.setAlignmentOptions(( short ) 0x20);
1335 retval.setIndentionOptions(( short ) 0xfffff400);
1336 retval.setBorderOptions(( short ) 0);
1337 retval.setPaletteOptions(( short ) 0);
1338 retval.setAdtlPaletteOptions(( short ) 0);
1339 retval.setFillPaletteOptions(( short ) 0x20c0);
1340 break;
1341
1342 case 3 :
1343 retval.setFontIndex(( short ) 2);
1344 retval.setFormatIndex(( short ) 0);
1345 retval.setCellOptions(( short ) 0xfffffff5);
1346 retval.setAlignmentOptions(( short ) 0x20);
1347 retval.setIndentionOptions(( short ) 0xfffff400);
1348 retval.setBorderOptions(( short ) 0);
1349 retval.setPaletteOptions(( short ) 0);
1350 retval.setAdtlPaletteOptions(( short ) 0);
1351 retval.setFillPaletteOptions(( short ) 0x20c0);
1352 break;
1353
1354 case 4 :
1355 retval.setFontIndex(( short ) 2);
1356 retval.setFormatIndex(( short ) 0);
1357 retval.setCellOptions(( short ) 0xfffffff5);
1358 retval.setAlignmentOptions(( short ) 0x20);
1359 retval.setIndentionOptions(( short ) 0xfffff400);
1360 retval.setBorderOptions(( short ) 0);
1361 retval.setPaletteOptions(( short ) 0);
1362 retval.setAdtlPaletteOptions(( short ) 0);
1363 retval.setFillPaletteOptions(( short ) 0x20c0);
1364 break;
1365
1366 case 5 :
1367 retval.setFontIndex(( short ) 0);
1368 retval.setFormatIndex(( short ) 0);
1369 retval.setCellOptions(( short ) 0xfffffff5);
1370 retval.setAlignmentOptions(( short ) 0x20);
1371 retval.setIndentionOptions(( short ) 0xfffff400);
1372 retval.setBorderOptions(( short ) 0);
1373 retval.setPaletteOptions(( short ) 0);
1374 retval.setAdtlPaletteOptions(( short ) 0);
1375 retval.setFillPaletteOptions(( short ) 0x20c0);
1376 break;
1377
1378 case 6 :
1379 retval.setFontIndex(( short ) 0);
1380 retval.setFormatIndex(( short ) 0);
1381 retval.setCellOptions(( short ) 0xfffffff5);
1382 retval.setAlignmentOptions(( short ) 0x20);
1383 retval.setIndentionOptions(( short ) 0xfffff400);
1384 retval.setBorderOptions(( short ) 0);
1385 retval.setPaletteOptions(( short ) 0);
1386 retval.setAdtlPaletteOptions(( short ) 0);
1387 retval.setFillPaletteOptions(( short ) 0x20c0);
1388 break;
1389
1390 case 7 :
1391 retval.setFontIndex(( short ) 0);
1392 retval.setFormatIndex(( short ) 0);
1393 retval.setCellOptions(( short ) 0xfffffff5);
1394 retval.setAlignmentOptions(( short ) 0x20);
1395 retval.setIndentionOptions(( short ) 0xfffff400);
1396 retval.setBorderOptions(( short ) 0);
1397 retval.setPaletteOptions(( short ) 0);
1398 retval.setAdtlPaletteOptions(( short ) 0);
1399 retval.setFillPaletteOptions(( short ) 0x20c0);
1400 break;
1401
1402 case 8 :
1403 retval.setFontIndex(( short ) 0);
1404 retval.setFormatIndex(( short ) 0);
1405 retval.setCellOptions(( short ) 0xfffffff5);
1406 retval.setAlignmentOptions(( short ) 0x20);
1407 retval.setIndentionOptions(( short ) 0xfffff400);
1408 retval.setBorderOptions(( short ) 0);
1409 retval.setPaletteOptions(( short ) 0);
1410 retval.setAdtlPaletteOptions(( short ) 0);
1411 retval.setFillPaletteOptions(( short ) 0x20c0);
1412 break;
1413
1414 case 9 :
1415 retval.setFontIndex(( short ) 0);
1416 retval.setFormatIndex(( short ) 0);
1417 retval.setCellOptions(( short ) 0xfffffff5);
1418 retval.setAlignmentOptions(( short ) 0x20);
1419 retval.setIndentionOptions(( short ) 0xfffff400);
1420 retval.setBorderOptions(( short ) 0);
1421 retval.setPaletteOptions(( short ) 0);
1422 retval.setAdtlPaletteOptions(( short ) 0);
1423 retval.setFillPaletteOptions(( short ) 0x20c0);
1424 break;
1425
1426 case 10 :
1427 retval.setFontIndex(( short ) 0);
1428 retval.setFormatIndex(( short ) 0);
1429 retval.setCellOptions(( short ) 0xfffffff5);
1430 retval.setAlignmentOptions(( short ) 0x20);
1431 retval.setIndentionOptions(( short ) 0xfffff400);
1432 retval.setBorderOptions(( short ) 0);
1433 retval.setPaletteOptions(( short ) 0);
1434 retval.setAdtlPaletteOptions(( short ) 0);
1435 retval.setFillPaletteOptions(( short ) 0x20c0);
1436 break;
1437
1438 case 11 :
1439 retval.setFontIndex(( short ) 0);
1440 retval.setFormatIndex(( short ) 0);
1441 retval.setCellOptions(( short ) 0xfffffff5);
1442 retval.setAlignmentOptions(( short ) 0x20);
1443 retval.setIndentionOptions(( short ) 0xfffff400);
1444 retval.setBorderOptions(( short ) 0);
1445 retval.setPaletteOptions(( short ) 0);
1446 retval.setAdtlPaletteOptions(( short ) 0);
1447 retval.setFillPaletteOptions(( short ) 0x20c0);
1448 break;
1449
1450 case 12 :
1451 retval.setFontIndex(( short ) 0);
1452 retval.setFormatIndex(( short ) 0);
1453 retval.setCellOptions(( short ) 0xfffffff5);
1454 retval.setAlignmentOptions(( short ) 0x20);
1455 retval.setIndentionOptions(( short ) 0xfffff400);
1456 retval.setBorderOptions(( short ) 0);
1457 retval.setPaletteOptions(( short ) 0);
1458 retval.setAdtlPaletteOptions(( short ) 0);
1459 retval.setFillPaletteOptions(( short ) 0x20c0);
1460 break;
1461
1462 case 13 :
1463 retval.setFontIndex(( short ) 0);
1464 retval.setFormatIndex(( short ) 0);
1465 retval.setCellOptions(( short ) 0xfffffff5);
1466 retval.setAlignmentOptions(( short ) 0x20);
1467 retval.setIndentionOptions(( short ) 0xfffff400);
1468 retval.setBorderOptions(( short ) 0);
1469 retval.setPaletteOptions(( short ) 0);
1470 retval.setAdtlPaletteOptions(( short ) 0);
1471 retval.setFillPaletteOptions(( short ) 0x20c0);
1472 break;
1473
1474 case 14 :
1475 retval.setFontIndex(( short ) 0);
1476 retval.setFormatIndex(( short ) 0);
1477 retval.setCellOptions(( short ) 0xfffffff5);
1478 retval.setAlignmentOptions(( short ) 0x20);
1479 retval.setIndentionOptions(( short ) 0xfffff400);
1480 retval.setBorderOptions(( short ) 0);
1481 retval.setPaletteOptions(( short ) 0);
1482 retval.setAdtlPaletteOptions(( short ) 0);
1483 retval.setFillPaletteOptions(( short ) 0x20c0);
1484 break;
1485
1486 // cell records
1487 case 15 :
1488 retval.setFontIndex(( short ) 0);
1489 retval.setFormatIndex(( short ) 0);
1490 retval.setCellOptions(( short ) 0x1);
1491 retval.setAlignmentOptions(( short ) 0x20);
1492 retval.setIndentionOptions(( short ) 0x0);
1493 retval.setBorderOptions(( short ) 0);
1494 retval.setPaletteOptions(( short ) 0);
1495 retval.setAdtlPaletteOptions(( short ) 0);
1496 retval.setFillPaletteOptions(( short ) 0x20c0);
1497 break;
1498
1499 // style
1500 case 16 :
1501 retval.setFontIndex(( short ) 1);
1502 retval.setFormatIndex(( short ) 0x2b);
1503 retval.setCellOptions(( short ) 0xfffffff5);
1504 retval.setAlignmentOptions(( short ) 0x20);
1505 retval.setIndentionOptions(( short ) 0xfffff800);
1506 retval.setBorderOptions(( short ) 0);
1507 retval.setPaletteOptions(( short ) 0);
1508 retval.setAdtlPaletteOptions(( short ) 0);
1509 retval.setFillPaletteOptions(( short ) 0x20c0);
1510 break;
1511
1512 case 17 :
1513 retval.setFontIndex(( short ) 1);
1514 retval.setFormatIndex(( short ) 0x29);
1515 retval.setCellOptions(( short ) 0xfffffff5);
1516 retval.setAlignmentOptions(( short ) 0x20);
1517 retval.setIndentionOptions(( short ) 0xfffff800);
1518 retval.setBorderOptions(( short ) 0);
1519 retval.setPaletteOptions(( short ) 0);
1520 retval.setAdtlPaletteOptions(( short ) 0);
1521 retval.setFillPaletteOptions(( short ) 0x20c0);
1522 break;
1523
1524 case 18 :
1525 retval.setFontIndex(( short ) 1);
1526 retval.setFormatIndex(( short ) 0x2c);
1527 retval.setCellOptions(( short ) 0xfffffff5);
1528 retval.setAlignmentOptions(( short ) 0x20);
1529 retval.setIndentionOptions(( short ) 0xfffff800);
1530 retval.setBorderOptions(( short ) 0);
1531 retval.setPaletteOptions(( short ) 0);
1532 retval.setAdtlPaletteOptions(( short ) 0);
1533 retval.setFillPaletteOptions(( short ) 0x20c0);
1534 break;
1535
1536 case 19 :
1537 retval.setFontIndex(( short ) 1);
1538 retval.setFormatIndex(( short ) 0x2a);
1539 retval.setCellOptions(( short ) 0xfffffff5);
1540 retval.setAlignmentOptions(( short ) 0x20);
1541 retval.setIndentionOptions(( short ) 0xfffff800);
1542 retval.setBorderOptions(( short ) 0);
1543 retval.setPaletteOptions(( short ) 0);
1544 retval.setAdtlPaletteOptions(( short ) 0);
1545 retval.setFillPaletteOptions(( short ) 0x20c0);
1546 break;
1547
1548 case 20 :
1549 retval.setFontIndex(( short ) 1);
1550 retval.setFormatIndex(( short ) 0x9);
1551 retval.setCellOptions(( short ) 0xfffffff5);
1552 retval.setAlignmentOptions(( short ) 0x20);
1553 retval.setIndentionOptions(( short ) 0xfffff800);
1554 retval.setBorderOptions(( short ) 0);
1555 retval.setPaletteOptions(( short ) 0);
1556 retval.setAdtlPaletteOptions(( short ) 0);
1557 retval.setFillPaletteOptions(( short ) 0x20c0);
1558 break;
1559
1560 // unused from this point down
1561 case 21 :
1562 retval.setFontIndex(( short ) 5);
1563 retval.setFormatIndex(( short ) 0x0);
1564 retval.setCellOptions(( short ) 0x1);
1565 retval.setAlignmentOptions(( short ) 0x20);
1566 retval.setIndentionOptions(( short ) 0x800);
1567 retval.setBorderOptions(( short ) 0);
1568 retval.setPaletteOptions(( short ) 0);
1569 retval.setAdtlPaletteOptions(( short ) 0);
1570 retval.setFillPaletteOptions(( short ) 0x20c0);
1571 break;
1572
1573 case 22 :
1574 retval.setFontIndex(( short ) 6);
1575 retval.setFormatIndex(( short ) 0x0);
1576 retval.setCellOptions(( short ) 0x1);
1577 retval.setAlignmentOptions(( short ) 0x20);
1578 retval.setIndentionOptions(( short ) 0x5c00);
1579 retval.setBorderOptions(( short ) 0);
1580 retval.setPaletteOptions(( short ) 0);
1581 retval.setAdtlPaletteOptions(( short ) 0);
1582 retval.setFillPaletteOptions(( short ) 0x20c0);
1583 break;
1584
1585 case 23 :
1586 retval.setFontIndex(( short ) 0);
1587 retval.setFormatIndex(( short ) 0x31);
1588 retval.setCellOptions(( short ) 0x1);
1589 retval.setAlignmentOptions(( short ) 0x20);
1590 retval.setIndentionOptions(( short ) 0x5c00);
1591 retval.setBorderOptions(( short ) 0);
1592 retval.setPaletteOptions(( short ) 0);
1593 retval.setAdtlPaletteOptions(( short ) 0);
1594 retval.setFillPaletteOptions(( short ) 0x20c0);
1595 break;
1596
1597 case 24 :
1598 retval.setFontIndex(( short ) 0);
1599 retval.setFormatIndex(( short ) 0x8);
1600 retval.setCellOptions(( short ) 0x1);
1601 retval.setAlignmentOptions(( short ) 0x20);
1602 retval.setIndentionOptions(( short ) 0x5c00);
1603 retval.setBorderOptions(( short ) 0);
1604 retval.setPaletteOptions(( short ) 0);
1605 retval.setAdtlPaletteOptions(( short ) 0);
1606 retval.setFillPaletteOptions(( short ) 0x20c0);
1607 break;
1608
1609 case 25 :
1610 retval.setFontIndex(( short ) 6);
1611 retval.setFormatIndex(( short ) 0x8);
1612 retval.setCellOptions(( short ) 0x1);
1613 retval.setAlignmentOptions(( short ) 0x20);
1614 retval.setIndentionOptions(( short ) 0x5c00);
1615 retval.setBorderOptions(( short ) 0);
1616 retval.setPaletteOptions(( short ) 0);
1617 retval.setAdtlPaletteOptions(( short ) 0);
1618 retval.setFillPaletteOptions(( short ) 0x20c0);
1619 break;
1620 }
1621 return retval;
1622 }
1623
1624 /**
1625 * creates an default cell type ExtendedFormatRecord object.
1626 * @return ExtendedFormatRecord with intial defaults (cell-type)
1627 */
1628
1629 protected ExtendedFormatRecord createExtendedFormat() {
1630 ExtendedFormatRecord retval = new ExtendedFormatRecord();
1631
1632 retval.setFontIndex(( short ) 0);
1633 retval.setFormatIndex(( short ) 0x0);
1634 retval.setCellOptions(( short ) 0x1);
1635 retval.setAlignmentOptions(( short ) 0x20);
1636 retval.setIndentionOptions(( short ) 0);
1637 retval.setBorderOptions(( short ) 0);
1638 retval.setPaletteOptions(( short ) 0);
1639 retval.setAdtlPaletteOptions(( short ) 0);
1640 retval.setFillPaletteOptions(( short ) 0x20c0);
1641 retval.setTopBorderPaletteIdx(HSSFColor.BLACK.index);
1642 retval.setBottomBorderPaletteIdx(HSSFColor.BLACK.index);
1643 retval.setLeftBorderPaletteIdx(HSSFColor.BLACK.index);
1644 retval.setRightBorderPaletteIdx(HSSFColor.BLACK.index);
1645 return retval;
1646 }
1647
1648 /**
1649 * Creates a StyleRecord object
1650 * @param id the number of the style record to create (meaning its position in
1651 * a file as MS Excel would create it.
1652 * @return record containing a StyleRecord
1653 * @see org.apache.poi.hssf.record.StyleRecord
1654 * @see org.apache.poi.hssf.record.Record
1655 */
1656
1657 protected Record createStyle(int id) { // we'll need multiple editions
1658 StyleRecord retval = new StyleRecord();
1659
1660 switch (id) {
1661
1662 case 0 :
1663 retval.setIndex(( short ) 0xffff8010);
1664 retval.setBuiltin(( byte ) 3);
1665 retval.setOutlineStyleLevel(( byte ) 0xffffffff);
1666 break;
1667
1668 case 1 :
1669 retval.setIndex(( short ) 0xffff8011);
1670 retval.setBuiltin(( byte ) 6);
1671 retval.setOutlineStyleLevel(( byte ) 0xffffffff);
1672 break;
1673
1674 case 2 :
1675 retval.setIndex(( short ) 0xffff8012);
1676 retval.setBuiltin(( byte ) 4);
1677 retval.setOutlineStyleLevel(( byte ) 0xffffffff);
1678 break;
1679
1680 case 3 :
1681 retval.setIndex(( short ) 0xffff8013);
1682 retval.setBuiltin(( byte ) 7);
1683 retval.setOutlineStyleLevel(( byte ) 0xffffffff);
1684 break;
1685
1686 case 4 :
1687 retval.setIndex(( short ) 0xffff8000);
1688 retval.setBuiltin(( byte ) 0);
1689 retval.setOutlineStyleLevel(( byte ) 0xffffffff);
1690 break;
1691
1692 case 5 :
1693 retval.setIndex(( short ) 0xffff8014);
1694 retval.setBuiltin(( byte ) 5);
1695 retval.setOutlineStyleLevel(( byte ) 0xffffffff);
1696 break;
1697 }
1698 return retval;
1699 }
1700
1701 /**
1702 * Creates a palette record initialized to the default palette
1703 * @return a PaletteRecord instance populated with the default colors
1704 * @see org.apache.poi.hssf.record.PaletteRecord
1705 */
1706 protected PaletteRecord createPalette()
1707 {
1708 return new PaletteRecord();
1709 }
1710
1711 /**
1712 * Creates the UseSelFS object with the use natural language flag set to 0 (false)
1713 * @return record containing a UseSelFSRecord
1714 * @see org.apache.poi.hssf.record.UseSelFSRecord
1715 * @see org.apache.poi.hssf.record.Record
1716 */
1717
1718 protected Record createUseSelFS() {
1719 UseSelFSRecord retval = new UseSelFSRecord();
1720
1721 retval.setFlag(( short ) 0);
1722 return retval;
1723 }
1724
1725 /**
1726 * create a "bound sheet" or "bundlesheet" (depending who you ask) record
1727 * Always sets the sheet's bof to 0. You'll need to set that yourself.
1728 * @param id either sheet 0,1 or 2.
1729 * @return record containing a BoundSheetRecord
1730 * @see org.apache.poi.hssf.record.BoundSheetRecord
1731 * @see org.apache.poi.hssf.record.Record
1732 */
1733
1734 protected Record createBoundSheet(int id) { // 1,2,3 sheets
1735 BoundSheetRecord retval = new BoundSheetRecord();
1736
1737 switch (id) {
1738
1739 case 0 :
1740 retval.setPositionOfBof(0x0); // should be set later
1741 retval.setOptionFlags(( short ) 0);
1742 retval.setSheetnameLength(( byte ) 0x6);
1743 retval.setCompressedUnicodeFlag(( byte ) 0);
1744 retval.setSheetname("Sheet1");
1745 break;
1746
1747 case 1 :
1748 retval.setPositionOfBof(0x0); // should be set later
1749 retval.setOptionFlags(( short ) 0);
1750 retval.setSheetnameLength(( byte ) 0x6);
1751 retval.setCompressedUnicodeFlag(( byte ) 0);
1752 retval.setSheetname("Sheet2");
1753 break;
1754
1755 case 2 :
1756 retval.setPositionOfBof(0x0); // should be set later
1757 retval.setOptionFlags(( short ) 0);
1758 retval.setSheetnameLength(( byte ) 0x6);
1759 retval.setCompressedUnicodeFlag(( byte ) 0);
1760 retval.setSheetname("Sheet3");
1761 break;
1762 }
1763 return retval;
1764 }
1765
1766 /**
1767 * Creates the Country record with the default country set to 1
1768 * and current country set to 7 in case of russian locale ("ru_RU") and 1 otherwise
1769 * @return record containing a CountryRecord
1770 * @see org.apache.poi.hssf.record.CountryRecord
1771 * @see org.apache.poi.hssf.record.Record
1772 */
1773
1774 protected Record createCountry() { // what a novel idea, create your own!
1775 CountryRecord retval = new CountryRecord();
1776
1777 retval.setDefaultCountry(( short ) 1);
1778
1779 // from Russia with love ;)
1780 if ( Locale.getDefault().toString().equals( "ru_RU" ) ) {
1781 retval.setCurrentCountry(( short ) 7);
1782 }
1783 else {
1784 retval.setCurrentCountry(( short ) 1);
1785 }
1786
1787 return retval;
1788 }
1789
1790 /**
1791 * Creates the SST record with no strings and the unique/num string set to 0
1792 * @return record containing a SSTRecord
1793 * @see org.apache.poi.hssf.record.SSTRecord
1794 * @see org.apache.poi.hssf.record.Record
1795 */
1796
1797 protected Record createSST() {
1798 return new SSTRecord();
1799 }
1800
1801 /**
1802 * Creates the ExtendedSST record with numstrings per bucket set to 0x8. HSSF
1803 * doesn't yet know what to do with this thing, but we create it with nothing in
1804 * it hardly just to make Excel happy and our sheets look like Excel's
1805 *
1806 * @return record containing an ExtSSTRecord
1807 * @see org.apache.poi.hssf.record.ExtSSTRecord
1808 * @see org.apache.poi.hssf.record.Record
1809 */
1810
1811 protected Record createExtendedSST() {
1812 ExtSSTRecord retval = new ExtSSTRecord();
1813
1814 retval.setNumStringsPerBucket(( short ) 0x8);
1815 return retval;
1816 }
1817
1818 /**
1819 * creates the EOF record
1820 * @see org.apache.poi.hssf.record.EOFRecord
1821 * @see org.apache.poi.hssf.record.Record
1822 * @return record containing a EOFRecord
1823 */
1824
1825 protected Record createEOF() {
1826 return new EOFRecord();
1827 }
1828
1829 /**
1830 * lazy initialization
1831 * Note - creating the link table causes creation of 1 EXTERNALBOOK and 1 EXTERNALSHEET record
1832 */
1833 private LinkTable getOrCreateLinkTable() {
1834 if(linkTable == null) {
1835 linkTable = new LinkTable((short) getNumSheets(), records);
1836 }
1837 return linkTable;
1838 }
1839
1840 public SheetReferences getSheetReferences() {
1841 SheetReferences refs = new SheetReferences();
1842
1843 if (linkTable != null) {
1844 int numRefStructures = linkTable.getNumberOfREFStructures();
1845 for (short k = 0; k < numRefStructures; k++) {
1846
1847 String sheetName = findSheetNameFromExternSheet(k);
1848 refs.addSheetReference(sheetName, k);
1849
1850 }
1851 }
1852 return refs;
1853 }
1854
1855 /** finds the sheet name by his extern sheet index
1856 * @param num extern sheet index
1857 * @return sheet name
1858 */
1859 public String findSheetNameFromExternSheet(short num){
1860 String result="";
1861
1862 short indexToSheet = linkTable.getIndexToSheet(num);
1863
1864 if (indexToSheet>-1) { //error check, bail out gracefully!
1865 result = getSheetName(indexToSheet);
1866 }
1867
1868 return result;
1869 }
1870
1871 /**
1872 * Finds the sheet index for a particular external sheet number.
1873 * @param externSheetNumber The external sheet number to convert
1874 * @return The index to the sheet found.
1875 */
1876 public int getSheetIndexFromExternSheetIndex(int externSheetNumber)
1877 {
1878 return linkTable.getSheetIndexFromExternSheetIndex(externSheetNumber);
1879 }
1880
1881 /** returns the extern sheet number for specific sheet number ,
1882 * if this sheet doesn't exist in extern sheet , add it
1883 * @param sheetNumber sheet number
1884 * @return index to extern sheet
1885 */
1886 public short checkExternSheet(int sheetNumber){
1887 return getOrCreateLinkTable().checkExternSheet(sheetNumber);
1888 }
1889
1890 /** gets the total number of names
1891 * @return number of names
1892 */
1893 public int getNumNames(){
1894 if(linkTable == null) {
1895 return 0;
1896 }
1897 return linkTable.getNumNames();
1898 }
1899
1900 /** gets the name record
1901 * @param index name index
1902 * @return name record
1903 */
1904 public NameRecord getNameRecord(int index){
1905 return linkTable.getNameRecord(index);
1906 }
1907
1908 /** creates new name
1909 * @return new name record
1910 */
1911 public NameRecord createName(){
1912 return addName(new NameRecord());
1913 }
1914
1915
1916 /** creates new name
1917 * @return new name record
1918 */
1919 public NameRecord addName(NameRecord name)
1920 {
1921
1922 getOrCreateLinkTable().addName(name);
1923
1924 return name;
1925 }
1926
1927 /**Generates a NameRecord to represent a built-in region
1928 * @return a new NameRecord unless the index is invalid
1929 */
1930 public NameRecord createBuiltInName(byte builtInName, int index)
1931 {
1932 if (index == -1 || index+1 > Short.MAX_VALUE)
1933 throw new IllegalArgumentException("Index is not valid ["+index+"]");
1934
1935 NameRecord name = new NameRecord(builtInName, (short)(index));
1936
1937 addName(name);
1938
1939 return name;
1940 }
1941
1942
1943 /** removes the name
1944 * @param namenum name index
1945 */
1946 public void removeName(int namenum){
1947
1948 if (linkTable.getNumNames() > namenum) {
1949 int idx = findFirstRecordLocBySid(NameRecord.sid);
1950 records.remove(idx + namenum);
1951 linkTable.removeName(namenum);
1952 }
1953
1954 }
1955
1956 /**
1957 * Returns a format index that matches the passed in format. It does not tie into HSSFDataFormat.
1958 * @param format the format string
1959 * @param createIfNotFound creates a new format if format not found
1960 * @return the format id of a format that matches or -1 if none found and createIfNotFound
1961 */
1962 public short getFormat(String format, boolean createIfNotFound) {
1963 Iterator iterator;
1964 for (iterator = formats.iterator(); iterator.hasNext();) {
1965 FormatRecord r = (FormatRecord)iterator.next();
1966 if (r.getFormatString().equals(format)) {
1967 return r.getIndexCode();
1968 }
1969 }
1970
1971 if (createIfNotFound) {
1972 return createFormat(format);
1973 }
1974
1975 return -1;
1976 }
1977
1978 /**
1979 * Returns the list of FormatRecords in the workbook.
1980 * @return ArrayList of FormatRecords in the notebook
1981 */
1982 public ArrayList getFormats() {
1983 return formats;
1984 }
1985
1986 /**
1987 * Creates a FormatRecord, inserts it, and returns the index code.
1988 * @param format the format string
1989 * @return the index code of the format record.
1990 * @see org.apache.poi.hssf.record.FormatRecord
1991 * @see org.apache.poi.hssf.record.Record
1992 */
1993 public short createFormat( String format )
1994 {
1995 // ++xfpos; //These are to ensure that positions are updated properly
1996 // ++palettepos;
1997 // ++bspos;
1998 FormatRecord rec = new FormatRecord();
1999 maxformatid = maxformatid >= (short) 0xa4 ? (short) ( maxformatid + 1 ) : (short) 0xa4; //Starting value from M$ empiracle study.
2000 rec.setIndexCode( maxformatid );
2001 rec.setFormatStringLength( (byte) format.length() );
2002 rec.setFormatString( format );
2003
2004 int pos = 0;
2005 while ( pos < records.size() && records.get( pos ).getSid() != FormatRecord.sid )
2006 pos++;
2007 pos += formats.size();
2008 formats.add( rec );
2009 records.add( pos, rec );
2010 return maxformatid;
2011 }
2012
2013
2014
2015 /**
2016 * Returns the first occurance of a record matching a particular sid.
2017 */
2018 public Record findFirstRecordBySid(short sid) {
2019 for (Iterator iterator = records.iterator(); iterator.hasNext(); ) {
2020 Record record = ( Record ) iterator.next();
2021
2022 if (record.getSid() == sid) {
2023 return record;
2024 }
2025 }
2026 return null;
2027 }
2028
2029 /**
2030 * Returns the index of a record matching a particular sid.
2031 * @param sid The sid of the record to match
2032 * @return The index of -1 if no match made.
2033 */
2034 public int findFirstRecordLocBySid(short sid) {
2035 int index = 0;
2036 for (Iterator iterator = records.iterator(); iterator.hasNext(); ) {
2037 Record record = ( Record ) iterator.next();
2038
2039 if (record.getSid() == sid) {
2040 return index;
2041 }
2042 index ++;
2043 }
2044 return -1;
2045 }
2046
2047 /**
2048 * Returns the next occurance of a record matching a particular sid.
2049 */
2050 public Record findNextRecordBySid(short sid, int pos) {
2051 int matches = 0;
2052 for (Iterator iterator = records.iterator(); iterator.hasNext(); ) {
2053 Record record = ( Record ) iterator.next();
2054
2055 if (record.getSid() == sid) {
2056 if (matches++ == pos)
2057 return record;
2058 }
2059 }
2060 return null;
2061 }
2062
2063 public List getHyperlinks()
2064 {
2065 return hyperlinks;
2066 }
2067
2068 public List getRecords()
2069 {
2070 return records.getRecords();
2071 }
2072
2073 // public void insertChartRecords( List chartRecords )
2074 // {
2075 // backuppos += chartRecords.size();
2076 // fontpos += chartRecords.size();
2077 // palettepos += chartRecords.size();
2078 // bspos += chartRecords.size();
2079 // xfpos += chartRecords.size();
2080 //
2081 // records.addAll(protpos, chartRecords);
2082 // }
2083
2084 /**
2085 * Whether date windowing is based on 1/2/1904 or 1/1/1900.
2086 * Some versions of Excel (Mac) can save workbooks using 1904 date windowing.
2087 *
2088 * @return true if using 1904 date windowing
2089 */
2090 public boolean isUsing1904DateWindowing() {
2091 return uses1904datewindowing;
2092 }
2093
2094 /**
2095 * Returns the custom palette in use for this workbook; if a custom palette record
2096 * does not exist, then it is created.
2097 */
2098 public PaletteRecord getCustomPalette()
2099 {
2100 PaletteRecord palette;
2101 int palettePos = records.getPalettepos();
2102 if (palettePos != -1) {
2103 Record rec = records.get(palettePos);
2104 if (rec instanceof PaletteRecord) {
2105 palette = (PaletteRecord) rec;
2106 } else throw new RuntimeException("InternalError: Expected PaletteRecord but got a '"+rec+"'");
2107 }
2108 else
2109 {
2110 palette = createPalette();
2111 //Add the palette record after the bof which is always the first record
2112 records.add(1, palette);
2113 records.setPalettepos(1);
2114 }
2115 return palette;
2116 }
2117
2118 /**
2119 * Finds the primary drawing group, if one already exists
2120 */
2121 public void findDrawingGroup() {
2122 // Need to find a DrawingGroupRecord that
2123 // contains a EscherDggRecord
2124 for(Iterator rit = records.iterator(); rit.hasNext();) {
2125 Record r = (Record)rit.next();
2126
2127 if(r instanceof DrawingGroupRecord) {
2128 DrawingGroupRecord dg = (DrawingGroupRecord)r;
2129 dg.processChildRecords();
2130
2131 EscherContainerRecord cr =
2132 dg.getEscherContainer();
2133 if(cr == null) {
2134 continue;
2135 }
2136
2137 EscherDggRecord dgg = null;
2138 for(Iterator it = cr.getChildRecords().iterator(); it.hasNext();) {
2139 Object er = it.next();
2140 if(er instanceof EscherDggRecord) {
2141 dgg = (EscherDggRecord)er;
2142 }
2143 }
2144
2145 if(dgg != null) {
2146 drawingManager = new DrawingManager2(dgg);
2147 return;
2148 }
2149 }
2150 }
2151
2152 // Look for the DrawingGroup record
2153 int dgLoc = findFirstRecordLocBySid(DrawingGroupRecord.sid);
2154
2155 // If there is one, does it have a EscherDggRecord?
2156 if(dgLoc != -1) {
2157 DrawingGroupRecord dg =
2158 (DrawingGroupRecord)records.get(dgLoc);
2159 EscherDggRecord dgg = null;
2160 for(Iterator it = dg.getEscherRecords().iterator(); it.hasNext();) {
2161 Object er = it.next();
2162 if(er instanceof EscherDggRecord) {
2163 dgg = (EscherDggRecord)er;
2164 }
2165 }
2166
2167 if(dgg != null) {
2168 drawingManager = new DrawingManager2(dgg);
2169 }
2170 }
2171 }
2172
2173 /**
2174 * Creates a primary drawing group record. If it already
2175 * exists then it's modified.
2176 */
2177 public void createDrawingGroup()
2178 {
2179 if (drawingManager == null)
2180 {
2181 EscherContainerRecord dggContainer = new EscherContainerRecord();
2182 EscherDggRecord dgg = new EscherDggRecord();
2183 EscherOptRecord opt = new EscherOptRecord();
2184 EscherSplitMenuColorsRecord splitMenuColors = new EscherSplitMenuColorsRecord();
2185
2186 dggContainer.setRecordId((short) 0xF000);
2187 dggContainer.setOptions((short) 0x000F);
2188 dgg.setRecordId(EscherDggRecord.RECORD_ID);
2189 dgg.setOptions((short)0x0000);
2190 dgg.setShapeIdMax(1024);
2191 dgg.setNumShapesSaved(0);
2192 dgg.setDrawingsSaved(0);
2193 dgg.setFileIdClusters(new EscherDggRecord.FileIdCluster[] {} );
2194 drawingManager = new DrawingManager2(dgg);
2195 EscherContainerRecord bstoreContainer = null;
2196 if (escherBSERecords.size() > 0)
2197 {
2198 bstoreContainer = new EscherContainerRecord();
2199 bstoreContainer.setRecordId( EscherContainerRecord.BSTORE_CONTAINER );
2200 bstoreContainer.setOptions( (short) ( (escherBSERecords.size() << 4) | 0xF ) );
2201 for ( Iterator iterator = escherBSERecords.iterator(); iterator.hasNext(); )
2202 {
2203 EscherRecord escherRecord = (EscherRecord) iterator.next();
2204 bstoreContainer.addChildRecord( escherRecord );
2205 }
2206 }
2207 opt.setRecordId((short) 0xF00B);
2208 opt.setOptions((short) 0x0033);
2209 opt.addEscherProperty( new EscherBoolProperty(EscherProperties.TEXT__SIZE_TEXT_TO_FIT_SHAPE, 524296) );
2210 opt.addEscherProperty( new EscherRGBProperty(EscherProperties.FILL__FILLCOLOR, 0x08000041) );
2211 opt.addEscherProperty( new EscherRGBProperty(EscherProperties.LINESTYLE__COLOR, 134217792) );
2212 splitMenuColors.setRecordId((short) 0xF11E);
2213 splitMenuColors.setOptions((short) 0x0040);
2214 splitMenuColors.setColor1(0x0800000D);
2215 splitMenuColors.setColor2(0x0800000C);
2216 splitMenuColors.setColor3(0x08000017);
2217 splitMenuColors.setColor4(0x100000F7);
2218
2219 dggContainer.addChildRecord(dgg);
2220 if (bstoreContainer != null)
2221 dggContainer.addChildRecord( bstoreContainer );
2222 dggContainer.addChildRecord(opt);
2223 dggContainer.addChildRecord(splitMenuColors);
2224
2225 int dgLoc = findFirstRecordLocBySid(DrawingGroupRecord.sid);
2226 if (dgLoc == -1)
2227 {
2228 DrawingGroupRecord drawingGroup = new DrawingGroupRecord();
2229 drawingGroup.addEscherRecord(dggContainer);
2230 int loc = findFirstRecordLocBySid(CountryRecord.sid);
2231
2232 getRecords().add(loc+1, drawingGroup);
2233 }
2234 else
2235 {
2236 DrawingGroupRecord drawingGroup = new DrawingGroupRecord();
2237 drawingGroup.addEscherRecord(dggContainer);
2238 getRecords().set(dgLoc, drawingGroup);
2239 }
2240
2241 }
2242 }
2243
2244 public WindowOneRecord getWindowOne() {
2245 return windowOne;
2246 }
2247
2248 public EscherBSERecord getBSERecord(int pictureIndex)
2249 {
2250 return (EscherBSERecord)escherBSERecords.get(pictureIndex-1);
2251 }
2252
2253 public int addBSERecord(EscherBSERecord e)
2254 {
2255 createDrawingGroup();
2256
2257 // maybe we don't need that as an instance variable anymore
2258 escherBSERecords.add( e );
2259
2260 int dgLoc = findFirstRecordLocBySid(DrawingGroupRecord.sid);
2261 DrawingGroupRecord drawingGroup = (DrawingGroupRecord) getRecords().get( dgLoc );
2262
2263 EscherContainerRecord dggContainer = (EscherContainerRecord) drawingGroup.getEscherRecord( 0 );
2264 EscherContainerRecord bstoreContainer;
2265 if (dggContainer.getChild( 1 ).getRecordId() == EscherContainerRecord.BSTORE_CONTAINER )
2266 {
2267 bstoreContainer = (EscherContainerRecord) dggContainer.getChild( 1 );
2268 }
2269 else
2270 {
2271 bstoreContainer = new EscherContainerRecord();
2272 bstoreContainer.setRecordId( EscherContainerRecord.BSTORE_CONTAINER );
2273 dggContainer.getChildRecords().add( 1, bstoreContainer );
2274 }
2275 bstoreContainer.setOptions( (short) ( (escherBSERecords.size() << 4) | 0xF ) );
2276
2277 bstoreContainer.addChildRecord( e );
2278
2279 return escherBSERecords.size();
2280 }
2281
2282 public DrawingManager2 getDrawingManager()
2283 {
2284 return drawingManager;
2285 }
2286
2287 public WriteProtectRecord getWriteProtect() {
2288 if (this.writeProtect == null) {
2289 this.writeProtect = new WriteProtectRecord();
2290 int i = 0;
2291 for (i = 0;
2292 i < records.size() && !(records.get(i) instanceof BOFRecord);
2293 i++) {
2294 }
2295 records.add(i+1,this.writeProtect);
2296 }
2297 return this.writeProtect;
2298 }
2299
2300 public WriteAccessRecord getWriteAccess() {
2301 if (this.writeAccess == null) {
2302 this.writeAccess = (WriteAccessRecord)createWriteAccess();
2303 int i = 0;
2304 for (i = 0;
2305 i < records.size() && !(records.get(i) instanceof InterfaceEndRecord);
2306 i++) {
2307 }
2308 records.add(i+1,this.writeAccess);
2309 }
2310 return this.writeAccess;
2311 }
2312
2313 public FileSharingRecord getFileSharing() {
2314 if (this.fileShare == null) {
2315 this.fileShare = new FileSharingRecord();
2316 int i = 0;
2317 for (i = 0;
2318 i < records.size() && !(records.get(i) instanceof WriteAccessRecord);
2319 i++) {
2320 }
2321 records.add(i+1,this.fileShare);
2322 }
2323 return this.fileShare;
2324 }
2325
2326 /**
2327 * is the workbook protected with a password (not encrypted)?
2328 */
2329 public boolean isWriteProtected() {
2330 if (this.fileShare == null) {
2331 return false;
2332 }
2333 FileSharingRecord frec = getFileSharing();
2334 return (frec.getReadOnly() == 1);
2335 }
2336
2337 /**
2338 * protect a workbook with a password (not encypted, just sets writeprotect
2339 * flags and the password.
2340 * @param password to set
2341 */
2342 public void writeProtectWorkbook( String password, String username ) {
2343 int protIdx = -1;
2344 FileSharingRecord frec = getFileSharing();
2345 WriteAccessRecord waccess = getWriteAccess();
2346 WriteProtectRecord wprotect = getWriteProtect();
2347 frec.setReadOnly((short)1);
2348 frec.setPassword(FileSharingRecord.hashPassword(password));
2349 frec.setUsername(username);
2350 waccess.setUsername(username);
2351 }
2352
2353 /**
2354 * removes the write protect flag
2355 */
2356 public void unwriteProtectWorkbook() {
2357 records.remove(fileShare);
2358 records.remove(writeProtect);
2359 fileShare = null;
2360 writeProtect = null;
2361 }
2362
2363 /**
2364 * @param refIndex Index to REF entry in EXTERNSHEET record in the Link Table
2365 * @param definedNameIndex zero-based to DEFINEDNAME or EXTERNALNAME record
2366 * @return the string representation of the defined or external name
2367 */
2368 public String resolveNameXText(int refIndex, int definedNameIndex) {
2369 return linkTable.resolveNameXText(refIndex, definedNameIndex);
2370 }
2371 }
2372
2373