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