1    /* ====================================================================
2     * The Apache Software License, Version 1.1
3     *
4     * Copyright (c) 2002 The Apache Software Foundation.  All rights
5     * reserved.
6     *
7     * Redistribution and use in source and binary forms, with or without
8     * modification, are permitted provided that the following conditions
9     * are met:
10    *
11    * 1. Redistributions of source code must retain the above copyright
12    *    notice, this list of conditions and the following disclaimer.
13    *
14    * 2. Redistributions in binary form must reproduce the above copyright
15    *    notice, this list of conditions and the following disclaimer in
16    *    the documentation and/or other materials provided with the
17    *    distribution.
18    *
19    * 3. The end-user documentation included with the redistribution,
20    *    if any, must include the following acknowledgment:
21    *       "This product includes software developed by the
22    *        Apache Software Foundation (http://www.apache.org/)."
23    *    Alternately, this acknowledgment may appear in the software itself,
24    *    if and wherever such third-party acknowledgments normally appear.
25    *
26    * 4. The names "Apache" and "Apache Software Foundation" and
27    *    "Apache POI" must not be used to endorse or promote products
28    *    derived from this software without prior written permission. For
29    *    written permission, please contact apache@apache.org.
30    *
31    * 5. Products derived from this software may not be called "Apache",
32    *    "Apache POI", nor may "Apache" appear in their name, without
33    *    prior written permission of the Apache Software Foundation.
34    *
35    * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
36    * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
37    * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
38    * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
39    * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
40    * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
41    * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
42    * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
43    * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
44    * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
45    * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
46    * SUCH DAMAGE.
47    * ====================================================================
48    *
49    * This software consists of voluntary contributions made by many
50    * individuals on behalf of the Apache Software Foundation.  For more
51    * information on the Apache Software Foundation, please see
52    * <http://www.apache.org/>.
53    */
54   
55   /*
56    * HSSFSheet.java
57    *
58    * Created on September 30, 2001, 3:40 PM
59    */
60   package org.apache.poi.hssf.usermodel;
61   
62   import org.apache.poi.hssf.model.Sheet;
63   import org.apache.poi.hssf.model.Workbook;
64   import org.apache.poi.hssf.record.*;
65   import org.apache.poi.hssf.util.Region;
66   import org.apache.poi.util.POILogFactory;
67   import org.apache.poi.util.POILogger;
68   
69   import java.util.Iterator;
70   import java.util.TreeMap;
71   import java.util.List;
72   
73   /**
74    * High level representation of a worksheet.
75    * @author  Andrew C. Oliver (acoliver at apache dot org)
76    * @author  Glen Stampoultzis (glens at apache.org)
77    * @author  Libin Roman (romal at vistaportal.com)
78    * @author  Shawn Laubach (slaubach at apache dot org) (Just a little)
79    */
80   
81   public class HSSFSheet
82   {
83       private static final int DEBUG = POILogger.DEBUG;
84   
85       /* Constants for margins */
86       public static final short LeftMargin = Sheet.LeftMargin;
87       public static final short RightMargin = Sheet.RightMargin;
88       public static final short TopMargin = Sheet.TopMargin;
89       public static final short BottomMargin = Sheet.BottomMargin;
90   
91       public static final byte PANE_LOWER_RIGHT = (byte)0;
92       public static final byte PANE_UPPER_RIGHT = (byte)1;
93       public static final byte PANE_LOWER_LEFT = (byte)2;
94       public static final byte PANE_UPPER_LEFT = (byte)3;
95   
96   
97       /**
98        * Used for compile-time optimization.  This is the initial size for the collection of
99        * rows.  It is currently set to 20.  If you generate larger sheets you may benefit
100       * by setting this to a higher number and recompiling a custom edition of HSSFSheet.
101       */
102  
103      public final static int INITIAL_CAPACITY = 20;
104  
105      /**
106       * reference to the low level Sheet object
107       */
108  
109      private Sheet sheet;
110      private TreeMap rows;
111      private Workbook book;
112      private int firstrow;
113      private int lastrow;
114      private static POILogger log = POILogFactory.getLogger(HSSFSheet.class);
115  
116      /**
117       * Creates new HSSFSheet   - called by HSSFWorkbook to create a sheet from
118       * scratch.  You should not be calling this from application code (its protected anyhow).
119       *
120       * @param book - lowlevel Workbook object associated with the sheet.
121       * @see org.apache.poi.hssf.usermodel.HSSFWorkbook#createSheet()
122       */
123  
124      protected HSSFSheet(Workbook book)
125      {
126          sheet = Sheet.createSheet();
127          rows = new TreeMap();   // new ArrayList(INITIAL_CAPACITY);
128          this.book = book;
129      }
130  
131      /**
132       * Creates an HSSFSheet representing the given Sheet object.  Should only be
133       * called by HSSFWorkbook when reading in an exisiting file.
134       *
135       * @param book - lowlevel Workbook object associated with the sheet.
136       * @param sheet - lowlevel Sheet object this sheet will represent
137       * @see org.apache.poi.hssf.usermodel.HSSFWorkbook#createSheet()
138       */
139  
140      protected HSSFSheet(Workbook book, Sheet sheet)
141      {
142          this.sheet = sheet;
143          rows = new TreeMap();
144          this.book = book;
145          setPropertiesFromSheet(sheet);
146      }
147  
148      HSSFSheet cloneSheet(Workbook book) {
149        return new HSSFSheet(book, sheet.cloneSheet());
150      }
151  
152  
153      /**
154       * used internally to set the properties given a Sheet object
155       */
156  
157      private void setPropertiesFromSheet(Sheet sheet)
158      {
159          int sloc = sheet.getLoc();
160          RowRecord row = sheet.getNextRow();
161  
162          while (row != null)
163          {
164              createRowFromRecord(row);
165  
166              row = sheet.getNextRow();
167          }
168          sheet.setLoc(sloc);
169          CellValueRecordInterface cval = sheet.getNextValueRecord();
170          long timestart = System.currentTimeMillis();
171  
172          log.log(DEBUG, "Time at start of cell creating in HSSF sheet = ",
173                  new Long(timestart));
174          HSSFRow lastrow = null;
175  
176          while (cval != null)
177          {
178              long cellstart = System.currentTimeMillis();
179              HSSFRow hrow = lastrow;
180  
181              if ( ( lastrow == null ) || ( lastrow.getRowNum() != cval.getRow() ) )
182              {
183                  hrow = getRow( cval.getRow() );
184              }
185              if ( hrow != null )
186              {
187                  lastrow = hrow;
188                  log.log( DEBUG, "record id = " + Integer.toHexString( ( (Record) cval ).getSid() ) );
189                  hrow.createCellFromRecord( cval );
190                  cval = sheet.getNextValueRecord();
191                  log.log( DEBUG, "record took ",
192                          new Long( System.currentTimeMillis() - cellstart ) );
193              }
194              else
195              {
196                  cval = null;
197              }
198          }
199          log.log(DEBUG, "total sheet cell creation took ",
200                  new Long(System.currentTimeMillis() - timestart));
201      }
202  
203      /**
204       * Create a new row within the sheet and return the high level representation
205       *
206       * @param rownum  row number
207       * @return High level HSSFRow object representing a row in the sheet
208       * @see org.apache.poi.hssf.usermodel.HSSFRow
209       * @see #removeRow(HSSFRow)
210       */
211  
212      //public HSSFRow createRow(short rownum)
213      public HSSFRow createRow(int rownum)
214      {
215          HSSFRow row = new HSSFRow(book, sheet, rownum);
216  
217          addRow(row, true);
218          return row;
219      }
220  
221      /**
222       * Used internally to create a high level Row object from a low level row object.
223       * USed when reading an existing file
224       * @param row  low level record to represent as a high level Row and add to sheet
225       * @return HSSFRow high level representation
226       */
227  
228      private HSSFRow createRowFromRecord(RowRecord row)
229      {
230          HSSFRow hrow = new HSSFRow(book, sheet, row);
231  
232          addRow(hrow, false);
233          return hrow;
234      }
235  
236      /**
237       * Remove a row from this sheet.  All cells contained in the row are removed as well
238       *
239       * @param row   representing a row to remove.
240       */
241  
242      public void removeRow(HSSFRow row)
243      {
244          sheet.setLoc(sheet.getDimsLoc());
245          if (rows.size() > 0)
246          {
247              rows.remove(row);
248              if (row.getRowNum() == getLastRowNum())
249              {
250                  lastrow = findLastRow(lastrow);
251              }
252              if (row.getRowNum() == getFirstRowNum())
253              {
254                  firstrow = findFirstRow(firstrow);
255              }
256              Iterator iter = row.cellIterator();
257  
258              while (iter.hasNext())
259              {
260                  HSSFCell cell = (HSSFCell) iter.next();
261  
262                  sheet.removeValueRecord(row.getRowNum(),
263                          cell.getCellValueRecord());
264              }
265              sheet.removeRow(row.getRowRecord());
266          }
267      }
268  
269      /**
270       * used internally to refresh the "last row" when the last row is removed.
271       */
272  
273      private int findLastRow(int lastrow)
274      {
275          int rownum = lastrow - 1;
276          HSSFRow r = getRow(rownum);
277  
278          while (r == null && rownum >= 0)
279          {
280              r = getRow(--rownum);
281          }
282          return rownum;
283      }
284  
285      /**
286       * used internally to refresh the "first row" when the first row is removed.
287       */
288  
289      private int findFirstRow(int firstrow)
290      {
291          int rownum = firstrow + 1;
292          HSSFRow r = getRow(rownum);
293  
294          while (r == null && rownum <= getLastRowNum())
295          {
296              r = getRow(++rownum);
297          }
298  
299          if (rownum > getLastRowNum())
300              return -1;
301  
302          return rownum;
303      }
304  
305      /**
306       * add a row to the sheet
307       *
308       * @param addLow whether to add the row to the low level model - false if its already there
309       */
310  
311      private void addRow(HSSFRow row, boolean addLow)
312      {
313          rows.put(row, row);
314          if (addLow)
315          {
316              sheet.addRow(row.getRowRecord());
317          }
318          if (row.getRowNum() > getLastRowNum())
319          {
320              lastrow = row.getRowNum();
321          }
322          if (row.getRowNum() < getFirstRowNum())
323          {
324              firstrow = row.getRowNum();
325          }
326      }
327  
328      /**
329       * Returns the logical row (not physical) 0-based.  If you ask for a row that is not
330       * defined you get a null.  This is to say row 4 represents the fifth row on a sheet.
331       * @param rownum  row to get
332       * @return HSSFRow representing the rownumber or null if its not defined on the sheet
333       */
334  
335      public HSSFRow getRow(int rownum)
336      {
337          HSSFRow row = new HSSFRow();
338  
339          //row.setRowNum((short) rownum);
340          row.setRowNum( rownum);
341          return (HSSFRow) rows.get(row);
342      }
343  
344      /**
345       * Returns the number of phsyically defined rows (NOT the number of rows in the sheet)
346       */
347  
348      public int getPhysicalNumberOfRows()
349      {
350          return rows.size();
351      }
352  
353      /**
354       * gets the first row on the sheet
355       * @return the number of the first logical row on the sheet
356       */
357  
358      public int getFirstRowNum()
359      {
360          return firstrow;
361      }
362  
363      /**
364       * gets the last row on the sheet
365       * @return last row contained n this sheet.
366       */
367  
368      public int getLastRowNum()
369      {
370          return lastrow;
371      }
372  
373      /**
374       * set the width (in units of 1/256th of a character width)
375       * @param column - the column to set (0-based)
376       * @param width - the width in units of 1/256th of a character width
377       */
378  
379      public void setColumnWidth(short column, short width)
380      {
381          sheet.setColumnWidth(column, width);
382      }
383  
384      /**
385       * get the width (in units of 1/256th of a character width )
386       * @param column - the column to set (0-based)
387       * @return width - the width in units of 1/256th of a character width
388       */
389  
390      public short getColumnWidth(short column)
391      {
392          return sheet.getColumnWidth(column);
393      }
394  
395      /**
396       * get the default column width for the sheet (if the columns do not define their own width) in
397       * characters
398       * @return default column width
399       */
400  
401      public short getDefaultColumnWidth()
402      {
403          return sheet.getDefaultColumnWidth();
404      }
405  
406      /**
407       * get the default row height for the sheet (if the rows do not define their own height) in
408       * twips (1/20 of  a point)
409       * @return  default row height
410       */
411  
412      public short getDefaultRowHeight()
413      {
414          return sheet.getDefaultRowHeight();
415      }
416  
417      /**
418       * get the default row height for the sheet (if the rows do not define their own height) in
419       * points.
420       * @return  default row height in points
421       */
422  
423      public float getDefaultRowHeightInPoints()
424      {
425          return (sheet.getDefaultRowHeight() / 20);
426      }
427  
428      /**
429       * set the default column width for the sheet (if the columns do not define their own width) in
430       * characters
431       * @param width default column width
432       */
433  
434      public void setDefaultColumnWidth(short width)
435      {
436          sheet.setDefaultColumnWidth(width);
437      }
438  
439      /**
440       * set the default row height for the sheet (if the rows do not define their own height) in
441       * twips (1/20 of  a point)
442       * @param  height default row height
443       */
444  
445      public void setDefaultRowHeight(short height)
446      {
447          sheet.setDefaultRowHeight(height);
448      }
449  
450      /**
451       * set the default row height for the sheet (if the rows do not define their own height) in
452       * points
453       * @param height default row height
454       */
455  
456      public void setDefaultRowHeightInPoints(float height)
457      {
458          sheet.setDefaultRowHeight((short) (height * 20));
459      }
460  
461      /**
462       * get whether gridlines are printed.
463       * @return true if printed
464       */
465  
466      public boolean isGridsPrinted()
467      {
468          return sheet.isGridsPrinted();
469      }
470  
471      /**
472       * set whether gridlines printed.
473       * @param value  false if not printed.
474       */
475  
476      public void setGridsPrinted(boolean value)
477      {
478          sheet.setGridsPrinted(value);
479      }
480  
481      /**
482       * adds a merged region of cells (hence those cells form one)
483       * @param region (rowfrom/colfrom-rowto/colto) to merge
484       * @return index of this region
485       */
486  
487      public int addMergedRegion(Region region)
488      {
489          //return sheet.addMergedRegion((short) region.getRowFrom(),
490          return sheet.addMergedRegion( region.getRowFrom(),
491                  region.getColumnFrom(),
492                  //(short) region.getRowTo(),
493                  region.getRowTo(),
494                  region.getColumnTo());
495      }
496  
497      /**
498       * determines whether the output is vertically centered on the page.
499       * @param value true to vertically center, false otherwise.
500       */
501  
502      public void setVerticallyCenter(boolean value)
503      {
504          VCenterRecord record =
505                  (VCenterRecord) sheet.findFirstRecordBySid(VCenterRecord.sid);
506  
507          record.setVCenter(value);
508      }
509  
510      /**
511       * Determine whether printed output for this sheet will be vertically centered.
512       */
513  
514      public boolean getVerticallyCenter(boolean value)
515      {
516          VCenterRecord record =
517                  (VCenterRecord) sheet.findFirstRecordBySid(VCenterRecord.sid);
518  
519          return record.getVCenter();
520      }
521  
522      /**
523       * determines whether the output is horizontally centered on the page.
524       * @param value true to horizontally center, false otherwise.
525       */
526  
527      public void setHorizontallyCenter(boolean value)
528      {
529          HCenterRecord record =
530                  (HCenterRecord) sheet.findFirstRecordBySid(HCenterRecord.sid);
531  
532          record.setHCenter(value);
533      }
534  
535      /**
536       * Determine whether printed output for this sheet will be horizontally centered.
537       */
538  
539      public boolean getHorizontallyCenter()
540      {
541          HCenterRecord record =
542                  (HCenterRecord) sheet.findFirstRecordBySid(HCenterRecord.sid);
543  
544          return record.getHCenter();
545      }
546  
547  
548  
549      /**
550       * removes a merged region of cells (hence letting them free)
551       * @param index of the region to unmerge
552       */
553  
554      public void removeMergedRegion(int index)
555      {
556          sheet.removeMergedRegion(index);
557      }
558  
559      /**
560       * returns the number of merged regions
561       * @return number of merged regions
562       */
563  
564      public int getNumMergedRegions()
565      {
566          return sheet.getNumMergedRegions();
567      }
568  
569      /**
570       * gets the region at a particular index
571       * @param index of the region to fetch
572       * @return the merged region (simple eh?)
573       */
574  
575      public Region getMergedRegionAt(int index)
576      {
577          return new Region(sheet.getMergedRegionAt(index));
578      }
579  
580      /**
581       * @return an iterator of the PHYSICAL rows.  Meaning the 3rd element may not
582       * be the third row if say for instance the second row is undefined.
583       */
584  
585      public Iterator rowIterator()
586      {
587          return rows.values().iterator();
588      }
589  
590      /**
591       * used internally in the API to get the low level Sheet record represented by this
592       * Object.
593       * @return Sheet - low level representation of this HSSFSheet.
594       */
595  
596      protected Sheet getSheet()
597      {
598          return sheet;
599      }
600  
601      /**
602       * whether alternate expression evaluation is on
603       * @param b  alternative expression evaluation or not
604       */
605  
606      public void setAlternativeExpression(boolean b)
607      {
608          WSBoolRecord record =
609                  (WSBoolRecord) sheet.findFirstRecordBySid(WSBoolRecord.sid);
610  
611          record.setAlternateExpression(b);
612      }
613  
614      /**
615       * whether alternative formula entry is on
616       * @param b  alternative formulas or not
617       */
618  
619      public void setAlternativeFormula(boolean b)
620      {
621          WSBoolRecord record =
622                  (WSBoolRecord) sheet.findFirstRecordBySid(WSBoolRecord.sid);
623  
624          record.setAlternateFormula(b);
625      }
626  
627      /**
628       * show automatic page breaks or not
629       * @param b  whether to show auto page breaks
630       */
631  
632      public void setAutobreaks(boolean b)
633      {
634          WSBoolRecord record =
635                  (WSBoolRecord) sheet.findFirstRecordBySid(WSBoolRecord.sid);
636  
637          record.setAutobreaks(b);
638      }
639  
640      /**
641       * set whether sheet is a dialog sheet or not
642       * @param b  isDialog or not
643       */
644  
645      public void setDialog(boolean b)
646      {
647          WSBoolRecord record =
648                  (WSBoolRecord) sheet.findFirstRecordBySid(WSBoolRecord.sid);
649  
650          record.setDialog(b);
651      }
652  
653      /**
654       * set whether to display the guts or not
655       *
656       * @param b  guts or no guts (or glory)
657       */
658  
659      public void setDisplayGuts(boolean b)
660      {
661          WSBoolRecord record =
662                  (WSBoolRecord) sheet.findFirstRecordBySid(WSBoolRecord.sid);
663  
664          record.setDisplayGuts(b);
665      }
666  
667      /**
668       * fit to page option is on
669       * @param b  fit or not
670       */
671  
672      public void setFitToPage(boolean b)
673      {
674          WSBoolRecord record =
675                  (WSBoolRecord) sheet.findFirstRecordBySid(WSBoolRecord.sid);
676  
677          record.setFitToPage(b);
678      }
679  
680      /**
681       * set if row summaries appear below detail in the outline
682       * @param b  below or not
683       */
684  
685      public void setRowSumsBelow(boolean b)
686      {
687          WSBoolRecord record =
688                  (WSBoolRecord) sheet.findFirstRecordBySid(WSBoolRecord.sid);
689  
690          record.setRowSumsBelow(b);
691      }
692  
693      /**
694       * set if col summaries appear right of the detail in the outline
695       * @param b  right or not
696       */
697  
698      public void setRowSumsRight(boolean b)
699      {
700          WSBoolRecord record =
701                  (WSBoolRecord) sheet.findFirstRecordBySid(WSBoolRecord.sid);
702  
703          record.setRowSumsRight(b);
704      }
705  
706      /**
707       * whether alternate expression evaluation is on
708       * @return alternative expression evaluation or not
709       */
710  
711      public boolean getAlternateExpression()
712      {
713          return ((WSBoolRecord) sheet.findFirstRecordBySid(WSBoolRecord.sid))
714                  .getAlternateExpression();
715      }
716  
717      /**
718       * whether alternative formula entry is on
719       * @return alternative formulas or not
720       */
721  
722      public boolean getAlternateFormula()
723      {
724          return ((WSBoolRecord) sheet.findFirstRecordBySid(WSBoolRecord.sid))
725                  .getAlternateFormula();
726      }
727  
728      /**
729       * show automatic page breaks or not
730       * @return whether to show auto page breaks
731       */
732  
733      public boolean getAutobreaks()
734      {
735          return ((WSBoolRecord) sheet.findFirstRecordBySid(WSBoolRecord.sid))
736                  .getAutobreaks();
737      }
738  
739      /**
740       * get whether sheet is a dialog sheet or not
741       * @return isDialog or not
742       */
743  
744      public boolean getDialog()
745      {
746          return ((WSBoolRecord) sheet.findFirstRecordBySid(WSBoolRecord.sid))
747                  .getDialog();
748      }
749  
750      /**
751       * get whether to display the guts or not
752       *
753       * @return guts or no guts (or glory)
754       */
755  
756      public boolean getDisplayGuts()
757      {
758          return ((WSBoolRecord) sheet.findFirstRecordBySid(WSBoolRecord.sid))
759                  .getDisplayGuts();
760      }
761  
762      /**
763       * fit to page option is on
764       * @return fit or not
765       */
766  
767      public boolean getFitToPage()
768      {
769          return ((WSBoolRecord) sheet.findFirstRecordBySid(WSBoolRecord.sid))
770                  .getFitToPage();
771      }
772  
773      /**
774       * get if row summaries appear below detail in the outline
775       * @return below or not
776       */
777  
778      public boolean getRowSumsBelow()
779      {
780          return ((WSBoolRecord) sheet.findFirstRecordBySid(WSBoolRecord.sid))
781                  .getRowSumsBelow();
782      }
783  
784      /**
785       * get if col summaries appear right of the detail in the outline
786       * @return right or not
787       */
788  
789      public boolean getRowSumsRight()
790      {
791          return ((WSBoolRecord) sheet.findFirstRecordBySid(WSBoolRecord.sid))
792                  .getRowSumsRight();
793      }
794  
795      /**
796       * Returns whether gridlines are printed.
797       * @return Gridlines are printed
798       */
799      public boolean isPrintGridlines() {
800          return getSheet().getPrintGridlines().getPrintGridlines();
801      }
802  
803      /**
804       * Turns on or off the printing of gridlines.
805       * @param newPrintGridlines boolean to turn on or off the printing of
806       * gridlines
807       */
808      public void setPrintGridlines( boolean newPrintGridlines )
809      {
810          getSheet().getPrintGridlines().setPrintGridlines( newPrintGridlines );
811      }
812  
813      /**
814       * Gets the print setup object.
815       * @return The user model for the print setup object.
816       */
817      public HSSFPrintSetup getPrintSetup()
818      {
819          return new HSSFPrintSetup( getSheet().getPrintSetup() );
820      }
821  
822      /**
823       * Gets the user model for the document header.
824       * @return The Document header.
825       */
826      public HSSFHeader getHeader()
827      {
828          return new HSSFHeader( getSheet().getHeader() );
829      }
830  
831      /**
832       * Gets the user model for the document footer.
833       * @return The Document footer.
834       */
835      public HSSFFooter getFooter()
836      {
837          return new HSSFFooter( getSheet().getFooter() );
838      }
839  
840      /**
841       * Sets whether sheet is selected.
842       * @param sel Whether to select the sheet or deselect the sheet.
843       */
844      public void setSelected( boolean sel )
845      {
846          getSheet().setSelected( sel );
847      }
848  
849      /**
850       * Gets the size of the margin in inches.
851       * @param margin which margin to get
852       * @return the size of the margin
853       */
854      public double getMargin( short margin )
855      {
856          return getSheet().getMargin( margin );
857      }
858  
859      /**
860       * Sets the size of the margin in inches.
861       * @param margin which margin to get
862       * @param size the size of the margin
863       */
864      public void setMargin( short margin, double size )
865      {
866          getSheet().setMargin( margin, size );
867      }
868  
869      /**
870       * Sets the zoom magnication for the sheet.  The zoom is expressed as a
871       * fraction.  For example to express a zoom of 75% use 3 for the numerator
872       * and 4 for the denominator.
873       *
874       * @param numerator     The numerator for the zoom magnification.
875       * @param denominator   The denominator for the zoom magnification.
876       */
877      public void setZoom( int numerator, int denominator)
878      {
879          if (numerator < 1 || numerator > 65535)
880              throw new IllegalArgumentException("Numerator must be greater than 1 and less than 65536");
881          if (denominator < 1 || denominator > 65535)
882              throw new IllegalArgumentException("Denominator must be greater than 1 and less than 65536");
883  
884          SCLRecord sclRecord = new SCLRecord();
885          sclRecord.setNumerator((short)numerator);
886          sclRecord.setDenominator((short)denominator);
887          getSheet().setSCLRecord(sclRecord);
888      }
889  
890      /**
891       * Shifts rows between startRow and endRow n number of rows.
892       * If you use a negative number, it will shift rows up.
893       * Code ensures that rows don't wrap around
894       *
895       * @param startRow the row to start shifting
896       * @param endRow the row to end shifting
897       * @param n the number of rows to shift
898       */
899      public void shiftRows( int startRow, int endRow, int n )
900      {
901          int s, e, inc;
902          if ( n < 0 )
903          {
904              s = startRow;
905              e = endRow;
906              inc = 1;
907          }
908          else
909          {
910              s = endRow;
911              e = startRow;
912              inc = -1;
913          }
914          for ( int rowNum = s; rowNum >= startRow && rowNum <= endRow && rowNum >= 0 && rowNum < 65536; rowNum += inc )
915          {
916              HSSFRow row = getRow( rowNum );
917              HSSFRow row2Replace = getRow( rowNum + n );
918              if ( row2Replace == null )
919                  row2Replace = createRow( rowNum + n );
920  
921              HSSFCell cell;
922              for ( short col = row2Replace.getFirstCellNum(); col <= row2Replace.getLastCellNum(); col++ )
923              {
924                  cell = row2Replace.getCell( col );
925                  if ( cell != null )
926                      row2Replace.removeCell( cell );
927              }
928  	    if (row == null) continue;
929              for ( short col = row.getFirstCellNum(); col <= row.getLastCellNum(); col++ )
930              {
931                  cell = row.getCell( col );
932                  if ( cell != null )
933                  {
934                      row.removeCell( cell );
935                      CellValueRecordInterface cellRecord = cell.getCellValueRecord();
936                      cellRecord.setRow( rowNum + n );
937                      row2Replace.createCellFromRecord( cellRecord );
938                      sheet.addValueRecord( rowNum + n, cellRecord );
939                  }
940              }
941          }
942          if ( endRow == lastrow || endRow + n > lastrow ) lastrow = Math.min( endRow + n, 65535 );
943          if ( startRow == firstrow || startRow + n < firstrow ) firstrow = Math.max( startRow + n, 0 );
944      }
945  
946      protected void insertChartRecords( List records )
947      {
948          int window2Loc = sheet.findFirstRecordLocBySid( WindowTwoRecord.sid );
949          sheet.getRecords().addAll( window2Loc, records );
950      }
951  
952      /**
953       * Creates a split (freezepane).
954       * @param colSplit      Horizonatal position of split.
955       * @param rowSplit      Vertical position of split.
956       * @param topRow        Top row visible in bottom pane
957       * @param leftmostColumn   Left column visible in right pane.
958       */
959      public void createFreezePane(int colSplit, int rowSplit, int leftmostColumn, int topRow )
960      {
961          if (colSplit < 0 || colSplit > 255) throw new IllegalArgumentException("Column must be between 0 and 255");
962          if (rowSplit < 0 || rowSplit > 65535) throw new IllegalArgumentException("Row must be between 0 and 65535");
963          if (leftmostColumn < colSplit) throw new IllegalArgumentException("leftmostColumn parameter must not be less than colSplit parameter");
964          if (topRow < rowSplit) throw new IllegalArgumentException("topRow parameter must not be less than leftmostColumn parameter");
965          getSheet().createFreezePane( colSplit, rowSplit, topRow, leftmostColumn );
966      }
967  
968      /**
969       * Creates a split (freezepane).
970       * @param colSplit      Horizonatal position of split.
971       * @param rowSplit      Vertical position of split.
972       */
973      public void createFreezePane( int colSplit, int rowSplit )
974      {
975          createFreezePane( colSplit, rowSplit, colSplit, rowSplit );
976      }
977  
978      /**
979       * Creates a split pane.
980       * @param xSplitPos      Horizonatal position of split (in 1/20th of a point).
981       * @param ySplitPos      Vertical position of split (in 1/20th of a point).
982       * @param topRow        Top row visible in bottom pane
983       * @param leftmostColumn   Left column visible in right pane.
984       * @param activePane    Active pane.  One of: PANE_LOWER_RIGHT,
985       *                      PANE_UPPER_RIGHT, PANE_LOWER_LEFT, PANE_UPPER_LEFT
986       * @see #PANE_LOWER_LEFT
987       * @see #PANE_LOWER_RIGHT
988       * @see #PANE_UPPER_LEFT
989       * @see #PANE_UPPER_RIGHT
990       */
991      public void createSplitPane(int xSplitPos, int ySplitPos, int leftmostColumn, int topRow, int activePane )
992      {
993          getSheet().createSplitPane( xSplitPos, ySplitPos, topRow, leftmostColumn, activePane );
994      }
995  
996  
997  }
998