Save This Page
Home » poi-src-3.2-FINAL-20081019 » org.apache » poi » hssf » model » [javadoc | source]
    1   /* ====================================================================
    2      Licensed to the Apache Software Foundation (ASF) under one or more
    3      contributor license agreements.  See the NOTICE file distributed with
    4      this work for additional information regarding copyright ownership.
    5      The ASF licenses this file to You under the Apache License, Version 2.0
    6      (the "License"); you may not use this file except in compliance with
    7      the License.  You may obtain a copy of the License at
    8   
    9          http://www.apache.org/licenses/LICENSE-2.0
   10   
   11      Unless required by applicable law or agreed to in writing, software
   12      distributed under the License is distributed on an "AS IS" BASIS,
   13      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   14      See the License for the specific language governing permissions and
   15      limitations under the License.
   16   ==================================================================== */
   17   
   18   package org.apache.poi.hssf.model;
   19   
   20   import org.apache.poi.hssf.record;
   21   import org.apache.poi.hssf.record.aggregates.ColumnInfoRecordsAggregate;
   22   import org.apache.poi.hssf.record.aggregates.FormulaRecordAggregate;
   23   import org.apache.poi.hssf.record.aggregates.RowRecordsAggregate;
   24   import org.apache.poi.hssf.record.aggregates.ValueRecordsAggregate;
   25   import org.apache.poi.hssf.record.aggregates.CFRecordsAggregate;
   26   import org.apache.poi.hssf.record.formula.Ptg;
   27   import org.apache.poi.hssf.util.PaneInformation;
   28   
   29   import org.apache.poi.util.POILogFactory;
   30   import org.apache.poi.util.POILogger;
   31   
   32   import java.util.ArrayList;
   33   import java.util.Iterator;
   34   import java.util.List;   // normally I don't do this, buy we literally mean ALL
   35   
   36   /**
   37    * Low level model implementation of a Sheet (one workbook contains many sheets)
   38    * This file contains the low level binary records starting at the sheets BOF and
   39    * ending with the sheets EOF.  Use HSSFSheet for a high level representation.
   40    * <P>
   41    * The structures of the highlevel API use references to this to perform most of their
   42    * operations.  Its probably unwise to use these low level structures directly unless you
   43    * really know what you're doing.  I recommend you read the Microsoft Excel 97 Developer's
   44    * Kit (Microsoft Press) and the documentation at http://sc.openoffice.org/excelfileformat.pdf
   45    * before even attempting to use this.
   46    * <P>
   47    * @author  Andrew C. Oliver (acoliver at apache dot org)
   48    * @author  Glen Stampoultzis (glens at apache.org)
   49    * @author  Shawn Laubach (slaubach at apache dot org) Gridlines, Headers, Footers, PrintSetup, and Setting Default Column Styles
   50    * @author Jason Height (jheight at chariot dot net dot au) Clone support. DBCell & Index Record writing support
   51    * @author  Brian Sanders (kestrel at burdell dot org) Active Cell support
   52    * @author  Jean-Pierre Paris (jean-pierre.paris at m4x dot org) (Just a little)
   53    *
   54    * @see org.apache.poi.hssf.model.Workbook
   55    * @see org.apache.poi.hssf.usermodel.HSSFSheet
   56    * @version 1.0-pre
   57    */
   58   public final class Sheet implements Model {
   59       public static final short   LeftMargin = 0;
   60       public static final short   RightMargin = 1;
   61       public static final short   TopMargin = 2;
   62       public static final short   BottomMargin = 3;
   63   
   64       private static POILogger            log              = POILogFactory.getLogger(Sheet.class);
   65   
   66       protected ArrayList                  records           =     null;
   67                 int                        preoffset         =     0;            // offset of the sheet in a new file
   68                 int                        loc               =     0;
   69       protected int                        dimsloc           =     -1;  // TODO - is it legal for dims record to be missing?
   70       protected DimensionsRecord           dims;
   71       protected DefaultColWidthRecord      defaultcolwidth   =     null;
   72       protected DefaultRowHeightRecord     defaultrowheight  =     null;
   73       protected GridsetRecord              gridset           =     null;
   74       protected PrintSetupRecord           printSetup        =     null;
   75       protected HeaderRecord               header            =     null;
   76       protected FooterRecord               footer            =     null;
   77       protected PrintGridlinesRecord       printGridlines    =     null;
   78       protected WindowTwoRecord            windowTwo         =     null;
   79       protected MergeCellsRecord           merged            =     null;
   80       protected Margin[]                   margins           =     null;
   81       protected List                       mergedRecords     =     new ArrayList();
   82       protected int                        numMergedRegions  =     0;
   83       protected SelectionRecord            selection         =     null;
   84       protected ColumnInfoRecordsAggregate columns           =     null;
   85       protected ValueRecordsAggregate      cells             =     null;
   86       protected RowRecordsAggregate        rows              =     null;
   87       private   Iterator                   valueRecIterator  =     null;
   88       private   Iterator                   rowRecIterator    =     null;
   89       protected int                        eofLoc            =     0;
   90       protected ProtectRecord              protect           =     null;
   91       protected PageBreakRecord            rowBreaks         =     null;
   92       protected PageBreakRecord            colBreaks         =     null;
   93       protected ObjectProtectRecord        objprotect        =     null;
   94       protected ScenarioProtectRecord      scenprotect       =     null;
   95       protected PasswordRecord             password          =     null;
   96       protected List                       condFormatting    =     new ArrayList();
   97   
   98       /** Add an UncalcedRecord if not true indicating formulas have not been calculated */
   99       protected boolean _isUncalced = false;
  100       
  101       public static final byte PANE_LOWER_RIGHT = (byte)0;
  102       public static final byte PANE_UPPER_RIGHT = (byte)1;
  103       public static final byte PANE_LOWER_LEFT = (byte)2;
  104       public static final byte PANE_UPPER_LEFT = (byte)3;
  105   
  106       /**
  107        * Creates new Sheet with no initialization --useless at this point
  108        * @see #createSheet(List,int,int)
  109        */
  110       public Sheet()
  111       {
  112       }
  113   
  114       /**
  115        * read support  (offset used as starting point for search) for low level
  116        * API.  Pass in an array of Record objects, the sheet number (0 based) and
  117        * a record offset (should be the location of the sheets BOF record).  A Sheet
  118        * object is constructed and passed back with all of its initialization set
  119        * to the passed in records and references to those records held. This function
  120        * is normally called via Workbook.
  121        *
  122        * @param recs array containing those records in the sheet in sequence (normally obtained from RecordFactory)
  123        * @param sheetnum integer specifying the sheet's number (0,1 or 2 in this release)
  124        * @param offset of the sheet's BOF record
  125        *
  126        * @return Sheet object with all values set to those read from the file
  127        *
  128        * @see org.apache.poi.hssf.model.Workbook
  129        * @see org.apache.poi.hssf.record.Record
  130        */
  131       public static Sheet createSheet(List recs, int sheetnum, int offset)
  132       {
  133           if (log.check( POILogger.DEBUG ))
  134               log.logFormatted(POILogger.DEBUG,
  135                       "Sheet createSheet (existing file) with %",
  136                       new Integer(recs.size()));
  137           Sheet     retval             = new Sheet();
  138           ArrayList records            = new ArrayList(recs.size() / 5);
  139           boolean   isfirstcell        = true;
  140           boolean   isfirstrow         = true;
  141           int       bofEofNestingLevel = 0;
  142   
  143           for (int k = offset; k < recs.size(); k++)
  144           {
  145               Record rec = ( Record ) recs.get(k);
  146   
  147               if (rec.getSid() == BOFRecord.sid)
  148               {
  149                   bofEofNestingLevel++;
  150                   if (log.check( POILogger.DEBUG ))
  151                       log.log(POILogger.DEBUG, "Hit BOF record. Nesting increased to " + bofEofNestingLevel);
  152               }
  153               else if (rec.getSid() == EOFRecord.sid)
  154               {
  155                   --bofEofNestingLevel;
  156                   if (log.check( POILogger.DEBUG ))
  157                       log.log(POILogger.DEBUG, "Hit EOF record. Nesting decreased to " + bofEofNestingLevel);
  158                   if (bofEofNestingLevel == 0) {
  159                       records.add(rec);
  160                       retval.eofLoc = k;
  161                       break;
  162                   }
  163               }
  164               else if (rec.getSid() == UncalcedRecord.sid) {
  165                   retval._isUncalced = true;
  166               }
  167               else if (rec.getSid() == DimensionsRecord.sid)
  168               {
  169                   // Make a columns aggregate if one hasn't ready been created.
  170                   if (retval.columns == null)
  171                   {
  172                       retval.columns = new ColumnInfoRecordsAggregate();
  173                       records.add(retval.columns);
  174                   }
  175   
  176                   retval.dims    = ( DimensionsRecord ) rec;
  177                   retval.dimsloc = records.size();
  178               }
  179               else if (rec.getSid() == MergeCellsRecord.sid)
  180               {
  181                   retval.mergedRecords.add(rec);
  182                   retval.merged = ( MergeCellsRecord ) rec;
  183                   retval.numMergedRegions += retval.merged.getNumAreas();
  184               }
  185               else if ( rec.getSid() == CFHeaderRecord.sid )
  186               {
  187                   CFRecordsAggregate cfAgg = CFRecordsAggregate.createCFAggregate(recs, k);
  188                   retval.condFormatting.add(cfAgg);
  189                   rec = cfAgg;
  190               }
  191               else if ( rec.getSid() == CFRuleRecord.sid )
  192               {
  193                   // Skip it since it is processed by CFRecordsAggregate
  194                   rec = null;
  195               }
  196               else if (rec.getSid() == ColumnInfoRecord.sid)
  197               {
  198                   ColumnInfoRecord col = (ColumnInfoRecord)rec;
  199                   if (retval.columns != null)
  200                   {
  201                       rec = null; //only add the aggregate once
  202                   }
  203                   else
  204                   {
  205                       rec = retval.columns = new ColumnInfoRecordsAggregate();
  206                   }
  207                   retval.columns.insertColumn(col);
  208               }
  209               else if (rec.getSid() == DefaultColWidthRecord.sid)
  210               {
  211                   retval.defaultcolwidth = ( DefaultColWidthRecord ) rec;
  212               }
  213               else if (rec.getSid() == DefaultRowHeightRecord.sid)
  214               {
  215                   retval.defaultrowheight = ( DefaultRowHeightRecord ) rec;
  216               }
  217               else if ( rec.isValue() && bofEofNestingLevel == 1 )
  218               {
  219                   if ( isfirstcell )
  220                   {
  221                       retval.cells = new ValueRecordsAggregate();
  222                       rec = retval.cells;
  223                       retval.cells.construct( k, recs );
  224                       isfirstcell = false;
  225                   }
  226                   else
  227                   {
  228                       rec = null;
  229                   }
  230               }
  231               else if ( rec.getSid() == StringRecord.sid )
  232               {
  233                   rec = null;
  234               }
  235               else if ( rec.getSid() == RowRecord.sid )
  236               {
  237                   RowRecord row = (RowRecord)rec;
  238                   if (!isfirstrow) rec = null; //only add the aggregate once
  239   
  240                   if ( isfirstrow )
  241                   {
  242                       retval.rows = new RowRecordsAggregate();
  243                       rec = retval.rows;
  244                       isfirstrow = false;
  245                   }
  246                   retval.rows.insertRow(row);
  247               }
  248               else if ( rec.getSid() == PrintGridlinesRecord.sid )
  249               {
  250                   retval.printGridlines = (PrintGridlinesRecord) rec;
  251               }
  252               else if ( rec.getSid() == GridsetRecord.sid )
  253               {
  254                   retval.gridset = (GridsetRecord) rec;
  255               }
  256               else if ( rec.getSid() == HeaderRecord.sid && bofEofNestingLevel == 1)
  257               {
  258                   retval.header = (HeaderRecord) rec;
  259               }
  260               else if ( rec.getSid() == FooterRecord.sid && bofEofNestingLevel == 1)
  261               {
  262                   retval.footer = (FooterRecord) rec;
  263               }
  264               else if ( rec.getSid() == PrintSetupRecord.sid && bofEofNestingLevel == 1)
  265               {
  266                   retval.printSetup = (PrintSetupRecord) rec;
  267               }
  268               else if ( rec.getSid() == LeftMarginRecord.sid)
  269               {
  270                   retval.getMargins()[LeftMargin] = (LeftMarginRecord) rec;
  271               }
  272               else if ( rec.getSid() == RightMarginRecord.sid)
  273               {
  274                   retval.getMargins()[RightMargin] = (RightMarginRecord) rec;
  275               }
  276               else if ( rec.getSid() == TopMarginRecord.sid)
  277               {
  278                   retval.getMargins()[TopMargin] = (TopMarginRecord) rec;
  279               }
  280               else if ( rec.getSid() == BottomMarginRecord.sid)
  281               {
  282                   retval.getMargins()[BottomMargin] = (BottomMarginRecord) rec;
  283               }
  284               else if ( rec.getSid() == SelectionRecord.sid )
  285               {
  286                   retval.selection = (SelectionRecord) rec;
  287               }
  288               else if ( rec.getSid() == WindowTwoRecord.sid )
  289               {
  290                   retval.windowTwo = (WindowTwoRecord) rec;
  291               }
  292               else if ( rec.getSid() == DBCellRecord.sid )
  293               {
  294                   rec = null;
  295               }
  296               else if ( rec.getSid() == IndexRecord.sid )
  297               {
  298                   // ignore INDEX record because it is only needed by Excel, 
  299                   // and POI always re-calculates its contents 
  300                   rec = null;
  301               }
  302   
  303               else if ( rec.getSid() == ProtectRecord.sid )
  304               {
  305                   retval.protect = (ProtectRecord) rec;
  306               }
  307               else if ( rec.getSid() == ObjectProtectRecord.sid )
  308               {
  309                   retval.objprotect = (ObjectProtectRecord) rec;
  310               }
  311               else if ( rec.getSid() == ScenarioProtectRecord.sid )
  312               {
  313                   retval.scenprotect = (ScenarioProtectRecord) rec;
  314               }
  315               else if ( rec.getSid() == PasswordRecord.sid )
  316               {
  317                   retval.password = (PasswordRecord) rec;
  318               }
  319               else if (rec.getSid() == PageBreakRecord.HORIZONTAL_SID)
  320               {
  321                   retval.rowBreaks = (PageBreakRecord)rec;
  322               }
  323               else if (rec.getSid() == PageBreakRecord.VERTICAL_SID)
  324               {
  325                   retval.colBreaks = (PageBreakRecord)rec;
  326               }
  327   
  328               if (rec != null)
  329               {
  330                   records.add(rec);
  331               }
  332           }
  333           retval.records = records;
  334           retval.checkRows();
  335           retval.checkCells();
  336           if (log.check( POILogger.DEBUG ))
  337               log.log(POILogger.DEBUG, "sheet createSheet (existing file) exited");
  338           return retval;
  339       }
  340   
  341       /**
  342        * Clones the low level records of this sheet and returns the new sheet instance.
  343        * This method is implemented by adding methods for deep cloning to all records that
  344        * can be added to a sheet. The <b>Record</b> object does not implement cloneable.
  345        * When adding a new record, implement a public clone method if and only if the record
  346        * belongs to a sheet.
  347        */
  348       public Sheet cloneSheet()
  349       {
  350         ArrayList clonedRecords = new ArrayList(this.records.size());
  351         for (int i=0; i<this.records.size();i++) {
  352           Record rec = (Record)((Record)this.records.get(i)).clone();
  353           //Need to pull out the Row record and the Value records from their
  354           //Aggregates.
  355           //This is probably the best way to do it since we probably dont want the createSheet
  356           //To cater for these artificial Record types
  357           if (rec instanceof RowRecordsAggregate) {
  358             RowRecordsAggregate rrAgg = (RowRecordsAggregate)rec;
  359             for (Iterator rowIter = rrAgg.getIterator();rowIter.hasNext();) {
  360               Record rowRec = (Record)rowIter.next();
  361               clonedRecords.add(rowRec);
  362             }
  363           } else if (rec instanceof ValueRecordsAggregate) {
  364             ValueRecordsAggregate vrAgg = (ValueRecordsAggregate)rec;
  365             for (Iterator cellIter = vrAgg.getIterator();cellIter.hasNext();) {
  366               Record valRec = (Record)cellIter.next();
  367   
  368               if (valRec instanceof FormulaRecordAggregate) {
  369                   FormulaRecordAggregate fmAgg = (FormulaRecordAggregate)valRec;
  370                   Record fmAggRec = fmAgg.getFormulaRecord();
  371                   if (fmAggRec != null)
  372                     clonedRecords.add(fmAggRec);
  373                   fmAggRec =   fmAgg.getStringRecord();
  374                   if (fmAggRec != null)
  375                     clonedRecords.add(fmAggRec);
  376                 } else {
  377                   clonedRecords.add(valRec);
  378                 }
  379             }
  380           } else if (rec instanceof FormulaRecordAggregate) {  //Is this required now??
  381             FormulaRecordAggregate fmAgg = (FormulaRecordAggregate)rec;
  382             Record fmAggRec = fmAgg.getFormulaRecord();
  383             if (fmAggRec != null)
  384               clonedRecords.add(fmAggRec);
  385             fmAggRec =   fmAgg.getStringRecord();
  386             if (fmAggRec != null)
  387               clonedRecords.add(fmAggRec);
  388           } else {
  389             clonedRecords.add(rec);
  390           }
  391         }
  392         return createSheet(clonedRecords, 0, 0);
  393       }
  394   
  395   
  396       /**
  397        * read support  (offset = 0) Same as createSheet(Record[] recs, int, int)
  398        * only the record offset is assumed to be 0.
  399        *
  400        * @param records  array containing those records in the sheet in sequence (normally obtained from RecordFactory)
  401        * @param sheetnum integer specifying the sheet's number (0,1 or 2 in this release)
  402        * @return Sheet object
  403        */
  404   
  405       public static Sheet createSheet(List records, int sheetnum)
  406       {
  407           if (log.check( POILogger.DEBUG ))
  408               log.log(POILogger.DEBUG,
  409                       "Sheet createSheet (exisiting file) assumed offset 0");
  410           return createSheet(records, sheetnum, 0);
  411       }
  412   
  413       /**
  414        * Creates a sheet with all the usual records minus values and the "index"
  415        * record (not required).  Sets the location pointer to where the first value
  416        * records should go.  Use this to create a sheet from "scratch".
  417        *
  418        * @return Sheet object with all values set to defaults
  419        */
  420   
  421       public static Sheet createSheet()
  422       {
  423           if (log.check( POILogger.DEBUG ))
  424               log.log(POILogger.DEBUG, "Sheet createsheet from scratch called");
  425           Sheet     retval  = new Sheet();
  426           ArrayList records = new ArrayList(30);
  427   
  428           records.add(retval.createBOF());
  429   
  430           // records.add(retval.createIndex());
  431           records.add(retval.createCalcMode());
  432           records.add(retval.createCalcCount() );
  433           records.add( retval.createRefMode() );
  434           records.add( retval.createIteration() );
  435           records.add( retval.createDelta() );
  436           records.add( retval.createSaveRecalc() );
  437           records.add( retval.createPrintHeaders() );
  438           retval.printGridlines = (PrintGridlinesRecord) retval.createPrintGridlines();
  439           records.add( retval.printGridlines );
  440           retval.gridset = (GridsetRecord) retval.createGridset();
  441           records.add( retval.gridset );
  442           records.add( retval.createGuts() );
  443           retval.defaultrowheight =
  444                   (DefaultRowHeightRecord) retval.createDefaultRowHeight();
  445           records.add( retval.defaultrowheight );
  446           records.add( retval.createWSBool() );
  447   
  448           retval.rowBreaks = new PageBreakRecord(PageBreakRecord.HORIZONTAL_SID);
  449           records.add(retval.rowBreaks);
  450           retval.colBreaks = new PageBreakRecord(PageBreakRecord.VERTICAL_SID);
  451           records.add(retval.colBreaks);
  452   
  453           retval.header = (HeaderRecord) retval.createHeader();
  454           records.add( retval.header );
  455           retval.footer = (FooterRecord) retval.createFooter();
  456           records.add( retval.footer );
  457           records.add( retval.createHCenter() );
  458           records.add( retval.createVCenter() );
  459           retval.printSetup = (PrintSetupRecord) retval.createPrintSetup();
  460           records.add( retval.printSetup );
  461           retval.defaultcolwidth =
  462                   (DefaultColWidthRecord) retval.createDefaultColWidth();
  463           records.add( retval.defaultcolwidth);
  464           ColumnInfoRecordsAggregate columns = new ColumnInfoRecordsAggregate();
  465           records.add( columns );
  466           retval.columns = columns;
  467           retval.dims    = ( DimensionsRecord ) retval.createDimensions();
  468           records.add(retval.dims);
  469           retval.dimsloc = records.size()-1;
  470           records.add(retval.windowTwo = retval.createWindowTwo());
  471           retval.setLoc(records.size() - 1);
  472           retval.selection =
  473                   (SelectionRecord) retval.createSelection();
  474           records.add(retval.selection);
  475           retval.protect = (ProtectRecord) retval.createProtect();
  476           records.add(retval.protect);
  477           records.add(retval.createEOF());
  478   
  479   
  480           retval.records = records;
  481           if (log.check( POILogger.DEBUG ))
  482               log.log(POILogger.DEBUG, "Sheet createsheet from scratch exit");
  483           return retval;
  484       }
  485   
  486       private void checkCells()
  487       {
  488           if (cells == null)
  489           {
  490               cells = new ValueRecordsAggregate();
  491               // In the worksheet stream, the row records always occur before the cell (value) 
  492               // records. Therefore POI's aggregates (RowRecordsAggregate, ValueRecordsAggregate) 
  493               // should follow suit. Some methods in this class tolerate either order, while 
  494               // others have been found to fail (see bug 45145).
  495               int rraIndex = getDimsLoc() + 1;
  496               if (records.get(rraIndex).getClass() != RowRecordsAggregate.class) {
  497                   throw new IllegalStateException("Cannot create value records before row records exist");
  498               }
  499               records.add(rraIndex+1, cells);
  500           }
  501       }
  502   
  503       private void checkRows()
  504       {
  505           if (rows == null)
  506           {
  507               rows = new RowRecordsAggregate();
  508               records.add(getDimsLoc() + 1, rows);
  509           }
  510       }
  511   
  512       public int addMergedRegion(int rowFrom, short colFrom, int rowTo, short colTo) {
  513           // Validate input
  514           if (rowTo < rowFrom) {
  515               throw new IllegalArgumentException("The 'to' row (" + rowTo
  516                       + ") must not be less than the 'from' row (" + rowFrom + ")");
  517           }
  518           if (colTo < colFrom) {
  519               throw new IllegalArgumentException("The 'to' col (" + colTo
  520                       + ") must not be less than the 'from' col (" + colFrom + ")");
  521           }
  522   
  523           if (merged == null || merged.getNumAreas() == 1027)
  524           {
  525               merged = ( MergeCellsRecord ) createMergedCells();
  526               mergedRecords.add(merged);
  527               records.add(records.size() - 1, merged);
  528           }
  529           merged.addArea(rowFrom, colFrom, rowTo, colTo);
  530           return numMergedRegions++;
  531       }
  532   
  533       public void removeMergedRegion(int index)
  534       {
  535           //safety checks
  536           if (index >= numMergedRegions || mergedRecords.size() == 0)
  537              return;
  538   
  539           int pos = 0;
  540           int startNumRegions = 0;
  541   
  542           //optimisation for current record
  543           if (numMergedRegions - index < merged.getNumAreas())
  544           {
  545               pos = mergedRecords.size() - 1;
  546               startNumRegions = numMergedRegions - merged.getNumAreas();
  547           }
  548           else
  549           {
  550               for (int n = 0; n < mergedRecords.size(); n++)
  551               {
  552                   MergeCellsRecord record = (MergeCellsRecord) mergedRecords.get(n);
  553                   if (startNumRegions + record.getNumAreas() > index)
  554                   {
  555                       pos = n;
  556                       break;
  557                   }
  558                   startNumRegions += record.getNumAreas();
  559               }
  560           }
  561   
  562           MergeCellsRecord rec = (MergeCellsRecord) mergedRecords.get(pos);
  563           rec.removeAreaAt(index - startNumRegions);
  564           numMergedRegions--;
  565           if (rec.getNumAreas() == 0)
  566           {
  567               mergedRecords.remove(pos);
  568               //get rid of the record from the sheet
  569               records.remove(merged);
  570               if (merged == rec) {
  571                   //pull up the LAST record for operations when we finally
  572                   //support continue records for mergedRegions
  573                   if (mergedRecords.size() > 0) {
  574                       merged = (MergeCellsRecord) mergedRecords.get(mergedRecords.size() - 1);
  575                   } else {
  576                       merged = null;
  577                   }
  578               }
  579           }
  580       }
  581   
  582       public MergeCellsRecord.MergedRegion getMergedRegionAt(int index)
  583       {
  584           //safety checks
  585           if (index >= numMergedRegions || mergedRecords.size() == 0)
  586               return null;
  587   
  588           int pos = 0;
  589           int startNumRegions = 0;
  590   
  591           //optimisation for current record
  592           if (numMergedRegions - index < merged.getNumAreas())
  593           {
  594               pos = mergedRecords.size() - 1;
  595               startNumRegions = numMergedRegions - merged.getNumAreas();
  596           }
  597           else
  598           {
  599               for (int n = 0; n < mergedRecords.size(); n++)
  600               {
  601                   MergeCellsRecord record = (MergeCellsRecord) mergedRecords.get(n);
  602                   if (startNumRegions + record.getNumAreas() > index)
  603                   {
  604                       pos = n;
  605                       break;
  606                   }
  607                   startNumRegions += record.getNumAreas();
  608               }
  609           }
  610           return ((MergeCellsRecord) mergedRecords.get(pos)).getAreaAt(index - startNumRegions);
  611       }
  612   
  613       public int getNumMergedRegions()
  614       {
  615           return numMergedRegions;
  616       }
  617       // Find correct position to add new CF record
  618       private int findConditionalFormattingPosition()
  619       {
  620           // This is default.
  621           // If the algorithm does not find the right position,
  622           // this one will be used (this is a position before EOF record)
  623           int index = records.size()-2;
  624   
  625           for( int i=index; i>=0; i-- )
  626           {
  627               Record rec = (Record)records.get(i);
  628               short sid = rec.getSid();
  629   
  630               // CFRecordsAggregate records already exist, just add to the end
  631               if (rec instanceof CFRecordsAggregate)    { return i+1; }
  632   
  633               if( sid == (short)0x00ef )                { return i+1; }// PHONETICPR
  634               if( sid == (short)0x015f )                { return i+1; }// LABELRANGES
  635               if( sid == MergeCellsRecord.sid )        { return i+1; }
  636               if( sid == (short)0x0099 )                { return i+1; }// STANDARDWIDTH
  637               if( sid == SelectionRecord.sid )        { return i+1; }
  638               if( sid == PaneRecord.sid )                { return i+1; }
  639               if( sid == SCLRecord.sid )                 { return i+1; }
  640               if( sid == WindowTwoRecord.sid )        { return i+1; }
  641           }
  642   
  643           return index;
  644       }
  645   
  646       public int addConditionalFormatting(CFRecordsAggregate cfAggregate)
  647       {
  648           int index = findConditionalFormattingPosition();
  649           records.add(index, cfAggregate);
  650           condFormatting.add(cfAggregate);
  651           return condFormatting.size()-1;
  652       }
  653   
  654       public void removeConditionalFormatting(int index)
  655       {
  656           if (index >= 0 && index <= condFormatting.size()-1 )
  657           {
  658               CFRecordsAggregate cfAggregate = getCFRecordsAggregateAt(index);
  659               records.remove(cfAggregate);
  660               condFormatting.remove(index);
  661           }
  662       }
  663   
  664       public CFRecordsAggregate getCFRecordsAggregateAt(int index)
  665       {
  666           if (index >= 0 && index <= condFormatting.size()-1 )
  667           {
  668               return (CFRecordsAggregate) condFormatting.get(index);
  669           }
  670           return null;
  671       }
  672   
  673       public int getNumConditionalFormattings()
  674       {
  675           return condFormatting.size();
  676       }
  677   
  678       /**
  679        * Returns the number of low level binary records in this sheet.  This adjusts things for the so called
  680        * AgregateRecords.
  681        *
  682        * @see org.apache.poi.hssf.record.Record
  683        */
  684   
  685       public int getNumRecords()
  686       {
  687           checkCells();
  688           checkRows();
  689           if (log.check( POILogger.DEBUG ))
  690           {
  691               log.log(POILogger.DEBUG, "Sheet.getNumRecords");
  692               log.logFormatted(POILogger.DEBUG, "returning % + % + % - 2 = %", new int[]
  693               {
  694                   records.size(), cells.getPhysicalNumberOfCells(),
  695                   rows.getPhysicalNumberOfRows(),
  696                   records.size() + cells.getPhysicalNumberOfCells()
  697                   + rows.getPhysicalNumberOfRows() - 2
  698               });
  699           }
  700           return records.size() + cells.getPhysicalNumberOfCells()
  701                  + rows.getPhysicalNumberOfRows() - 2;
  702       }
  703   
  704       /**
  705        * Per an earlier reported bug in working with Andy Khan's excel read library.  This
  706        * sets the values in the sheet's DimensionsRecord object to be correct.  Excel doesn't
  707        * really care, but we want to play nice with other libraries.
  708        *
  709        * @see org.apache.poi.hssf.record.DimensionsRecord
  710        */
  711       public void setDimensions(int firstrow, short firstcol, int lastrow,
  712                                 short lastcol)
  713       {
  714           if (log.check( POILogger.DEBUG ))
  715           {
  716               log.log(POILogger.DEBUG, "Sheet.setDimensions");
  717               log.log(POILogger.DEBUG,
  718                       (new StringBuffer("firstrow")).append(firstrow)
  719                           .append("firstcol").append(firstcol).append("lastrow")
  720                           .append(lastrow).append("lastcol").append(lastcol)
  721                           .toString());
  722           }
  723           dims.setFirstCol(firstcol);
  724           dims.setFirstRow(firstrow);
  725           dims.setLastCol(lastcol);
  726           dims.setLastRow(lastrow);
  727           if (log.check( POILogger.DEBUG ))
  728               log.log(POILogger.DEBUG, "Sheet.setDimensions exiting");
  729       }
  730   
  731       /**
  732        * set the locator for where we should look for the next value record.  The
  733        * algorithm will actually start here and find the correct location so you
  734        * can set this to 0 and watch performance go down the tubes but it will work.
  735        * After a value is set this is automatically advanced.  Its also set by the
  736        * create method.  So you probably shouldn't mess with this unless you have
  737        * a compelling reason why or the help for the method you're calling says so.
  738        * Check the other methods for whether they care about
  739        * the loc pointer.  Many of the "modify" and "remove" methods re-initialize this
  740        * to "dimsloc" which is the location of the Dimensions Record and presumably the
  741        * start of the value section (at or around 19 dec).
  742        *
  743        * @param loc the record number to start at
  744        *
  745        */
  746   
  747       public void setLoc(int loc)
  748       {
  749           valueRecIterator = null;
  750           if (log.check( POILogger.DEBUG ))
  751               log.log(POILogger.DEBUG, "sheet.setLoc(): " + loc);
  752           this.loc = loc;
  753       }
  754   
  755       /**
  756        * Returns the location pointer to the first record to look for when adding rows/values
  757        *
  758        */
  759   
  760       public int getLoc()
  761       {
  762           if (log.check( POILogger.DEBUG ))
  763               log.log(POILogger.DEBUG, "sheet.getLoc():" + loc);
  764           return loc;
  765       }
  766   
  767       /**
  768        * Set the preoffset when using DBCELL records (currently unused) - this is
  769        * the position of this sheet within the whole file.
  770        *
  771        * @param offset the offset of the sheet's BOF within the file.
  772        */
  773   
  774       public void setPreOffset(int offset)
  775       {
  776           this.preoffset = offset;
  777       }
  778   
  779       /**
  780        * get the preoffset when using DBCELL records (currently unused) - this is
  781        * the position of this sheet within the whole file.
  782        *
  783        * @return offset the offset of the sheet's BOF within the file.
  784        */
  785   
  786       public int getPreOffset()
  787       {
  788           return preoffset;
  789       }
  790   
  791       /**
  792        * Serializes all records in the sheet into one big byte array.  Use this to write
  793        * the sheet out.
  794        *
  795        * @param offset to begin write at
  796        * @param data   array containing the binary representation of the records in this sheet
  797        *
  798        */
  799   
  800       public int serialize(int offset, byte [] data)
  801       {
  802           if (log.check( POILogger.DEBUG ))
  803               log.log(POILogger.DEBUG, "Sheet.serialize using offsets");
  804   
  805           int pos       = offset;
  806           boolean haveSerializedIndex = false;
  807   
  808           for (int k = 0; k < records.size(); k++)
  809           {
  810               Record record = (( Record ) records.get(k));
  811   
  812               // Don't write out UncalcedRecord entries, as
  813               //  we handle those specially just below
  814               if (record instanceof UncalcedRecord) {
  815                   continue;
  816               }
  817   
  818               // Once the rows have been found in the list of records, start
  819               //  writing out the blocked row information. This includes the DBCell references
  820               if (record instanceof RowRecordsAggregate) {
  821                 pos += ((RowRecordsAggregate)record).serialize(pos, data, cells);
  822               } else if (record instanceof ValueRecordsAggregate) {
  823                 //Do nothing here. The records were serialized during the RowRecordAggregate block serialization
  824               } else {
  825                 pos += record.serialize(pos, data );
  826               }
  827   
  828               // If the BOF record was just serialized then add the IndexRecord
  829               if (record.getSid() == BOFRecord.sid) {
  830                 // Add an optional UncalcedRecord
  831                 if (_isUncalced) {
  832                     UncalcedRecord rec = new UncalcedRecord();
  833                     pos += rec.serialize(pos, data);
  834                 }
  835                 //Can there be more than one BOF for a sheet? If not then we can
  836                 //remove this guard. So be safe it is left here.
  837                 if (rows != null && !haveSerializedIndex) {
  838                   haveSerializedIndex = true;
  839                   pos += serializeIndexRecord(k, pos, data);
  840                 }
  841               }
  842           }
  843           if (log.check( POILogger.DEBUG )) {
  844               log.log(POILogger.DEBUG, "Sheet.serialize returning ");
  845           }
  846           return pos-offset;
  847       }
  848   
  849       /**
  850        * @param indexRecordOffset also happens to be the end of the BOF record
  851        * @return the size of the serialized INDEX record
  852        */
  853       private int serializeIndexRecord(final int bofRecordIndex, final int indexRecordOffset,
  854               byte[] data) {
  855           IndexRecord index = new IndexRecord();
  856           index.setFirstRow(rows.getFirstRowNum());
  857           index.setLastRowAdd1(rows.getLastRowNum() + 1);
  858           // Calculate the size of the records from the end of the BOF
  859           // and up to the RowRecordsAggregate...
  860   
  861           // 'initial sheet records' are between INDEX and first ROW record.
  862           int sizeOfInitialSheetRecords = 0;
  863           // start just after BOF record (INDEX is not present in this list)
  864           for (int j = bofRecordIndex + 1; j < records.size(); j++) {
  865               Record tmpRec = ((Record) records.get(j));
  866               if (tmpRec instanceof UncalcedRecord) {
  867                   continue;
  868               }
  869               if (tmpRec instanceof RowRecordsAggregate) {
  870                   break;
  871               }
  872               sizeOfInitialSheetRecords += tmpRec.getRecordSize();
  873           }
  874           if (_isUncalced) {
  875               sizeOfInitialSheetRecords += UncalcedRecord.getStaticRecordSize();
  876           }
  877   
  878           // Add the references to the DBCells in the IndexRecord (one for each block)
  879           // Note: The offsets are relative to the Workbook BOF. Assume that this is
  880           // 0 for now.....
  881   
  882           int blockCount = rows.getRowBlockCount();
  883           // Calculate the size of this IndexRecord
  884           int indexRecSize = IndexRecord.getRecordSizeForBlockCount(blockCount);
  885   
  886           int currentOffset = indexRecordOffset + indexRecSize + sizeOfInitialSheetRecords;
  887   
  888           for (int block = 0; block < blockCount; block++) {
  889               // each row-block has a DBCELL record.
  890               // The offset of each DBCELL record needs to be updated in the INDEX record
  891   
  892               // account for row records in this row-block
  893               currentOffset += rows.getRowBlockSize(block);
  894               // account for cell value records after those
  895               currentOffset += null == cells ? 0 : cells.getRowCellBlockSize(rows
  896                       .getStartRowNumberForBlock(block), rows.getEndRowNumberForBlock(block));
  897   
  898               // currentOffset is now the location of the DBCELL record for this row-block
  899               index.addDbcell(currentOffset);
  900               // Add space required to write the DBCELL record (whose reference was just added).
  901               currentOffset += (8 + (rows.getRowCountForBlock(block) * 2));
  902           }
  903           return index.serialize(indexRecordOffset, data);
  904       }
  905   
  906   
  907       /**
  908        * Create a row record.  (does not add it to the records contained in this sheet)
  909        *
  910        * @param row number
  911        * @return RowRecord created for the passed in row number
  912        * @see org.apache.poi.hssf.record.RowRecord
  913        */
  914   
  915       public RowRecord createRow(int row)
  916       {
  917           return RowRecordsAggregate.createRow( row );
  918       }
  919   
  920       /**
  921        * Create a LABELSST Record (does not add it to the records contained in this sheet)
  922        *
  923        * @param row the row the LabelSST is a member of
  924        * @param col the column the LabelSST defines
  925        * @param index the index of the string within the SST (use workbook addSSTString method)
  926        * @return LabelSSTRecord newly created containing your SST Index, row,col.
  927        * @see org.apache.poi.hssf.record.SSTRecord
  928        */
  929       public LabelSSTRecord createLabelSST(int row, short col, int index)
  930       {
  931           log.logFormatted(POILogger.DEBUG, "create labelsst row,col,index %,%,%",
  932                            new int[]
  933           {
  934               row, col, index
  935           });
  936           LabelSSTRecord rec = new LabelSSTRecord();
  937   
  938           rec.setRow(row);
  939           rec.setColumn(col);
  940           rec.setSSTIndex(index);
  941           rec.setXFIndex(( short ) 0x0f);
  942           return rec;
  943       }
  944   
  945       /**
  946        * Create a NUMBER Record (does not add it to the records contained in this sheet)
  947        *
  948        * @param row the row the NumberRecord is a member of
  949        * @param col the column the NumberRecord defines
  950        * @param value for the number record
  951        *
  952        * @return NumberRecord for that row, col containing that value as added to the sheet
  953        */
  954       public NumberRecord createNumber(int row, short col, double value)
  955       {
  956           log.logFormatted(POILogger.DEBUG, "create number row,col,value %,%,%",
  957                            new double[]
  958           {
  959               row, col, value
  960           });
  961           NumberRecord rec = new NumberRecord();
  962   
  963           rec.setRow(row);
  964           rec.setColumn(col);
  965           rec.setValue(value);
  966           rec.setXFIndex(( short ) 0x0f);
  967           return rec;
  968       }
  969   
  970       /**
  971        * create a BLANK record (does not add it to the records contained in this sheet)
  972        *
  973        * @param row - the row the BlankRecord is a member of
  974        * @param col - the column the BlankRecord is a member of
  975        */
  976       public BlankRecord createBlank(int row, short col)
  977       {
  978           log.logFormatted(POILogger.DEBUG, "create blank row,col %,%", new int[]
  979           {
  980               row, col
  981           });
  982           BlankRecord rec = new BlankRecord();
  983   
  984           rec.setRow(row);
  985           rec.setColumn(col);
  986           rec.setXFIndex(( short ) 0x0f);
  987           return rec;
  988       }
  989   
  990       /**
  991        * Attempts to parse the formula into PTGs and create a formula record
  992        * DOES NOT WORK YET
  993        *
  994        * @param row - the row for the formula record
  995        * @param col - the column of the formula record
  996        * @param formula - a String representing the formula.  To be parsed to PTGs
  997        * @return bogus/useless formula record
  998        */
  999       public FormulaRecord createFormula(int row, short col, String formula)
 1000       {
 1001           log.logFormatted(POILogger.DEBUG, "create formula row,col,formula %,%,%",
 1002                            new int[]
 1003           {
 1004               row, col
 1005           }, formula);
 1006           FormulaRecord rec = new FormulaRecord();
 1007   
 1008           rec.setRow(row);
 1009           rec.setColumn(col);
 1010           rec.setOptions(( short ) 2);
 1011           rec.setValue(0);
 1012           rec.setXFIndex(( short ) 0x0f);
 1013           FormulaParser fp = new FormulaParser(formula,null); //fix - do we need this method?
 1014           fp.parse();
 1015           Ptg[] ptg  = fp.getRPNPtg();
 1016           int   size = 0;
 1017   
 1018           for (int k = 0; k < ptg.length; k++)
 1019           {
 1020               size += ptg[ k ].getSize();
 1021               rec.pushExpressionToken(ptg[ k ]);
 1022           }
 1023           rec.setExpressionLength(( short ) size);
 1024           return rec;
 1025       }
 1026   
 1027       /**
 1028        * Adds a value record to the sheet's contained binary records
 1029        * (i.e. LabelSSTRecord or NumberRecord).
 1030        * <P>
 1031        * This method is "loc" sensitive.  Meaning you need to set LOC to where you
 1032        * want it to start searching.  If you don't know do this: setLoc(getDimsLoc).
 1033        * When adding several rows you can just start at the last one by leaving loc
 1034        * at what this sets it to.
 1035        *
 1036        * @param row the row to add the cell value to
 1037        * @param col the cell value record itself.
 1038        */
 1039       public void addValueRecord(int row, CellValueRecordInterface col)
 1040       {
 1041           checkCells();
 1042           if(log.check(POILogger.DEBUG))
 1043           {
 1044             log.logFormatted(POILogger.DEBUG, "add value record  row,loc %,%", new int[]
 1045             {
 1046                 row, loc
 1047             });
 1048           }
 1049           DimensionsRecord d = ( DimensionsRecord ) records.get(getDimsLoc());
 1050   
 1051           if (col.getColumn() > d.getLastCol())
 1052           {
 1053               d.setLastCol(( short ) (col.getColumn() + 1));
 1054           }
 1055           if (col.getColumn() < d.getFirstCol())
 1056           {
 1057               d.setFirstCol(col.getColumn());
 1058           }
 1059           cells.insertCell(col);
 1060       }
 1061   
 1062       /**
 1063        * remove a value record from the records array.
 1064        *
 1065        * This method is not loc sensitive, it resets loc to = dimsloc so no worries.
 1066        *
 1067        * @param row - the row of the value record you wish to remove
 1068        * @param col - a record supporting the CellValueRecordInterface.
 1069        * @see org.apache.poi.hssf.record.CellValueRecordInterface
 1070        */
 1071       public void removeValueRecord(int row, CellValueRecordInterface col)
 1072       {
 1073           checkCells();
 1074           log.logFormatted(POILogger.DEBUG, "remove value record row,dimsloc %,%",
 1075                            new int[]{row, dimsloc} );
 1076           loc = dimsloc;
 1077           cells.removeCell(col);
 1078       }
 1079   
 1080       /**
 1081        * replace a value record from the records array.
 1082        *
 1083        * This method is not loc sensitive, it resets loc to = dimsloc so no worries.
 1084        *
 1085        * @param newval - a record supporting the CellValueRecordInterface.  this will replace
 1086        *                the cell value with the same row and column.  If there isn't one, one will
 1087        *                be added.
 1088        */
 1089   
 1090       public void replaceValueRecord(CellValueRecordInterface newval)
 1091       {
 1092           checkCells();
 1093           setLoc(dimsloc);
 1094           if (log.check( POILogger.DEBUG ))
 1095               log.log(POILogger.DEBUG, "replaceValueRecord ");
 1096           //The ValueRecordsAggregate use a tree map underneath.
 1097           //The tree Map uses the CellValueRecordInterface as both the
 1098           //key and the value, if we dont do a remove, then
 1099           //the previous instance of the key is retained, effectively using
 1100           //double the memory
 1101           cells.removeCell(newval);
 1102           cells.insertCell(newval);
 1103       }
 1104   
 1105       /**
 1106        * Adds a row record to the sheet
 1107        *
 1108        * <P>
 1109        * This method is "loc" sensitive.  Meaning you need to set LOC to where you
 1110        * want it to start searching.  If you don't know do this: setLoc(getDimsLoc).
 1111        * When adding several rows you can just start at the last one by leaving loc
 1112        * at what this sets it to.
 1113        *
 1114        * @param row the row record to be added
 1115        * @see #setLoc(int)
 1116        */
 1117   
 1118       public void addRow(RowRecord row)
 1119       {
 1120           checkRows();
 1121           if (log.check( POILogger.DEBUG ))
 1122               log.log(POILogger.DEBUG, "addRow ");
 1123           DimensionsRecord d = ( DimensionsRecord ) records.get(getDimsLoc());
 1124   
 1125           if (row.getRowNumber() >= d.getLastRow())
 1126           {
 1127               d.setLastRow(row.getRowNumber() + 1);
 1128           }
 1129           if (row.getRowNumber() < d.getFirstRow())
 1130           {
 1131               d.setFirstRow(row.getRowNumber());
 1132           }
 1133           //IndexRecord index = null;
 1134            //If the row exists remove it, so that any cells attached to the row are removed
 1135            RowRecord existingRow = rows.getRow(row.getRowNumber());
 1136            if (existingRow != null)
 1137              rows.removeRow(existingRow);
 1138   
 1139           rows.insertRow(row);
 1140   
 1141           if (log.check( POILogger.DEBUG ))
 1142               log.log(POILogger.DEBUG, "exit addRow");
 1143       }
 1144   
 1145       /**
 1146        * Removes a row record
 1147        *
 1148        * This method is not loc sensitive, it resets loc to = dimsloc so no worries.
 1149        *
 1150        * @param row  the row record to remove
 1151        */
 1152   
 1153       public void removeRow(RowRecord row)
 1154       {
 1155           checkRows();
 1156   
 1157           setLoc(getDimsLoc());
 1158           rows.removeRow(row);
 1159       }
 1160   
 1161       /**
 1162        * get the NEXT value record (from LOC).  The first record that is a value record
 1163        * (starting at LOC) will be returned.
 1164        *
 1165        * <P>
 1166        * This method is "loc" sensitive.  Meaning you need to set LOC to where you
 1167        * want it to start searching.  If you don't know do this: setLoc(getDimsLoc).
 1168        * When adding several rows you can just start at the last one by leaving loc
 1169        * at what this sets it to.  For this method, set loc to dimsloc to start with,
 1170        * subsequent calls will return values in (physical) sequence or NULL when you get to the end.
 1171        *
 1172        * @return CellValueRecordInterface representing the next value record or NULL if there are no more
 1173        * @see #setLoc(int)
 1174        */
 1175   
 1176       public CellValueRecordInterface getNextValueRecord()
 1177       {
 1178           if (log.check( POILogger.DEBUG ))
 1179               log.log(POILogger.DEBUG, "getNextValue loc= " + loc);
 1180           if (valueRecIterator == null)
 1181           {
 1182               valueRecIterator = cells.getIterator();
 1183           }
 1184           if (!valueRecIterator.hasNext())
 1185           {
 1186               return null;
 1187           }
 1188           return ( CellValueRecordInterface ) valueRecIterator.next();
 1189       }
 1190   
 1191       /**
 1192        * get the NEXT RowRecord (from LOC).  The first record that is a Row record
 1193        * (starting at LOC) will be returned.
 1194        * <P>
 1195        * This method is "loc" sensitive.  Meaning you need to set LOC to where you
 1196        * want it to start searching.  If you don't know do this: setLoc(getDimsLoc).
 1197        * When adding several rows you can just start at the last one by leaving loc
 1198        * at what this sets it to.  For this method, set loc to dimsloc to start with.
 1199        * subsequent calls will return rows in (physical) sequence or NULL when you get to the end.
 1200        *
 1201        * @return RowRecord representing the next row record or NULL if there are no more
 1202        * @see #setLoc(int)
 1203        *
 1204        */
 1205   
 1206       public RowRecord getNextRow()
 1207       {
 1208           if (log.check( POILogger.DEBUG ))
 1209               log.log(POILogger.DEBUG, "getNextRow loc= " + loc);
 1210           if (rowRecIterator == null)
 1211           {
 1212               rowRecIterator = rows.getIterator();
 1213           }
 1214           if (!rowRecIterator.hasNext())
 1215           {
 1216               return null;
 1217           }
 1218           return ( RowRecord ) rowRecIterator.next();
 1219       }
 1220   
 1221       /**
 1222        * get the NEXT (from LOC) RowRecord where rownumber matches the given rownum.
 1223        * The first record that is a Row record (starting at LOC) that has the
 1224        * same rownum as the given rownum will be returned.
 1225        * <P>
 1226        * This method is "loc" sensitive.  Meaning you need to set LOC to where you
 1227        * want it to start searching.  If you don't know do this: setLoc(getDimsLoc).
 1228        * When adding several rows you can just start at the last one by leaving loc
 1229        * at what this sets it to.  For this method, set loc to dimsloc to start with.
 1230        * subsequent calls will return rows in (physical) sequence or NULL when you get to the end.
 1231        *
 1232        * @param rownum   which row to return (careful with LOC)
 1233        * @return RowRecord representing the next row record or NULL if there are no more
 1234        * @see #setLoc(int)
 1235        *
 1236        */
 1237       public RowRecord getRow(int rownum) {
 1238           if (log.check( POILogger.DEBUG ))
 1239               log.log(POILogger.DEBUG, "getNextRow loc= " + loc);
 1240           return rows.getRow(rownum);
 1241       }
 1242   
 1243       /**
 1244        * creates the BOF record
 1245        * @see org.apache.poi.hssf.record.BOFRecord
 1246        * @see org.apache.poi.hssf.record.Record
 1247        * @return record containing a BOFRecord
 1248        */
 1249   
 1250       protected Record createBOF()
 1251       {
 1252           BOFRecord retval = new BOFRecord();
 1253   
 1254           retval.setVersion(( short ) 0x600);
 1255           retval.setType(( short ) 0x010);
 1256   
 1257           retval.setBuild(( short ) 0x0dbb);
 1258           retval.setBuildYear(( short ) 1996);
 1259           retval.setHistoryBitMask(0xc1);
 1260           retval.setRequiredVersion(0x6);
 1261           return retval;
 1262       }
 1263   
 1264       /**
 1265        * creates the Index record  - not currently used
 1266        * @see org.apache.poi.hssf.record.IndexRecord
 1267        * @see org.apache.poi.hssf.record.Record
 1268        * @return record containing a IndexRecord
 1269        */
 1270   
 1271       protected Record createIndex()
 1272       {
 1273           IndexRecord retval = new IndexRecord();
 1274   
 1275           retval.setFirstRow(0);   // must be set explicitly
 1276           retval.setLastRowAdd1(0);
 1277           return retval;
 1278       }
 1279   
 1280       /**
 1281        * creates the CalcMode record and sets it to 1 (automatic formula caculation)
 1282        * @see org.apache.poi.hssf.record.CalcModeRecord
 1283        * @see org.apache.poi.hssf.record.Record
 1284        * @return record containing a CalcModeRecord
 1285        */
 1286   
 1287       protected Record createCalcMode()
 1288       {
 1289           CalcModeRecord retval = new CalcModeRecord();
 1290   
 1291           retval.setCalcMode(( short ) 1);
 1292           return retval;
 1293       }
 1294   
 1295       /**
 1296        * creates the CalcCount record and sets it to 0x64 (default number of iterations)
 1297        * @see org.apache.poi.hssf.record.CalcCountRecord
 1298        * @see org.apache.poi.hssf.record.Record
 1299        * @return record containing a CalcCountRecord
 1300        */
 1301   
 1302       protected Record createCalcCount()
 1303       {
 1304           CalcCountRecord retval = new CalcCountRecord();
 1305   
 1306           retval.setIterations(( short ) 0x64);   // default 64 iterations
 1307           return retval;
 1308       }
 1309   
 1310       /**
 1311        * creates the RefMode record and sets it to A1 Mode (default reference mode)
 1312        * @see org.apache.poi.hssf.record.RefModeRecord
 1313        * @see org.apache.poi.hssf.record.Record
 1314        * @return record containing a RefModeRecord
 1315        */
 1316   
 1317       protected Record createRefMode()
 1318       {
 1319           RefModeRecord retval = new RefModeRecord();
 1320   
 1321           retval.setMode(RefModeRecord.USE_A1_MODE);
 1322           return retval;
 1323       }
 1324   
 1325       /**
 1326        * creates the Iteration record and sets it to false (don't iteratively calculate formulas)
 1327        * @see org.apache.poi.hssf.record.IterationRecord
 1328        * @see org.apache.poi.hssf.record.Record
 1329        * @return record containing a IterationRecord
 1330        */
 1331   
 1332       protected Record createIteration()
 1333       {
 1334           IterationRecord retval = new IterationRecord();
 1335   
 1336           retval.setIteration(false);
 1337           return retval;
 1338       }
 1339   
 1340       /**
 1341        * creates the Delta record and sets it to 0.0010 (default accuracy)
 1342        * @see org.apache.poi.hssf.record.DeltaRecord
 1343        * @see org.apache.poi.hssf.record.Record
 1344        * @return record containing a DeltaRecord
 1345        */
 1346   
 1347       protected Record createDelta()
 1348       {
 1349           DeltaRecord retval = new DeltaRecord();
 1350   
 1351           retval.setMaxChange(0.0010);
 1352           return retval;
 1353       }
 1354   
 1355       /**
 1356        * creates the SaveRecalc record and sets it to true (recalculate before saving)
 1357        * @see org.apache.poi.hssf.record.SaveRecalcRecord
 1358        * @see org.apache.poi.hssf.record.Record
 1359        * @return record containing a SaveRecalcRecord
 1360        */
 1361   
 1362       protected Record createSaveRecalc()
 1363       {
 1364           SaveRecalcRecord retval = new SaveRecalcRecord();
 1365   
 1366           retval.setRecalc(true);
 1367           return retval;
 1368       }
 1369   
 1370       /**
 1371        * creates the PrintHeaders record and sets it to false (we don't create headers yet so why print them)
 1372        * @see org.apache.poi.hssf.record.PrintHeadersRecord
 1373        * @see org.apache.poi.hssf.record.Record
 1374        * @return record containing a PrintHeadersRecord
 1375        */
 1376   
 1377       protected Record createPrintHeaders()
 1378       {
 1379           PrintHeadersRecord retval = new PrintHeadersRecord();
 1380   
 1381           retval.setPrintHeaders(false);
 1382           return retval;
 1383       }
 1384   
 1385       /**
 1386        * creates the PrintGridlines record and sets it to false (that makes for ugly sheets).  As far as I can
 1387        * tell this does the same thing as the GridsetRecord
 1388        *
 1389        * @see org.apache.poi.hssf.record.PrintGridlinesRecord
 1390        * @see org.apache.poi.hssf.record.Record
 1391        * @return record containing a PrintGridlinesRecord
 1392        */
 1393   
 1394       protected Record createPrintGridlines()
 1395       {
 1396           PrintGridlinesRecord retval = new PrintGridlinesRecord();
 1397   
 1398           retval.setPrintGridlines(false);
 1399           return retval;
 1400       }
 1401   
 1402       /**
 1403        * creates the Gridset record and sets it to true (user has mucked with the gridlines)
 1404        * @see org.apache.poi.hssf.record.GridsetRecord
 1405        * @see org.apache.poi.hssf.record.Record
 1406        * @return record containing a GridsetRecord
 1407        */
 1408   
 1409       protected Record createGridset()
 1410       {
 1411           GridsetRecord retval = new GridsetRecord();
 1412   
 1413           retval.setGridset(true);
 1414           return retval;
 1415       }
 1416   
 1417       /**
 1418        * creates the Guts record and sets leftrow/topcol guttter and rowlevelmax/collevelmax to 0
 1419        * @see org.apache.poi.hssf.record.GutsRecord
 1420        * @see org.apache.poi.hssf.record.Record
 1421        * @return record containing a GutsRecordRecord
 1422        */
 1423   
 1424       protected Record createGuts()
 1425       {
 1426           GutsRecord retval = new GutsRecord();
 1427   
 1428           retval.setLeftRowGutter(( short ) 0);
 1429           retval.setTopColGutter(( short ) 0);
 1430           retval.setRowLevelMax(( short ) 0);
 1431           retval.setColLevelMax(( short ) 0);
 1432           return retval;
 1433       }
 1434   
 1435       /**
 1436        * creates the DefaultRowHeight Record and sets its options to 0 and rowheight to 0xff
 1437        * @see org.apache.poi.hssf.record.DefaultRowHeightRecord
 1438        * @see org.apache.poi.hssf.record.Record
 1439        * @return record containing a DefaultRowHeightRecord
 1440        */
 1441   
 1442       protected Record createDefaultRowHeight()
 1443       {
 1444           DefaultRowHeightRecord retval = new DefaultRowHeightRecord();
 1445   
 1446           retval.setOptionFlags(( short ) 0);
 1447           retval.setRowHeight(( short ) 0xff);
 1448           return retval;
 1449       }
 1450   
 1451       /**
 1452        * creates the WSBoolRecord and sets its values to defaults
 1453        * @see org.apache.poi.hssf.record.WSBoolRecord
 1454        * @see org.apache.poi.hssf.record.Record
 1455        * @return record containing a WSBoolRecord
 1456        */
 1457   
 1458       protected Record createWSBool()
 1459       {
 1460           WSBoolRecord retval = new WSBoolRecord();
 1461   
 1462           retval.setWSBool1(( byte ) 0x4);
 1463           retval.setWSBool2(( byte ) 0xffffffc1);
 1464           return retval;
 1465       }
 1466   
 1467       /**
 1468        * creates the Header Record and sets it to nothing/0 length
 1469        * @see org.apache.poi.hssf.record.HeaderRecord
 1470        * @see org.apache.poi.hssf.record.Record
 1471        * @return record containing a HeaderRecord
 1472        */
 1473   
 1474       protected Record createHeader()
 1475       {
 1476           HeaderRecord retval = new HeaderRecord();
 1477   
 1478           retval.setHeaderLength(( byte ) 0);
 1479           retval.setHeader(null);
 1480           return retval;
 1481       }
 1482   
 1483       /**
 1484        * creates the Footer Record and sets it to nothing/0 length
 1485        * @see org.apache.poi.hssf.record.FooterRecord
 1486        * @see org.apache.poi.hssf.record.Record
 1487        * @return record containing a FooterRecord
 1488        */
 1489   
 1490       protected Record createFooter()
 1491       {
 1492           FooterRecord retval = new FooterRecord();
 1493   
 1494           retval.setFooterLength(( byte ) 0);
 1495           retval.setFooter(null);
 1496           return retval;
 1497       }
 1498   
 1499       /**
 1500        * creates the HCenter Record and sets it to false (don't horizontally center)
 1501        * @see org.apache.poi.hssf.record.HCenterRecord
 1502        * @see org.apache.poi.hssf.record.Record
 1503        * @return record containing a HCenterRecord
 1504        */
 1505   
 1506       protected Record createHCenter()
 1507       {
 1508           HCenterRecord retval = new HCenterRecord();
 1509   
 1510           retval.setHCenter(false);
 1511           return retval;
 1512       }
 1513   
 1514       /**
 1515        * creates the VCenter Record and sets it to false (don't horizontally center)
 1516        * @see org.apache.poi.hssf.record.VCenterRecord
 1517        * @see org.apache.poi.hssf.record.Record
 1518        * @return record containing a VCenterRecord
 1519        */
 1520   
 1521       protected Record createVCenter()
 1522       {
 1523           VCenterRecord retval = new VCenterRecord();
 1524   
 1525           retval.setVCenter(false);
 1526           return retval;
 1527       }
 1528   
 1529       /**
 1530        * creates the PrintSetup Record and sets it to defaults and marks it invalid
 1531        * @see org.apache.poi.hssf.record.PrintSetupRecord
 1532        * @see org.apache.poi.hssf.record.Record
 1533        * @return record containing a PrintSetupRecord
 1534        */
 1535   
 1536       protected Record createPrintSetup()
 1537       {
 1538           PrintSetupRecord retval = new PrintSetupRecord();
 1539   
 1540           retval.setPaperSize(( short ) 1);
 1541           retval.setScale(( short ) 100);
 1542           retval.setPageStart(( short ) 1);
 1543           retval.setFitWidth(( short ) 1);
 1544           retval.setFitHeight(( short ) 1);
 1545           retval.setOptions(( short ) 2);
 1546           retval.setHResolution(( short ) 300);
 1547           retval.setVResolution(( short ) 300);
 1548           retval.setHeaderMargin( 0.5);
 1549           retval.setFooterMargin( 0.5);
 1550           retval.setCopies(( short ) 0);
 1551           return retval;
 1552       }
 1553   
 1554       /**
 1555        * creates the DefaultColWidth Record and sets it to 8
 1556        * @see org.apache.poi.hssf.record.DefaultColWidthRecord
 1557        * @see org.apache.poi.hssf.record.Record
 1558        * @return record containing a DefaultColWidthRecord
 1559        */
 1560   
 1561       protected Record createDefaultColWidth()
 1562       {
 1563           DefaultColWidthRecord retval = new DefaultColWidthRecord();
 1564   
 1565           retval.setColWidth(( short ) 8);
 1566           return retval;
 1567       }
 1568   
 1569       /**
 1570        * creates the ColumnInfo Record and sets it to a default column/width
 1571        * @see org.apache.poi.hssf.record.ColumnInfoRecord
 1572        * @return record containing a ColumnInfoRecord
 1573        */
 1574       // TODO change return type to ColumnInfoRecord 
 1575       protected Record createColInfo()
 1576       {
 1577           return ColumnInfoRecordsAggregate.createColInfo();
 1578       }
 1579   
 1580       /**
 1581        * get the default column width for the sheet (if the columns do not define their own width)
 1582        * @return default column width
 1583        */
 1584   
 1585       public short getDefaultColumnWidth()
 1586       {
 1587           return defaultcolwidth.getColWidth();
 1588       }
 1589   
 1590       /**
 1591        * get whether gridlines are printed.
 1592        * @return true if printed
 1593        */
 1594   
 1595       public boolean isGridsPrinted()
 1596       {
 1597           if (gridset == null) {
 1598               gridset = (GridsetRecord)createGridset();
 1599               //Insert the newlycreated Gridset record at the end of the record (just before the EOF)
 1600               int loc = findFirstRecordLocBySid(EOFRecord.sid);
 1601               records.add(loc, gridset);
 1602           }
 1603           return !gridset.getGridset();
 1604       }
 1605   
 1606       /**
 1607        * set whether gridlines printed or not.
 1608        * @param value     True if gridlines printed.
 1609        */
 1610   
 1611       public void setGridsPrinted(boolean value)
 1612       {
 1613           gridset.setGridset(!value);
 1614       }
 1615   
 1616       /**
 1617        * set the default column width for the sheet (if the columns do not define their own width)
 1618        * @param dcw  default column width
 1619        */
 1620   
 1621       public void setDefaultColumnWidth(short dcw)
 1622       {
 1623           defaultcolwidth.setColWidth(dcw);
 1624       }
 1625   
 1626       /**
 1627        * set the default row height for the sheet (if the rows do not define their own height)
 1628        */
 1629   
 1630       public void setDefaultRowHeight(short dch)
 1631       {
 1632           defaultrowheight.setRowHeight(dch);
 1633       }
 1634   
 1635       /**
 1636        * get the default row height for the sheet (if the rows do not define their own height)
 1637        * @return  default row height
 1638        */
 1639   
 1640       public short getDefaultRowHeight()
 1641       {
 1642           return defaultrowheight.getRowHeight();
 1643       }
 1644   
 1645       /**
 1646        * get the width of a given column in units of 1/256th of a character width
 1647        * @param column index
 1648        * @see org.apache.poi.hssf.record.DefaultColWidthRecord
 1649        * @see org.apache.poi.hssf.record.ColumnInfoRecord
 1650        * @see #setColumnWidth(short,short)
 1651        * @return column width in units of 1/256th of a character width
 1652        */
 1653   
 1654       public short getColumnWidth(short column)
 1655       {
 1656           short            retval = 0;
 1657           ColumnInfoRecord ci     = null;
 1658   
 1659           if (columns != null)
 1660           {
 1661               int count=columns.getNumColumns();
 1662               for ( int k=0;k<count;k++ )
 1663               {
 1664                   ci = columns.getColInfo(k);
 1665                   if ((ci.getFirstColumn() <= column)
 1666                           && (column <= ci.getLastColumn()))
 1667                   {
 1668                       break;
 1669                   }
 1670                   ci = null;
 1671               }
 1672           }
 1673           if (ci != null)
 1674           {
 1675               retval = ci.getColumnWidth();
 1676           }
 1677           else
 1678           {
 1679               //default column width is measured in characters
 1680               //multiply
 1681               retval = (short)(256*defaultcolwidth.getColWidth());
 1682           }
 1683           return retval;
 1684       }
 1685   
 1686       /**
 1687        * get the index to the ExtendedFormatRecord "associated" with
 1688        * the column at specified 0-based index. (In this case, an
 1689        * ExtendedFormatRecord index is actually associated with a
 1690        * ColumnInfoRecord which spans 1 or more columns)
 1691        * <br/>
 1692        * Returns the index to the default ExtendedFormatRecord (0xF)
 1693        * if no ColumnInfoRecord exists that includes the column
 1694        * index specified.
 1695        * @param column
 1696        * @return index of ExtendedFormatRecord associated with
 1697        * ColumnInfoRecord that includes the column index or the
 1698        * index of the default ExtendedFormatRecord (0xF)
 1699        */
 1700       public short getXFIndexForColAt(short column) {
 1701           short retval = 0;
 1702           ColumnInfoRecord ci = null;
 1703           if (columns != null) {
 1704             int count=columns.getNumColumns();
 1705             for ( int k=0;k<count;k++ )
 1706             {
 1707                   ci = columns.getColInfo(k);
 1708                   if ((ci.getFirstColumn() <= column)
 1709                           && (column <= ci.getLastColumn())) {
 1710                       break;
 1711                   }
 1712                   ci = null;
 1713               }
 1714           }
 1715           retval = (ci != null) ? ci.getXFIndex() : 0xF;
 1716           return retval;
 1717       }
 1718   
 1719       /**
 1720        * set the width for a given column in 1/256th of a character width units
 1721        * @param column - the column number
 1722        * @param width (in units of 1/256th of a character width)
 1723        */
 1724       public void setColumnWidth(short column, short width)
 1725       {
 1726           setColumn( column, new Short(width), null, null, null);
 1727       }
 1728   
 1729       /**
 1730        * Get the hidden property for a given column.
 1731        * @param column index
 1732        * @see org.apache.poi.hssf.record.DefaultColWidthRecord
 1733        * @see org.apache.poi.hssf.record.ColumnInfoRecord
 1734        * @see #setColumnHidden(short,boolean)
 1735        * @return whether the column is hidden or not.
 1736        */
 1737   
 1738       public boolean isColumnHidden(short column)
 1739       {
 1740           boolean          retval = false;
 1741           ColumnInfoRecord ci     = null;
 1742   
 1743           if (columns != null)
 1744           {
 1745               for ( Iterator iterator = columns.getIterator(); iterator.hasNext(); )
 1746               {
 1747                   ci = ( ColumnInfoRecord ) iterator.next();
 1748                   if ((ci.getFirstColumn() <= column)
 1749                           && (column <= ci.getLastColumn()))
 1750                   {
 1751                       break;
 1752                   }
 1753                   ci = null;
 1754               }
 1755           }
 1756           if (ci != null)
 1757           {
 1758               retval = ci.getHidden();
 1759           }
 1760           return retval;
 1761       }
 1762   
 1763       /**
 1764        * Get the hidden property for a given column.
 1765        * @param column - the column number
 1766        * @param hidden - whether the column is hidden or not
 1767        */
 1768       public void setColumnHidden(short column, boolean hidden)
 1769       {
 1770           setColumn( column, null, null, new Boolean(hidden), null);
 1771       }
 1772   
 1773       public void setColumn(short column, Short width, Integer level, Boolean hidden, Boolean collapsed)
 1774       {
 1775           if (columns == null)
 1776               columns = new ColumnInfoRecordsAggregate();
 1777   
 1778           columns.setColumn( column, null, width, level, hidden, collapsed );
 1779       }
 1780   
 1781       public void setColumn(short column, Short xfStyle, Short width, Integer level, Boolean hidden, Boolean collapsed)
 1782       {
 1783           if (columns == null)
 1784               columns = new ColumnInfoRecordsAggregate();
 1785   
 1786           columns.setColumn( column, xfStyle, width, level, hidden, collapsed );
 1787       }
 1788   
 1789   
 1790       /**
 1791        * Creates an outline group for the specified columns.
 1792        * @param fromColumn    group from this column (inclusive)
 1793        * @param toColumn      group to this column (inclusive)
 1794        * @param indent        if true the group will be indented by one level,
 1795        *                      if false indenting will be removed by one level.
 1796        */
 1797       public void groupColumnRange(short fromColumn, short toColumn, boolean indent)
 1798       {
 1799   
 1800           // Set the level for each column
 1801           columns.groupColumnRange( fromColumn, toColumn, indent);
 1802   
 1803           // Determine the maximum overall level
 1804           int maxLevel = 0;
 1805           int count=columns.getNumColumns();
 1806           for ( int k=0;k<count;k++ )
 1807           {
 1808               ColumnInfoRecord columnInfoRecord = columns.getColInfo(k);
 1809               maxLevel = Math.max(columnInfoRecord.getOutlineLevel(), maxLevel);
 1810           }
 1811   
 1812           GutsRecord guts = (GutsRecord) findFirstRecordBySid( GutsRecord.sid );
 1813           guts.setColLevelMax( (short) ( maxLevel+1 ) );
 1814           if (maxLevel == 0)
 1815               guts.setTopColGutter( (short)0 );
 1816           else
 1817               guts.setTopColGutter( (short) ( 29 + (12 * (maxLevel-1)) ) );
 1818       }
 1819   
 1820       /**
 1821        * creates the Dimensions Record and sets it to bogus values (you should set this yourself
 1822        * or let the high level API do it for you)
 1823        * @see org.apache.poi.hssf.record.DimensionsRecord
 1824        * @see org.apache.poi.hssf.record.Record
 1825        * @return record containing a DimensionsRecord
 1826        */
 1827   
 1828       protected Record createDimensions()
 1829       {
 1830           DimensionsRecord retval = new DimensionsRecord();
 1831   
 1832           retval.setFirstCol(( short ) 0);
 1833           retval.setLastRow(1);             // one more than it is
 1834           retval.setFirstRow(0);
 1835           retval.setLastCol(( short ) 1);   // one more than it is
 1836           return retval;
 1837       }
 1838   
 1839       /**
 1840        * creates the WindowTwo Record and sets it to:  <P>
 1841        * options        = 0x6b6 <P>
 1842        * toprow         = 0 <P>
 1843        * leftcol        = 0 <P>
 1844        * headercolor    = 0x40 <P>
 1845        * pagebreakzoom  = 0x0 <P>
 1846        * normalzoom     = 0x0 <p>
 1847        * @see org.apache.poi.hssf.record.WindowTwoRecord
 1848        * @see org.apache.poi.hssf.record.Record
 1849        * @return record containing a WindowTwoRecord
 1850        */
 1851   
 1852       protected WindowTwoRecord createWindowTwo()
 1853       {
 1854           WindowTwoRecord retval = new WindowTwoRecord();
 1855   
 1856           retval.setOptions(( short ) 0x6b6);
 1857           retval.setTopRow(( short ) 0);
 1858           retval.setLeftCol(( short ) 0);
 1859           retval.setHeaderColor(0x40);
 1860           retval.setPageBreakZoom(( short ) 0);
 1861           retval.setNormalZoom(( short ) 0);
 1862           return retval;
 1863       }
 1864   
 1865       /**
 1866        * Creates the Selection record and sets it to nothing selected
 1867        *
 1868        * @see org.apache.poi.hssf.record.SelectionRecord
 1869        * @see org.apache.poi.hssf.record.Record
 1870        * @return record containing a SelectionRecord
 1871        */
 1872   
 1873       protected Record createSelection()
 1874       {
 1875           SelectionRecord retval = new SelectionRecord();
 1876   
 1877           retval.setPane(( byte ) 0x3);
 1878           retval.setActiveCellCol(( short ) 0x0);
 1879           retval.setActiveCellRow(( short ) 0x0);
 1880           retval.setNumRefs(( short ) 0x0);
 1881           return retval;
 1882       }
 1883   
 1884       public short getTopRow()
 1885       {
 1886           return (windowTwo==null) ? (short) 0 : windowTwo.getTopRow();
 1887       }
 1888   
 1889       public void setTopRow(short topRow)
 1890       {
 1891           if (windowTwo!=null)
 1892           {
 1893               windowTwo.setTopRow(topRow);
 1894           }
 1895       }
 1896   
 1897       /**
 1898        * Sets the left column to show in desktop window pane.
 1899        * @param leftCol the left column to show in desktop window pane
 1900        */
 1901           public void setLeftCol(short leftCol){
 1902               if (windowTwo!=null)
 1903               {
 1904               windowTwo.setLeftCol(leftCol);
 1905               }
 1906           }
 1907   
 1908           public short getLeftCol()
 1909           {
 1910               return (windowTwo==null) ? (short) 0 : windowTwo.getLeftCol();
 1911           }
 1912   
 1913   
 1914   
 1915       /**
 1916        * Returns the active row
 1917        *
 1918        * @see org.apache.poi.hssf.record.SelectionRecord
 1919        * @return row the active row index
 1920        */
 1921       public int getActiveCellRow()
 1922       {
 1923           if (selection == null)
 1924           {
 1925               return 0;
 1926           }
 1927           return selection.getActiveCellRow();
 1928       }
 1929   
 1930       /**
 1931        * Sets the active row
 1932        *
 1933        * @param row the row index
 1934        * @see org.apache.poi.hssf.record.SelectionRecord
 1935        */
 1936       public void setActiveCellRow(int row)
 1937       {
 1938           //shouldn't have a sheet w/o a SelectionRecord, but best to guard anyway
 1939           if (selection != null)
 1940           {
 1941               selection.setActiveCellRow(row);
 1942           }
 1943       }
 1944   
 1945       /**
 1946        * Returns the active column
 1947        *
 1948        * @see org.apache.poi.hssf.record.SelectionRecord
 1949        * @return row the active column index
 1950        */
 1951       public short getActiveCellCol()
 1952       {
 1953           if (selection == null)
 1954           {
 1955               return (short) 0;
 1956           }
 1957           return selection.getActiveCellCol();
 1958       }
 1959   
 1960       /**
 1961        * Sets the active column
 1962        *
 1963        * @param col the column index
 1964        * @see org.apache.poi.hssf.record.SelectionRecord
 1965        */
 1966       public void setActiveCellCol(short col)
 1967       {
 1968           //shouldn't have a sheet w/o a SelectionRecord, but best to guard anyway
 1969           if (selection != null)
 1970           {
 1971               selection.setActiveCellCol(col);
 1972           }
 1973       }
 1974   
 1975       protected Record createMergedCells()
 1976       {
 1977           MergeCellsRecord retval = new MergeCellsRecord();
 1978           retval.setNumAreas(( short ) 0);
 1979           return retval;
 1980       }
 1981   
 1982       /**
 1983        * creates the EOF record
 1984        * @see org.apache.poi.hssf.record.EOFRecord
 1985        * @see org.apache.poi.hssf.record.Record
 1986        * @return record containing a EOFRecord
 1987        */
 1988   
 1989       protected Record createEOF()
 1990       {
 1991           return new EOFRecord();
 1992       }
 1993   
 1994       /**
 1995        * get the location of the DimensionsRecord (which is the last record before the value section)
 1996        * @return location in the array of records of the DimensionsRecord
 1997        */
 1998   
 1999       public int getDimsLoc()
 2000       {
 2001           if (log.check( POILogger.DEBUG ))
 2002               log.log(POILogger.DEBUG, "getDimsLoc dimsloc= " + dimsloc);
 2003           return dimsloc;
 2004       }
 2005   
 2006       /**
 2007        * in the event the record is a dimensions record, resets both the loc index and dimsloc index
 2008        */
 2009   
 2010       public void checkDimsLoc(Record rec, int recloc)
 2011       {
 2012           if (rec.getSid() == DimensionsRecord.sid)
 2013           {
 2014               loc     = recloc;
 2015               dimsloc = recloc;
 2016           }
 2017       }
 2018   
 2019       public int getSize()
 2020       {
 2021           int retval = 0;
 2022   
 2023           for ( int k = 0; k < records.size(); k++) {
 2024               Record record = (Record) records.get(k);
 2025               if (record instanceof UncalcedRecord) {
 2026                   // skip the UncalcedRecord if present, it's only encoded if the isUncalced flag is set
 2027                   continue;
 2028               }
 2029               retval += record.getRecordSize();
 2030           }
 2031           if (rows != null) {
 2032               // Add space for the IndexRecord and DBCell records
 2033               final int nBlocks = rows.getRowBlockCount();
 2034               int nRows = 0;
 2035               if (cells != null) {
 2036                   for (Iterator itr = rows.getIterator(); itr.hasNext();) {
 2037                       RowRecord row = (RowRecord)itr.next();
 2038                       if (cells.rowHasCells(row.getRowNumber())) {
 2039                           nRows++;
 2040                       }
 2041                   }
 2042               }
 2043               retval += IndexRecord.getRecordSizeForBlockCount(nBlocks);
 2044               retval += DBCellRecord.calculateSizeOfRecords(nBlocks, nRows);
 2045           }
 2046           // Add space for UncalcedRecord
 2047           if (_isUncalced) {
 2048               retval += UncalcedRecord.getStaticRecordSize();
 2049           }
 2050           return retval;
 2051       }
 2052   
 2053       public List getRecords()
 2054       {
 2055           return records;
 2056       }
 2057   
 2058       /**
 2059        * Gets the gridset record for this sheet.
 2060        */
 2061   
 2062       public GridsetRecord getGridsetRecord()
 2063       {
 2064           return gridset;
 2065       }
 2066   
 2067       /**
 2068        * Returns the first occurance of a record matching a particular sid.
 2069        */
 2070   
 2071       public Record findFirstRecordBySid(short sid)
 2072       {
 2073           for (Iterator iterator = records.iterator(); iterator.hasNext(); )
 2074           {
 2075               Record record = ( Record ) iterator.next();
 2076   
 2077               if (record.getSid() == sid)
 2078               {
 2079                   return record;
 2080               }
 2081           }
 2082           return null;
 2083       }
 2084   
 2085       /**
 2086        * Sets the SCL record or creates it in the correct place if it does not
 2087        * already exist.
 2088        *
 2089        * @param sclRecord     The record to set.
 2090        */
 2091       public void setSCLRecord(SCLRecord sclRecord)
 2092       {
 2093           int oldRecordLoc = findFirstRecordLocBySid(SCLRecord.sid);
 2094           if (oldRecordLoc == -1)
 2095           {
 2096               // Insert it after the window record
 2097               int windowRecordLoc = findFirstRecordLocBySid(WindowTwoRecord.sid);
 2098               records.add(windowRecordLoc+1, sclRecord);
 2099           }
 2100           else
 2101           {
 2102               records.set(oldRecordLoc, sclRecord);
 2103           }
 2104   
 2105       }
 2106   
 2107       /**
 2108        * Finds the first occurance of a record matching a particular sid and
 2109        * returns it's position.
 2110        * @param sid   the sid to search for
 2111        * @return  the record position of the matching record or -1 if no match
 2112        *          is made.
 2113        */
 2114       public int findFirstRecordLocBySid( short sid )
 2115       {
 2116           int index = 0;
 2117           for (Iterator iterator = records.iterator(); iterator.hasNext(); )
 2118           {
 2119               Record record = ( Record ) iterator.next();
 2120   
 2121               if (record.getSid() == sid)
 2122               {
 2123                   return index;
 2124               }
 2125               index++;
 2126           }
 2127           return -1;
 2128       }
 2129   
 2130       /**
 2131        * Returns the HeaderRecord.
 2132        * @return HeaderRecord for the sheet.
 2133        */
 2134       public HeaderRecord getHeader ()
 2135       {
 2136       return header;
 2137       }
 2138   
 2139       public WindowTwoRecord getWindowTwo() {
 2140           return windowTwo;
 2141       }
 2142       /**
 2143        * Sets the HeaderRecord.
 2144        * @param newHeader The new HeaderRecord for the sheet.
 2145        */
 2146       public void setHeader (HeaderRecord newHeader)
 2147       {
 2148           header = newHeader;
 2149       }
 2150   
 2151       /**
 2152        * Returns the FooterRecord.
 2153        * @return FooterRecord for the sheet.
 2154        */
 2155       public FooterRecord getFooter ()
 2156       {
 2157           return footer;
 2158       }
 2159   
 2160       /**
 2161        * Sets the FooterRecord.
 2162        * @param newFooter The new FooterRecord for the sheet.
 2163        */
 2164       public void setFooter (FooterRecord newFooter)
 2165       {
 2166           footer = newFooter;
 2167       }
 2168   
 2169       /**
 2170        * Returns the PrintSetupRecord.
 2171        * @return PrintSetupRecord for the sheet.
 2172        */
 2173       public PrintSetupRecord getPrintSetup ()
 2174       {
 2175           return printSetup;
 2176       }
 2177   
 2178       /**
 2179        * Sets the PrintSetupRecord.
 2180        * @param newPrintSetup The new PrintSetupRecord for the sheet.
 2181        */
 2182       public void setPrintSetup (PrintSetupRecord newPrintSetup)
 2183       {
 2184           printSetup = newPrintSetup;
 2185       }
 2186   
 2187       /**
 2188        * Returns the PrintGridlinesRecord.
 2189        * @return PrintGridlinesRecord for the sheet.
 2190        */
 2191       public PrintGridlinesRecord getPrintGridlines ()
 2192       {
 2193           return printGridlines;
 2194       }
 2195   
 2196       /**
 2197        * Sets the PrintGridlinesRecord.
 2198        * @param newPrintGridlines The new PrintGridlinesRecord for the sheet.
 2199        */
 2200       public void setPrintGridlines (PrintGridlinesRecord newPrintGridlines)
 2201       {
 2202           printGridlines = newPrintGridlines;
 2203       }
 2204   
 2205       /**
 2206        * Sets whether the sheet is selected
 2207        * @param sel True to select the sheet, false otherwise.
 2208        */
 2209       public void setSelected(boolean sel) {
 2210           windowTwo.setSelected(sel);
 2211       }
 2212   
 2213        /**
 2214         * Gets the size of the margin in inches.
 2215         * @param margin which margin to get
 2216         * @return the size of the margin
 2217         */
 2218       public double getMargin(short margin) {
 2219       if (getMargins()[margin] != null)
 2220           return margins[margin].getMargin();
 2221       else {
 2222           switch ( margin )
 2223           {
 2224           case LeftMargin:
 2225               return .75;
 2226           case RightMargin:
 2227               return .75;
 2228           case TopMargin:
 2229               return 1.0;
 2230           case BottomMargin:
 2231               return 1.0;
 2232           default :
 2233               throw new RuntimeException( "Unknown margin constant:  " + margin );
 2234           }
 2235       }
 2236       }
 2237   
 2238        /**
 2239         * Sets the size of the margin in inches.
 2240         * @param margin which margin to get
 2241         * @param size the size of the margin
 2242         */
 2243       public void setMargin(short margin, double size) {
 2244       Margin m = getMargins()[margin];
 2245       if (m  == null) {
 2246           switch ( margin )
 2247           {
 2248           case LeftMargin:
 2249               m = new LeftMarginRecord();
 2250               records.add( getDimsLoc() + 1, m );
 2251               break;
 2252           case RightMargin:
 2253               m = new RightMarginRecord();
 2254               records.add( getDimsLoc() + 1, m );
 2255               break;
 2256           case TopMargin:
 2257               m = new TopMarginRecord();
 2258               records.add( getDimsLoc() + 1, m );
 2259               break;
 2260           case BottomMargin:
 2261               m = new BottomMarginRecord();
 2262               records.add( getDimsLoc() + 1, m );
 2263               break;
 2264           default :
 2265               throw new RuntimeException( "Unknown margin constant:  " + margin );
 2266           }
 2267           margins[margin] = m;
 2268       }
 2269       m.setMargin( size );
 2270       }
 2271   
 2272       public int getEofLoc()
 2273       {
 2274           return eofLoc;
 2275       }
 2276   
 2277       /**
 2278        * Creates a split (freezepane). Any existing freezepane or split pane is overwritten.
 2279        * @param colSplit      Horizonatal position of split.
 2280        * @param rowSplit      Vertical position of split.
 2281        * @param topRow        Top row visible in bottom pane
 2282        * @param leftmostColumn   Left column visible in right pane.
 2283        */
 2284       public void createFreezePane(int colSplit, int rowSplit, int topRow, int leftmostColumn )
 2285       {
 2286           int paneLoc = findFirstRecordLocBySid(PaneRecord.sid);
 2287           if (paneLoc != -1)
 2288               records.remove(paneLoc);
 2289   
 2290           int loc = findFirstRecordLocBySid(WindowTwoRecord.sid);
 2291           PaneRecord pane = new PaneRecord();
 2292           pane.setX((short)colSplit);
 2293           pane.setY((short)rowSplit);
 2294           pane.setTopRow((short) topRow);
 2295           pane.setLeftColumn((short) leftmostColumn);
 2296           if (rowSplit == 0)
 2297           {
 2298               pane.setTopRow((short)0);
 2299               pane.setActivePane((short)1);
 2300           }
 2301           else if (colSplit == 0)
 2302           {
 2303               pane.setLeftColumn((short)64);
 2304               pane.setActivePane((short)2);
 2305           }
 2306           else
 2307           {
 2308               pane.setActivePane((short)0);
 2309           }
 2310           records.add(loc+1, pane);
 2311   
 2312           windowTwo.setFreezePanes(true);
 2313           windowTwo.setFreezePanesNoSplit(true);
 2314   
 2315           SelectionRecord sel = (SelectionRecord) findFirstRecordBySid(SelectionRecord.sid);
 2316           sel.setPane((byte)pane.getActivePane());
 2317   
 2318       }
 2319   
 2320       /**
 2321        * Creates a split pane. Any existing freezepane or split pane is overwritten.
 2322        * @param xSplitPos      Horizonatal position of split (in 1/20th of a point).
 2323        * @param ySplitPos      Vertical position of split (in 1/20th of a point).
 2324        * @param topRow        Top row visible in bottom pane
 2325        * @param leftmostColumn   Left column visible in right pane.
 2326        * @param activePane    Active pane.  One of: PANE_LOWER_RIGHT,
 2327        *                      PANE_UPPER_RIGHT, PANE_LOWER_LEFT, PANE_UPPER_LEFT
 2328        * @see #PANE_LOWER_LEFT
 2329        * @see #PANE_LOWER_RIGHT
 2330        * @see #PANE_UPPER_LEFT
 2331        * @see #PANE_UPPER_RIGHT
 2332        */
 2333       public void createSplitPane(int xSplitPos, int ySplitPos, int topRow, int leftmostColumn, int activePane )
 2334       {
 2335           int paneLoc = findFirstRecordLocBySid(PaneRecord.sid);
 2336           if (paneLoc != -1)
 2337               records.remove(paneLoc);
 2338   
 2339           int loc = findFirstRecordLocBySid(WindowTwoRecord.sid);
 2340           PaneRecord r = new PaneRecord();
 2341           r.setX((short)xSplitPos);
 2342           r.setY((short)ySplitPos);
 2343           r.setTopRow((short) topRow);
 2344           r.setLeftColumn((short) leftmostColumn);
 2345           r.setActivePane((short) activePane);
 2346           records.add(loc+1, r);
 2347   
 2348           windowTwo.setFreezePanes(false);
 2349           windowTwo.setFreezePanesNoSplit(false);
 2350   
 2351           SelectionRecord sel = (SelectionRecord) findFirstRecordBySid(SelectionRecord.sid);
 2352           sel.setPane(PANE_LOWER_RIGHT);
 2353   
 2354       }
 2355   
 2356       /**
 2357        * Returns the information regarding the currently configured pane (split or freeze).
 2358        * @return null if no pane configured, or the pane information.
 2359        */
 2360       public PaneInformation getPaneInformation() {
 2361         PaneRecord rec = (PaneRecord)findFirstRecordBySid(PaneRecord.sid);
 2362         if (rec == null)
 2363           return null;
 2364   
 2365         return new PaneInformation(rec.getX(), rec.getY(), rec.getTopRow(),
 2366                                    rec.getLeftColumn(), (byte)rec.getActivePane(), windowTwo.getFreezePanes());
 2367       }
 2368   
 2369       public SelectionRecord getSelection()
 2370       {
 2371           return selection;
 2372       }
 2373   
 2374       public void setSelection( SelectionRecord selection )
 2375       {
 2376           this.selection = selection;
 2377       }
 2378   
 2379       /**
 2380        * creates a Protect record with protect set to false.
 2381        * @see org.apache.poi.hssf.record.ProtectRecord
 2382        * @see org.apache.poi.hssf.record.Record
 2383        * @return a ProtectRecord
 2384        */
 2385       protected Record createProtect()
 2386       {
 2387           if (log.check( POILogger.DEBUG ))
 2388               log.log(POILogger.DEBUG, "create protect record with protection disabled");
 2389           ProtectRecord retval = new ProtectRecord();
 2390   
 2391           retval.setProtect(false);
 2392           return retval;
 2393       }
 2394   
 2395       /**
 2396        * creates an ObjectProtect record with protect set to false.
 2397        * @see org.apache.poi.hssf.record.ObjectProtectRecord
 2398        * @see org.apache.poi.hssf.record.Record
 2399        * @return an ObjectProtectRecord
 2400        */
 2401       protected ObjectProtectRecord createObjectProtect()
 2402       {
 2403           if (log.check( POILogger.DEBUG ))
 2404               log.log(POILogger.DEBUG, "create protect record with protection disabled");
 2405           ObjectProtectRecord retval = new ObjectProtectRecord();
 2406   
 2407           retval.setProtect(false);
 2408           return retval;
 2409       }
 2410   
 2411       /**
 2412        * creates a ScenarioProtect record with protect set to false.
 2413        * @see org.apache.poi.hssf.record.ScenarioProtectRecord
 2414        * @see org.apache.poi.hssf.record.Record
 2415        * @return a ScenarioProtectRecord
 2416        */
 2417       protected ScenarioProtectRecord createScenarioProtect()
 2418       {
 2419           if (log.check( POILogger.DEBUG ))
 2420               log.log(POILogger.DEBUG, "create protect record with protection disabled");
 2421           ScenarioProtectRecord retval = new ScenarioProtectRecord();
 2422   
 2423           retval.setProtect(false);
 2424           return retval;
 2425       }
 2426   
 2427       /** Returns the ProtectRecord.
 2428        * If one is not contained in the sheet, then one is created.
 2429        */
 2430       public ProtectRecord getProtect()
 2431       {
 2432           if (protect == null) {
 2433               protect = (ProtectRecord)createProtect();
 2434               //Insert the newlycreated protect record at the end of the record (just before the EOF)
 2435               int loc = findFirstRecordLocBySid(EOFRecord.sid);
 2436               records.add(loc, protect);
 2437           }
 2438           return protect;
 2439       }
 2440   
 2441       /** Returns the PasswordRecord.
 2442        * If one is not contained in the sheet, then one is created.
 2443        */
 2444       public PasswordRecord getPassword()
 2445       {
 2446           if (password == null) {
 2447               password = createPassword();
 2448               //Insert the newly created password record at the end of the record (just before the EOF)
 2449               int loc = findFirstRecordLocBySid(EOFRecord.sid);
 2450               records.add(loc, password);
 2451           }
 2452           return password;
 2453       }
 2454   
 2455       /**
 2456        * creates a Password record with password set to 00.
 2457        * @see org.apache.poi.hssf.record.PasswordRecord
 2458        * @see org.apache.poi.hssf.record.Record
 2459        * @return a PasswordRecord
 2460        */
 2461       protected PasswordRecord createPassword()
 2462       {
 2463           if (log.check( POILogger.DEBUG ))
 2464               log.log(POILogger.DEBUG, "create password record with 00 password");
 2465           PasswordRecord retval = new PasswordRecord();
 2466   
 2467           retval.setPassword((short)00);
 2468           return retval;
 2469       }
 2470   
 2471       /**
 2472   
 2473       /**
 2474        * Sets whether the gridlines are shown in a viewer.
 2475        * @param show whether to show gridlines or not
 2476        */
 2477       public void setDisplayGridlines(boolean show) {
 2478           windowTwo.setDisplayGridlines(show);
 2479       }
 2480   
 2481       /**
 2482        * Returns if gridlines are displayed.
 2483        * @return whether gridlines are displayed
 2484        */
 2485       public boolean isDisplayGridlines() {
 2486       return windowTwo.getDisplayGridlines();
 2487       }
 2488   
 2489       /**
 2490        * Sets whether the formulas are shown in a viewer.
 2491        * @param show whether to show formulas or not
 2492        */
 2493       public void setDisplayFormulas(boolean show) {
 2494           windowTwo.setDisplayFormulas(show);
 2495       }
 2496   
 2497       /**
 2498        * Returns if formulas are displayed.
 2499        * @return whether formulas are displayed
 2500        */
 2501       public boolean isDisplayFormulas() {
 2502       return windowTwo.getDisplayFormulas();
 2503       }
 2504   
 2505       /**
 2506        * Sets whether the RowColHeadings are shown in a viewer.
 2507        * @param show whether to show RowColHeadings or not
 2508        */
 2509       public void setDisplayRowColHeadings(boolean show) {
 2510           windowTwo.setDisplayRowColHeadings(show);
 2511       }
 2512   
 2513       /**
 2514        * Returns if RowColHeadings are displayed.
 2515        * @return whether RowColHeadings are displayed
 2516        */
 2517       public boolean isDisplayRowColHeadings() {
 2518           return windowTwo.getDisplayRowColHeadings();
 2519       }
 2520   
 2521   
 2522       /**
 2523        * @return whether an uncalced record must be inserted or not at generation
 2524        */
 2525       public boolean getUncalced() {
 2526           return _isUncalced;
 2527       }
 2528       /**
 2529        * @param uncalced whether an uncalced record must be inserted or not at generation
 2530        */
 2531       public void setUncalced(boolean uncalced) {
 2532           this._isUncalced = uncalced;
 2533       }
 2534   
 2535       /**
 2536        * Returns the array of margins.  If not created, will create.
 2537        *
 2538        * @return the array of marings.
 2539        */
 2540       protected Margin[] getMargins() {
 2541           if (margins == null)
 2542               margins = new Margin[4];
 2543           return margins;
 2544       }
 2545   
 2546       /**
 2547        * Finds the DrawingRecord for our sheet, and
 2548        *  attaches it to the DrawingManager (which knows about
 2549        *  the overall DrawingGroup for our workbook).
 2550        * If requested, will create a new DrawRecord
 2551        *  if none currently exist
 2552        * @param drawingManager The DrawingManager2 for our workbook
 2553        * @param createIfMissing Should one be created if missing?
 2554        */
 2555       public int aggregateDrawingRecords(DrawingManager2 drawingManager, boolean createIfMissing)
 2556       {
 2557           int loc = findFirstRecordLocBySid(DrawingRecord.sid);
 2558           boolean noDrawingRecordsFound = (loc == -1);
 2559           if (noDrawingRecordsFound)
 2560           {
 2561               if(!createIfMissing) {
 2562                   // None found, and not allowed to add in
 2563                   return -1;
 2564               }
 2565   
 2566               EscherAggregate aggregate = new EscherAggregate( drawingManager );
 2567               loc = findFirstRecordLocBySid(EscherAggregate.sid);
 2568               if (loc == -1)
 2569               {
 2570                   loc = findFirstRecordLocBySid( WindowTwoRecord.sid );
 2571               }
 2572               else
 2573               {
 2574                   getRecords().remove(loc);
 2575               }
 2576               getRecords().add( loc, aggregate );
 2577               return loc;
 2578           }
 2579           else
 2580           {
 2581               List records = getRecords();
 2582               EscherAggregate r = EscherAggregate.createAggregate( records, loc, drawingManager );
 2583               int startloc = loc;
 2584               while ( loc + 1 < records.size()
 2585                       && records.get( loc ) instanceof DrawingRecord
 2586                       && records.get( loc + 1 ) instanceof ObjRecord )
 2587               {
 2588                   loc += 2;
 2589               }
 2590               int endloc = loc-1;
 2591               for(int i = 0; i < (endloc - startloc + 1); i++)
 2592                   records.remove(startloc);
 2593               records.add(startloc, r);
 2594   
 2595               return startloc;
 2596           }
 2597       }
 2598   
 2599       /**
 2600        * Perform any work necessary before the sheet is about to be serialized.
 2601        * For instance the escher aggregates size needs to be calculated before
 2602        * serialization so that the dgg record (which occurs first) can be written.
 2603        */
 2604       public void preSerialize()
 2605       {
 2606           for ( Iterator iterator = getRecords().iterator(); iterator.hasNext(); )
 2607           {
 2608               Record r = (Record) iterator.next();
 2609               if (r instanceof EscherAggregate)
 2610                   r.getRecordSize();   // Trigger flatterning of user model and corresponding update of dgg record.
 2611           }
 2612       }
 2613   
 2614       /**
 2615        * Shifts all the page breaks in the range "count" number of rows/columns
 2616        * @param breaks The page record to be shifted
 2617        * @param start Starting "main" value to shift breaks
 2618        * @param stop Ending "main" value to shift breaks
 2619        * @param count number of units (rows/columns) to shift by
 2620        */
 2621       public void shiftBreaks(PageBreakRecord breaks, short start, short stop, int count) {
 2622   
 2623           if(rowBreaks == null)
 2624               return;
 2625           Iterator iterator = breaks.getBreaksIterator();
 2626           List shiftedBreak = new ArrayList();
 2627           while(iterator.hasNext())
 2628           {
 2629               PageBreakRecord.Break breakItem = (PageBreakRecord.Break)iterator.next();
 2630               short breakLocation = breakItem.main;
 2631               boolean inStart = (breakLocation >= start);
 2632               boolean inEnd = (breakLocation <= stop);
 2633               if(inStart && inEnd)
 2634                   shiftedBreak.add(breakItem);
 2635           }
 2636   
 2637           iterator = shiftedBreak.iterator();
 2638           while (iterator.hasNext()) {
 2639               PageBreakRecord.Break breakItem = (PageBreakRecord.Break)iterator.next();
 2640               breaks.removeBreak(breakItem.main);
 2641               breaks.addBreak((short)(breakItem.main+count), breakItem.subFrom, breakItem.subTo);
 2642           }
 2643       }
 2644   
 2645       /**
 2646        * Sets a page break at the indicated row
 2647        * @param row
 2648        */
 2649       public void setRowBreak(int row, short fromCol, short toCol) {
 2650           if (rowBreaks == null) {
 2651               int loc = findFirstRecordLocBySid(WindowTwoRecord.sid);
 2652               rowBreaks = new PageBreakRecord(PageBreakRecord.HORIZONTAL_SID);
 2653               records.add(loc, rowBreaks);
 2654           }
 2655           rowBreaks.addBreak((short)row, fromCol, toCol);
 2656       }
 2657   
 2658       /**
 2659        * Removes a page break at the indicated row
 2660        * @param row
 2661        */
 2662       public void removeRowBreak(int row) {
 2663           if (rowBreaks == null)
 2664               throw new IllegalArgumentException("Sheet does not define any row breaks");
 2665           rowBreaks.removeBreak((short)row);
 2666       }
 2667   
 2668       /**
 2669        * Queries if the specified row has a page break
 2670        * @param row
 2671        * @return true if the specified row has a page break
 2672        */
 2673       public boolean isRowBroken(int row) {
 2674           return (rowBreaks == null) ? false : rowBreaks.getBreak((short)row) != null;
 2675       }
 2676   
 2677       /**
 2678        * Sets a page break at the indicated column
 2679        *
 2680        */
 2681       public void setColumnBreak(short column, short fromRow, short toRow) {
 2682           if (colBreaks == null) {
 2683               int loc = findFirstRecordLocBySid(WindowTwoRecord.sid);
 2684               colBreaks = new PageBreakRecord(PageBreakRecord.VERTICAL_SID);
 2685               records.add(loc, colBreaks);
 2686           }
 2687           colBreaks.addBreak(column, fromRow, toRow);
 2688       }
 2689   
 2690       /**
 2691        * Removes a page break at the indicated column
 2692        *
 2693        */
 2694       public void removeColumnBreak(short column) {
 2695           if (colBreaks == null)
 2696               throw new IllegalArgumentException("Sheet does not define any column breaks");
 2697   
 2698           colBreaks.removeBreak(column);
 2699       }
 2700   
 2701       /**
 2702        * Queries if the specified column has a page break
 2703        *
 2704        * @return true if the specified column has a page break
 2705        */
 2706       public boolean isColumnBroken(short column) {
 2707           return (colBreaks == null) ? false : colBreaks.getBreak(column) != null;
 2708       }
 2709   
 2710       /**
 2711        * Shifts the horizontal page breaks for the indicated count
 2712        * @param startingRow
 2713        * @param endingRow
 2714        * @param count
 2715        */
 2716       public void shiftRowBreaks(int startingRow, int endingRow, int count) {
 2717           shiftBreaks(rowBreaks, (short)startingRow, (short)endingRow, (short)count);
 2718       }
 2719   
 2720       /**
 2721        * Shifts the vertical page breaks for the indicated count
 2722        * @param startingCol
 2723        * @param endingCol
 2724        * @param count
 2725        */
 2726       public void shiftColumnBreaks(short startingCol, short endingCol, short count) {
 2727           shiftBreaks(colBreaks, startingCol, endingCol, count);
 2728       }
 2729   
 2730       /**
 2731        * Returns all the row page breaks
 2732        * @return all the row page breaks
 2733        */
 2734       public Iterator getRowBreaks() {
 2735           return rowBreaks.getBreaksIterator();
 2736       }
 2737   
 2738       /**
 2739        * Returns the number of row page breaks
 2740        * @return the number of row page breaks
 2741        */
 2742       public int getNumRowBreaks(){
 2743           return (rowBreaks == null) ? 0 : (int)rowBreaks.getNumBreaks();
 2744       }
 2745   
 2746       /**
 2747        * Returns all the column page breaks
 2748        * @return all the column page breaks
 2749        */
 2750       public Iterator getColumnBreaks(){
 2751           return colBreaks.getBreaksIterator();
 2752       }
 2753   
 2754       /**
 2755        * Returns the number of column page breaks
 2756        * @return the number of column page breaks
 2757        */
 2758       public int getNumColumnBreaks(){
 2759           return (colBreaks == null) ? 0 : (int)colBreaks.getNumBreaks();
 2760       }
 2761   
 2762       public void setColumnGroupCollapsed( short columnNumber, boolean collapsed )
 2763       {
 2764           if (collapsed)
 2765           {
 2766               columns.collapseColumn( columnNumber );
 2767           }
 2768           else
 2769           {
 2770               columns.expandColumn( columnNumber );
 2771           }
 2772       }
 2773   
 2774       /**
 2775        * protect a spreadsheet with a password (not encypted, just sets protect
 2776        * flags and the password.
 2777        * @param password to set
 2778        * @param objects are protected
 2779        * @param scenarios are protected
 2780        */
 2781       public void protectSheet( String password, boolean objects, boolean scenarios ) {
 2782           int protIdx = -1;
 2783           ProtectRecord prec = getProtect();
 2784           PasswordRecord pass = getPassword();
 2785           prec.setProtect(true);
 2786           pass.setPassword(PasswordRecord.hashPassword(password));
 2787           if((objprotect == null && objects) || (scenprotect != null && scenarios)) {
 2788               protIdx = records.indexOf( protect );
 2789           }
 2790           if(objprotect == null && objects) {
 2791               ObjectProtectRecord rec = createObjectProtect();
 2792               rec.setProtect(true);
 2793               records.add(protIdx+1,rec);
 2794               objprotect = rec;
 2795           }
 2796           if(scenprotect == null && scenarios) {
 2797               ScenarioProtectRecord srec = createScenarioProtect();
 2798               srec.setProtect(true);
 2799               records.add(protIdx+2,srec);
 2800               scenprotect = srec;
 2801           }
 2802       }
 2803   
 2804       /**
 2805        * unprotect objects in the sheet (will not protect them, but any set to false are
 2806        * unprotected.
 2807        * @param sheet is unprotected (false = unprotect)
 2808        * @param objects are unprotected (false = unprotect)
 2809        * @param scenarios are unprotected (false = unprotect)
 2810        */
 2811       public void unprotectSheet( boolean sheet, boolean objects, boolean scenarios ) {
 2812   
 2813           if (!sheet) {
 2814              ProtectRecord prec = getProtect();
 2815              prec.setProtect(sheet);
 2816              PasswordRecord pass = getPassword();
 2817              pass.setPassword((short)00);
 2818           }
 2819           if(objprotect != null && !objects) {
 2820               objprotect.setProtect(false);
 2821           }
 2822           if(scenprotect != null && !scenarios) {
 2823               scenprotect.setProtect(false);
 2824           }
 2825       }
 2826   
 2827       /**
 2828        * @return {sheet is protected, objects are proteced, scenarios are protected}
 2829        */
 2830       public boolean[] isProtected() {
 2831           return new boolean[] { (protect != null && protect.getProtect()),
 2832                                (objprotect != null && objprotect.getProtect()),
 2833                                (scenprotect != null && scenprotect.getProtect())};
 2834       }
 2835   
 2836   
 2837       public void groupRowRange(int fromRow, int toRow, boolean indent)
 2838       {
 2839           checkRows();
 2840           for (int rowNum = fromRow; rowNum <= toRow; rowNum++)
 2841           {
 2842               RowRecord row = getRow( rowNum );
 2843               if (row == null)
 2844               {
 2845                   row = createRow( rowNum );
 2846                   addRow( row );
 2847               }
 2848               int level = row.getOutlineLevel();
 2849               if (indent) level++; else level--;
 2850               level = Math.max(0, level);
 2851               level = Math.min(7, level);
 2852               row.setOutlineLevel((short) ( level ));
 2853           }
 2854   
 2855           recalcRowGutter();
 2856       }
 2857   
 2858       private void recalcRowGutter()
 2859       {
 2860           int maxLevel = 0;
 2861           Iterator iterator = rows.getIterator();
 2862           while ( iterator.hasNext() )
 2863           {
 2864               RowRecord rowRecord = (RowRecord) iterator.next();
 2865               maxLevel = Math.max(rowRecord.getOutlineLevel(), maxLevel);
 2866           }
 2867   
 2868           // Grab the guts record, adding if needed
 2869           GutsRecord guts = (GutsRecord) findFirstRecordBySid( GutsRecord.sid );
 2870           if(guts == null) {
 2871               guts = new GutsRecord();
 2872               records.add(guts);
 2873           }
 2874           // Set the levels onto it
 2875           guts.setRowLevelMax( (short) ( maxLevel + 1 ) );
 2876           guts.setLeftRowGutter( (short) ( 29 + (12 * (maxLevel)) ) );
 2877       }
 2878   
 2879       public void setRowGroupCollapsed( int row, boolean collapse )
 2880       {
 2881           if (collapse)
 2882           {
 2883               rows.collapseRow( row );
 2884           }
 2885           else
 2886           {
 2887               rows.expandRow( row );
 2888           }
 2889       }
 2890   }

Save This Page
Home » poi-src-3.2-FINAL-20081019 » org.apache » poi » hssf » model » [javadoc | source]