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.io.OutputStream;
59   
60   import java.util.List;
61   import java.util.ArrayList;
62   import java.util.Iterator;
63   
64   import org.apache.poi.util.POILogFactory;
65   import org.apache.poi.hssf
66       .record.*;       // normally I don't do this, buy we literally mean ALL
67   import org.apache.poi.hssf.record.formula.FormulaParser;
68   import org.apache.poi.hssf.record.formula.Ptg;
69   import org.apache.poi.util.IntList;
70   import org.apache.poi.util.POILogger;
71   import org.apache.poi.hssf.record
72       .aggregates.*;   // normally I don't do this, buy we literally mean ALL
73   
74   /**
75    * Low level model implementation of a Sheet (one workbook contains many sheets)
76    * This file contains the low level binary records starting at the sheets BOF and
77    * ending with the sheets EOF.  Use HSSFSheet for a high level representation.
78    * <P>
79    * The structures of the highlevel API use references to this to perform most of their
80    * operations.  Its probably unwise to use these low level structures directly unless you
81    * really know what you're doing.  I recommend you read the Microsoft Excel 97 Developer's
82    * Kit (Microsoft Press) and the documentation at http://sc.openoffice.org/excelfileformat.pdf
83    * before even attempting to use this.
84    * <P>
85    * @author  Andrew C. Oliver (acoliver at apache dot org)
86    * @author  Glen Stampoultzis (glens at apache.org)
87    * @see org.apache.poi.hssf.model.Workbook
88    * @see org.apache.poi.hssf.usermodel.HSSFSheet
89    * @author  Shawn Laubach (laubach at acm.org) Just Gridlines, Headers, Footers, and PrintSetup
90    * @version 1.0-pre
91    */
92   
93   public class Sheet
94       extends java.lang.java.lang.Objecttected ArrayList              records        = null;
95       int                              preoffset      =
96           0;      // offset of the sheet in a new file
97       int                              loc            = 0;
98       protected boolean                containsLabels = false;
99       ;
100      protected int                    dimsloc        = 0;
101      protected DimensionsRecord       dims;
102      protected DefaultColWidthRecord  defaultcolwidth  = null;
103      protected DefaultRowHeightRecord defaultrowheight = null;
104      protected GridsetRecord          gridset          = null;
105      protected PrintSetupRecord printSetup             = null;
106      protected HeaderRecord header                     = null;
107      protected FooterRecord footer                     = null;
108      protected PrintGridlinesRecord printGridlines     = null;
109      protected MergeCellsRecord       merged           = null;
110      protected int                    mergedloc        = 0;
111      private static POILogger         log              =
112          POILogFactory.getLogger(Sheet.class);
113      private ArrayList                columnSizes      =
114          null;   // holds column info
115      protected ValueRecordsAggregate  cells            = null;
116      protected RowRecordsAggregate    rows             = null;
117      private Iterator                 valueRecIterator = null;
118      private Iterator                 rowRecIterator   = null;
119  
120      /**
121       * Creates new Sheet with no intialization --useless at this point
122       * @see #createSheet(List,int,int)
123       */
124  
125      public Sheet()
126      {
127      }
128  
129      /**
130       * read support  (offset used as starting point for search) for low level
131       * API.  Pass in an array of Record objects, the sheet number (0 based) and
132       * a record offset (should be the location of the sheets BOF record).  A Sheet
133       * object is constructed and passed back with all of its initialization set
134       * to the passed in records and references to those records held. This function
135       * is normally called via Workbook.
136       *
137       * @param recs array containing those records in the sheet in sequence (normally obtained from RecordFactory)
138       * @param sheetnum integer specifying the sheet's number (0,1 or 2 in this release)
139       * @param offset of the sheet's BOF record
140       *
141       * @return Sheet object with all values set to those read from the file
142       *
143       * @see org.apache.poi.hssf.model.Workbook
144       * @see org.apache.poi.hssf.record.Record
145       */
146      public static Sheet createSheet(List recs, int sheetnum, int offset)
147      {
148          log.logFormatted(log.DEBUG,
149                           "Sheet createSheet (existing file) with %",
150                           new Integer(recs.size()));
151          Sheet     retval             = new Sheet();
152          ArrayList records            = new ArrayList(recs.size() / 5);
153          boolean   isfirstcell        = true;
154          boolean   isfirstrow         = true;
155          int       bofEofNestingLevel = 0;
156  
157          for (int k = offset; k < recs.size(); k++)
158          {
159              Record rec = ( Record ) recs.get(k);
160  
161              if (rec.getSid() == LabelRecord.sid)
162              {
163                  log.log(log.DEBUG, "Hit label record");
164                  retval.containsLabels = true;
165              }
166              else if (rec.getSid() == BOFRecord.sid)
167              {
168                  bofEofNestingLevel++;
169              }
170              else if ((rec.getSid() == EOFRecord.sid)
171                       && (--bofEofNestingLevel == 0))
172              {
173                  log.log(log.DEBUG, "Hit EOF record at ");
174                  records.add(rec);
175                  break;
176              }
177              else if (rec.getSid() == DimensionsRecord.sid)
178              {
179                  retval.dims    = ( DimensionsRecord ) rec;
180                  retval.dimsloc = records.size();
181              }
182              else if (rec.getSid() == MergeCellsRecord.sid)
183              {
184                  retval.merged    = ( MergeCellsRecord ) rec;
185                  retval.mergedloc = k - offset;
186              }
187              else if (rec.getSid() == ColumnInfoRecord.sid)
188              {
189                  if (retval.columnSizes == null)
190                  {
191                      retval.columnSizes = new ArrayList();
192                  }
193                  retval.columnSizes.add(( ColumnInfoRecord ) rec);
194              }
195              else if (rec.getSid() == DefaultColWidthRecord.sid)
196              {
197                  retval.defaultcolwidth = ( DefaultColWidthRecord ) rec;
198              }
199              else if (rec.getSid() == DefaultRowHeightRecord.sid)
200              {
201                  retval.defaultrowheight = ( DefaultRowHeightRecord ) rec;
202              }
203              else if (rec.isValue())
204              {
205                  if (isfirstcell)
206                  {
207                      retval.cells = new ValueRecordsAggregate();
208                      rec          = retval.cells;
209                      retval.cells.construct(k, recs);
210                      isfirstcell = false;
211                  }
212                  else
213                  {
214                      rec = null;
215                  }
216              }
217              else if (rec.getSid() == RowRecord.sid)
218              {
219                  if (isfirstrow)
220                  {
221                      retval.rows = new RowRecordsAggregate();
222                      rec         = retval.rows;
223                      retval.rows.construct(k, recs);
224                      isfirstrow = false;
225                  }
226                  else
227                  {
228                      rec = null;
229                  }
230              }
231  	    else if (rec.getSid () == PrintGridlinesRecord.sid)
232  		{
233  		    retval.printGridlines = (PrintGridlinesRecord) rec;
234  		}
235  	    else if (rec.getSid () == HeaderRecord.sid)
236  		{
237  		    retval.header = (HeaderRecord) rec;
238  		}
239  	    else if (rec.getSid () == FooterRecord.sid)
240  		{
241  		    retval.footer = (FooterRecord) rec;
242  		}
243  	    else if (rec.getSid () == PrintSetupRecord.sid)
244  		{
245  		    retval.printSetup = (PrintSetupRecord) rec;
246  		}
247  
248              if (rec != null)
249              {
250                  records.add(rec);
251              }
252          }
253          retval.records = records;
254          log.log(log.DEBUG, "sheet createSheet (existing file) exited");
255          return retval;
256      }
257  
258      /**
259       * read support  (offset = 0) Same as createSheet(Record[] recs, int, int)
260       * only the record offset is assumed to be 0.
261       *
262       * @param records  array containing those records in the sheet in sequence (normally obtained from RecordFactory)
263       * @param sheetnum integer specifying the sheet's number (0,1 or 2 in this release)
264       * @return Sheet object
265       */
266  
267      public static Sheet createSheet(List records, int sheetnum)
268      {
269          log.log(log.DEBUG,
270                  "Sheet createSheet (exisiting file) assumed offset 0");
271          return createSheet(records, sheetnum, 0);
272      }
273  
274      /**
275       * Creates a sheet with all the usual records minus values and the "index"
276       * record (not required).  Sets the location pointer to where the first value
277       * records should go.  Use this to create a sheet from "scratch".
278       *
279       * @return Sheet object with all values set to defaults
280       */
281  
282      public static Sheet createSheet()
283      {
284          log.log(log.DEBUG, "Sheet createsheet from scratch called");
285          Sheet     retval  = new Sheet();
286          ArrayList records = new ArrayList(30);
287  
288          records.add(retval.createBOF());
289  
290          // records.add(retval.createIndex());
291          records.add(retval.createCalcMode());
292          records.add(retval.createCalcCount());
293          records.add(retval.createRefMode());
294          records.add(retval.createIteration());
295          records.add(retval.createDelta());
296          records.add(retval.createSaveRecalc());
297          records.add(retval.createPrintHeaders());
298  	retval.printGridlines = (PrintGridlinesRecord) retval.createPrintGridlines ();
299  	records.add (retval.printGridlines);
300          retval.gridset = ( GridsetRecord ) retval.createGridset();
301          records.add(retval.gridset);
302          records.add(retval.createGuts());
303          retval.defaultrowheight =
304              ( DefaultRowHeightRecord ) retval.createDefaultRowHeight();
305          records.add(retval.defaultrowheight);
306          records.add(retval.createWSBool());
307  	retval.header = (HeaderRecord) retval.createHeader ();
308  	records.add (retval.header);
309  	retval.footer = (FooterRecord) retval.createFooter ();
310  	records.add (retval.footer);
311          records.add(retval.createHCenter());
312          records.add(retval.createVCenter());
313  	retval.printSetup = (PrintSetupRecord) retval.createPrintSetup ();
314  	records.add (retval.printSetup);
315          retval.defaultcolwidth =
316              ( DefaultColWidthRecord ) retval.createDefaultColWidth();
317          records.add(retval.defaultcolwidth);
318          retval.dims    = ( DimensionsRecord ) retval.createDimensions();
319          retval.dimsloc = 19;
320          records.add(retval.dims);
321          records.add(retval.createWindowTwo());
322          retval.setLoc(records.size() - 1);
323          records.add(retval.createSelection());
324          records.add(retval.createEOF());
325          retval.records = records;
326          log.log(log.DEBUG, "Sheet createsheet from scratch exit");
327          return retval;
328      }
329  
330      private void checkCells()
331      {
332          if (cells == null)
333          {
334              cells = new ValueRecordsAggregate();
335              records.add(getDimsLoc() + 1, cells);
336          }
337      }
338  
339      private void checkRows()
340      {
341          if (rows == null)
342          {
343              rows = new RowRecordsAggregate();
344              records.add(getDimsLoc() + 1, rows);
345          }
346      }
347  
348      //public int addMergedRegion(short rowFrom, short colFrom, short rowTo,
349      public int addMergedRegion(int rowFrom, short colFrom, int rowTo,
350                                 short colTo)
351      {
352          if (merged == null)
353          {
354              merged    = ( MergeCellsRecord ) createMergedCells();
355              mergedloc = records.size() - 1;
356              records.add(records.size() - 1, merged);
357          }
358          return merged.addArea(rowFrom, colFrom, rowTo, colTo);
359      }
360  
361      public void removeMergedRegion(int index)
362      {
363          merged.removeAreaAt(index);
364          if (merged.getNumAreas() == 0)
365          {
366              merged = null;
367              records.remove(mergedloc);
368              mergedloc = 0;
369          }
370      }
371  
372      public MergeCellsRecord.MergedRegion getMergedRegionAt(int index)
373      {
374          return merged.getAreaAt(index);
375      }
376  
377      public int getNumMergedRegions()
378      {
379          return merged.getNumAreas();
380      }
381  
382      /**
383       * This is basically a kludge to deal with the now obsolete Label records.  If
384       * you have to read in a sheet that contains Label records, be aware that the rest
385       * of the API doesn't deal with them, the low level structure only provides read-only
386       * semi-immutable structures (the sets are there for interface conformance with NO
387       * impelmentation).  In short, you need to call this function passing it a reference
388       * to the Workbook object.  All labels will be converted to LabelSST records and their
389       * contained strings will be written to the Shared String tabel (SSTRecord) within
390       * the Workbook.
391       *
392       * @param wb sheet's matching low level Workbook structure containing the SSTRecord.
393       * @see org.apache.poi.hssf.record.LabelRecord
394       * @see org.apache.poi.hssf.record.LabelSSTRecord
395       * @see org.apache.poi.hssf.record.SSTRecord
396       */
397  
398      public void convertLabelRecords(Workbook wb)
399      {
400          log.log(log.DEBUG, "convertLabelRecords called");
401          if (containsLabels)
402          {
403              for (int k = 0; k < records.size(); k++)
404              {
405                  Record rec = ( Record ) records.get(k);
406  
407                  if (rec.getSid() == LabelRecord.sid)
408                  {
409                      LabelRecord oldrec = ( LabelRecord ) rec;
410  
411                      records.remove(k);
412                      LabelSSTRecord newrec   = new LabelSSTRecord();
413                      int            stringid =
414                          wb.addSSTString(oldrec.getValue());
415  
416                      newrec.setRow(oldrec.getRow());
417                      newrec.setColumn(oldrec.getColumn());
418                      newrec.setXFIndex(oldrec.getXFIndex());
419                      newrec.setSSTIndex(stringid);
420                      records.add(k, newrec);
421                  }
422              }
423          }
424          log.log(log.DEBUG, "convertLabelRecords exit");
425      }
426  
427      /**
428       * Returns the number of low level binary records in this sheet.  This adjusts things for the so called
429       * AgregateRecords.
430       *
431       * @see org.apache.poi.hssf.record.Record
432       */
433  
434      public int getNumRecords()
435      {
436          checkCells();
437          checkRows();
438          log.log(log.DEBUG, "Sheet.getNumRecords");
439          log.logFormatted(log.DEBUG, "returning % + % + % - 2 = %", new int[]
440          {
441              records.size(), cells.getPhysicalNumberOfCells(),
442              rows.getPhysicalNumberOfRows(),
443              records.size() + cells.getPhysicalNumberOfCells()
444              + rows.getPhysicalNumberOfRows() - 2
445          });
446          return records.size() + cells.getPhysicalNumberOfCells()
447                 + rows.getPhysicalNumberOfRows() - 2;
448      }
449  
450      /**
451       * Per an earlier reported bug in working with Andy Khan's excel read library.  This
452       * sets the values in the sheet's DimensionsRecord object to be correct.  Excel doesn't
453       * really care, but we want to play nice with other libraries.
454       *
455       * @see org.apache.poi.hssf.record.DimensionsRecord
456       */
457  
458      //public void setDimensions(short firstrow, short firstcol, short lastrow,
459      public void setDimensions(int firstrow, short firstcol, int lastrow,
460                                short lastcol)
461      {
462          log.log(log.DEBUG, "Sheet.setDimensions");
463          log.log(log.DEBUG,
464                  (new StringBuffer("firstrow")).append(firstrow)
465                      .append("firstcol").append(firstcol).append("lastrow")
466                      .append(lastrow).append("lastcol").append(lastcol)
467                      .toString());
468          dims.setFirstCol(firstcol);
469          dims.setFirstRow(firstrow);
470          dims.setLastCol(lastcol);
471          dims.setLastRow(lastrow);
472          log.log(log.DEBUG, "Sheet.setDimensions exiting");
473      }
474  
475      /**
476       * set the locator for where we should look for the next value record.  The
477       * algorythm will actually start here and find the correct location so you
478       * can set this to 0 and watch performance go down the tubes but it will work.
479       * After a value is set this is automatically advanced.  Its also set by the
480       * create method.  So you probably shouldn't mess with this unless you have
481       * a compelling reason why or the help for the method you're calling says so.
482       * Check the other methods for whether they care about
483       * the loc pointer.  Many of the "modify" and "remove" methods re-initialize this
484       * to "dimsloc" which is the location of the Dimensions Record and presumably the
485       * start of the value section (at or around 19 dec).
486       *
487       * @param loc the record number to start at
488       *
489       */
490  
491      public void setLoc(int loc)
492      {
493          valueRecIterator = null;
494          log.log(log.DEBUG, "sheet.setLoc(): " + loc);
495          this.loc = loc;
496      }
497  
498      /**
499       * Returns the location pointer to the first record to look for when adding rows/values
500       *
501       */
502  
503      public int getLoc()
504      {
505          log.log(log.DEBUG, "sheet.getLoc():" + loc);
506          return loc;
507      }
508  
509      /**
510       * Set the preoffset when using DBCELL records (currently unused) - this is
511       * the position of this sheet within the whole file.
512       *
513       * @param offset the offset of the sheet's BOF within the file.
514       */
515  
516      public void setPreOffset(int offset)
517      {
518          this.preoffset = offset;
519      }
520  
521      /**
522       * get the preoffset when using DBCELL records (currently unused) - this is
523       * the position of this sheet within the whole file.
524       *
525       * @return offset the offset of the sheet's BOF within the file.
526       */
527  
528      public int getPreOffset()
529      {
530          return preoffset;
531      }
532  
533      /**
534       * Serializes all records in the sheet into one big byte array.  Use this to write
535       * the sheet out.
536       *
537       * @return byte[] array containing the binary representation of the records in this sheet
538       *
539       */
540  
541      public byte [] serialize()
542      {
543          log.log(log.DEBUG, "Sheet.serialize");
544  
545          // addDBCellRecords();
546          byte[] retval    = null;
547  
548          // ArrayList bytes     = new ArrayList(4096);
549          int    arraysize = getSize();
550          int    pos       = 0;
551  
552          // for (int k = 0; k < records.size(); k++)
553          // {
554          // bytes.add((( Record ) records.get(k)).serialize());
555          //
556          // }
557          // for (int k = 0; k < bytes.size(); k++)
558          // {
559          // arraysize += (( byte [] ) bytes.get(k)).length;
560          // log.debug((new StringBuffer("arraysize=")).append(arraysize)
561          // .toString());
562          // }
563          retval = new byte[ arraysize ];
564          for (int k = 0; k < records.size(); k++)
565          {
566  
567              // byte[] rec = (( byte [] ) bytes.get(k));
568              // System.arraycopy(rec, 0, retval, pos, rec.length);
569              pos += (( Record ) records.get(k)).serialize(pos,
570                      retval);   // rec.length;
571          }
572          log.log(log.DEBUG, "Sheet.serialize returning " + retval);
573          return retval;
574      }
575  
576      /**
577       * Serializes all records in the sheet into one big byte array.  Use this to write
578       * the sheet out.
579       *
580       * @param offset to begin write at
581       * @param data   array containing the binary representation of the records in this sheet
582       *
583       */
584  
585      public int serialize(int offset, byte [] data)
586      {
587          log.log(log.DEBUG, "Sheet.serialize using offsets");
588  
589          // addDBCellRecords();
590          // ArrayList bytes     = new ArrayList(4096);
591          // int arraysize = getSize();   // 0;
592          int pos       = 0;
593  
594          // for (int k = 0; k < records.size(); k++)
595          // {
596          // bytes.add((( Record ) records.get(k)).serialize());
597          //
598          // }
599          // for (int k = 0; k < bytes.size(); k++)
600          // {
601          // arraysize += (( byte [] ) bytes.get(k)).length;
602          // log.debug((new StringBuffer("arraysize=")).append(arraysize)
603          // .toString());
604          // }
605          for (int k = 0; k < records.size(); k++)
606          {
607  
608              // byte[] rec = (( byte [] ) bytes.get(k));
609              // System.arraycopy(rec, 0, data, offset + pos, rec.length);
610              pos += (( Record ) records.get(k)).serialize(pos + offset,
611                      data);   // rec.length;
612          }
613          log.log(log.DEBUG, "Sheet.serialize returning ");
614          return pos;
615      }
616  
617      /**
618       * Create a row record.  (does not add it to the records contained in this sheet)
619       *
620       * @param row number
621       * @return RowRecord created for the passed in row number
622       * @see org.apache.poi.hssf.record.RowRecord
623       */
624  
625      public RowRecord createRow(int row)
626      {
627          log.log(log.DEBUG, "create row number " + row);
628          RowRecord rowrec = new RowRecord();
629  
630          //rowrec.setRowNumber(( short ) row);
631          rowrec.setRowNumber(row);
632          rowrec.setHeight(( short ) 0xff);
633          rowrec.setOptimize(( short ) 0x0);
634          rowrec.setOptionFlags(( short ) 0x0);
635          rowrec.setXFIndex(( short ) 0x0);
636          return rowrec;
637      }
638  
639      /**
640       * Create a LABELSST Record (does not add it to the records contained in this sheet)
641       *
642       * @param row the row the LabelSST is a member of
643       * @param col the column the LabelSST defines
644       * @param index the index of the string within the SST (use workbook addSSTString method)
645       * @return LabelSSTRecord newly created containing your SST Index, row,col.
646       * @see org.apache.poi.hssf.record.SSTRecord
647       */
648  
649      //public LabelSSTRecord createLabelSST(short row, short col, int index)
650      public LabelSSTRecord createLabelSST(int row, short col, int index)
651      {
652          log.logFormatted(log.DEBUG, "create labelsst row,col,index %,%,%",
653                           new int[]
654          {
655              row, col, index
656          });
657          LabelSSTRecord rec = new LabelSSTRecord();
658  
659          rec.setRow(row);
660          rec.setColumn(col);
661          rec.setSSTIndex(index);
662          rec.setXFIndex(( short ) 0x0f);
663          return rec;
664      }
665  
666      /**
667       * Create a NUMBER Record (does not add it to the records contained in this sheet)
668       *
669       * @param row the row the NumberRecord is a member of
670       * @param col the column the NumberRecord defines
671       * @param value for the number record
672       *
673       * @return NumberRecord for that row, col containing that value as added to the sheet
674       */
675  
676      //public NumberRecord createNumber(short row, short col, double value)
677      public NumberRecord createNumber(int row, short col, double value)
678      {
679          log.logFormatted(log.DEBUG, "create number row,col,value %,%,%",
680                           new double[]
681          {
682              row, col, value
683          });
684          NumberRecord rec = new NumberRecord();
685  
686          //rec.setRow(( short ) row);
687          rec.setRow(row);
688          rec.setColumn(col);
689          rec.setValue(value);
690          rec.setXFIndex(( short ) 0x0f);
691          return rec;
692      }
693  
694      /**
695       * create a BLANK record (does not add it to the records contained in this sheet)
696       *
697       * @param row - the row the BlankRecord is a member of
698       * @param col - the column the BlankRecord is a member of
699       */
700  
701      //public BlankRecord createBlank(short row, short col)
702      public BlankRecord createBlank(int row, short col)
703      {
704          //log.logFormatted(log.DEBUG, "create blank row,col %,%", new short[]
705          log.logFormatted(log.DEBUG, "create blank row,col %,%", new int[]
706          {
707              row, col
708          });
709          BlankRecord rec = new BlankRecord();
710  
711          //rec.setRow(( short ) row);
712          rec.setRow(row);
713          rec.setColumn(col);
714          rec.setXFIndex(( short ) 0x0f);
715          return rec;
716      }
717  
718      /**
719       * Attempts to parse the formula into PTGs and create a formula record
720       * DOES NOT WORK YET
721       *
722       * @param row - the row for the formula record
723       * @param col - the column of the formula record
724       * @param formula - a String representing the formula.  To be parsed to PTGs
725       * @return bogus/useless formula record
726       */
727  
728      //public FormulaRecord createFormula(short row, short col, String formula)
729      public FormulaRecord createFormula(int row, short col, String formula)
730      {
731          log.logFormatted(log.DEBUG, "create formula row,col,formula %,%,%",
732                           //new short[]
733                           new int[]
734          {
735              row, col
736          }, formula);
737          FormulaRecord rec = new FormulaRecord();
738  
739          rec.setRow(row);
740          rec.setColumn(col);
741          rec.setOptions(( short ) 2);
742          rec.setValue(0);
743          rec.setXFIndex(( short ) 0x0f);
744          FormulaParser fp = new FormulaParser(formula);
745          fp.parse();
746          Ptg[] ptg  = fp.getRPNPtg();
747          int   size = 0;
748  
749          for (int k = 0; k < ptg.length; k++)
750          {
751              size += ptg[ k ].getSize();
752              rec.pushExpressionToken(ptg[ k ]);
753          }
754          rec.setExpressionLength(( short ) size);
755          return rec;
756      }
757  
758      /**
759       * Adds a value record to the sheet's contained binary records
760       * (i.e. LabelSSTRecord or NumberRecord).
761       * <P>
762       * This method is "loc" sensitive.  Meaning you need to set LOC to where you
763       * want it to start searching.  If you don't know do this: setLoc(getDimsLoc).
764       * When adding several rows you can just start at the last one by leaving loc
765       * at what this sets it to.
766       *
767       * @param row the row to add the cell value to
768       * @param col the cell value record itself.
769       */
770  
771      //public void addValueRecord(short row, CellValueRecordInterface col)
772      public void addValueRecord(int row, CellValueRecordInterface col)
773      {
774          checkCells();
775          log.logFormatted(log.DEBUG, "add value record  row,loc %,%", new int[]
776          {
777              row, loc
778          });
779          DimensionsRecord d = ( DimensionsRecord ) records.get(getDimsLoc());
780  
781          if (col.getColumn() > d.getLastCol())
782          {
783              d.setLastCol(( short ) (col.getColumn() + 1));
784          }
785          if (col.getColumn() < d.getFirstCol())
786          {
787              d.setFirstCol(col.getColumn());
788          }
789          cells.insertCell(col);
790  
791          /*
792           * for (int k = loc; k < records.size(); k++)
793           * {
794           *   Record rec = ( Record ) records.get(k);
795           *
796           *   if (rec.getSid() == RowRecord.sid)
797           *   {
798           *       RowRecord rowrec = ( RowRecord ) rec;
799           *
800           *       if (rowrec.getRowNumber() == col.getRow())
801           *       {
802           *           records.add(k + 1, col);
803           *           loc = k;
804           *           if (rowrec.getLastCol() <= col.getColumn())
805           *           {
806           *               rowrec.setLastCol((( short ) (col.getColumn() + 1)));
807           *           }
808           *           break;
809           *       }
810           *   }
811           * }
812           */
813      }
814  
815      /**
816       * remove a value record from the records array.
817       *
818       * This method is not loc sensitive, it resets loc to = dimsloc so no worries.
819       *
820       * @param row - the row of the value record you wish to remove
821       * @param col - a record supporting the CellValueRecordInterface.
822       * @see org.apache.poi.hssf.record.CellValueRecordInterface
823       */
824  
825      //public void removeValueRecord(short row, CellValueRecordInterface col)
826      public void removeValueRecord(int row, CellValueRecordInterface col)
827      {
828          checkCells();
829          log.logFormatted(log.DEBUG, "remove value record row,dimsloc %,%",
830                           new int[]
831          {
832              row, dimsloc
833          });
834          loc = dimsloc;
835          cells.removeCell(col);
836  
837          /*
838           * for (int k = loc; k < records.size(); k++)
839           * {
840           *   Record rec = ( Record ) records.get(k);
841           *
842           *   // checkDimsLoc(rec,k);
843           *   if (rec.isValue())
844           *   {
845           *       CellValueRecordInterface cell =
846           *           ( CellValueRecordInterface ) rec;
847           *
848           *       if ((cell.getRow() == col.getRow())
849           *               && (cell.getColumn() == col.getColumn()))
850           *       {
851           *           records.remove(k);
852           *           break;
853           *       }
854           *   }
855           * }
856           */
857      }
858  
859      /**
860       * replace a value record from the records array.
861       *
862       * This method is not loc sensitive, it resets loc to = dimsloc so no worries.
863       *
864       * @param newval - a record supporting the CellValueRecordInterface.  this will replace
865       *                the cell value with the same row and column.  If there isn't one, one will
866       *                be added.
867       */
868  
869      public void replaceValueRecord(CellValueRecordInterface newval)
870      {
871          checkCells();
872          setLoc(dimsloc);
873          log.log(log.DEBUG, "replaceValueRecord ");
874          cells.insertCell(newval);
875  
876          /*
877           * CellValueRecordInterface oldval = getNextValueRecord();
878           *
879           * while (oldval != null)
880           * {
881           *   if (oldval.isEqual(newval))
882           *   {
883           *       records.set(( short ) (getLoc() - 1), newval);
884           *       return;
885           *   }
886           *   oldval = getNextValueRecord();
887           * }
888           * addValueRecord(newval.getRow(), newval);
889           * setLoc(dimsloc);
890           */
891      }
892  
893      /**
894       * Adds a row record to the sheet
895       *
896       * <P>
897       * This method is "loc" sensitive.  Meaning you need to set LOC to where you
898       * want it to start searching.  If you don't know do this: setLoc(getDimsLoc).
899       * When adding several rows you can just start at the last one by leaving loc
900       * at what this sets it to.
901       *
902       * @param row the row record to be added
903       * @see #setLoc(int)
904       */
905  
906      public void addRow(RowRecord row)
907      {
908          checkRows();
909          log.log(log.DEBUG, "addRow ");
910          DimensionsRecord d = ( DimensionsRecord ) records.get(getDimsLoc());
911  
912          if (row.getRowNumber() > d.getLastRow())
913          {
914              d.setLastRow(row.getRowNumber() + 1);
915          }
916          if (row.getRowNumber() < d.getFirstRow())
917          {
918              d.setFirstRow(row.getRowNumber());
919          }
920          //IndexRecord index = null;
921  
922          rows.insertRow(row);
923  
924          /*
925           * for (int k = loc; k < records.size(); k++)
926           * {
927           *   Record rec = ( Record ) records.get(k);
928           *
929           *   if (rec.getSid() == IndexRecord.sid)
930           *   {
931           *       index = ( IndexRecord ) rec;
932           *   }
933           *   if (rec.getSid() == RowRecord.sid)
934           *   {
935           *       RowRecord rowrec = ( RowRecord ) rec;
936           *
937           *       if (rowrec.getRowNumber() > row.getRowNumber())
938           *       {
939           *           records.add(k, row);
940           *           loc = k;
941           *           break;
942           *       }
943           *   }
944           *   if (rec.getSid() == WindowTwoRecord.sid)
945           *   {
946           *       records.add(k, row);
947           *       loc = k;
948           *       break;
949           *   }
950           * }
951           * if (index != null)
952           * {
953           *   if (index.getLastRowAdd1() <= row.getRowNumber())
954           *   {
955           *       index.setLastRowAdd1(row.getRowNumber() + 1);
956           *   }
957           * }
958           */
959          log.log(log.DEBUG, "exit addRow");
960      }
961  
962      /**
963       * Removes a row record
964       *
965       * This method is not loc sensitive, it resets loc to = dimsloc so no worries.
966       *
967       * @param row  the row record to remove
968       */
969  
970      public void removeRow(RowRecord row)
971      {
972          checkRows();
973          // IndexRecord index = null;
974  
975          setLoc(getDimsLoc());
976          rows.removeRow(row);
977  
978          /*
979           * for (int k = loc; k < records.size(); k++)
980           * {
981           *   Record rec = ( Record ) records.get(k);
982           *
983           *   // checkDimsLoc(rec,k);
984           *   if (rec.getSid() == RowRecord.sid)
985           *   {
986           *       RowRecord rowrec = ( RowRecord ) rec;
987           *
988           *       if (rowrec.getRowNumber() == row.getRowNumber())
989           *       {
990           *           records.remove(k);
991           *           break;
992           *       }
993           *   }
994           *   if (rec.getSid() == WindowTwoRecord.sid)
995           *   {
996           *       break;
997           *   }
998           * }
999           */
1000     }
1001 
1002     /**
1003      * get the NEXT value record (from LOC).  The first record that is a value record
1004      * (starting at LOC) will be returned.
1005      *
1006      * <P>
1007      * This method is "loc" sensitive.  Meaning you need to set LOC to where you
1008      * want it to start searching.  If you don't know do this: setLoc(getDimsLoc).
1009      * When adding several rows you can just start at the last one by leaving loc
1010      * at what this sets it to.  For this method, set loc to dimsloc to start with,
1011      * subsequent calls will return values in (physical) sequence or NULL when you get to the end.
1012      *
1013      * @return CellValueRecordInterface representing the next value record or NULL if there are no more
1014      * @see #setLoc(int)
1015      */
1016 
1017     public CellValueRecordInterface getNextValueRecord()
1018     {
1019         log.log(log.DEBUG, "getNextValue loc= " + loc);
1020         if (valueRecIterator == null)
1021         {
1022             valueRecIterator = cells.getIterator();
1023         }
1024         if (!valueRecIterator.hasNext())
1025         {
1026             return null;
1027         }
1028         return ( CellValueRecordInterface ) valueRecIterator.next();
1029 
1030         /*
1031          *      if (this.getLoc() < records.size())
1032          *     {
1033          *         for (int k = getLoc(); k < records.size(); k++)
1034          *         {
1035          *             Record rec = ( Record ) records.get(k);
1036          *
1037          *             this.setLoc(k + 1);
1038          *             if (rec instanceof CellValueRecordInterface)
1039          *             {
1040          *                 return ( CellValueRecordInterface ) rec;
1041          *             }
1042          *         }
1043          *     }
1044          *     return null;
1045          */
1046     }
1047 
1048     /**
1049      * get the NEXT RowRecord or CellValueRecord(from LOC).  The first record that
1050      * is a Row record or CellValueRecord(starting at LOC) will be returned.
1051      * <P>
1052      * This method is "loc" sensitive.  Meaning you need to set LOC to where you
1053      * want it to start searching.  If you don't know do this: setLoc(getDimsLoc).
1054      * When adding several rows you can just start at the last one by leaving loc
1055      * at what this sets it to.  For this method, set loc to dimsloc to start with.
1056      * subsequent calls will return rows in (physical) sequence or NULL when you get to the end.
1057      *
1058      * @return RowRecord representing the next row record or CellValueRecordInterface
1059      *  representing the next cellvalue or NULL if there are no more
1060      * @see #setLoc(int)
1061      *
1062      */
1063 
1064 /*    public Record getNextRowOrValue()
1065     {
1066         log.debug((new StringBuffer("getNextRow loc= ")).append(loc)
1067             .toString());
1068         if (this.getLoc() < records.size())
1069         {
1070             for (int k = this.getLoc(); k < records.size(); k++)
1071             {
1072                 Record rec = ( Record ) records.get(k);
1073 
1074                 this.setLoc(k + 1);
1075                 if (rec.getSid() == RowRecord.sid)
1076                 {
1077                     return rec;
1078                 }
1079                 else if (rec.isValue())
1080                 {
1081                     return rec;
1082                 }
1083             }
1084         }
1085         return null;
1086     }
1087  */
1088 
1089     /**
1090      * get the NEXT RowRecord (from LOC).  The first record that is a Row record
1091      * (starting at LOC) will be returned.
1092      * <P>
1093      * This method is "loc" sensitive.  Meaning you need to set LOC to where you
1094      * want it to start searching.  If you don't know do this: setLoc(getDimsLoc).
1095      * When adding several rows you can just start at the last one by leaving loc
1096      * at what this sets it to.  For this method, set loc to dimsloc to start with.
1097      * subsequent calls will return rows in (physical) sequence or NULL when you get to the end.
1098      *
1099      * @return RowRecord representing the next row record or NULL if there are no more
1100      * @see #setLoc(int)
1101      *
1102      */
1103 
1104     public RowRecord getNextRow()
1105     {
1106         log.log(log.DEBUG, "getNextRow loc= " + loc);
1107         if (rowRecIterator == null)
1108         {
1109             rowRecIterator = rows.getIterator();
1110         }
1111         if (!rowRecIterator.hasNext())
1112         {
1113             return null;
1114         }
1115         return ( RowRecord ) rowRecIterator.next();
1116 
1117 /*        if (this.getLoc() < records.size())
1118         {
1119             for (int k = this.getLoc(); k < records.size(); k++)
1120             {
1121                 Record rec = ( Record ) records.get(k);
1122 
1123                 this.setLoc(k + 1);
1124                 if (rec.getSid() == RowRecord.sid)
1125                 {
1126                     return ( RowRecord ) rec;
1127                 }
1128             }
1129         }*/
1130     }
1131 
1132     /**
1133      * get the NEXT (from LOC) RowRecord where rownumber matches the given rownum.
1134      * The first record that is a Row record (starting at LOC) that has the
1135      * same rownum as the given rownum will be returned.
1136      * <P>
1137      * This method is "loc" sensitive.  Meaning you need to set LOC to where you
1138      * want it to start searching.  If you don't know do this: setLoc(getDimsLoc).
1139      * When adding several rows you can just start at the last one by leaving loc
1140      * at what this sets it to.  For this method, set loc to dimsloc to start with.
1141      * subsequent calls will return rows in (physical) sequence or NULL when you get to the end.
1142      *
1143      * @param rownum   which row to return (careful with LOC)
1144      * @return RowRecord representing the next row record or NULL if there are no more
1145      * @see #setLoc(int)
1146      *
1147      */
1148 
1149     //public RowRecord getRow(short rownum)
1150     public RowRecord getRow(int rownum)
1151     {
1152         log.log(log.DEBUG, "getNextRow loc= " + loc);
1153         return rows.getRow(rownum);
1154 
1155         /*
1156          * if (this.getLoc() < records.size())
1157          * {
1158          *   for (int k = this.getLoc(); k < records.size(); k++)
1159          *   {
1160          *       Record rec = ( Record ) records.get(k);
1161          *
1162          *       this.setLoc(k + 1);
1163          *       if (rec.getSid() == RowRecord.sid)
1164          *       {
1165          *           if ((( RowRecord ) rec).getRowNumber() == rownum)
1166          *           {
1167          *               return ( RowRecord ) rec;
1168          *           }
1169          *       }
1170          *   }
1171          * }
1172          */
1173 
1174         // return null;
1175     }
1176 
1177     /**
1178      * Not currently used method to calculate and add dbcell records
1179      *
1180      */
1181 
1182     public void addDBCellRecords()
1183     {
1184         int         offset        = 0;
1185         int         recnum        = 0;
1186         int         rownum        = 0;
1187         //int         lastrow       = 0;
1188         //long        lastrowoffset = 0;
1189         IndexRecord index         = null;
1190 
1191         // ArrayList rowOffsets = new ArrayList();
1192         IntList     rowOffsets    = new IntList();
1193 
1194         for (recnum = 0; recnum < records.size(); recnum++)
1195         {
1196             Record rec = ( Record ) records.get(recnum);
1197 
1198             if (rec.getSid() == IndexRecord.sid)
1199             {
1200                 index = ( IndexRecord ) rec;
1201             }
1202             if (rec.getSid() != RowRecord.sid)
1203             {
1204                 offset += rec.serialize().length;
1205             }
1206             else
1207             {
1208                 break;
1209             }
1210         }
1211 
1212         // First Row Record
1213         for (; recnum < records.size(); recnum++)
1214         {
1215             Record rec = ( Record ) records.get(recnum);
1216 
1217             if (rec.getSid() == RowRecord.sid)
1218             {
1219                 rownum++;
1220                 rowOffsets.add(offset);
1221                 if ((rownum % 32) == 0)
1222                 {
1223 
1224                     // if this is the last rec in a  dbcell block
1225                     // find the next row or last value record
1226                     for (int rn = recnum; rn < records.size(); rn++)
1227                     {
1228                         rec = ( Record ) records.get(rn);
1229                         if ((!rec.isInValueSection())
1230                                 || (rec.getSid() == RowRecord.sid))
1231                         {
1232 
1233                             // here is the next row or last value record
1234                             records.add(rn,
1235                                         createDBCell(offset, rowOffsets,
1236                                                      index));
1237                             recnum = rn;
1238                             break;
1239                         }
1240                     }
1241                 }
1242                 else
1243                 {
1244                 }
1245             }
1246             if (!rec.isInValueSection())
1247             {
1248                 records.add(recnum, createDBCell(offset, rowOffsets, index));
1249                 break;
1250             }
1251             offset += rec.serialize().length;
1252         }
1253     }
1254 
1255     /** not currently used */
1256 
1257     private DBCellRecord createDBCell(int offset, IntList rowoffsets,
1258                                       IndexRecord index)
1259     {
1260         DBCellRecord rec = new DBCellRecord();
1261 
1262         rec.setRowOffset(offset - rowoffsets.get(0));
1263 
1264         // test hack
1265         rec.addCellOffset(( short ) 0x0);
1266 
1267         // end test hack
1268         addDbCellToIndex(offset, index);
1269         return rec;
1270     }
1271 
1272     /** not currently used */
1273 
1274     private void addDbCellToIndex(int offset, IndexRecord index)
1275     {
1276         int numdbcells = index.getNumDbcells() + 1;
1277 
1278         index.addDbcell(offset + preoffset);
1279 
1280         // stupid but whenever we add an offset that causes everything to be shifted down 4
1281         for (int k = 0; k < numdbcells; k++)
1282         {
1283             int dbval = index.getDbcellAt(k);
1284 
1285             index.setDbcell(k, dbval + 4);
1286         }
1287     }
1288 
1289     /**
1290      * creates the BOF record
1291      * @see org.apache.poi.hssf.record.BOFRecord
1292      * @see org.apache.poi.hssf.record.Record
1293      * @return record containing a BOFRecord
1294      */
1295 
1296     protected Record createBOF()
1297     {
1298         BOFRecord retval = new BOFRecord();
1299 
1300         retval.setVersion(( short ) 0x600);
1301         retval.setType(( short ) 0x010);
1302 
1303         // retval.setBuild((short)0x10d3);
1304         retval.setBuild(( short ) 0x0dbb);
1305         retval.setBuildYear(( short ) 1996);
1306         retval.setHistoryBitMask(0xc1);
1307         retval.setRequiredVersion(0x6);
1308         return retval;
1309     }
1310 
1311     /**
1312      * creates the Index record  - not currently used
1313      * @see org.apache.poi.hssf.record.IndexRecord
1314      * @see org.apache.poi.hssf.record.Record
1315      * @return record containing a IndexRecord
1316      */
1317 
1318     protected Record createIndex()
1319     {
1320         IndexRecord retval = new IndexRecord();
1321 
1322         retval.setFirstRow(0);   // must be set explicitly
1323         retval.setLastRowAdd1(0);
1324         return retval;
1325     }
1326 
1327     /**
1328      * creates the CalcMode record and sets it to 1 (automatic formula caculation)
1329      * @see org.apache.poi.hssf.record.CalcModeRecord
1330      * @see org.apache.poi.hssf.record.Record
1331      * @return record containing a CalcModeRecord
1332      */
1333 
1334     protected Record createCalcMode()
1335     {
1336         CalcModeRecord retval = new CalcModeRecord();
1337 
1338         retval.setCalcMode(( short ) 1);
1339         return retval;
1340     }
1341 
1342     /**
1343      * creates the CalcCount record and sets it to 0x64 (default number of iterations)
1344      * @see org.apache.poi.hssf.record.CalcCountRecord
1345      * @see org.apache.poi.hssf.record.Record
1346      * @return record containing a CalcCountRecord
1347      */
1348 
1349     protected Record createCalcCount()
1350     {
1351         CalcCountRecord retval = new CalcCountRecord();
1352 
1353         retval.setIterations(( short ) 0x64);   // default 64 iterations
1354         return retval;
1355     }
1356 
1357     /**
1358      * creates the RefMode record and sets it to A1 Mode (default reference mode)
1359      * @see org.apache.poi.hssf.record.RefModeRecord
1360      * @see org.apache.poi.hssf.record.Record
1361      * @return record containing a RefModeRecord
1362      */
1363 
1364     protected Record createRefMode()
1365     {
1366         RefModeRecord retval = new RefModeRecord();
1367 
1368         retval.setMode(retval.USE_A1_MODE);
1369         return retval;
1370     }
1371 
1372     /**
1373      * creates the Iteration record and sets it to false (don't iteratively calculate formulas)
1374      * @see org.apache.poi.hssf.record.IterationRecord
1375      * @see org.apache.poi.hssf.record.Record
1376      * @return record containing a IterationRecord
1377      */
1378 
1379     protected Record createIteration()
1380     {
1381         IterationRecord retval = new IterationRecord();
1382 
1383         retval.setIteration(false);
1384         return retval;
1385     }
1386 
1387     /**
1388      * creates the Delta record and sets it to 0.0010 (default accuracy)
1389      * @see org.apache.poi.hssf.record.DeltaRecord
1390      * @see org.apache.poi.hssf.record.Record
1391      * @return record containing a DeltaRecord
1392      */
1393 
1394     protected Record createDelta()
1395     {
1396         DeltaRecord retval = new DeltaRecord();
1397 
1398         retval.setMaxChange((( double ) 0.0010));
1399         return retval;
1400     }
1401 
1402     /**
1403      * creates the SaveRecalc record and sets it to true (recalculate before saving)
1404      * @see org.apache.poi.hssf.record.SaveRecalcRecord
1405      * @see org.apache.poi.hssf.record.Record
1406      * @return record containing a SaveRecalcRecord
1407      */
1408 
1409     protected Record createSaveRecalc()
1410     {
1411         SaveRecalcRecord retval = new SaveRecalcRecord();
1412 
1413         retval.setRecalc(true);
1414         return retval;
1415     }
1416 
1417     /**
1418      * creates the PrintHeaders record and sets it to false (we don't create headers yet so why print them)
1419      * @see org.apache.poi.hssf.record.PrintHeadersRecord
1420      * @see org.apache.poi.hssf.record.Record
1421      * @return record containing a PrintHeadersRecord
1422      */
1423 
1424     protected Record createPrintHeaders()
1425     {
1426         PrintHeadersRecord retval = new PrintHeadersRecord();
1427 
1428         retval.setPrintHeaders(false);
1429         return retval;
1430     }
1431 
1432     /**
1433      * creates the PrintGridlines record and sets it to false (that makes for ugly sheets).  As far as I can
1434      * tell this does the same thing as the GridsetRecord
1435      *
1436      * @see org.apache.poi.hssf.record.PrintGridlinesRecord
1437      * @see org.apache.poi.hssf.record.Record
1438      * @return record containing a PrintGridlinesRecord
1439      */
1440 
1441     protected Record createPrintGridlines()
1442     {
1443         PrintGridlinesRecord retval = new PrintGridlinesRecord();
1444 
1445         retval.setPrintGridlines(false);
1446         return retval;
1447     }
1448 
1449     /**
1450      * creates the Gridset record and sets it to true (user has mucked with the gridlines)
1451      * @see org.apache.poi.hssf.record.GridsetRecord
1452      * @see org.apache.poi.hssf.record.Record
1453      * @return record containing a GridsetRecord
1454      */
1455 
1456     protected Record createGridset()
1457     {
1458         GridsetRecord retval = new GridsetRecord();
1459 
1460         retval.setGridset(true);
1461         return retval;
1462     }
1463 
1464     /**
1465      * creates the Guts record and sets leftrow/topcol guttter and rowlevelmax/collevelmax to 0
1466      * @see org.apache.poi.hssf.record.GutsRecord
1467      * @see org.apache.poi.hssf.record.Record
1468      * @return record containing a GutsRecordRecord
1469      */
1470 
1471     protected Record createGuts()
1472     {
1473         GutsRecord retval = new GutsRecord();
1474 
1475         retval.setLeftRowGutter(( short ) 0);
1476         retval.setTopColGutter(( short ) 0);
1477         retval.setRowLevelMax(( short ) 0);
1478         retval.setColLevelMax(( short ) 0);
1479         return retval;
1480     }
1481 
1482     /**
1483      * creates the DefaultRowHeight Record and sets its options to 0 and rowheight to 0xff
1484      * @see org.apache.poi.hssf.record.DefaultRowHeightRecord
1485      * @see org.apache.poi.hssf.record.Record
1486      * @return record containing a DefaultRowHeightRecord
1487      */
1488 
1489     protected Record createDefaultRowHeight()
1490     {
1491         DefaultRowHeightRecord retval = new DefaultRowHeightRecord();
1492 
1493         retval.setOptionFlags(( short ) 0);
1494         retval.setRowHeight(( short ) 0xff);
1495         return retval;
1496     }
1497 
1498     /**
1499      * creates the WSBoolRecord and sets its values to defaults
1500      * @see org.apache.poi.hssf.record.WSBoolRecord
1501      * @see org.apache.poi.hssf.record.Record
1502      * @return record containing a WSBoolRecord
1503      */
1504 
1505     protected Record createWSBool()
1506     {
1507         WSBoolRecord retval = new WSBoolRecord();
1508 
1509         retval.setWSBool1(( byte ) 0x4);
1510         retval.setWSBool2(( byte ) 0xffffffc1);
1511         return retval;
1512     }
1513 
1514     /**
1515      * creates the Header Record and sets it to nothing/0 length
1516      * @see org.apache.poi.hssf.record.HeaderRecord
1517      * @see org.apache.poi.hssf.record.Record
1518      * @return record containing a HeaderRecord
1519      */
1520 
1521     protected Record createHeader()
1522     {
1523         HeaderRecord retval = new HeaderRecord();
1524 
1525         retval.setHeaderLength(( byte ) 0);
1526         retval.setHeader(null);
1527         return retval;
1528     }
1529 
1530     /**
1531      * creates the Footer Record and sets it to nothing/0 length
1532      * @see org.apache.poi.hssf.record.FooterRecord
1533      * @see org.apache.poi.hssf.record.Record
1534      * @return record containing a FooterRecord
1535      */
1536 
1537     protected Record createFooter()
1538     {
1539         FooterRecord retval = new FooterRecord();
1540 
1541         retval.setFooterLength(( byte ) 0);
1542         retval.setFooter(null);
1543         return retval;
1544     }
1545 
1546     /**
1547      * creates the HCenter Record and sets it to false (don't horizontally center)
1548      * @see org.apache.poi.hssf.record.HCenterRecord
1549      * @see org.apache.poi.hssf.record.Record
1550      * @return record containing a HCenterRecord
1551      */
1552 
1553     protected Record createHCenter()
1554     {
1555         HCenterRecord retval = new HCenterRecord();
1556 
1557         retval.setHCenter(false);
1558         return retval;
1559     }
1560 
1561     /**
1562      * creates the VCenter Record and sets it to false (don't horizontally center)
1563      * @see org.apache.poi.hssf.record.VCenterRecord
1564      * @see org.apache.poi.hssf.record.Record
1565      * @return record containing a VCenterRecord
1566      */
1567 
1568     protected Record createVCenter()
1569     {
1570         VCenterRecord retval = new VCenterRecord();
1571 
1572         retval.setVCenter(false);
1573         return retval;
1574     }
1575 
1576     /**
1577      * creates the PrintSetup Record and sets it to defaults and marks it invalid
1578      * @see org.apache.poi.hssf.record.PrintSetupRecord
1579      * @see org.apache.poi.hssf.record.Record
1580      * @return record containing a PrintSetupRecord
1581      */
1582 
1583     protected Record createPrintSetup()
1584     {
1585         PrintSetupRecord retval = new PrintSetupRecord();
1586 
1587         retval.setPaperSize(( short ) 1);
1588         retval.setScale(( short ) 100);
1589         retval.setPageStart(( short ) 1);
1590         retval.setFitWidth(( short ) 1);
1591         retval.setFitHeight(( short ) 1);
1592         retval.setOptions(( short ) 2);
1593         retval.setHResolution(( short ) 300);
1594         retval.setVResolution(( short ) 300);
1595         retval.setHeaderMargin(( double ) 0.5);
1596         retval.setFooterMargin(( double ) 0.5);
1597         retval.setCopies(( short ) 0);
1598         return retval;
1599     }
1600 
1601     /**
1602      * creates the DefaultColWidth Record and sets it to 8
1603      * @see org.apache.poi.hssf.record.DefaultColWidthRecord
1604      * @see org.apache.poi.hssf.record.Record
1605      * @return record containing a DefaultColWidthRecord
1606      */
1607 
1608     protected Record createDefaultColWidth()
1609     {
1610         DefaultColWidthRecord retval = new DefaultColWidthRecord();
1611 
1612         retval.setColWidth(( short ) 8);
1613         return retval;
1614     }
1615 
1616     /**
1617      * creates the ColumnInfo Record and sets it to a default column/width
1618      * @see org.apache.poi.hssf.record.ColumnInfoRecord
1619      * @return record containing a ColumnInfoRecord
1620      */
1621 
1622     protected Record createColInfo()
1623     {
1624         ColumnInfoRecord retval = new ColumnInfoRecord();
1625 
1626         retval.setColumnWidth(( short ) 0x8);
1627         retval.setOptions(( short ) 6);
1628         retval.setXFIndex(( short ) 0x0f);
1629         return retval;
1630     }
1631 
1632     /**
1633      * get the default column width for the sheet (if the columns do not define their own width)
1634      * @return default column width
1635      */
1636 
1637     public short getDefaultColumnWidth()
1638     {
1639         return defaultcolwidth.getColWidth();
1640     }
1641 
1642     /**
1643      * get whether gridlines are printed.
1644      * @return true if printed
1645      */
1646 
1647     public boolean isGridsPrinted()
1648     {
1649         return !gridset.getGridset();
1650     }
1651 
1652     /**
1653      * set whether gridlines printed or not.
1654      * @param value     True if gridlines printed.
1655      */
1656 
1657     public void setGridsPrinted(boolean value)
1658     {
1659         gridset.setGridset(!value);
1660     }
1661 
1662     /**
1663      * set the default column width for the sheet (if the columns do not define their own width)
1664      * @param dcw  default column width
1665      */
1666 
1667     public void setDefaultColumnWidth(short dcw)
1668     {
1669         defaultcolwidth.setColWidth(dcw);
1670     }
1671 
1672     /**
1673      * set the default row height for the sheet (if the rows do not define their own height)
1674      */
1675 
1676     public void setDefaultRowHeight(short dch)
1677     {
1678         defaultrowheight.setRowHeight(dch);
1679     }
1680 
1681     /**
1682      * get the default row height for the sheet (if the rows do not define their own height)
1683      * @return  default row height
1684      */
1685 
1686     public short getDefaultRowHeight()
1687     {
1688         return defaultrowheight.getRowHeight();
1689     }
1690 
1691     /**
1692      * get the width of a given column in units of 1/20th of a point width (twips?)
1693      * @param column index
1694      * @see org.apache.poi.hssf.record.DefaultColWidthRecord
1695      * @see org.apache.poi.hssf.record.ColumnInfoRecord
1696      * @see #setColumnWidth(short,short)
1697      * @return column width in units of 1/20th of a point (twips?)
1698      */
1699 
1700     public short getColumnWidth(short column)
1701     {
1702         short            retval = 0;
1703         ColumnInfoRecord ci     = null;
1704         int              k      = 0;
1705 
1706         if (columnSizes != null)
1707         {
1708             for (k = 0; k < columnSizes.size(); k++)
1709             {
1710                 ci = ( ColumnInfoRecord ) columnSizes.get(k);
1711                 if ((ci.getFirstColumn() >= column)
1712                         && (ci.getLastColumn() <= column))
1713                 {
1714                     break;
1715                 }
1716                 ci = null;
1717             }
1718         }
1719         if (ci != null)
1720         {
1721             retval = ci.getColumnWidth();
1722         }
1723         else
1724         {
1725             retval = defaultcolwidth.getColWidth();
1726         }
1727         return retval;
1728     }
1729 
1730     /**
1731      * set the width for a given column in 1/20th of a character width units
1732      * @param column - the column number
1733      * @param width (in units of 1/20th of a character width)
1734      */
1735 
1736     public void setColumnWidth(short column, short width)
1737     {
1738         ColumnInfoRecord ci = null;
1739         int              k  = 0;
1740 
1741         if (columnSizes == null)
1742         {
1743             columnSizes = new ArrayList();
1744         }
1745         //int cioffset = getDimsLoc() - columnSizes.size();
1746 
1747         for (k = 0; k < columnSizes.size(); k++)
1748         {
1749             ci = ( ColumnInfoRecord ) columnSizes.get(k);
1750             if ((ci.getFirstColumn() >= column)
1751                     && (ci.getLastColumn() <= column))
1752             {
1753                 break;
1754             }
1755             ci = null;
1756         }
1757         if (ci != null)
1758         {
1759             if (ci.getColumnWidth() == width)
1760             {
1761 
1762                 // do nothing...the cell's width is equal to what we're setting it to.
1763             }
1764             else if ((ci.getFirstColumn() == column)
1765                      && (ci.getLastColumn() == column))
1766             {                               // if its only for this cell then
1767                 ci.setColumnWidth(width);   // who cares, just change the width
1768             }
1769             else if ((ci.getFirstColumn() == column)
1770                      || (ci.getLastColumn() == column))
1771             {
1772 
1773                 // okay so the width is different but the first or last column == the column we'return setting
1774                 // we'll just divide the info and create a new one
1775                 if (ci.getFirstColumn() == column)
1776                 {
1777                     ci.setFirstColumn(( short ) (column + 1));
1778                 }
1779                 else
1780                 {
1781                     ci.setLastColumn(( short ) (column - 1));
1782                 }
1783                 ColumnInfoRecord nci = ( ColumnInfoRecord ) createColInfo();
1784 
1785                 nci.setFirstColumn(column);
1786                 nci.setLastColumn(column);
1787                 nci.setOptions(ci.getOptions());
1788                 nci.setXFIndex(ci.getXFIndex());
1789                 nci.setColumnWidth(width);
1790                 columnSizes.add(k, nci);
1791                 records.add((1 + getDimsLoc() - columnSizes.size()) + k, nci);
1792                 dimsloc++;
1793             }
1794         }
1795         else
1796         {
1797 
1798             // okay so there ISN'T a column info record that cover's this column so lets create one!
1799             ColumnInfoRecord nci = ( ColumnInfoRecord ) createColInfo();
1800 
1801             nci.setFirstColumn(column);
1802             nci.setLastColumn(column);
1803             nci.setColumnWidth(width);
1804             columnSizes.add(k, nci);
1805             records.add((1 + getDimsLoc() - columnSizes.size()) + k, nci);
1806             dimsloc++;
1807         }
1808     }
1809 
1810     /**
1811      * creates the Dimensions Record and sets it to bogus values (you should set this yourself
1812      * or let the high level API do it for you)
1813      * @see org.apache.poi.hssf.record.DimensionsRecord
1814      * @see org.apache.poi.hssf.record.Record
1815      * @return record containing a DimensionsRecord
1816      */
1817 
1818     protected Record createDimensions()
1819     {
1820         DimensionsRecord retval = new DimensionsRecord();
1821 
1822         retval.setFirstCol(( short ) 0);
1823         retval.setLastRow(1);             // one more than it is
1824         retval.setFirstRow(0);
1825         retval.setLastCol(( short ) 1);   // one more than it is
1826         return retval;
1827     }
1828 
1829     /**
1830      * creates the WindowTwo Record and sets it to:  <P>
1831      * options        = 0x6b6 <P>
1832      * toprow         = 0 <P>
1833      * leftcol        = 0 <P>
1834      * headercolor    = 0x40 <P>
1835      * pagebreakzoom  = 0x0 <P>
1836      * normalzoom     = 0x0 <p>
1837      * @see org.apache.poi.hssf.record.WindowTwoRecord
1838      * @see org.apache.poi.hssf.record.Record
1839      * @return record containing a WindowTwoRecord
1840      */
1841 
1842     protected Record createWindowTwo()
1843     {
1844         WindowTwoRecord retval = new WindowTwoRecord();
1845 
1846         retval.setOptions(( short ) 0x6b6);
1847         retval.setTopRow(( short ) 0);
1848         retval.setLeftCol(( short ) 0);
1849         retval.setHeaderColor(0x40);
1850         retval.setPageBreakZoom(( short ) 0);
1851         retval.setNormalZoom(( short ) 0);
1852         return retval;
1853     }
1854 
1855     /**
1856      * Creates the Selection record and sets it to nothing selected
1857      *
1858      * @see org.apache.poi.hssf.record.SelectionRecord
1859      * @see org.apache.poi.hssf.record.Record
1860      * @return record containing a SelectionRecord
1861      */
1862 
1863     protected Record createSelection()
1864     {
1865         SelectionRecord retval = new SelectionRecord();
1866 
1867         retval.setPane(( byte ) 0x3);
1868         retval.setActiveCellCol(( short ) 0x0);
1869         retval.setActiveCellRow(( short ) 0x0);
1870         retval.setNumRefs(( short ) 0x0);
1871         return retval;
1872     }
1873 
1874     protected Record createMergedCells()
1875     {
1876         MergeCellsRecord retval = new MergeCellsRecord();
1877 
1878         retval.setNumAreas(( short ) 0);
1879         return retval;
1880     }
1881 
1882     /**
1883      * creates the EOF record
1884      * @see org.apache.poi.hssf.record.EOFRecord
1885      * @see org.apache.poi.hssf.record.Record
1886      * @return record containing a EOFRecord
1887      */
1888 
1889     protected Record createEOF()
1890     {
1891         return new EOFRecord();
1892     }
1893 
1894     /**
1895      * get the location of the DimensionsRecord (which is the last record before the value section)
1896      * @return location in the array of records of the DimensionsRecord
1897      */
1898 
1899     public int getDimsLoc()
1900     {
1901         log.log(log.DEBUG, "getDimsLoc dimsloc= " + dimsloc);
1902         return dimsloc;
1903     }
1904 
1905     /**
1906      * in the event the record is a dimensions record, resets both the loc index and dimsloc index
1907      */
1908 
1909     public void checkDimsLoc(Record rec, int recloc)
1910     {
1911         if (rec.getSid() == DimensionsRecord.sid)
1912         {
1913             loc     = recloc;
1914             dimsloc = recloc;
1915         }
1916     }
1917 
1918     public int getSize()
1919     {
1920         int retval = 0;
1921 
1922         for (int k = 0; k < records.size(); k++)
1923         {
1924             retval += (( Record ) records.get(k)).getRecordSize();
1925         }
1926         return retval;
1927     }
1928 
1929     public List getRecords()
1930     {
1931         return records;
1932     }
1933 
1934     /**
1935      * Gets the gridset record for this sheet.
1936      */
1937 
1938     public GridsetRecord getGridsetRecord()
1939     {
1940         return gridset;
1941     }
1942 
1943     /**
1944      * Returns the first occurance of a record matching a particular sid.
1945      */
1946 
1947     public Record findFirstRecordBySid(short sid)
1948     {
1949         for (Iterator iterator = records.iterator(); iterator.hasNext(); )
1950         {
1951             Record record = ( Record ) iterator.next();
1952 
1953             if (record.getSid() == sid)
1954             {
1955                 return record;
1956             }
1957         }
1958         return null;
1959     }
1960 
1961     /**
1962      * Returns the HeaderRecord.
1963      * @return HeaderRecord for the sheet.
1964      */
1965     public HeaderRecord getHeader ()
1966     {
1967 	return header;
1968     }
1969     
1970     /**
1971      * Sets the HeaderRecord.
1972      * @param newHeader The new HeaderRecord for the sheet.
1973      */
1974     public void setHeader (HeaderRecord newHeader)
1975     {
1976 	header = newHeader;
1977     }
1978     
1979     /**
1980      * Returns the FooterRecord.
1981      * @return FooterRecord for the sheet.
1982      */
1983     public FooterRecord getFooter ()
1984     {
1985 	return footer;
1986     }
1987     
1988     /**
1989      * Sets the FooterRecord.
1990      * @param newFooter The new FooterRecord for the sheet.
1991      */
1992     public void setFooter (FooterRecord newFooter)
1993     {
1994 	footer = newFooter;
1995     }
1996     
1997     /**
1998      * Returns the PrintSetupRecord.
1999      * @return PrintSetupRecord for the sheet.
2000      */
2001     public PrintSetupRecord getPrintSetup ()
2002     {
2003 	return printSetup;
2004     }
2005 
2006     /**
2007      * Sets the PrintSetupRecord.
2008      * @param newPrintSetup The new PrintSetupRecord for the sheet.
2009      */
2010     public void setPrintSetup (PrintSetupRecord newPrintSetup)
2011     {
2012 	printSetup = newPrintSetup;
2013     }
2014     
2015     /**
2016      * Returns the PrintGridlinesRecord.
2017      * @return PrintGridlinesRecord for the sheet.
2018      */
2019     public PrintGridlinesRecord getPrintGridlines ()
2020     {
2021 	return printGridlines;
2022     }
2023     
2024     /**
2025      * Sets the PrintGridlinesRecord.
2026      * @param newPrintGridlines The new PrintGridlinesRecord for the sheet.
2027      */
2028     public void setPrintGridlines (PrintGridlinesRecord newPrintGridlines)
2029     {
2030 	printGridlines = newPrintGridlines;
2031     }
2032 
2033 }
2034