1    
2    /* ====================================================================
3     * The Apache Software License, Version 1.1
4     *
5     * Copyright (c) 2002 The Apache Software Foundation.  All rights
6     * reserved.
7     *
8     * Redistribution and use in source and binary forms, with or without
9     * modification, are permitted provided that the following conditions
10    * are met:
11    *
12    * 1. Redistributions of source code must retain the above copyright
13    *    notice, this list of conditions and the following disclaimer.
14    *
15    * 2. Redistributions in binary form must reproduce the above copyright
16    *    notice, this list of conditions and the following disclaimer in
17    *    the documentation and/or other materials provided with the
18    *    distribution.
19    *
20    * 3. The end-user documentation included with the redistribution,
21    *    if any, must include the following acknowledgment:
22    *       "This product includes software developed by the
23    *        Apache Software Foundation (http://www.apache.org/)."
24    *    Alternately, this acknowledgment may appear in the software itself,
25    *    if and wherever such third-party acknowledgments normally appear.
26    *
27    * 4. The names "Apache" and "Apache Software Foundation" and
28    *    "Apache POI" must not be used to endorse or promote products
29    *    derived from this software without prior written permission. For
30    *    written permission, please contact apache@apache.org.
31    *
32    * 5. Products derived from this software may not be called "Apache",
33    *    "Apache POI", nor may "Apache" appear in their name, without
34    *    prior written permission of the Apache Software Foundation.
35    *
36    * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
37    * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
38    * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
39    * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
40    * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
41    * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
42    * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
43    * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
44    * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
45    * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
46    * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
47    * SUCH DAMAGE.
48    * ====================================================================
49    *
50    * This software consists of voluntary contributions made by many
51    * individuals on behalf of the Apache Software Foundation.  For more
52    * information on the Apache Software Foundation, please see
53    * <http://www.apache.org/>.
54    */
55   
56   package org.apache.poi.hssf.model;
57   
58   import java.util.List;
59   import java.util.ArrayList;
60   import java.util.Iterator;
61   
62   import org.apache.poi.hssf
63       .record.*;       // normally I don't do this, buy we literally mean ALL
64   import org.apache.poi.hssf.record.formula.Ptg;
65   import org.apache.poi.util.*;
66   import org.apache.poi.hssf.record
67       .aggregates.*;   // normally I don't do this, buy we literally mean ALL
68   
69   /**
70    * Low level model implementation of a Sheet (one workbook contains many sheets)
71    * This file contains the low level binary records starting at the sheets BOF and
72    * ending with the sheets EOF.  Use HSSFSheet for a high level representation.
73    * <P>
74    * The structures of the highlevel API use references to this to perform most of their
75    * operations.  Its probably unwise to use these low level structures directly unless you
76    * really know what you're doing.  I recommend you read the Microsoft Excel 97 Developer's
77    * Kit (Microsoft Press) and the documentation at http://sc.openoffice.org/excelfileformat.pdf
78    * before even attempting to use this.
79    * <P>
80    * @author  Andrew C. Oliver (acoliver at apache dot org)
81    * @author  Glen Stampoultzis (glens at apache.org)
82    * @author  Shawn Laubach (slaubach at apache dot org) Gridlines, Headers, Footers, and PrintSetup
83    * @author Jason Height (jheight at chariot dot net dot au) Clone support
84    * @author  Brian Sanders (kestrel at burdell dot org) Active Cell support
85    *
86    * @see org.apache.poi.hssf.model.Workbook
87    * @see org.apache.poi.hssf.usermodel.HSSFSheet
88    * @version 1.0-pre
89    */
90   
91   public class Sheet implements Model
92   {
93       public static final short   LeftMargin = 0;
94       public static final short   RightMargin = 1;
95       public static final short   TopMargin = 2;
96       public static final short   BottomMargin = 3;
97   
98       protected ArrayList                 records        = null;
99       int                                 preoffset      = 0;      // offset of the sheet in a new file
100      int                                 loc            = 0;
101      protected boolean                   containsLabels = false;
102      protected int                       dimsloc        = 0;
103      protected DimensionsRecord          dims;
104      protected DefaultColWidthRecord     defaultcolwidth  = null;
105      protected DefaultRowHeightRecord    defaultrowheight = null;
106      protected GridsetRecord             gridset          = null;
107      protected PrintSetupRecord          printSetup       = null;
108      protected HeaderRecord              header           = null;
109      protected FooterRecord              footer           = null;
110      protected PrintGridlinesRecord      printGridlines   = null;
111      protected MergeCellsRecord          merged           = null;
112      protected SelectionRecord           selection        = null;
113      protected int                       mergedloc        = 0;
114      private static POILogger            log              = POILogFactory.getLogger(Sheet.class);
115      private ArrayList                   columnSizes      = null;  // holds column info
116      protected ValueRecordsAggregate     cells            = null;
117      protected RowRecordsAggregate       rows             = null;
118      private Iterator                    valueRecIterator = null;
119      private Iterator                    rowRecIterator   = null;
120      protected int                       eofLoc           = 0;
121  
122      public static final byte PANE_LOWER_RIGHT = (byte)0;
123      public static final byte PANE_UPPER_RIGHT = (byte)1;
124      public static final byte PANE_LOWER_LEFT = (byte)2;
125      public static final byte PANE_UPPER_LEFT = (byte)3;
126  
127      /**
128       * Creates new Sheet with no intialization --useless at this point
129       * @see #createSheet(List,int,int)
130       */
131      public Sheet()
132      {
133      }
134  
135      /**
136       * read support  (offset used as starting point for search) for low level
137       * API.  Pass in an array of Record objects, the sheet number (0 based) and
138       * a record offset (should be the location of the sheets BOF record).  A Sheet
139       * object is constructed and passed back with all of its initialization set
140       * to the passed in records and references to those records held. This function
141       * is normally called via Workbook.
142       *
143       * @param recs array containing those records in the sheet in sequence (normally obtained from RecordFactory)
144       * @param sheetnum integer specifying the sheet's number (0,1 or 2 in this release)
145       * @param offset of the sheet's BOF record
146       *
147       * @return Sheet object with all values set to those read from the file
148       *
149       * @see org.apache.poi.hssf.model.Workbook
150       * @see org.apache.poi.hssf.record.Record
151       */
152      public static Sheet createSheet(List recs, int sheetnum, int offset)
153      {
154          log.logFormatted(log.DEBUG,
155                           "Sheet createSheet (existing file) with %",
156                           new Integer(recs.size()));
157          Sheet     retval             = new Sheet();
158          ArrayList records            = new ArrayList(recs.size() / 5);
159          boolean   isfirstcell        = true;
160          boolean   isfirstrow         = true;
161          int       bofEofNestingLevel = 0;
162  
163          for (int k = offset; k < recs.size(); k++)
164          {
165              Record rec = ( Record ) recs.get(k);
166  
167              if (rec.getSid() == LabelRecord.sid)
168              {
169                  log.log(log.DEBUG, "Hit label record.");
170                  retval.containsLabels = true;
171              }
172              else if (rec.getSid() == BOFRecord.sid)
173              {
174                  bofEofNestingLevel++;
175                  log.log(log.DEBUG, "Hit BOF record. Nesting increased to " + bofEofNestingLevel);
176              }
177              else if (rec.getSid() == EOFRecord.sid)
178              {
179                  --bofEofNestingLevel;
180                  log.log(log.DEBUG, "Hit EOF record. Nesting decreased to " + bofEofNestingLevel);
181                  if (bofEofNestingLevel == 0) {
182                      records.add(rec);
183                      retval.eofLoc = k;
184                      break;
185                  }
186              }
187              else if (rec.getSid() == DimensionsRecord.sid)
188              {
189                  retval.dims    = ( DimensionsRecord ) rec;
190                  retval.dimsloc = records.size();
191              }
192              else if (rec.getSid() == MergeCellsRecord.sid)
193              {
194                  retval.merged    = ( MergeCellsRecord ) rec;
195                  retval.mergedloc = k - offset;
196              }
197              else if (rec.getSid() == ColumnInfoRecord.sid)
198              {
199                  if (retval.columnSizes == null)
200                  {
201                      retval.columnSizes = new ArrayList();
202                  }
203                  retval.columnSizes.add(rec);
204              }
205              else if (rec.getSid() == DefaultColWidthRecord.sid)
206              {
207                  retval.defaultcolwidth = ( DefaultColWidthRecord ) rec;
208              }
209              else if (rec.getSid() == DefaultRowHeightRecord.sid)
210              {
211                  retval.defaultrowheight = ( DefaultRowHeightRecord ) rec;
212              }
213              else if ( rec.isValue() && bofEofNestingLevel == 1 )
214              {
215                  if ( isfirstcell )
216                  {
217                      retval.cells = new ValueRecordsAggregate();
218                      rec = retval.cells;
219                      retval.cells.construct( k, recs );
220                      isfirstcell = false;
221                  }
222                  else
223                  {
224                      rec = null;
225                  }
226              }
227              else if ( rec.getSid() == StringRecord.sid )
228              {
229                  rec = null;
230              }
231              else if ( rec.getSid() == RowRecord.sid )
232              {
233                  if ( isfirstrow )
234                  {
235                      retval.rows = new RowRecordsAggregate();
236                      rec = retval.rows;
237                      retval.rows.construct( k, recs );
238                      isfirstrow = false;
239                  }
240                  else
241                  {
242                      rec = null;
243                  }
244              }
245              else if ( rec.getSid() == PrintGridlinesRecord.sid )
246              {
247                  retval.printGridlines = (PrintGridlinesRecord) rec;
248              }
249              else if ( rec.getSid() == HeaderRecord.sid )
250              {
251                  retval.header = (HeaderRecord) rec;
252              }
253              else if ( rec.getSid() == FooterRecord.sid )
254              {
255                  retval.footer = (FooterRecord) rec;
256              }
257              else if ( rec.getSid() == PrintSetupRecord.sid )
258              {
259                  retval.printSetup = (PrintSetupRecord) rec;
260              }
261              else if ( rec.getSid() == SelectionRecord.sid )
262              {
263                  retval.selection = (SelectionRecord) rec;
264              }
265  
266              if (rec != null)
267              {
268                  records.add(rec);
269              }
270          }
271          retval.records = records;
272          if (retval.rows == null)
273          {
274              retval.rows = new RowRecordsAggregate();
275          }
276          if (retval.cells == null)
277          {
278              retval.cells = new ValueRecordsAggregate();
279          }
280          log.log(log.DEBUG, "sheet createSheet (existing file) exited");
281          return retval;
282      }
283  
284      /**
285       * Clones the low level records of this sheet and returns the new sheet instance.
286       */
287      public Sheet cloneSheet()
288      {
289        ArrayList clonedRecords = new ArrayList(this.records.size());
290        for (int i=0; i<this.records.size();i++) {
291          Record rec = (Record)((Record)this.records.get(i)).clone();
292          //Need to pull out the Row record and the Value records from their
293          //Aggregates.
294          //This is probably the best way to do it since we probably dont want the createSheet
295          //To cater for these artificial Record types
296          if (rec instanceof RowRecordsAggregate) {
297            RowRecordsAggregate rrAgg = (RowRecordsAggregate)rec;
298            for (Iterator rowIter = rrAgg.getIterator();rowIter.hasNext();) {
299              Record rowRec = (Record)rowIter.next();
300              clonedRecords.add(rowRec);
301            }
302          } else if (rec instanceof ValueRecordsAggregate) {
303            ValueRecordsAggregate vrAgg = (ValueRecordsAggregate)rec;
304            for (Iterator cellIter = vrAgg.getIterator();cellIter.hasNext();) {
305              Record valRec = (Record)cellIter.next();
306              clonedRecords.add(valRec);
307            }
308          } else if (rec instanceof FormulaRecordAggregate) {
309            FormulaRecordAggregate fmAgg = (FormulaRecordAggregate)rec;
310            Record fmAggRec = fmAgg.getFormulaRecord();
311            if (fmAggRec != null)
312              clonedRecords.add(fmAggRec);
313            fmAggRec =   fmAgg.getStringRecord();
314            if (fmAggRec != null)
315              clonedRecords.add(fmAggRec);
316          } else {
317            clonedRecords.add(rec);
318          }
319        }
320        return createSheet(clonedRecords, 0, 0);
321      }
322  
323  
324      /**
325       * read support  (offset = 0) Same as createSheet(Record[] recs, int, int)
326       * only the record offset is assumed to be 0.
327       *
328       * @param records  array containing those records in the sheet in sequence (normally obtained from RecordFactory)
329       * @param sheetnum integer specifying the sheet's number (0,1 or 2 in this release)
330       * @return Sheet object
331       */
332  
333      public static Sheet createSheet(List records, int sheetnum)
334      {
335          log.log(log.DEBUG,
336                  "Sheet createSheet (exisiting file) assumed offset 0");
337          return createSheet(records, sheetnum, 0);
338      }
339  
340      /**
341       * Creates a sheet with all the usual records minus values and the "index"
342       * record (not required).  Sets the location pointer to where the first value
343       * records should go.  Use this to create a sheet from "scratch".
344       *
345       * @return Sheet object with all values set to defaults
346       */
347  
348      public static Sheet createSheet()
349      {
350          log.log(log.DEBUG, "Sheet createsheet from scratch called");
351          Sheet     retval  = new Sheet();
352          ArrayList records = new ArrayList(30);
353  
354          records.add(retval.createBOF());
355  
356          // records.add(retval.createIndex());
357          records.add(retval.createCalcMode());
358          records.add(retval.createCalcCount() );
359          records.add( retval.createRefMode() );
360          records.add( retval.createIteration() );
361          records.add( retval.createDelta() );
362          records.add( retval.createSaveRecalc() );
363          records.add( retval.createPrintHeaders() );
364          retval.printGridlines = (PrintGridlinesRecord) retval.createPrintGridlines();
365          records.add( retval.printGridlines );
366          retval.gridset = (GridsetRecord) retval.createGridset();
367          records.add( retval.gridset );
368          records.add( retval.createGuts() );
369          retval.defaultrowheight =
370                  (DefaultRowHeightRecord) retval.createDefaultRowHeight();
371          records.add( retval.defaultrowheight );
372          records.add( retval.createWSBool() );
373          retval.header = (HeaderRecord) retval.createHeader();
374          records.add( retval.header );
375          retval.footer = (FooterRecord) retval.createFooter();
376          records.add( retval.footer );
377          records.add( retval.createHCenter() );
378          records.add( retval.createVCenter() );
379          retval.printSetup = (PrintSetupRecord) retval.createPrintSetup();
380          records.add( retval.printSetup );
381          retval.defaultcolwidth =
382                  (DefaultColWidthRecord) retval.createDefaultColWidth();
383          records.add( retval.defaultcolwidth);
384          retval.dims    = ( DimensionsRecord ) retval.createDimensions();
385          retval.dimsloc = 19;
386          records.add(retval.dims);
387          records.add(retval.createWindowTwo());
388          retval.setLoc(records.size() - 1);
389          retval.selection = 
390                  (SelectionRecord) retval.createSelection();
391          records.add(retval.selection);
392          records.add(retval.createEOF());
393          retval.records = records;
394          log.log(log.DEBUG, "Sheet createsheet from scratch exit");
395          return retval;
396      }
397  
398      private void checkCells()
399      {
400          if (cells == null)
401          {
402              cells = new ValueRecordsAggregate();
403              records.add(getDimsLoc() + 1, cells);
404          }
405      }
406  
407      private void checkRows()
408      {
409          if (rows == null)
410          {
411              rows = new RowRecordsAggregate();
412              records.add(getDimsLoc() + 1, rows);
413          }
414      }
415  
416      //public int addMergedRegion(short rowFrom, short colFrom, short rowTo,
417      public int addMergedRegion(int rowFrom, short colFrom, int rowTo,
418                                 short colTo)
419      {
420          if (merged == null)
421          {
422              merged    = ( MergeCellsRecord ) createMergedCells();
423              mergedloc = records.size() - 1;
424              records.add(records.size() - 1, merged);
425          }
426          return merged.addArea(rowFrom, colFrom, rowTo, colTo);
427      }
428  
429      public void removeMergedRegion(int index)
430      {
431          merged.removeAreaAt(index);
432          if (merged.getNumAreas() == 0)
433          {
434              merged = null;
435              records.remove(mergedloc);
436              mergedloc = 0;
437          }
438      }
439  
440      public MergeCellsRecord.MergedRegion getMergedRegionAt(int index)
441      {
442          return merged.getAreaAt(index);
443      }
444  
445      public int getNumMergedRegions()
446  	{
447  	    return merged!=null ? merged.getNumAreas() : 0;
448  	}
449  
450  
451      /**
452       * This is basically a kludge to deal with the now obsolete Label records.  If
453       * you have to read in a sheet that contains Label records, be aware that the rest
454       * of the API doesn't deal with them, the low level structure only provides read-only
455       * semi-immutable structures (the sets are there for interface conformance with NO
456       * impelmentation).  In short, you need to call this function passing it a reference
457       * to the Workbook object.  All labels will be converted to LabelSST records and their
458       * contained strings will be written to the Shared String tabel (SSTRecord) within
459       * the Workbook.
460       *
461       * @param wb sheet's matching low level Workbook structure containing the SSTRecord.
462       * @see org.apache.poi.hssf.record.LabelRecord
463       * @see org.apache.poi.hssf.record.LabelSSTRecord
464       * @see org.apache.poi.hssf.record.SSTRecord
465       */
466  
467      public void convertLabelRecords(Workbook wb)
468      {
469          log.log(log.DEBUG, "convertLabelRecords called");
470          if (containsLabels)
471          {
472              for (int k = 0; k < records.size(); k++)
473              {
474                  Record rec = ( Record ) records.get(k);
475  
476                  if (rec.getSid() == LabelRecord.sid)
477                  {
478                      LabelRecord oldrec = ( LabelRecord ) rec;
479  
480                      records.remove(k);
481                      LabelSSTRecord newrec   = new LabelSSTRecord();
482                      int            stringid =
483                          wb.addSSTString(oldrec.getValue());
484  
485                      newrec.setRow(oldrec.getRow());
486                      newrec.setColumn(oldrec.getColumn());
487                      newrec.setXFIndex(oldrec.getXFIndex());
488                      newrec.setSSTIndex(stringid);
489                            records.add(k, newrec);
490                  }
491              }
492          }
493          log.log(log.DEBUG, "convertLabelRecords exit");
494      }
495  
496      /**
497       * Returns the number of low level binary records in this sheet.  This adjusts things for the so called
498       * AgregateRecords.
499       *
500       * @see org.apache.poi.hssf.record.Record
501       */
502  
503      public int getNumRecords()
504      {
505          checkCells();
506          checkRows();
507          log.log(log.DEBUG, "Sheet.getNumRecords");
508          log.logFormatted(log.DEBUG, "returning % + % + % - 2 = %", new int[]
509          {
510              records.size(), cells.getPhysicalNumberOfCells(),
511              rows.getPhysicalNumberOfRows(),
512              records.size() + cells.getPhysicalNumberOfCells()
513              + rows.getPhysicalNumberOfRows() - 2
514          });
515          return records.size() + cells.getPhysicalNumberOfCells()
516                 + rows.getPhysicalNumberOfRows() - 2;
517      }
518  
519      /**
520       * Per an earlier reported bug in working with Andy Khan's excel read library.  This
521       * sets the values in the sheet's DimensionsRecord object to be correct.  Excel doesn't
522       * really care, but we want to play nice with other libraries.
523       *
524       * @see org.apache.poi.hssf.record.DimensionsRecord
525       */
526  
527      //public void setDimensions(short firstrow, short firstcol, short lastrow,
528      public void setDimensions(int firstrow, short firstcol, int lastrow,
529                                short lastcol)
530      {
531          log.log(log.DEBUG, "Sheet.setDimensions");
532          log.log(log.DEBUG,
533                  (new StringBuffer("firstrow")).append(firstrow)
534                      .append("firstcol").append(firstcol).append("lastrow")
535                      .append(lastrow).append("lastcol").append(lastcol)
536                      .toString());
537          dims.setFirstCol(firstcol);
538          dims.setFirstRow(firstrow);
539          dims.setLastCol(lastcol);
540          dims.setLastRow(lastrow);
541          log.log(log.DEBUG, "Sheet.setDimensions exiting");
542      }
543  
544      /**
545       * set the locator for where we should look for the next value record.  The
546       * algorythm will actually start here and find the correct location so you
547       * can set this to 0 and watch performance go down the tubes but it will work.
548       * After a value is set this is automatically advanced.  Its also set by the
549       * create method.  So you probably shouldn't mess with this unless you have
550       * a compelling reason why or the help for the method you're calling says so.
551       * Check the other methods for whether they care about
552       * the loc pointer.  Many of the "modify" and "remove" methods re-initialize this
553       * to "dimsloc" which is the location of the Dimensions Record and presumably the
554       * start of the value section (at or around 19 dec).
555       *
556       * @param loc the record number to start at
557       *
558       */
559  
560      public void setLoc(int loc)
561      {
562          valueRecIterator = null;
563          log.log(log.DEBUG, "sheet.setLoc(): " + loc);
564          this.loc = loc;
565      }
566  
567      /**
568       * Returns the location pointer to the first record to look for when adding rows/values
569       *
570       */
571  
572      public int getLoc()
573      {
574          log.log(log.DEBUG, "sheet.getLoc():" + loc);
575          return loc;
576      }
577  
578      /**
579       * Set the preoffset when using DBCELL records (currently unused) - this is
580       * the position of this sheet within the whole file.
581       *
582       * @param offset the offset of the sheet's BOF within the file.
583       */
584  
585      public void setPreOffset(int offset)
586      {
587          this.preoffset = offset;
588      }
589  
590      /**
591       * get the preoffset when using DBCELL records (currently unused) - this is
592       * the position of this sheet within the whole file.
593       *
594       * @return offset the offset of the sheet's BOF within the file.
595       */
596  
597      public int getPreOffset()
598      {
599          return preoffset;
600      }
601  
602      /**
603       * Serializes all records in the sheet into one big byte array.  Use this to write
604       * the sheet out.
605       *
606       * @return byte[] array containing the binary representation of the records in this sheet
607       *
608       */
609  
610      public byte [] serialize()
611      {
612          log.log(log.DEBUG, "Sheet.serialize");
613  
614          // addDBCellRecords();
615          byte[] retval    = null;
616  
617          // ArrayList bytes     = new ArrayList(4096);
618          int    arraysize = getSize();
619          int    pos       = 0;
620  
621          // for (int k = 0; k < records.size(); k++)
622          // {
623          // bytes.add((( Record ) records.get(k)).serialize());
624          //
625          // }
626          // for (int k = 0; k < bytes.size(); k++)
627          // {
628          // arraysize += (( byte [] ) bytes.get(k)).length;
629          // log.debug((new StringBuffer("arraysize=")).append(arraysize)
630          // .toString());
631          // }
632          retval = new byte[ arraysize ];
633          for (int k = 0; k < records.size(); k++)
634          {
635  
636              // byte[] rec = (( byte [] ) bytes.get(k));
637              // System.arraycopy(rec, 0, retval, pos, rec.length);
638              pos += (( Record ) records.get(k)).serialize(pos,
639                      retval);   // rec.length;
640          }
641          log.log(log.DEBUG, "Sheet.serialize returning " + retval);
642          return retval;
643      }
644  
645      /**
646       * Serializes all records in the sheet into one big byte array.  Use this to write
647       * the sheet out.
648       *
649       * @param offset to begin write at
650       * @param data   array containing the binary representation of the records in this sheet
651       *
652       */
653  
654      public int serialize(int offset, byte [] data)
655      {
656          log.log(log.DEBUG, "Sheet.serialize using offsets");
657  
658          // addDBCellRecords();
659          // ArrayList bytes     = new ArrayList(4096);
660          // int arraysize = getSize();   // 0;
661          int pos       = 0;
662  
663          // for (int k = 0; k < records.size(); k++)
664          // {
665          // bytes.add((( Record ) records.get(k)).serialize());
666          //
667          // }
668          // for (int k = 0; k < bytes.size(); k++)
669          // {
670          // arraysize += (( byte [] ) bytes.get(k)).length;
671          // log.debug((new StringBuffer("arraysize=")).append(arraysize)
672          // .toString());
673          // }
674          for (int k = 0; k < records.size(); k++)
675          {
676  //             byte[] rec = (( byte [] ) bytes.get(k));
677              // System.arraycopy(rec, 0, data, offset + pos, rec.length);
678              Record record = (( Record ) records.get(k));
679  
680              //uncomment to test record sizes
681  //            byte[] data2 = new byte[record.getRecordSize()];
682  //            record.serialize(0, data2 );   // rec.length;
683  //            if (LittleEndian.getUShort(data2, 2) != record.getRecordSize() - 4
684  //                    && record instanceof RowRecordsAggregate == false && record instanceof ValueRecordsAggregate == false)
685  //                throw new RuntimeException("Blah!!!");
686  
687              pos += record.serialize(pos + offset, data );   // rec.length;
688  
689          }
690          log.log(log.DEBUG, "Sheet.serialize returning ");
691          return pos;
692      }
693  
694      /**
695       * Create a row record.  (does not add it to the records contained in this sheet)
696       *
697       * @param row number
698       * @return RowRecord created for the passed in row number
699       * @see org.apache.poi.hssf.record.RowRecord
700       */
701  
702      public RowRecord createRow(int row)
703      {
704          log.log(log.DEBUG, "create row number " + row);
705          RowRecord rowrec = new RowRecord();
706  
707          //rowrec.setRowNumber(( short ) row);
708          rowrec.setRowNumber(row);
709          rowrec.setHeight(( short ) 0xff);
710          rowrec.setOptimize(( short ) 0x0);
711          rowrec.setOptionFlags(( short ) 0x0);
712          rowrec.setXFIndex(( short ) 0x0);
713          return rowrec;
714      }
715  
716      /**
717       * Create a LABELSST Record (does not add it to the records contained in this sheet)
718       *
719       * @param row the row the LabelSST is a member of
720       * @param col the column the LabelSST defines
721       * @param index the index of the string within the SST (use workbook addSSTString method)
722       * @return LabelSSTRecord newly created containing your SST Index, row,col.
723       * @see org.apache.poi.hssf.record.SSTRecord
724       */
725  
726      //public LabelSSTRecord createLabelSST(short row, short col, int index)
727      public LabelSSTRecord createLabelSST(int row, short col, int index)
728      {
729          log.logFormatted(log.DEBUG, "create labelsst row,col,index %,%,%",
730                           new int[]
731          {
732              row, col, index
733          });
734          LabelSSTRecord rec = new LabelSSTRecord();
735  
736          rec.setRow(row);
737          rec.setColumn(col);
738          rec.setSSTIndex(index);
739          rec.setXFIndex(( short ) 0x0f);
740          return rec;
741      }
742  
743      /**
744       * Create a NUMBER Record (does not add it to the records contained in this sheet)
745       *
746       * @param row the row the NumberRecord is a member of
747       * @param col the column the NumberRecord defines
748       * @param value for the number record
749       *
750       * @return NumberRecord for that row, col containing that value as added to the sheet
751       */
752  
753      //public NumberRecord createNumber(short row, short col, double value)
754      public NumberRecord createNumber(int row, short col, double value)
755      {
756          log.logFormatted(log.DEBUG, "create number row,col,value %,%,%",
757                           new double[]
758          {
759              row, col, value
760          });
761          NumberRecord rec = new NumberRecord();
762  
763          //rec.setRow(( short ) row);
764          rec.setRow(row);
765          rec.setColumn(col);
766          rec.setValue(value);
767          rec.setXFIndex(( short ) 0x0f);
768          return rec;
769      }
770  
771      /**
772       * create a BLANK record (does not add it to the records contained in this sheet)
773       *
774       * @param row - the row the BlankRecord is a member of
775       * @param col - the column the BlankRecord is a member of
776       */
777  
778      //public BlankRecord createBlank(short row, short col)
779      public BlankRecord createBlank(int row, short col)
780      {
781          //log.logFormatted(log.DEBUG, "create blank row,col %,%", new short[]
782          log.logFormatted(log.DEBUG, "create blank row,col %,%", new int[]
783          {
784              row, col
785          });
786          BlankRecord rec = new BlankRecord();
787  
788          //rec.setRow(( short ) row);
789          rec.setRow(row);
790          rec.setColumn(col);
791          rec.setXFIndex(( short ) 0x0f);
792          return rec;
793      }
794  
795      /**
796       * Attempts to parse the formula into PTGs and create a formula record
797       * DOES NOT WORK YET
798       *
799       * @param row - the row for the formula record
800       * @param col - the column of the formula record
801       * @param formula - a String representing the formula.  To be parsed to PTGs
802       * @return bogus/useless formula record
803       */
804  
805      //public FormulaRecord createFormula(short row, short col, String formula)
806      public FormulaRecord createFormula(int row, short col, String formula)
807      {
808          log.logFormatted(log.DEBUG, "create formula row,col,formula %,%,%",
809                           //new short[]
810                           new int[]
811          {
812              row, col
813          }, formula);
814          FormulaRecord rec = new FormulaRecord();
815  
816          rec.setRow(row);
817          rec.setColumn(col);
818          rec.setOptions(( short ) 2);
819          rec.setValue(0);
820          rec.setXFIndex(( short ) 0x0f);
821          FormulaParser fp = new FormulaParser(formula,null); //fix - do we need this method?
822          fp.parse();
823          Ptg[] ptg  = fp.getRPNPtg();
824          int   size = 0;
825  
826          for (int k = 0; k < ptg.length; k++)
827          {
828              size += ptg[ k ].getSize();
829              rec.pushExpressionToken(ptg[ k ]);
830          }
831          rec.setExpressionLength(( short ) size);
832          return rec;
833      }
834  
835      /**
836       * Adds a value record to the sheet's contained binary records
837       * (i.e. LabelSSTRecord or NumberRecord).
838       * <P>
839       * This method is "loc" sensitive.  Meaning you need to set LOC to where you
840       * want it to start searching.  If you don't know do this: setLoc(getDimsLoc).
841       * When adding several rows you can just start at the last one by leaving loc
842       * at what this sets it to.
843       *
844       * @param row the row to add the cell value to
845       * @param col the cell value record itself.
846       */
847  
848      //public void addValueRecord(short row, CellValueRecordInterface col)
849      public void addValueRecord(int row, CellValueRecordInterface col)
850      {
851          checkCells();
852          log.logFormatted(log.DEBUG, "add value record  row,loc %,%", new int[]
853          {
854              row, loc
855          });
856          DimensionsRecord d = ( DimensionsRecord ) records.get(getDimsLoc());
857  
858          if (col.getColumn() > d.getLastCol())
859          {
860              d.setLastCol(( short ) (col.getColumn() + 1));
861          }
862          if (col.getColumn() < d.getFirstCol())
863          {
864              d.setFirstCol(col.getColumn());
865          }
866          cells.insertCell(col);
867  
868          /*
869           * for (int k = loc; k < records.size(); k++)
870           * {
871           *   Record rec = ( Record ) records.get(k);
872           *
873           *   if (rec.getSid() == RowRecord.sid)
874           *   {
875           *       RowRecord rowrec = ( RowRecord ) rec;
876           *
877           *       if (rowrec.getRowNumber() == col.getRow())
878           *       {
879           *           records.add(k + 1, col);
880           *           loc = k;
881           *           if (rowrec.getLastCol() <= col.getColumn())
882           *           {
883           *               rowrec.setLastCol((( short ) (col.getColumn() + 1)));
884           *           }
885           *           break;
886           *       }
887           *   }
888           * }
889           */
890      }
891  
892      /**
893       * remove a value record from the records array.
894       *
895       * This method is not loc sensitive, it resets loc to = dimsloc so no worries.
896       *
897       * @param row - the row of the value record you wish to remove
898       * @param col - a record supporting the CellValueRecordInterface.
899       * @see org.apache.poi.hssf.record.CellValueRecordInterface
900       */
901  
902      //public void removeValueRecord(short row, CellValueRecordInterface col)
903      public void removeValueRecord(int row, CellValueRecordInterface col)
904      {
905          checkCells();
906          log.logFormatted(log.DEBUG, "remove value record row,dimsloc %,%",
907                           new int[]
908          {
909              row, dimsloc
910          });
911          loc = dimsloc;
912          cells.removeCell(col);
913  
914          /*
915           * for (int k = loc; k < records.size(); k++)
916           * {
917           *   Record rec = ( Record ) records.get(k);
918           *
919           *   // checkDimsLoc(rec,k);
920           *   if (rec.isValue())
921           *   {
922           *       CellValueRecordInterface cell =
923           *           ( CellValueRecordInterface ) rec;
924           *
925           *       if ((cell.getRow() == col.getRow())
926           *               && (cell.getColumn() == col.getColumn()))
927           *       {
928           *           records.remove(k);
929           *           break;
930           *       }
931           *   }
932           * }
933           */
934      }
935  
936      /**
937       * replace a value record from the records array.
938       *
939       * This method is not loc sensitive, it resets loc to = dimsloc so no worries.
940       *
941       * @param newval - a record supporting the CellValueRecordInterface.  this will replace
942       *                the cell value with the same row and column.  If there isn't one, one will
943       *                be added.
944       */
945  
946      public void replaceValueRecord(CellValueRecordInterface newval)
947      {
948          checkCells();
949          setLoc(dimsloc);
950          log.log(log.DEBUG, "replaceValueRecord ");
951          cells.insertCell(newval);
952  
953          /*
954           * CellValueRecordInterface oldval = getNextValueRecord();
955           *
956           * while (oldval != null)
957           * {
958           *   if (oldval.isEqual(newval))
959           *   {
960           *       records.set(( short ) (getLoc() - 1), newval);
961           *       return;
962           *   }
963           *   oldval = getNextValueRecord();
964           * }
965           * addValueRecord(newval.getRow(), newval);
966           * setLoc(dimsloc);
967           */
968      }
969  
970      /**
971       * Adds a row record to the sheet
972       *
973       * <P>
974       * This method is "loc" sensitive.  Meaning you need to set LOC to where you
975       * want it to start searching.  If you don't know do this: setLoc(getDimsLoc).
976       * When adding several rows you can just start at the last one by leaving loc
977       * at what this sets it to.
978       *
979       * @param row the row record to be added
980       * @see #setLoc(int)
981       */
982  
983      public void addRow(RowRecord row)
984      {
985          checkRows();
986          log.log(log.DEBUG, "addRow ");
987          DimensionsRecord d = ( DimensionsRecord ) records.get(getDimsLoc());
988  
989          if (row.getRowNumber() > d.getLastRow())
990          {
991              d.setLastRow(row.getRowNumber() + 1);
992          }
993          if (row.getRowNumber() < d.getFirstRow())
994          {
995              d.setFirstRow(row.getRowNumber());
996          }
997          //IndexRecord index = null;
998           //If the row exists remove it, so that any cells attached to the row are removed
999           RowRecord existingRow = rows.getRow(row.getRowNumber());
1000          if (existingRow != null)
1001            rows.removeRow(existingRow);
1002 
1003         rows.insertRow(row);
1004 
1005         /*
1006          * for (int k = loc; k < records.size(); k++)
1007          * {
1008          *   Record rec = ( Record ) records.get(k);
1009          *
1010          *   if (rec.getSid() == IndexRecord.sid)
1011          *   {
1012          *       index = ( IndexRecord ) rec;
1013          *   }
1014          *   if (rec.getSid() == RowRecord.sid)
1015          *   {
1016          *       RowRecord rowrec = ( RowRecord ) rec;
1017          *
1018          *       if (rowrec.getRowNumber() > row.getRowNumber())
1019          *       {
1020          *           records.add(k, row);
1021          *           loc = k;
1022          *           break;
1023          *       }
1024          *   }
1025          *   if (rec.getSid() == WindowTwoRecord.sid)
1026          *   {
1027          *       records.add(k, row);
1028          *       loc = k;
1029          *       break;
1030          *   }
1031          * }
1032          * if (index != null)
1033          * {
1034          *   if (index.getLastRowAdd1() <= row.getRowNumber())
1035          *   {
1036          *       index.setLastRowAdd1(row.getRowNumber() + 1);
1037          *   }
1038          * }
1039          */
1040         log.log(log.DEBUG, "exit addRow");
1041     }
1042 
1043     /**
1044      * Removes a row record
1045      *
1046      * This method is not loc sensitive, it resets loc to = dimsloc so no worries.
1047      *
1048      * @param row  the row record to remove
1049      */
1050 
1051     public void removeRow(RowRecord row)
1052     {
1053         checkRows();
1054         // IndexRecord index = null;
1055 
1056         setLoc(getDimsLoc());
1057         rows.removeRow(row);
1058 
1059         /*
1060          * for (int k = loc; k < records.size(); k++)
1061          * {
1062          *   Record rec = ( Record ) records.get(k);
1063          *
1064          *   // checkDimsLoc(rec,k);
1065          *   if (rec.getSid() == RowRecord.sid)
1066          *   {
1067          *       RowRecord rowrec = ( RowRecord ) rec;
1068          *
1069          *       if (rowrec.getRowNumber() == row.getRowNumber())
1070          *       {
1071          *           records.remove(k);
1072          *           break;
1073          *       }
1074          *   }
1075          *   if (rec.getSid() == WindowTwoRecord.sid)
1076          *   {
1077          *       break;
1078          *   }
1079          * }
1080          */
1081     }
1082 
1083     /**
1084      * get the NEXT value record (from LOC).  The first record that is a value record
1085      * (starting at LOC) will be returned.
1086      *
1087      * <P>
1088      * This method is "loc" sensitive.  Meaning you need to set LOC to where you
1089      * want it to start searching.  If you don't know do this: setLoc(getDimsLoc).
1090      * When adding several rows you can just start at the last one by leaving loc
1091      * at what this sets it to.  For this method, set loc to dimsloc to start with,
1092      * subsequent calls will return values in (physical) sequence or NULL when you get to the end.
1093      *
1094      * @return CellValueRecordInterface representing the next value record or NULL if there are no more
1095      * @see #setLoc(int)
1096      */
1097 
1098     public CellValueRecordInterface getNextValueRecord()
1099     {
1100         log.log(log.DEBUG, "getNextValue loc= " + loc);
1101         if (valueRecIterator == null)
1102         {
1103             valueRecIterator = cells.getIterator();
1104         }
1105         if (!valueRecIterator.hasNext())
1106         {
1107             return null;
1108         }
1109         return ( CellValueRecordInterface ) valueRecIterator.next();
1110 
1111         /*
1112          *      if (this.getLoc() < records.size())
1113          *     {
1114          *         for (int k = getLoc(); k < records.size(); k++)
1115          *         {
1116          *             Record rec = ( Record ) records.get(k);
1117          *
1118          *             this.setLoc(k + 1);
1119          *             if (rec instanceof CellValueRecordInterface)
1120          *             {
1121          *                 return ( CellValueRecordInterface ) rec;
1122          *             }
1123          *         }
1124          *     }
1125          *     return null;
1126          */
1127     }
1128 
1129     /**
1130      * get the NEXT RowRecord or CellValueRecord(from LOC).  The first record that
1131      * is a Row record or CellValueRecord(starting at LOC) will be returned.
1132      * <P>
1133      * This method is "loc" sensitive.  Meaning you need to set LOC to where you
1134      * want it to start searching.  If you don't know do this: setLoc(getDimsLoc).
1135      * When adding several rows you can just start at the last one by leaving loc
1136      * at what this sets it to.  For this method, set loc to dimsloc to start with.
1137      * subsequent calls will return rows in (physical) sequence or NULL when you get to the end.
1138      *
1139      * @return RowRecord representing the next row record or CellValueRecordInterface
1140      *  representing the next cellvalue or NULL if there are no more
1141      * @see #setLoc(int)
1142      *
1143      */
1144 
1145 /*    public Record getNextRowOrValue()
1146     {
1147         log.debug((new StringBuffer("getNextRow loc= ")).append(loc)
1148             .toString());
1149         if (this.getLoc() < records.size())
1150         {
1151             for (int k = this.getLoc(); k < records.size(); k++)
1152             {
1153                 Record rec = ( Record ) records.get(k);
1154 
1155                 this.setLoc(k + 1);
1156                 if (rec.getSid() == RowRecord.sid)
1157                 {
1158                     return rec;
1159                 }
1160                 else if (rec.isValue())
1161                 {
1162                     return rec;
1163                 }
1164             }
1165         }
1166         return null;
1167     }
1168  */
1169 
1170     /**
1171      * get the NEXT RowRecord (from LOC).  The first record that is a Row record
1172      * (starting at LOC) will be returned.
1173      * <P>
1174      * This method is "loc" sensitive.  Meaning you need to set LOC to where you
1175      * want it to start searching.  If you don't know do this: setLoc(getDimsLoc).
1176      * When adding several rows you can just start at the last one by leaving loc
1177      * at what this sets it to.  For this method, set loc to dimsloc to start with.
1178      * subsequent calls will return rows in (physical) sequence or NULL when you get to the end.
1179      *
1180      * @return RowRecord representing the next row record or NULL if there are no more
1181      * @see #setLoc(int)
1182      *
1183      */
1184 
1185     public RowRecord getNextRow()
1186     {
1187         log.log(log.DEBUG, "getNextRow loc= " + loc);
1188         if (rowRecIterator == null)
1189         {
1190             rowRecIterator = rows.getIterator();
1191         }
1192         if (!rowRecIterator.hasNext())
1193         {
1194             return null;
1195         }
1196         return ( RowRecord ) rowRecIterator.next();
1197 
1198 /*        if (this.getLoc() < records.size())
1199         {
1200             for (int k = this.getLoc(); k < records.size(); k++)
1201             {
1202                 Record rec = ( Record ) records.get(k);
1203 
1204                 this.setLoc(k + 1);
1205                 if (rec.getSid() == RowRecord.sid)
1206                 {
1207                     return ( RowRecord ) rec;
1208                 }
1209             }
1210         }*/
1211     }
1212 
1213     /**
1214      * get the NEXT (from LOC) RowRecord where rownumber matches the given rownum.
1215      * The first record that is a Row record (starting at LOC) that has the
1216      * same rownum as the given rownum will be returned.
1217      * <P>
1218      * This method is "loc" sensitive.  Meaning you need to set LOC to where you
1219      * want it to start searching.  If you don't know do this: setLoc(getDimsLoc).
1220      * When adding several rows you can just start at the last one by leaving loc
1221      * at what this sets it to.  For this method, set loc to dimsloc to start with.
1222      * subsequent calls will return rows in (physical) sequence or NULL when you get to the end.
1223      *
1224      * @param rownum   which row to return (careful with LOC)
1225      * @return RowRecord representing the next row record or NULL if there are no more
1226      * @see #setLoc(int)
1227      *
1228      */
1229 
1230     //public RowRecord getRow(short rownum)
1231     public RowRecord getRow(int rownum)
1232     {
1233         log.log(log.DEBUG, "getNextRow loc= " + loc);
1234         return rows.getRow(rownum);
1235 
1236         /*
1237          * if (this.getLoc() < records.size())
1238          * {
1239          *   for (int k = this.getLoc(); k < records.size(); k++)
1240          *   {
1241          *       Record rec = ( Record ) records.get(k);
1242          *
1243          *       this.setLoc(k + 1);
1244          *       if (rec.getSid() == RowRecord.sid)
1245          *       {
1246          *           if ((( RowRecord ) rec).getRowNumber() == rownum)
1247          *           {
1248          *               return ( RowRecord ) rec;
1249          *           }
1250          *       }
1251          *   }
1252          * }
1253          */
1254 
1255         // return null;
1256     }
1257 
1258     /**
1259      * Not currently used method to calculate and add dbcell records
1260      *
1261      */
1262 
1263     public void addDBCellRecords()
1264     {
1265         int         offset        = 0;
1266         int         recnum        = 0;
1267         int         rownum        = 0;
1268         //int         lastrow       = 0;
1269         //long        lastrowoffset = 0;
1270         IndexRecord index         = null;
1271 
1272         // ArrayList rowOffsets = new ArrayList();
1273         IntList     rowOffsets    = new IntList();
1274 
1275         for (recnum = 0; recnum < records.size(); recnum++)
1276         {
1277             Record rec = ( Record ) records.get(recnum);
1278 
1279             if (rec.getSid() == IndexRecord.sid)
1280             {
1281                 index = ( IndexRecord ) rec;
1282             }
1283             if (rec.getSid() != RowRecord.sid)
1284             {
1285                 offset += rec.serialize().length;
1286             }
1287             else
1288             {
1289                 break;
1290             }
1291         }
1292 
1293         // First Row Record
1294         for (; recnum < records.size(); recnum++)
1295         {
1296             Record rec = ( Record ) records.get(recnum);
1297 
1298             if (rec.getSid() == RowRecord.sid)
1299             {
1300                 rownum++;
1301                 rowOffsets.add(offset);
1302                 if ((rownum % 32) == 0)
1303                 {
1304 
1305                     // if this is the last rec in a  dbcell block
1306                     // find the next row or last value record
1307                     for (int rn = recnum; rn < records.size(); rn++)
1308                     {
1309                         rec = ( Record ) records.get(rn);
1310                         if ((!rec.isInValueSection())
1311                                 || (rec.getSid() == RowRecord.sid))
1312                         {
1313 
1314                             // here is the next row or last value record
1315                             records.add(rn,
1316                                         createDBCell(offset, rowOffsets,
1317                                                      index));
1318                             recnum = rn;
1319                             break;
1320                         }
1321                     }
1322                 }
1323                 else
1324                 {
1325                 }
1326             }
1327             if (!rec.isInValueSection())
1328             {
1329                 records.add(recnum, createDBCell(offset, rowOffsets, index));
1330                 break;
1331             }
1332             offset += rec.serialize().length;
1333         }
1334     }
1335 
1336     /** not currently used */
1337 
1338     private DBCellRecord createDBCell(int offset, IntList rowoffsets,
1339                                       IndexRecord index)
1340     {
1341         DBCellRecord rec = new DBCellRecord();
1342 
1343         rec.setRowOffset(offset - rowoffsets.get(0));
1344 
1345         // test hack
1346         rec.addCellOffset(( short ) 0x0);
1347 
1348         // end test hack
1349         addDbCellToIndex(offset, index);
1350         return rec;
1351     }
1352 
1353     /** not currently used */
1354 
1355     private void addDbCellToIndex(int offset, IndexRecord index)
1356     {
1357         int numdbcells = index.getNumDbcells() + 1;
1358 
1359         index.addDbcell(offset + preoffset);
1360 
1361         // stupid but whenever we add an offset that causes everything to be shifted down 4
1362         for (int k = 0; k < numdbcells; k++)
1363         {
1364             int dbval = index.getDbcellAt(k);
1365 
1366             index.setDbcell(k, dbval + 4);
1367         }
1368     }
1369 
1370     /**
1371      * creates the BOF record
1372      * @see org.apache.poi.hssf.record.BOFRecord
1373      * @see org.apache.poi.hssf.record.Record
1374      * @return record containing a BOFRecord
1375      */
1376 
1377     protected Record createBOF()
1378     {
1379         BOFRecord retval = new BOFRecord();
1380 
1381         retval.setVersion(( short ) 0x600);
1382         retval.setType(( short ) 0x010);
1383 
1384         // retval.setBuild((short)0x10d3);
1385         retval.setBuild(( short ) 0x0dbb);
1386         retval.setBuildYear(( short ) 1996);
1387         retval.setHistoryBitMask(0xc1);
1388         retval.setRequiredVersion(0x6);
1389         return retval;
1390     }
1391 
1392     /**
1393      * creates the Index record  - not currently used
1394      * @see org.apache.poi.hssf.record.IndexRecord
1395      * @see org.apache.poi.hssf.record.Record
1396      * @return record containing a IndexRecord
1397      */
1398 
1399     protected Record createIndex()
1400     {
1401         IndexRecord retval = new IndexRecord();
1402 
1403         retval.setFirstRow(0);   // must be set explicitly
1404         retval.setLastRowAdd1(0);
1405         return retval;
1406     }
1407 
1408     /**
1409      * creates the CalcMode record and sets it to 1 (automatic formula caculation)
1410      * @see org.apache.poi.hssf.record.CalcModeRecord
1411      * @see org.apache.poi.hssf.record.Record
1412      * @return record containing a CalcModeRecord
1413      */
1414 
1415     protected Record createCalcMode()
1416     {
1417         CalcModeRecord retval = new CalcModeRecord();
1418 
1419         retval.setCalcMode(( short ) 1);
1420         return retval;
1421     }
1422 
1423     /**
1424      * creates the CalcCount record and sets it to 0x64 (default number of iterations)
1425      * @see org.apache.poi.hssf.record.CalcCountRecord
1426      * @see org.apache.poi.hssf.record.Record
1427      * @return record containing a CalcCountRecord
1428      */
1429 
1430     protected Record createCalcCount()
1431     {
1432         CalcCountRecord retval = new CalcCountRecord();
1433 
1434         retval.setIterations(( short ) 0x64);   // default 64 iterations
1435         return retval;
1436     }
1437 
1438     /**
1439      * creates the RefMode record and sets it to A1 Mode (default reference mode)
1440      * @see org.apache.poi.hssf.record.RefModeRecord
1441      * @see org.apache.poi.hssf.record.Record
1442      * @return record containing a RefModeRecord
1443      */
1444 
1445     protected Record createRefMode()
1446     {
1447         RefModeRecord retval = new RefModeRecord();
1448 
1449         retval.setMode(retval.USE_A1_MODE);
1450         return retval;
1451     }
1452 
1453     /**
1454      * creates the Iteration record and sets it to false (don't iteratively calculate formulas)
1455      * @see org.apache.poi.hssf.record.IterationRecord
1456      * @see org.apache.poi.hssf.record.Record
1457      * @return record containing a IterationRecord
1458      */
1459 
1460     protected Record createIteration()
1461     {
1462         IterationRecord retval = new IterationRecord();
1463 
1464         retval.setIteration(false);
1465         return retval;
1466     }
1467 
1468     /**
1469      * creates the Delta record and sets it to 0.0010 (default accuracy)
1470      * @see org.apache.poi.hssf.record.DeltaRecord
1471      * @see org.apache.poi.hssf.record.Record
1472      * @return record containing a DeltaRecord
1473      */
1474 
1475     protected Record createDelta()
1476     {
1477         DeltaRecord retval = new DeltaRecord();
1478 
1479         retval.setMaxChange(0.0010);
1480         return retval;
1481     }
1482 
1483     /**
1484      * creates the SaveRecalc record and sets it to true (recalculate before saving)
1485      * @see org.apache.poi.hssf.record.SaveRecalcRecord
1486      * @see org.apache.poi.hssf.record.Record
1487      * @return record containing a SaveRecalcRecord
1488      */
1489 
1490     protected Record createSaveRecalc()
1491     {
1492         SaveRecalcRecord retval = new SaveRecalcRecord();
1493 
1494         retval.setRecalc(true);
1495         return retval;
1496     }
1497 
1498     /**
1499      * creates the PrintHeaders record and sets it to false (we don't create headers yet so why print them)
1500      * @see org.apache.poi.hssf.record.PrintHeadersRecord
1501      * @see org.apache.poi.hssf.record.Record
1502      * @return record containing a PrintHeadersRecord
1503      */
1504 
1505     protected Record createPrintHeaders()
1506     {
1507         PrintHeadersRecord retval = new PrintHeadersRecord();
1508 
1509         retval.setPrintHeaders(false);
1510         return retval;
1511     }
1512 
1513     /**
1514      * creates the PrintGridlines record and sets it to false (that makes for ugly sheets).  As far as I can
1515      * tell this does the same thing as the GridsetRecord
1516      *
1517      * @see org.apache.poi.hssf.record.PrintGridlinesRecord
1518      * @see org.apache.poi.hssf.record.Record
1519      * @return record containing a PrintGridlinesRecord
1520      */
1521 
1522     protected Record createPrintGridlines()
1523     {
1524         PrintGridlinesRecord retval = new PrintGridlinesRecord();
1525 
1526         retval.setPrintGridlines(false);
1527         return retval;
1528     }
1529 
1530     /**
1531      * creates the Gridset record and sets it to true (user has mucked with the gridlines)
1532      * @see org.apache.poi.hssf.record.GridsetRecord
1533      * @see org.apache.poi.hssf.record.Record
1534      * @return record containing a GridsetRecord
1535      */
1536 
1537     protected Record createGridset()
1538     {
1539         GridsetRecord retval = new GridsetRecord();
1540 
1541         retval.setGridset(true);
1542         return retval;
1543     }
1544 
1545     /**
1546      * creates the Guts record and sets leftrow/topcol guttter and rowlevelmax/collevelmax to 0
1547      * @see org.apache.poi.hssf.record.GutsRecord
1548      * @see org.apache.poi.hssf.record.Record
1549      * @return record containing a GutsRecordRecord
1550      */
1551 
1552     protected Record createGuts()
1553     {
1554         GutsRecord retval = new GutsRecord();
1555 
1556         retval.setLeftRowGutter(( short ) 0);
1557         retval.setTopColGutter(( short ) 0);
1558         retval.setRowLevelMax(( short ) 0);
1559         retval.setColLevelMax(( short ) 0);
1560         return retval;
1561     }
1562 
1563     /**
1564      * creates the DefaultRowHeight Record and sets its options to 0 and rowheight to 0xff
1565      * @see org.apache.poi.hssf.record.DefaultRowHeightRecord
1566      * @see org.apache.poi.hssf.record.Record
1567      * @return record containing a DefaultRowHeightRecord
1568      */
1569 
1570     protected Record createDefaultRowHeight()
1571     {
1572         DefaultRowHeightRecord retval = new DefaultRowHeightRecord();
1573 
1574         retval.setOptionFlags(( short ) 0);
1575         retval.setRowHeight(( short ) 0xff);
1576         return retval;
1577     }
1578 
1579     /**
1580      * creates the WSBoolRecord and sets its values to defaults
1581      * @see org.apache.poi.hssf.record.WSBoolRecord
1582      * @see org.apache.poi.hssf.record.Record
1583      * @return record containing a WSBoolRecord
1584      */
1585 
1586     protected Record createWSBool()
1587     {
1588         WSBoolRecord retval = new WSBoolRecord();
1589 
1590         retval.setWSBool1(( byte ) 0x4);
1591         retval.setWSBool2(( byte ) 0xffffffc1);
1592         return retval;
1593     }
1594 
1595     /**
1596      * creates the Header Record and sets it to nothing/0 length
1597      * @see org.apache.poi.hssf.record.HeaderRecord
1598      * @see org.apache.poi.hssf.record.Record
1599      * @return record containing a HeaderRecord
1600      */
1601 
1602     protected Record createHeader()
1603     {
1604         HeaderRecord retval = new HeaderRecord();
1605 
1606         retval.setHeaderLength(( byte ) 0);
1607         retval.setHeader(null);
1608         return retval;
1609     }
1610 
1611     /**
1612      * creates the Footer Record and sets it to nothing/0 length
1613      * @see org.apache.poi.hssf.record.FooterRecord
1614      * @see org.apache.poi.hssf.record.Record
1615      * @return record containing a FooterRecord
1616      */
1617 
1618     protected Record createFooter()
1619     {
1620         FooterRecord retval = new FooterRecord();
1621 
1622         retval.setFooterLength(( byte ) 0);
1623         retval.setFooter(null);
1624         return retval;
1625     }
1626 
1627     /**
1628      * creates the HCenter Record and sets it to false (don't horizontally center)
1629      * @see org.apache.poi.hssf.record.HCenterRecord
1630      * @see org.apache.poi.hssf.record.Record
1631      * @return record containing a HCenterRecord
1632      */
1633 
1634     protected Record createHCenter()
1635     {
1636         HCenterRecord retval = new HCenterRecord();
1637 
1638         retval.setHCenter(false);
1639         return retval;
1640     }
1641 
1642     /**
1643      * creates the VCenter Record and sets it to false (don't horizontally center)
1644      * @see org.apache.poi.hssf.record.VCenterRecord
1645      * @see org.apache.poi.hssf.record.Record
1646      * @return record containing a VCenterRecord
1647      */
1648 
1649     protected Record createVCenter()
1650     {
1651         VCenterRecord retval = new VCenterRecord();
1652 
1653         retval.setVCenter(false);
1654         return retval;
1655     }
1656 
1657     /**
1658      * creates the PrintSetup Record and sets it to defaults and marks it invalid
1659      * @see org.apache.poi.hssf.record.PrintSetupRecord
1660      * @see org.apache.poi.hssf.record.Record
1661      * @return record containing a PrintSetupRecord
1662      */
1663 
1664     protected Record createPrintSetup()
1665     {
1666         PrintSetupRecord retval = new PrintSetupRecord();
1667 
1668         retval.setPaperSize(( short ) 1);
1669         retval.setScale(( short ) 100);
1670         retval.setPageStart(( short ) 1);
1671         retval.setFitWidth(( short ) 1);
1672         retval.setFitHeight(( short ) 1);
1673         retval.setOptions(( short ) 2);
1674         retval.setHResolution(( short ) 300);
1675         retval.setVResolution(( short ) 300);
1676         retval.setHeaderMargin( 0.5);
1677         retval.setFooterMargin( 0.5);
1678         retval.setCopies(( short ) 0);
1679         return retval;
1680     }
1681 
1682     /**
1683      * creates the DefaultColWidth Record and sets it to 8
1684      * @see org.apache.poi.hssf.record.DefaultColWidthRecord
1685      * @see org.apache.poi.hssf.record.Record
1686      * @return record containing a DefaultColWidthRecord
1687      */
1688 
1689     protected Record createDefaultColWidth()
1690     {
1691         DefaultColWidthRecord retval = new DefaultColWidthRecord();
1692 
1693         retval.setColWidth(( short ) 8);
1694         return retval;
1695     }
1696 
1697     /**
1698      * creates the ColumnInfo Record and sets it to a default column/width
1699      * @see org.apache.poi.hssf.record.ColumnInfoRecord
1700      * @return record containing a ColumnInfoRecord
1701      */
1702 
1703     protected Record createColInfo()
1704     {
1705         ColumnInfoRecord retval = new ColumnInfoRecord();
1706 
1707         retval.setColumnWidth(( short ) 0x8);
1708         retval.setOptions(( short ) 6);
1709         retval.setXFIndex(( short ) 0x0f);
1710         return retval;
1711     }
1712 
1713     /**
1714      * get the default column width for the sheet (if the columns do not define their own width)
1715      * @return default column width
1716      */
1717 
1718     public short getDefaultColumnWidth()
1719     {
1720         return defaultcolwidth.getColWidth();
1721     }
1722 
1723     /**
1724      * get whether gridlines are printed.
1725      * @return true if printed
1726      */
1727 
1728     public boolean isGridsPrinted()
1729     {
1730         return !gridset.getGridset();
1731     }
1732 
1733     /**
1734      * set whether gridlines printed or not.
1735      * @param value     True if gridlines printed.
1736      */
1737 
1738     public void setGridsPrinted(boolean value)
1739     {
1740         gridset.setGridset(!value);
1741     }
1742 
1743     /**
1744      * set the default column width for the sheet (if the columns do not define their own width)
1745      * @param dcw  default column width
1746      */
1747 
1748     public void setDefaultColumnWidth(short dcw)
1749     {
1750         defaultcolwidth.setColWidth(dcw);
1751     }
1752 
1753     /**
1754      * set the default row height for the sheet (if the rows do not define their own height)
1755      */
1756 
1757     public void setDefaultRowHeight(short dch)
1758     {
1759         defaultrowheight.setRowHeight(dch);
1760     }
1761 
1762     /**
1763      * get the default row height for the sheet (if the rows do not define their own height)
1764      * @return  default row height
1765      */
1766 
1767     public short getDefaultRowHeight()
1768     {
1769         return defaultrowheight.getRowHeight();
1770     }
1771 
1772     /**
1773      * get the width of a given column in units of 1/20th of a point width (twips?)
1774      * @param column index
1775      * @see org.apache.poi.hssf.record.DefaultColWidthRecord
1776      * @see org.apache.poi.hssf.record.ColumnInfoRecord
1777      * @see #setColumnWidth(short,short)
1778      * @return column width in units of 1/20th of a point (twips?)
1779      */
1780 
1781     public short getColumnWidth(short column)
1782     {
1783         short            retval = 0;
1784         ColumnInfoRecord ci     = null;
1785         int              k      = 0;
1786 
1787         if (columnSizes != null)
1788         {
1789             for (k = 0; k < columnSizes.size(); k++)
1790             {
1791                 ci = ( ColumnInfoRecord ) columnSizes.get(k);
1792                 if ((ci.getFirstColumn() >= column)
1793                         && (column <= ci.getLastColumn()))
1794                 {
1795                     break;
1796                 }
1797                 ci = null;
1798             }
1799         }
1800         if (ci != null)
1801         {
1802             retval = ci.getColumnWidth();
1803         }
1804         else
1805         {
1806             retval = defaultcolwidth.getColWidth();
1807         }
1808         return retval;
1809     }
1810 
1811     /**
1812      * set the width for a given column in 1/20th of a character width units
1813      * @param column - the column number
1814      * @param width (in units of 1/20th of a character width)
1815      */
1816 
1817     public void setColumnWidth(short column, short width)
1818     {
1819         ColumnInfoRecord ci = null;
1820         int              k  = 0;
1821 
1822         if (columnSizes == null)
1823         {
1824             columnSizes = new ArrayList();
1825         }
1826         //int cioffset = getDimsLoc() - columnSizes.size();
1827 
1828         for (k = 0; k < columnSizes.size(); k++)
1829         {
1830             ci = ( ColumnInfoRecord ) columnSizes.get(k);
1831             if ((ci.getFirstColumn() >= column)
1832                     && (column <= ci.getLastColumn()))
1833             {
1834                 break;
1835             }
1836             ci = null;
1837         }
1838         if (ci != null)
1839         {
1840             if (ci.getColumnWidth() == width)
1841             {
1842 
1843                 // do nothing...the cell's width is equal to what we're setting it to.
1844             }
1845             else if ((ci.getFirstColumn() == column)
1846                      && (ci.getLastColumn() == column))
1847             {                               // if its only for this cell then
1848                 ci.setColumnWidth(width);   // who cares, just change the width
1849             }
1850             else if ((ci.getFirstColumn() == column)
1851                      || (ci.getLastColumn() == column))
1852             {
1853 
1854                 // okay so the width is different but the first or last column == the column we'return setting
1855                 // we'll just divide the info and create a new one
1856                 if (ci.getFirstColumn() == column)
1857                 {
1858                     ci.setFirstColumn(( short ) (column + 1));
1859                 }
1860                 else
1861                 {
1862                     ci.setLastColumn(( short ) (column - 1));
1863                 }
1864                 ColumnInfoRecord nci = ( ColumnInfoRecord ) createColInfo();
1865 
1866                 nci.setFirstColumn(column);
1867                 nci.setLastColumn(column);
1868                 nci.setOptions(ci.getOptions());
1869                 nci.setXFIndex(ci.getXFIndex());
1870                 nci.setColumnWidth(width);
1871                 columnSizes.add(k, nci);
1872                 records.add((1 + getDimsLoc() - columnSizes.size()) + k, nci);
1873                 dimsloc++;
1874             }
1875         }
1876         else
1877         {
1878 
1879             // okay so there ISN'T a column info record that cover's this column so lets create one!
1880             ColumnInfoRecord nci = ( ColumnInfoRecord ) createColInfo();
1881 
1882             nci.setFirstColumn(column);
1883             nci.setLastColumn(column);
1884             nci.setColumnWidth(width);
1885             columnSizes.add(k, nci);
1886             records.add((1 + getDimsLoc() - columnSizes.size()) + k, nci);
1887             dimsloc++;
1888         }
1889     }
1890 
1891     /**
1892      * creates the Dimensions Record and sets it to bogus values (you should set this yourself
1893      * or let the high level API do it for you)
1894      * @see org.apache.poi.hssf.record.DimensionsRecord
1895      * @see org.apache.poi.hssf.record.Record
1896      * @return record containing a DimensionsRecord
1897      */
1898 
1899     protected Record createDimensions()
1900     {
1901         DimensionsRecord retval = new DimensionsRecord();
1902 
1903         retval.setFirstCol(( short ) 0);
1904         retval.setLastRow(1);             // one more than it is
1905         retval.setFirstRow(0);
1906         retval.setLastCol(( short ) 1);   // one more than it is
1907         return retval;
1908     }
1909 
1910     /**
1911      * creates the WindowTwo Record and sets it to:  <P>
1912      * options        = 0x6b6 <P>
1913      * toprow         = 0 <P>
1914      * leftcol        = 0 <P>
1915      * headercolor    = 0x40 <P>
1916      * pagebreakzoom  = 0x0 <P>
1917      * normalzoom     = 0x0 <p>
1918      * @see org.apache.poi.hssf.record.WindowTwoRecord
1919      * @see org.apache.poi.hssf.record.Record
1920      * @return record containing a WindowTwoRecord
1921      */
1922 
1923     protected Record createWindowTwo()
1924     {
1925         WindowTwoRecord retval = new WindowTwoRecord();
1926 
1927         retval.setOptions(( short ) 0x6b6);
1928         retval.setTopRow(( short ) 0);
1929         retval.setLeftCol(( short ) 0);
1930         retval.setHeaderColor(0x40);
1931         retval.setPageBreakZoom(( short ) 0);
1932         retval.setNormalZoom(( short ) 0);
1933         return retval;
1934     }
1935 
1936     /**
1937      * Creates the Selection record and sets it to nothing selected
1938      *
1939      * @see org.apache.poi.hssf.record.SelectionRecord
1940      * @see org.apache.poi.hssf.record.Record
1941      * @return record containing a SelectionRecord
1942      */
1943 
1944     protected Record createSelection()
1945     {
1946         SelectionRecord retval = new SelectionRecord();
1947 
1948         retval.setPane(( byte ) 0x3);
1949         retval.setActiveCellCol(( short ) 0x0);
1950         retval.setActiveCellRow(( short ) 0x0);
1951         retval.setNumRefs(( short ) 0x0);
1952         return retval;
1953     }
1954     
1955     /**
1956      * Returns the active row
1957      *
1958      * @see org.apache.poi.hssf.record.SelectionRecord
1959      * @return row the active row index
1960      */
1961     public int getActiveCellRow()
1962     {
1963         if (selection == null)
1964         {
1965             return 0;
1966         }
1967         return selection.getActiveCellRow();
1968     }
1969     
1970     /**
1971      * Sets the active row
1972      *
1973      * @param row the row index
1974      * @see org.apache.poi.hssf.record.SelectionRecord
1975      */
1976     public void setActiveCellRow(int row)
1977     {
1978         //shouldn't have a sheet w/o a SelectionRecord, but best to guard anyway
1979         if (selection != null)
1980         {
1981             selection.setActiveCellRow(row);
1982         }
1983     }
1984     
1985     /**
1986      * Returns the active column
1987      *
1988      * @see org.apache.poi.hssf.record.SelectionRecord
1989      * @return row the active column index
1990      */
1991     public short getActiveCellCol()
1992     {
1993         if (selection == null)
1994         {
1995             return (short) 0;
1996         }
1997         return selection.getActiveCellCol();
1998     }
1999     
2000     /**
2001      * Sets the active column
2002      *
2003      * @param col the column index
2004      * @see org.apache.poi.hssf.record.SelectionRecord
2005      */
2006     public void setActiveCellCol(short col)
2007     {
2008         //shouldn't have a sheet w/o a SelectionRecord, but best to guard anyway
2009         if (selection != null)
2010         {
2011             selection.setActiveCellCol(col);
2012         }
2013     }
2014 
2015     protected Record createMergedCells()
2016     {
2017         MergeCellsRecord retval = new MergeCellsRecord();
2018 
2019         retval.setNumAreas(( short ) 0);
2020         return retval;
2021     }
2022 
2023     /**
2024      * creates the EOF record
2025      * @see org.apache.poi.hssf.record.EOFRecord
2026      * @see org.apache.poi.hssf.record.Record
2027      * @return record containing a EOFRecord
2028      */
2029 
2030     protected Record createEOF()
2031     {
2032         return new EOFRecord();
2033     }
2034 
2035     /**
2036      * get the location of the DimensionsRecord (which is the last record before the value section)
2037      * @return location in the array of records of the DimensionsRecord
2038      */
2039 
2040     public int getDimsLoc()
2041     {
2042         log.log(log.DEBUG, "getDimsLoc dimsloc= " + dimsloc);
2043         return dimsloc;
2044     }
2045 
2046     /**
2047      * in the event the record is a dimensions record, resets both the loc index and dimsloc index
2048      */
2049 
2050     public void checkDimsLoc(Record rec, int recloc)
2051     {
2052         if (rec.getSid() == DimensionsRecord.sid)
2053         {
2054             loc     = recloc;
2055             dimsloc = recloc;
2056         }
2057     }
2058 
2059     public int getSize()
2060     {
2061         int retval = 0;
2062 
2063         for (int k = 0; k < records.size(); k++)
2064         {
2065             retval += (( Record ) records.get(k)).getRecordSize();
2066         }
2067         return retval;
2068     }
2069 
2070     public List getRecords()
2071     {
2072         return records;
2073     }
2074 
2075     /**
2076      * Gets the gridset record for this sheet.
2077      */
2078 
2079     public GridsetRecord getGridsetRecord()
2080     {
2081         return gridset;
2082     }
2083 
2084     /**
2085      * Returns the first occurance of a record matching a particular sid.
2086      */
2087 
2088     public Record findFirstRecordBySid(short sid)
2089     {
2090         for (Iterator iterator = records.iterator(); iterator.hasNext(); )
2091         {
2092             Record record = ( Record ) iterator.next();
2093 
2094             if (record.getSid() == sid)
2095             {
2096                 return record;
2097             }
2098         }
2099         return null;
2100     }
2101 
2102     /**
2103      * Sets the SCL record or creates it in the correct place if it does not
2104      * already exist.
2105      *
2106      * @param sclRecord     The record to set.
2107      */
2108     public void setSCLRecord(SCLRecord sclRecord)
2109     {
2110         int oldRecordLoc = findFirstRecordLocBySid(SCLRecord.sid);
2111         if (oldRecordLoc == -1)
2112         {
2113             // Insert it after the window record
2114             int windowRecordLoc = findFirstRecordLocBySid(WindowTwoRecord.sid);
2115             records.add(windowRecordLoc+1, sclRecord);
2116         }
2117         else
2118         {
2119             records.set(oldRecordLoc, sclRecord);
2120         }
2121 
2122     }
2123 
2124     /**
2125      * Finds the first occurance of a record matching a particular sid and
2126      * returns it's position.
2127      * @param sid   the sid to search for
2128      * @return  the record position of the matching record or -1 if no match
2129      *          is made.
2130      */
2131     public int findFirstRecordLocBySid( short sid )
2132     {
2133         int index = 0;
2134         for (Iterator iterator = records.iterator(); iterator.hasNext(); )
2135         {
2136             Record record = ( Record ) iterator.next();
2137 
2138             if (record.getSid() == sid)
2139             {
2140                 return index;
2141             }
2142             index++;
2143         }
2144         return -1;
2145     }
2146 
2147     /**
2148      * Returns the HeaderRecord.
2149      * @return HeaderRecord for the sheet.
2150      */
2151     public HeaderRecord getHeader ()
2152     {
2153 	return header;
2154     }
2155 
2156     /**
2157      * Sets the HeaderRecord.
2158      * @param newHeader The new HeaderRecord for the sheet.
2159      */
2160     public void setHeader (HeaderRecord newHeader)
2161     {
2162     	header = newHeader;
2163     }
2164 
2165     /**
2166      * Returns the FooterRecord.
2167      * @return FooterRecord for the sheet.
2168      */
2169     public FooterRecord getFooter ()
2170     {
2171 	    return footer;
2172     }
2173 
2174     /**
2175      * Sets the FooterRecord.
2176      * @param newFooter The new FooterRecord for the sheet.
2177      */
2178     public void setFooter (FooterRecord newFooter)
2179     {
2180 	    footer = newFooter;
2181     }
2182 
2183     /**
2184      * Returns the PrintSetupRecord.
2185      * @return PrintSetupRecord for the sheet.
2186      */
2187     public PrintSetupRecord getPrintSetup ()
2188     {
2189 	    return printSetup;
2190     }
2191 
2192     /**
2193      * Sets the PrintSetupRecord.
2194      * @param newPrintSetup The new PrintSetupRecord for the sheet.
2195      */
2196     public void setPrintSetup (PrintSetupRecord newPrintSetup)
2197     {
2198 	    printSetup = newPrintSetup;
2199     }
2200 
2201     /**
2202      * Returns the PrintGridlinesRecord.
2203      * @return PrintGridlinesRecord for the sheet.
2204      */
2205     public PrintGridlinesRecord getPrintGridlines ()
2206     {
2207 	    return printGridlines;
2208     }
2209 
2210     /**
2211      * Sets the PrintGridlinesRecord.
2212      * @param newPrintGridlines The new PrintGridlinesRecord for the sheet.
2213      */
2214     public void setPrintGridlines (PrintGridlinesRecord newPrintGridlines)
2215     {
2216 	    printGridlines = newPrintGridlines;
2217     }
2218 
2219     /**
2220      * Sets whether the sheet is selected
2221      * @param sel True to select the sheet, false otherwise.
2222      */
2223     public void setSelected(boolean sel) {
2224         WindowTwoRecord windowTwo = (WindowTwoRecord) findFirstRecordBySid(WindowTwoRecord.sid);
2225         windowTwo.setSelected(sel);
2226     }
2227 
2228      /**
2229       * Gets the size of the margin in inches.
2230       * @param margin which margin to get
2231       * @return the size of the margin
2232       */
2233      public double getMargin(short margin) {
2234          Margin m;
2235          switch ( margin )
2236          {
2237              case LeftMargin:
2238                  m = (Margin) findFirstRecordBySid( LeftMarginRecord.sid );
2239                  if ( m == null )
2240                      return .75;
2241                  break;
2242              case RightMargin:
2243                  m = (Margin) findFirstRecordBySid( RightMarginRecord.sid );
2244                  if ( m == null )
2245                      return .75;
2246                  break;
2247              case TopMargin:
2248                  m = (Margin) findFirstRecordBySid( TopMarginRecord.sid );
2249                  if ( m == null )
2250                      return 1.0;
2251                  break;
2252              case BottomMargin:
2253                  m = (Margin) findFirstRecordBySid( BottomMarginRecord.sid );
2254                  if ( m == null )
2255                      return 1.0;
2256                  break;
2257              default :
2258                  throw new RuntimeException( "Unknown margin constant:  " + margin );
2259          }
2260          return m.getMargin();
2261      }
2262 
2263      /**
2264       * Sets the size of the margin in inches.
2265       * @param margin which margin to get
2266       * @param size the size of the margin
2267       */
2268      public void setMargin(short margin, double size) {
2269          Margin m;
2270          switch ( margin )
2271          {
2272              case LeftMargin:
2273                  m = (Margin) findFirstRecordBySid( LeftMarginRecord.sid );
2274                  if ( m == null )
2275                  {
2276                      m = new LeftMarginRecord();
2277                      records.add( getDimsLoc() + 1, m );
2278                  }
2279                  break;
2280              case RightMargin:
2281                  m = (Margin) findFirstRecordBySid( RightMarginRecord.sid );
2282                  if ( m == null )
2283                  {
2284                      m = new RightMarginRecord();
2285                      records.add( getDimsLoc() + 1, m );
2286                  }
2287                  break;
2288              case TopMargin:
2289                  m = (Margin) findFirstRecordBySid( TopMarginRecord.sid );
2290                  if ( m == null )
2291                  {
2292                      m = new TopMarginRecord();
2293                      records.add( getDimsLoc() + 1, m );
2294                  }
2295                  break;
2296              case BottomMargin:
2297                  m = (Margin) findFirstRecordBySid( BottomMarginRecord.sid );
2298                  if ( m == null )
2299                  {
2300                      m = new BottomMarginRecord();
2301                      records.add( getDimsLoc() + 1, m );
2302                  }
2303                  break;
2304              default :
2305                  throw new RuntimeException( "Unknown margin constant:  " + margin );
2306          }
2307          m.setMargin( size );
2308      }
2309 
2310     public int getEofLoc()
2311     {
2312         return eofLoc;
2313     }
2314 
2315     /**
2316      * Creates a split (freezepane).
2317      * @param colSplit      Horizonatal position of split.
2318      * @param rowSplit      Vertical position of split.
2319      * @param topRow        Top row visible in bottom pane
2320      * @param leftmostColumn   Left column visible in right pane.
2321      */
2322     public void createFreezePane(int colSplit, int rowSplit, int topRow, int leftmostColumn )
2323     {
2324         int loc = findFirstRecordLocBySid(WindowTwoRecord.sid);
2325         PaneRecord pane = new PaneRecord();
2326         pane.setX((short)colSplit);
2327         pane.setY((short)rowSplit);
2328         pane.setTopRow((short) topRow);
2329         pane.setLeftColumn((short) leftmostColumn);
2330         if (rowSplit == 0)
2331         {
2332             pane.setTopRow((short)0);
2333             pane.setActivePane((short)1);
2334         }
2335         else if (colSplit == 0)
2336         {
2337             pane.setLeftColumn((short)64);
2338             pane.setActivePane((short)2);
2339         }
2340         else
2341         {
2342             pane.setActivePane((short)0);
2343         }
2344         records.add(loc+1, pane);
2345 
2346         WindowTwoRecord windowRecord = (WindowTwoRecord) records.get(loc);
2347         windowRecord.setFreezePanes(true);
2348         windowRecord.setFreezePanesNoSplit(true);
2349 
2350         SelectionRecord sel = (SelectionRecord) findFirstRecordBySid(SelectionRecord.sid);
2351 //        SelectionRecord sel2 = (SelectionRecord) sel.clone();
2352 //        SelectionRecord sel3 = (SelectionRecord) sel.clone();
2353 //        SelectionRecord sel4 = (SelectionRecord) sel.clone();
2354 //        sel.setPane(PANE_LOWER_RIGHT);         // 0
2355 //        sel3.setPane(PANE_UPPER_RIGHT);        // 1
2356         sel.setPane((byte)pane.getActivePane());         // 2
2357 //        sel2.setPane(PANE_UPPER_LEFT);         // 3
2358 //        sel4.setActiveCellCol((short)Math.max(sel3.getActiveCellCol(), colSplit));
2359 //        sel3.setActiveCellRow((short)Math.max(sel4.getActiveCellRow(), rowSplit));
2360 
2361         int selLoc = findFirstRecordLocBySid(SelectionRecord.sid);
2362 //        sel.setActiveCellCol((short)15);
2363 //        sel.setActiveCellRow((short)15);
2364 //        sel2.setActiveCellCol((short)0);
2365 //        sel2.setActiveCellRow((short)0);
2366 
2367 //        records.add(selLoc+1,sel2);
2368 //        records.add(selLoc+2,sel3);
2369 //        records.add(selLoc+3,sel4);
2370     }
2371 
2372     /**
2373      * Creates a split pane.
2374      * @param xSplitPos      Horizonatal position of split (in 1/20th of a point).
2375      * @param ySplitPos      Vertical position of split (in 1/20th of a point).
2376      * @param topRow        Top row visible in bottom pane
2377      * @param leftmostColumn   Left column visible in right pane.
2378      * @param activePane    Active pane.  One of: PANE_LOWER_RIGHT,
2379      *                      PANE_UPPER_RIGHT, PANE_LOWER_LEFT, PANE_UPPER_LEFT
2380      * @see #PANE_LOWER_LEFT
2381      * @see #PANE_LOWER_RIGHT
2382      * @see #PANE_UPPER_LEFT
2383      * @see #PANE_UPPER_RIGHT
2384      */
2385     public void createSplitPane(int xSplitPos, int ySplitPos, int topRow, int leftmostColumn, int activePane )
2386     {
2387         int loc = findFirstRecordLocBySid(WindowTwoRecord.sid);
2388         PaneRecord r = new PaneRecord();
2389         r.setX((short)xSplitPos);
2390         r.setY((short)ySplitPos);
2391         r.setTopRow((short) topRow);
2392         r.setLeftColumn((short) leftmostColumn);
2393         r.setActivePane((short) activePane);
2394         records.add(loc+1, r);
2395 
2396         WindowTwoRecord windowRecord = (WindowTwoRecord) records.get(loc);
2397         windowRecord.setFreezePanes(false);
2398         windowRecord.setFreezePanesNoSplit(false);
2399 
2400         SelectionRecord sel = (SelectionRecord) findFirstRecordBySid(SelectionRecord.sid);
2401 //        SelectionRecord sel2 = (SelectionRecord) sel.clone();
2402 //        SelectionRecord sel3 = (SelectionRecord) sel.clone();
2403 //        SelectionRecord sel4 = (SelectionRecord) sel.clone();
2404         sel.setPane(PANE_LOWER_RIGHT);         // 0
2405 //        sel3.setPane(PANE_UPPER_RIGHT);        // 1
2406 //        sel4.setPane(PANE_LOWER_LEFT);         // 2
2407 //        sel2.setPane(PANE_UPPER_LEFT);         // 3
2408 //        sel4.setActiveCellCol((short)Math.max(sel3.getActiveCellCol(), colSplit));
2409 //        sel3.setActiveCellRow((short)Math.max(sel4.getActiveCellRow(), rowSplit));
2410 
2411         int selLoc = findFirstRecordLocBySid(SelectionRecord.sid);
2412 //        sel.setActiveCellCol((short)15);
2413 //        sel.setActiveCellRow((short)15);
2414 //        sel2.setActiveCellCol((short)0);
2415 //        sel2.setActiveCellRow((short)0);
2416 
2417 //        records.add(selLoc+1,sel2);
2418 //        records.add(selLoc+2,sel3);
2419 //        records.add(selLoc+3,sel4);
2420     }
2421 
2422     public SelectionRecord getSelection()
2423     {
2424         return selection;
2425     }
2426 
2427     public void setSelection( SelectionRecord selection )
2428     {
2429         this.selection = selection;
2430     }
2431 
2432 }
2433