Save This Page
Home » poi-src-3.2-FINAL-20081019 » org.apache » poi » hssf » usermodel » [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.usermodel;
   19   
   20   import org.apache.poi.POIDocument;
   21   import org.apache.poi.ddf.EscherBSERecord;
   22   import org.apache.poi.ddf.EscherBitmapBlip;
   23   import org.apache.poi.ddf.EscherRecord;
   24   import org.apache.poi.ddf.EscherBlipRecord;
   25   import org.apache.poi.hssf.eventmodel.EventRecordFactory;
   26   import org.apache.poi.hssf.model.Sheet;
   27   import org.apache.poi.hssf.model.Workbook;
   28   import org.apache.poi.hssf.record;
   29   import org.apache.poi.hssf.record.formula.Area3DPtg;
   30   import org.apache.poi.hssf.record.formula.MemFuncPtg;
   31   import org.apache.poi.hssf.record.formula.UnionPtg;
   32   import org.apache.poi.hssf.usermodel.HSSFRow.MissingCellPolicy;
   33   import org.apache.poi.hssf.util.CellReference;
   34   import org.apache.poi.hssf.util.SheetReferences;
   35   import org.apache.poi.poifs.filesystem;
   36   import org.apache.poi.util.POILogFactory;
   37   import org.apache.poi.util.POILogger;
   38   
   39   import java.io.ByteArrayInputStream;
   40   import java.io.FileNotFoundException;
   41   import java.io.IOException;
   42   import java.io.InputStream;
   43   import java.io.OutputStream;
   44   import java.io.PrintWriter;
   45   import java.util.ArrayList;
   46   import java.util.Iterator;
   47   import java.util.List;
   48   import java.util.Stack;
   49   
   50   /**
   51    * High level representation of a workbook.  This is the first object most users
   52    * will construct whether they are reading or writing a workbook.  It is also the
   53    * top level object for creating new sheets/etc.
   54    *
   55    * @see org.apache.poi.hssf.model.Workbook
   56    * @see org.apache.poi.hssf.usermodel.HSSFSheet
   57    * @author  Andrew C. Oliver (acoliver at apache dot org)
   58    * @author  Glen Stampoultzis (glens at apache.org)
   59    * @author  Shawn Laubach (slaubach at apache dot org)
   60    * @version 2.0-pre
   61    */
   62   public class HSSFWorkbook extends POIDocument
   63   {
   64       private static final int DEBUG = POILogger.DEBUG;
   65   
   66       /**
   67        * used for compile-time performance/memory optimization.  This determines the
   68        * initial capacity for the sheet collection.  Its currently set to 3.
   69        * Changing it in this release will decrease performance
   70        * since you're never allowed to have more or less than three sheets!
   71        */
   72   
   73       public final static int INITIAL_CAPACITY = 3;
   74   
   75       /**
   76        * this is the reference to the low level Workbook object
   77        */
   78   
   79       private Workbook workbook;
   80   
   81       /**
   82        * this holds the HSSFSheet objects attached to this workbook
   83        */
   84   
   85       protected List _sheets;
   86   
   87       /**
   88        * this holds the HSSFName objects attached to this workbook
   89        */
   90   
   91       private ArrayList names;
   92   
   93       /**
   94        * holds whether or not to preserve other nodes in the POIFS.  Used
   95        * for macros and embedded objects.
   96        */
   97       private boolean   preserveNodes;
   98   
   99       /**
  100        * Used to keep track of the data formatter so that all
  101        * createDataFormatter calls return the same one for a given
  102        * book.  This ensures that updates from one places is visible
  103        * someplace else.
  104        */
  105       private HSSFDataFormat formatter;
  106       
  107       /**
  108        * The policy to apply in the event of missing or
  109        *  blank cells when fetching from a row.
  110        * See {@link MissingCellPolicy}
  111        */
  112       private MissingCellPolicy missingCellPolicy = HSSFRow.RETURN_NULL_AND_BLANK;
  113   
  114   
  115       /** Extended windows meta file */
  116       public static final int PICTURE_TYPE_EMF = 2;
  117       /** Windows Meta File */
  118       public static final int PICTURE_TYPE_WMF = 3;
  119       /** Mac PICT format */
  120       public static final int PICTURE_TYPE_PICT = 4;
  121       /** JPEG format */
  122       public static final int PICTURE_TYPE_JPEG = 5;
  123       /** PNG format */
  124       public static final int PICTURE_TYPE_PNG = 6;
  125       /** Device independant bitmap */
  126       public static final int PICTURE_TYPE_DIB = 7;
  127   
  128   
  129       private static POILogger log = POILogFactory.getLogger(HSSFWorkbook.class);
  130   
  131   
  132   
  133       /**
  134        * Creates new HSSFWorkbook from scratch (start here!)
  135        *
  136        */
  137       public HSSFWorkbook()
  138       {
  139           this(Workbook.createWorkbook());
  140       }
  141   
  142       protected HSSFWorkbook( Workbook book )
  143       {
  144           super(null, null);
  145           workbook = book;
  146           _sheets = new ArrayList( INITIAL_CAPACITY );
  147           names = new ArrayList( INITIAL_CAPACITY );
  148       }
  149   
  150       public HSSFWorkbook(POIFSFileSystem fs) throws IOException {
  151         this(fs,true);
  152       }
  153   
  154       /**
  155        * given a POI POIFSFileSystem object, read in its Workbook and populate the high and
  156        * low level models.  If you're reading in a workbook...start here.
  157        *
  158        * @param fs the POI filesystem that contains the Workbook stream.
  159        * @param preserveNodes whether to preseve other nodes, such as
  160        *        macros.  This takes more memory, so only say yes if you
  161        *        need to. If set, will store all of the POIFSFileSystem
  162        *        in memory
  163        * @see org.apache.poi.poifs.filesystem.POIFSFileSystem
  164        * @exception IOException if the stream cannot be read
  165        */
  166       public HSSFWorkbook(POIFSFileSystem fs, boolean preserveNodes)
  167               throws IOException
  168       {
  169           this(fs.getRoot(), fs, preserveNodes);
  170       }
  171   
  172       /**
  173        * Normally, the Workbook will be in a POIFS Stream
  174        * called "Workbook". However, some weird XLS generators use "WORKBOOK"
  175        */
  176       private static final String[] WORKBOOK_DIR_ENTRY_NAMES = {
  177           "Workbook", // as per BIFF8 spec
  178           "WORKBOOK",
  179       };
  180   
  181   
  182       private static String getWorkbookDirEntryName(DirectoryNode directory) {
  183   
  184           String[] potentialNames = WORKBOOK_DIR_ENTRY_NAMES;
  185           for (int i = 0; i < potentialNames.length; i++) {
  186               String wbName = potentialNames[i];
  187               try {
  188                   directory.getEntry(wbName);
  189                   return wbName;
  190               } catch (FileNotFoundException e) {
  191                   // continue - to try other options
  192               }
  193           }
  194   
  195           // check for previous version of file format
  196           try {
  197               directory.getEntry("Book");
  198               throw new IllegalArgumentException("The supplied spreadsheet seems to be Excel 5.0/7.0 (BIFF5) format. "
  199                       + "POI only supports BIFF8 format (from Excel versions 97/2000/XP/2003)");
  200           } catch (FileNotFoundException e) {
  201               // fall through
  202           }
  203   
  204           throw new IllegalArgumentException("The supplied POIFSFileSystem does not contain a BIFF8 'Workbook' entry. "
  205               + "Is it really an excel file?");
  206       }
  207   
  208       /**
  209        * given a POI POIFSFileSystem object, and a specific directory
  210        *  within it, read in its Workbook and populate the high and
  211        *  low level models.  If you're reading in a workbook...start here.
  212        *
  213        * @param directory the POI filesystem directory to process from
  214        * @param fs the POI filesystem that contains the Workbook stream.
  215        * @param preserveNodes whether to preseve other nodes, such as
  216        *        macros.  This takes more memory, so only say yes if you
  217        *        need to. If set, will store all of the POIFSFileSystem
  218        *        in memory
  219        * @see org.apache.poi.poifs.filesystem.POIFSFileSystem
  220        * @exception IOException if the stream cannot be read
  221        */
  222       public HSSFWorkbook(DirectoryNode directory, POIFSFileSystem fs, boolean preserveNodes)
  223               throws IOException
  224       {
  225           super(directory, fs);
  226           String workbookName = getWorkbookDirEntryName(directory);
  227   
  228           this.preserveNodes = preserveNodes;
  229   
  230           // If we're not preserving nodes, don't track the
  231           //  POIFS any more
  232           if(! preserveNodes) {
  233              this.filesystem = null;
  234              this.directory = null;
  235           }
  236   
  237           _sheets = new ArrayList(INITIAL_CAPACITY);
  238           names  = new ArrayList(INITIAL_CAPACITY);
  239   
  240           // Grab the data from the workbook stream, however
  241           //  it happens to be spelled.
  242           InputStream stream = directory.createDocumentInputStream(workbookName);
  243   
  244           EventRecordFactory factory = new EventRecordFactory();
  245   
  246           List records = RecordFactory.createRecords(stream);
  247   
  248           workbook = Workbook.createWorkbook(records);
  249           setPropertiesFromWorkbook(workbook);
  250           int recOffset = workbook.getNumRecords();
  251           int sheetNum = 0;
  252   
  253           // convert all LabelRecord records to LabelSSTRecord
  254           convertLabelRecords(records, recOffset);
  255           while (recOffset < records.size())
  256           {
  257               Sheet sheet = Sheet.createSheet(records, sheetNum++, recOffset );
  258   
  259               recOffset = sheet.getEofLoc()+1;
  260               if (recOffset == 1)
  261               {
  262                   break;
  263               }
  264   
  265               HSSFSheet hsheet = new HSSFSheet(this, sheet);
  266   
  267               _sheets.add(hsheet);
  268   
  269               // workbook.setSheetName(sheets.size() -1, "Sheet"+sheets.size());
  270           }
  271   
  272           for (int i = 0 ; i < workbook.getNumNames() ; ++i){
  273               HSSFName name = new HSSFName(this, workbook.getNameRecord(i));
  274               names.add(name);
  275           }
  276       }
  277   
  278        public HSSFWorkbook(InputStream s) throws IOException {
  279            this(s,true);
  280        }
  281   
  282       /**
  283        * Companion to HSSFWorkbook(POIFSFileSystem), this constructs the POI filesystem around your
  284        * inputstream.
  285        *
  286        * @param s  the POI filesystem that contains the Workbook stream.
  287        * @param preserveNodes whether to preseve other nodes, such as
  288        *        macros.  This takes more memory, so only say yes if you
  289        *        need to.
  290        * @see org.apache.poi.poifs.filesystem.POIFSFileSystem
  291        * @see #HSSFWorkbook(POIFSFileSystem)
  292        * @exception IOException if the stream cannot be read
  293        */
  294   
  295       public HSSFWorkbook(InputStream s, boolean preserveNodes)
  296               throws IOException
  297       {
  298           this(new POIFSFileSystem(s), preserveNodes);
  299       }
  300   
  301       /**
  302        * used internally to set the workbook properties.
  303        */
  304   
  305       private void setPropertiesFromWorkbook(Workbook book)
  306       {
  307           this.workbook = book;
  308   
  309           // none currently
  310       }
  311   
  312       /**
  313         * This is basically a kludge to deal with the now obsolete Label records.  If
  314         * you have to read in a sheet that contains Label records, be aware that the rest
  315         * of the API doesn't deal with them, the low level structure only provides read-only
  316         * semi-immutable structures (the sets are there for interface conformance with NO
  317         * impelmentation).  In short, you need to call this function passing it a reference
  318         * to the Workbook object.  All labels will be converted to LabelSST records and their
  319         * contained strings will be written to the Shared String tabel (SSTRecord) within
  320         * the Workbook.
  321         *
  322         * @param wb sheet's matching low level Workbook structure containing the SSTRecord.
  323         * @see org.apache.poi.hssf.record.LabelRecord
  324         * @see org.apache.poi.hssf.record.LabelSSTRecord
  325         * @see org.apache.poi.hssf.record.SSTRecord
  326         */
  327   
  328        private void convertLabelRecords(List records, int offset)
  329        {
  330            if (log.check( POILogger.DEBUG ))
  331                log.log(POILogger.DEBUG, "convertLabelRecords called");
  332            for (int k = offset; k < records.size(); k++)
  333            {
  334                Record rec = ( Record ) records.get(k);
  335   
  336                if (rec.getSid() == LabelRecord.sid)
  337                {
  338                    LabelRecord oldrec = ( LabelRecord ) rec;
  339   
  340                    records.remove(k);
  341                    LabelSSTRecord newrec   = new LabelSSTRecord();
  342                    int            stringid =
  343                        workbook.addSSTString(new UnicodeString(oldrec.getValue()));
  344   
  345                    newrec.setRow(oldrec.getRow());
  346                    newrec.setColumn(oldrec.getColumn());
  347                    newrec.setXFIndex(oldrec.getXFIndex());
  348                    newrec.setSSTIndex(stringid);
  349                          records.add(k, newrec);
  350                }
  351            }
  352            if (log.check( POILogger.DEBUG ))
  353                log.log(POILogger.DEBUG, "convertLabelRecords exit");
  354        }
  355   
  356   	/**
  357   	 * Retrieves the current policy on what to do when
  358   	 *  getting missing or blank cells from a row.
  359   	 * The default is to return blank and null cells.
  360   	 *  {@link MissingCellPolicy}
  361   	 */
  362   	public MissingCellPolicy getMissingCellPolicy() {
  363   		return missingCellPolicy;
  364   	}
  365   
  366   	/**
  367   	 * Sets the policy on what to do when
  368   	 *  getting missing or blank cells from a row.
  369   	 * This will then apply to all calls to 
  370   	 *  {@link HSSFRow.getCell()}. See
  371   	 *  {@link MissingCellPolicy}
  372   	 */
  373   	public void setMissingCellPolicy(MissingCellPolicy missingCellPolicy) {
  374   		this.missingCellPolicy = missingCellPolicy;
  375   	}
  376   
  377   	/**
  378        * sets the order of appearance for a given sheet.
  379        *
  380        * @param sheetname the name of the sheet to reorder
  381        * @param pos the position that we want to insert the sheet into (0 based)
  382        */
  383   
  384       public void setSheetOrder(String sheetname, int pos ) {
  385           _sheets.add(pos,_sheets.remove(getSheetIndex(sheetname)));
  386           workbook.setSheetOrder(sheetname, pos);
  387       }
  388   
  389       private void validateSheetIndex(int index) {
  390           int lastSheetIx = _sheets.size() - 1;
  391           if (index < 0 || index > lastSheetIx) {
  392               throw new IllegalArgumentException("Sheet index (" 
  393                       + index +") is out of range (0.." +    lastSheetIx + ")");
  394           }
  395       }
  396       
  397       /**
  398        * Selects a single sheet. This may be different to
  399        * the 'active' sheet (which is the sheet with focus).  
  400        */
  401       public void setSelectedTab(int index) {
  402           
  403           validateSheetIndex(index);
  404           int nSheets = _sheets.size();
  405           for (int i=0; i<nSheets; i++) {
  406                  getSheetAt(i).setSelected(i == index);
  407           }
  408           workbook.getWindowOne().setNumSelectedTabs((short)1);
  409       }
  410       /**
  411        * deprecated May 2008
  412        * @deprecated use setSelectedTab(int)
  413        */
  414       public void setSelectedTab(short index) {
  415           setSelectedTab((int)index);
  416       }
  417       public void setSelectedTabs(int[] indexes) {
  418           
  419           for (int i = 0; i < indexes.length; i++) {
  420               validateSheetIndex(indexes[i]);
  421           }
  422           int nSheets = _sheets.size();
  423           for (int i=0; i<nSheets; i++) {
  424               boolean bSelect = false;
  425               for (int j = 0; j < indexes.length; j++) {
  426                   if (indexes[j] == i) {
  427                       bSelect = true;
  428                       break;
  429                   }
  430                   
  431               }
  432                  getSheetAt(i).setSelected(bSelect);
  433           }
  434           workbook.getWindowOne().setNumSelectedTabs((short)indexes.length);
  435       }
  436       /**
  437        * Convenience method to set the active sheet.  The active sheet is is the sheet
  438        * which is currently displayed when the workbook is viewed in Excel.
  439        * 'Selected' sheet(s) is a distinct concept.
  440        */
  441       public void setActiveSheet(int index) {
  442           
  443           validateSheetIndex(index);
  444           int nSheets = _sheets.size();
  445           for (int i=0; i<nSheets; i++) {
  446                getSheetAt(i).setActive(i == index);
  447           }
  448           workbook.getWindowOne().setActiveSheetIndex(index);
  449       }
  450   
  451       /**
  452        * gets the tab whose data is actually seen when the sheet is opened.
  453        * This may be different from the "selected sheet" since excel seems to
  454        * allow you to show the data of one sheet when another is seen "selected"
  455        * in the tabs (at the bottom).
  456        * @see org.apache.poi.hssf.usermodel.HSSFSheet#setSelected(boolean)
  457        */
  458       public int getActiveSheetIndex() {
  459           return workbook.getWindowOne().getActiveSheetIndex();
  460       }
  461       /**
  462        * deprecated May 2008
  463        * @deprecated - Misleading name - use getActiveSheetIndex() 
  464        */
  465       public short getSelectedTab() {
  466           return (short) getActiveSheetIndex();
  467       }
  468   
  469       
  470       /**
  471        * sets the first tab that is displayed in the list of tabs
  472        * in excel.
  473        * @param index
  474        */
  475       public void setFirstVisibleTab(int index) {
  476           workbook.getWindowOne().setFirstVisibleTab(index);
  477       }
  478       /**
  479        * deprecated May 2008
  480        * @deprecated - Misleading name - use setFirstVisibleTab() 
  481        */
  482       public void setDisplayedTab(short index) {
  483          setFirstVisibleTab(index);
  484       }
  485   
  486       /**
  487        * sets the first tab that is displayed in the list of tabs in excel.
  488        */
  489       public int getFirstVisibleTab() {
  490           return workbook.getWindowOne().getFirstVisibleTab();
  491       }
  492       /**
  493        * deprecated May 2008
  494        * @deprecated - Misleading name - use getFirstVisibleTab() 
  495        */
  496       public short getDisplayedTab() {
  497           return (short) getFirstVisibleTab();
  498       }
  499   
  500       /**
  501        * @deprecated POI will now properly handle unicode strings without
  502        * forceing an encoding
  503        */
  504       public final static byte ENCODING_COMPRESSED_UNICODE = 0;
  505       /**
  506        * @deprecated POI will now properly handle unicode strings without
  507        * forceing an encoding
  508        */
  509       public final static byte ENCODING_UTF_16             = 1;
  510   
  511   
  512       /**
  513        * set the sheet name.
  514        * Will throw IllegalArgumentException if the name is greater than 31 chars
  515        * or contains /\?*[]
  516        * @param sheetIx number (0 based)
  517        */
  518       public void setSheetName(int sheetIx, String name)
  519       {
  520           if (workbook.doesContainsSheetName( name, sheetIx )) {
  521               throw new IllegalArgumentException( "The workbook already contains a sheet with this name" );
  522           }
  523           validateSheetIndex(sheetIx);
  524           workbook.setSheetName(sheetIx, name);
  525       }
  526   
  527   
  528       /**
  529        * set the sheet name forcing the encoding. Forcing the encoding IS A BAD IDEA!!!
  530        * @deprecated 3-Jan-2006 POI now automatically detects unicode and sets the encoding
  531        * appropriately. Simply use setSheetName(int sheet, String encoding)
  532        * @throws IllegalArgumentException if the name is greater than 31 chars
  533        * or contains /\?*[]
  534        * @param sheet number (0 based)
  535        */
  536       public void setSheetName(int sheetIx, String name, short encoding)
  537       {
  538           if (workbook.doesContainsSheetName( name, sheetIx )) {
  539               throw new IllegalArgumentException( "The workbook already contains a sheet with this name" );
  540           }
  541           validateSheetIndex(sheetIx);
  542   
  543           switch ( encoding ) {
  544           case ENCODING_COMPRESSED_UNICODE:
  545           case ENCODING_UTF_16:
  546               break;
  547   
  548           default:
  549               // TODO java.io.UnsupportedEncodingException
  550               throw new RuntimeException( "Unsupported encoding" );
  551           }
  552   
  553           workbook.setSheetName( sheetIx, name, encoding );
  554       }
  555   
  556       /**
  557        * get the sheet name
  558        * @param sheetIx Number
  559        * @return Sheet name
  560        */
  561       public String getSheetName(int sheetIx)
  562       {
  563           validateSheetIndex(sheetIx);
  564           return workbook.getSheetName(sheetIx);
  565       }
  566   
  567       /**
  568        * check whether a sheet is hidden
  569        * @param sheetIx Number
  570        * @return True if sheet is hidden
  571        */
  572       public boolean isSheetHidden(int sheetIx) {
  573           validateSheetIndex(sheetIx);
  574           return workbook.isSheetHidden(sheetIx);
  575       }
  576   
  577       /**
  578        * Hide or unhide a sheet
  579        *
  580        * @param sheetIx The sheet index
  581        * @param hidden True to mark the sheet as hidden, false otherwise
  582        */
  583       public void setSheetHidden(int sheetIx, boolean hidden) {
  584           validateSheetIndex(sheetIx);
  585           workbook.setSheetHidden(sheetIx, hidden);
  586       }
  587   
  588       /*
  589        * get the sheet's index
  590        * @param name  sheet name
  591        * @return sheet index or -1 if it was not found.
  592        */
  593   
  594       /** Returns the index of the sheet by his name
  595        * @param name the sheet name
  596        * @return index of the sheet (0 based)
  597        */
  598       public int getSheetIndex(String name)
  599       {
  600           int retval = workbook.getSheetIndex(name);
  601   
  602           return retval;
  603       }
  604   
  605       /** Returns the index of the given sheet
  606        * @param sheet the sheet to look up
  607        * @return index of the sheet (0 based). <tt>-1</tt> if not found
  608        */
  609       public int getSheetIndex(HSSFSheet sheet)
  610       {
  611           for(int i=0; i<_sheets.size(); i++) {
  612               if(_sheets.get(i) == sheet) {
  613                   return i;
  614               }
  615           }
  616           return -1;
  617       }
  618   
  619       /**
  620        * Returns the external sheet index of the sheet
  621        *  with the given internal index, creating one
  622        *  if needed.
  623        * Used by some of the more obscure formula and
  624        *  named range things.
  625        */
  626       public short getExternalSheetIndex(int internalSheetIndex) {
  627           return workbook.checkExternSheet(internalSheetIndex);
  628       }
  629   
  630       /**
  631        * create an HSSFSheet for this HSSFWorkbook, adds it to the sheets and returns
  632        * the high level representation.  Use this to create new sheets.
  633        *
  634        * @return HSSFSheet representing the new sheet.
  635        */
  636   
  637       public HSSFSheet createSheet()
  638       {
  639           HSSFSheet sheet = new HSSFSheet(this);
  640   
  641           _sheets.add(sheet);
  642           workbook.setSheetName(_sheets.size() - 1, "Sheet" + (_sheets.size() - 1));
  643           boolean isOnlySheet = _sheets.size() == 1;
  644           sheet.setSelected(isOnlySheet);
  645           sheet.setActive(isOnlySheet);
  646           return sheet;
  647       }
  648   
  649       /**
  650        * create an HSSFSheet from an existing sheet in the HSSFWorkbook.
  651        *
  652        * @return HSSFSheet representing the cloned sheet.
  653        */
  654   
  655       public HSSFSheet cloneSheet(int sheetNum) {
  656           validateSheetIndex(sheetNum);
  657           HSSFSheet srcSheet = (HSSFSheet) _sheets.get(sheetNum);
  658           String srcName = workbook.getSheetName(sheetNum);
  659           HSSFSheet clonedSheet = srcSheet.cloneSheet(this);
  660           clonedSheet.setSelected(false);
  661           clonedSheet.setActive(false);
  662   
  663           _sheets.add(clonedSheet);
  664           int i = 1;
  665           while (true) {
  666               // Try and find the next sheet name that is unique
  667               String name = srcName;
  668               String index = Integer.toString(i++);
  669               if (name.length() + index.length() + 2 < 31) {
  670                   name = name + "(" + index + ")";
  671               } else {
  672                   name = name.substring(0, 31 - index.length() - 2) + "(" + index + ")";
  673               }
  674   
  675               //If the sheet name is unique, then set it otherwise move on to the next number.
  676               if (workbook.getSheetIndex(name) == -1) {
  677                 workbook.setSheetName(_sheets.size()-1, name);
  678                 break;
  679               }
  680           }
  681           return clonedSheet;
  682       }
  683   
  684       /**
  685        * create an HSSFSheet for this HSSFWorkbook, adds it to the sheets and
  686        * returns the high level representation. Use this to create new sheets.
  687        * 
  688        * @param sheetname
  689        *            sheetname to set for the sheet.
  690        * @return HSSFSheet representing the new sheet.
  691        * @throws IllegalArgumentException
  692        *             if there is already a sheet present with a case-insensitive
  693        *             match for the specified name.
  694        */
  695   
  696       public HSSFSheet createSheet(String sheetname)
  697       {
  698           if (workbook.doesContainsSheetName( sheetname, _sheets.size() ))
  699               throw new IllegalArgumentException( "The workbook already contains a sheet of this name" );
  700   
  701           HSSFSheet sheet = new HSSFSheet(this);
  702   
  703           _sheets.add(sheet);
  704           workbook.setSheetName(_sheets.size() - 1, sheetname);
  705           boolean isOnlySheet = _sheets.size() == 1;
  706           sheet.setSelected(isOnlySheet);
  707           sheet.setActive(isOnlySheet);
  708           return sheet;
  709       }
  710   
  711       /**
  712        * get the number of spreadsheets in the workbook (this will be three after serialization)
  713        * @return number of sheets
  714        */
  715   
  716       public int getNumberOfSheets()
  717       {
  718           return _sheets.size();
  719       }
  720   
  721       private HSSFSheet[] getSheets() {
  722           HSSFSheet[] result = new HSSFSheet[_sheets.size()];
  723           _sheets.toArray(result);
  724           return result;
  725       }
  726       /**
  727        * Get the HSSFSheet object at the given index.
  728        * @param index of the sheet number (0-based physical & logical)
  729        * @return HSSFSheet at the provided index
  730        */
  731   
  732       public HSSFSheet getSheetAt(int index)
  733       {
  734           return (HSSFSheet) _sheets.get(index);
  735       }
  736   
  737       /**
  738        * Get sheet with the given name (case insensitive match)
  739        * @param name of the sheet
  740        * @return HSSFSheet with the name provided or <code>null</code> if it does not exist
  741        */
  742   
  743       public HSSFSheet getSheet(String name)
  744       {
  745           HSSFSheet retval = null;
  746   
  747           for (int k = 0; k < _sheets.size(); k++)
  748           {
  749               String sheetname = workbook.getSheetName(k);
  750   
  751               if (sheetname.equalsIgnoreCase(name))
  752               {
  753                   retval = (HSSFSheet) _sheets.get(k);
  754               }
  755           }
  756           return retval;
  757       }
  758   
  759       public SheetReferences getSheetReferences() {
  760           return workbook.getSheetReferences();
  761       }
  762   
  763       /**
  764        * Removes sheet at the given index.<p/>
  765        * 
  766        * Care must be taken if the removed sheet is the currently active or only selected sheet in 
  767        * the workbook. There are a few situations when Excel must have a selection and/or active 
  768        * sheet. (For example when printing - see Bug 40414).<br/>
  769        * 
  770        * This method makes sure that if the removed sheet was active, another sheet will become
  771        * active in its place.  Furthermore, if the removed sheet was the only selected sheet, another
  772        * sheet will become selected.  The newly active/selected sheet will have the same index, or 
  773        * one less if the removed sheet was the last in the workbook.
  774        * 
  775        * @param index of the sheet  (0-based)
  776        */
  777       public void removeSheetAt(int index) {
  778           validateSheetIndex(index);
  779           boolean wasActive = getSheetAt(index).isActive();
  780           boolean wasSelected = getSheetAt(index).isSelected();
  781   
  782           _sheets.remove(index);
  783           workbook.removeSheet(index);
  784   
  785           // set the remaining active/selected sheet
  786           int nSheets = _sheets.size();
  787           if (nSheets < 1) {
  788               // nothing more to do if there are no sheets left
  789               return;
  790           }
  791           // the index of the closest remaining sheet to the one just deleted
  792           int newSheetIndex = index;
  793           if (newSheetIndex >= nSheets) {
  794               newSheetIndex = nSheets-1;
  795           }
  796           if (wasActive) {
  797               setActiveSheet(newSheetIndex);
  798           }
  799   
  800           if (wasSelected) {
  801               boolean someOtherSheetIsStillSelected = false;
  802               for (int i =0; i < nSheets; i++) {
  803                   if (getSheetAt(i).isSelected()) {
  804                       someOtherSheetIsStillSelected = true;
  805                       break;
  806                   }
  807               }
  808               if (!someOtherSheetIsStillSelected) {
  809                   setSelectedTab(newSheetIndex);
  810               }
  811           }
  812       }
  813   
  814       /**
  815        * determine whether the Excel GUI will backup the workbook when saving.
  816        *
  817        * @param backupValue   true to indicate a backup will be performed.
  818        */
  819   
  820       public void setBackupFlag(boolean backupValue)
  821       {
  822           BackupRecord backupRecord = workbook.getBackupRecord();
  823   
  824           backupRecord.setBackup(backupValue ? (short) 1
  825                   : (short) 0);
  826       }
  827   
  828       /**
  829        * determine whether the Excel GUI will backup the workbook when saving.
  830        *
  831        * @return the current setting for backups.
  832        */
  833   
  834       public boolean getBackupFlag()
  835       {
  836           BackupRecord backupRecord = workbook.getBackupRecord();
  837   
  838           return (backupRecord.getBackup() == 0) ? false
  839                   : true;
  840       }
  841   
  842       /**
  843        * Sets the repeating rows and columns for a sheet (as found in
  844        * File->PageSetup->Sheet).  This is function is included in the workbook
  845        * because it creates/modifies name records which are stored at the
  846        * workbook level.
  847        * <p>
  848        * To set just repeating columns:
  849        * <pre>
  850        *  workbook.setRepeatingRowsAndColumns(0,0,1,-1-1);
  851        * </pre>
  852        * To set just repeating rows:
  853        * <pre>
  854        *  workbook.setRepeatingRowsAndColumns(0,-1,-1,0,4);
  855        * </pre>
  856        * To remove all repeating rows and columns for a sheet.
  857        * <pre>
  858        *  workbook.setRepeatingRowsAndColumns(0,-1,-1,-1,-1);
  859        * </pre>
  860        *
  861        * @param sheetIndex    0 based index to sheet.
  862        * @param startColumn   0 based start of repeating columns.
  863        * @param endColumn     0 based end of repeating columns.
  864        * @param startRow      0 based start of repeating rows.
  865        * @param endRow        0 based end of repeating rows.
  866        */
  867       public void setRepeatingRowsAndColumns(int sheetIndex,
  868                                              int startColumn, int endColumn,
  869                                              int startRow, int endRow)
  870       {
  871           // Check arguments
  872           if (startColumn == -1 && endColumn != -1) throw new IllegalArgumentException("Invalid column range specification");
  873           if (startRow == -1 && endRow != -1) throw new IllegalArgumentException("Invalid row range specification");
  874           if (startColumn < -1 || startColumn >= 0xFF) throw new IllegalArgumentException("Invalid column range specification");
  875           if (endColumn < -1 || endColumn >= 0xFF) throw new IllegalArgumentException("Invalid column range specification");
  876           if (startRow < -1 || startRow > 65535) throw new IllegalArgumentException("Invalid row range specification");
  877           if (endRow < -1 || endRow > 65535) throw new IllegalArgumentException("Invalid row range specification");
  878           if (startColumn > endColumn) throw new IllegalArgumentException("Invalid column range specification");
  879           if (startRow > endRow) throw new IllegalArgumentException("Invalid row range specification");
  880   
  881           HSSFSheet sheet = getSheetAt(sheetIndex);
  882           short externSheetIndex = getWorkbook().checkExternSheet(sheetIndex);
  883   
  884           boolean settingRowAndColumn =
  885                   startColumn != -1 && endColumn != -1 && startRow != -1 && endRow != -1;
  886           boolean removingRange =
  887                   startColumn == -1 && endColumn == -1 && startRow == -1 && endRow == -1;
  888   
  889           boolean isNewRecord = false;
  890           NameRecord nameRecord;
  891           nameRecord = findExistingRowColHeaderNameRecord(sheetIndex);
  892           if (removingRange )
  893           {
  894               if (nameRecord != null)
  895                   workbook.removeName(findExistingRowColHeaderNameRecordIdx(sheetIndex+1));
  896               return;
  897           }
  898           if ( nameRecord == null )
  899           {
  900               nameRecord = workbook.createBuiltInName(NameRecord.BUILTIN_PRINT_TITLE, sheetIndex+1);
  901               //does a lot of the house keeping for builtin records, like setting lengths to zero etc
  902               isNewRecord = true;
  903           }
  904   
  905           short definitionTextLength = settingRowAndColumn ? (short)0x001a : (short)0x000b;
  906           nameRecord.setDefinitionTextLength(definitionTextLength);
  907   
  908           Stack ptgs = new Stack();
  909   
  910           if (settingRowAndColumn)
  911           {
  912               ptgs.add(new MemFuncPtg(23)); // TODO - where did constant '23' come from?
  913           }
  914           if (startColumn >= 0)
  915           {
  916               Area3DPtg area3DPtg1 = new Area3DPtg();
  917               area3DPtg1.setExternSheetIndex(externSheetIndex);
  918               area3DPtg1.setFirstColumn((short)startColumn);
  919               area3DPtg1.setLastColumn((short)endColumn);
  920               area3DPtg1.setFirstRow((short)0);
  921               area3DPtg1.setLastRow((short)0xFFFF);
  922               ptgs.add(area3DPtg1);
  923           }
  924           if (startRow >= 0)
  925           {
  926               Area3DPtg area3DPtg2 = new Area3DPtg();
  927               area3DPtg2.setExternSheetIndex(externSheetIndex);
  928               area3DPtg2.setFirstColumn((short)0);
  929               area3DPtg2.setLastColumn((short)0x00FF);
  930               area3DPtg2.setFirstRow((short)startRow);
  931               area3DPtg2.setLastRow((short)endRow);
  932               ptgs.add(area3DPtg2);
  933           }
  934           if (settingRowAndColumn)
  935           {
  936               ptgs.add(UnionPtg.instance);
  937           }
  938           nameRecord.setNameDefinition(ptgs);
  939   
  940           if (isNewRecord)
  941           {
  942               HSSFName newName = new HSSFName(this, nameRecord);
  943               names.add(newName);
  944           }
  945   
  946           HSSFPrintSetup printSetup = sheet.getPrintSetup();
  947           printSetup.setValidSettings(false);
  948   
  949           sheet.setActive(true);
  950       }
  951   
  952       private NameRecord findExistingRowColHeaderNameRecord( int sheetIndex )
  953       {
  954           int index = findExistingRowColHeaderNameRecordIdx(sheetIndex);
  955           if (index == -1)
  956               return null;
  957           else
  958               return (NameRecord)workbook.findNextRecordBySid(NameRecord.sid, index);
  959       }
  960   
  961       private int findExistingRowColHeaderNameRecordIdx( int sheetIndex )
  962       {
  963           int index = 0;
  964           NameRecord r = null;
  965           while ((r = (NameRecord) workbook.findNextRecordBySid(NameRecord.sid, index)) != null)
  966           {
  967               int indexToSheet = r.getEqualsToIndexToSheet() -1;
  968               if(indexToSheet > -1) { //ignore "GLOBAL" name records
  969                   int nameRecordSheetIndex = workbook.getSheetIndexFromExternSheetIndex(indexToSheet);
  970                   if (isRowColHeaderRecord( r ) && nameRecordSheetIndex == sheetIndex)
  971                   {
  972                       return index;
  973                   }
  974               }
  975               index++;
  976           }
  977   
  978           return -1;
  979       }
  980   
  981       private boolean isRowColHeaderRecord( NameRecord r )
  982       {
  983           return r.getOptionFlag() == 0x20 && ("" + ((char)7)).equals(r.getNameText());
  984       }
  985   
  986       /**
  987        * create a new Font and add it to the workbook's font table
  988        * @return new font object
  989        */
  990   
  991       public HSSFFont createFont()
  992       {
  993           FontRecord font = workbook.createNewFont();
  994           short fontindex = (short) (getNumberOfFonts() - 1);
  995   
  996           if (fontindex > 3)
  997           {
  998               fontindex++;   // THERE IS NO FOUR!!
  999           }
 1000           if(fontindex == Short.MAX_VALUE){
 1001               throw new IllegalArgumentException("Maximum number of fonts was exceeded");
 1002           }
 1003           HSSFFont retval = new HSSFFont(fontindex, font);
 1004   
 1005           return retval;
 1006       }
 1007   
 1008       /**
 1009        * Finds a font that matches the one with the supplied attributes
 1010        */
 1011       public HSSFFont findFont(short boldWeight, short color, short fontHeight,
 1012                                String name, boolean italic, boolean strikeout,
 1013                                short typeOffset, byte underline)
 1014       {
 1015   //        System.out.println( boldWeight + ", " + color + ", " + fontHeight + ", " + name + ", " + italic + ", " + strikeout + ", " + typeOffset + ", " + underline );
 1016           for (short i = 0; i < workbook.getNumberOfFontRecords(); i++)
 1017           {
 1018               if (i == 4)
 1019                   continue;
 1020   
 1021               FontRecord font = workbook.getFontRecordAt(i);
 1022               HSSFFont hssfFont = new HSSFFont(i, font);
 1023   //            System.out.println( hssfFont.getBoldweight() + ", " + hssfFont.getColor() + ", " + hssfFont.getFontHeight() + ", " + hssfFont.getFontName() + ", " + hssfFont.getItalic() + ", " + hssfFont.getStrikeout() + ", " + hssfFont.getTypeOffset() + ", " + hssfFont.getUnderline() );
 1024               if (hssfFont.getBoldweight() == boldWeight
 1025                       && hssfFont.getColor() == color
 1026                       && hssfFont.getFontHeight() == fontHeight
 1027                       && hssfFont.getFontName().equals(name)
 1028                       && hssfFont.getItalic() == italic
 1029                       && hssfFont.getStrikeout() == strikeout
 1030                       && hssfFont.getTypeOffset() == typeOffset
 1031                       && hssfFont.getUnderline() == underline)
 1032               {
 1033   //                System.out.println( "Found font" );
 1034                   return hssfFont;
 1035               }
 1036           }
 1037   
 1038   //        System.out.println( "No font found" );
 1039           return null;
 1040       }
 1041   
 1042       /**
 1043        * get the number of fonts in the font table
 1044        * @return number of fonts
 1045        */
 1046   
 1047       public short getNumberOfFonts()
 1048       {
 1049           return (short) workbook.getNumberOfFontRecords();
 1050       }
 1051   
 1052       /**
 1053        * get the font at the given index number
 1054        * @param idx  index number
 1055        * @return HSSFFont at the index
 1056        */
 1057   
 1058       public HSSFFont getFontAt(short idx)
 1059       {
 1060           FontRecord font = workbook.getFontRecordAt(idx);
 1061           HSSFFont retval = new HSSFFont(idx, font);
 1062   
 1063           return retval;
 1064       }
 1065   
 1066       /**
 1067        * create a new Cell style and add it to the workbook's style table
 1068        * @return the new Cell Style object
 1069        */
 1070   
 1071       public HSSFCellStyle createCellStyle()
 1072       {
 1073           ExtendedFormatRecord xfr = workbook.createCellXF();
 1074           short index = (short) (getNumCellStyles() - 1);
 1075           HSSFCellStyle style = new HSSFCellStyle(index, xfr, this);
 1076   
 1077           return style;
 1078       }
 1079   
 1080       /**
 1081        * get the number of styles the workbook contains
 1082        * @return count of cell styles
 1083        */
 1084   
 1085       public short getNumCellStyles()
 1086       {
 1087           return (short) workbook.getNumExFormats();
 1088       }
 1089   
 1090       /**
 1091        * get the cell style object at the given index
 1092        * @param idx  index within the set of styles
 1093        * @return HSSFCellStyle object at the index
 1094        */
 1095   
 1096       public HSSFCellStyle getCellStyleAt(short idx)
 1097       {
 1098           ExtendedFormatRecord xfr = workbook.getExFormatAt(idx);
 1099           HSSFCellStyle style = new HSSFCellStyle(idx, xfr, this);
 1100   
 1101           return style;
 1102       }
 1103   
 1104       /**
 1105        * Method write - write out this workbook to an Outputstream.  Constructs
 1106        * a new POI POIFSFileSystem, passes in the workbook binary representation  and
 1107        * writes it out.
 1108        *
 1109        * @param stream - the java OutputStream you wish to write the XLS to
 1110        *
 1111        * @exception IOException if anything can't be written.
 1112        * @see org.apache.poi.poifs.filesystem.POIFSFileSystem
 1113        */
 1114   
 1115       public void write(OutputStream stream)
 1116               throws IOException
 1117       {
 1118           byte[] bytes = getBytes();
 1119           POIFSFileSystem fs = new POIFSFileSystem();
 1120   
 1121           // For tracking what we've written out, used if we're
 1122           //  going to be preserving nodes
 1123           List excepts = new ArrayList(1);
 1124   
 1125           // Write out the Workbook stream
 1126           fs.createDocument(new ByteArrayInputStream(bytes), "Workbook");
 1127   
 1128           // Write out our HPFS properties, if we have them
 1129           writeProperties(fs, excepts);
 1130   
 1131           if (preserveNodes) {
 1132               // Don't write out the old Workbook, we'll be doing our new one
 1133               excepts.add("Workbook");
 1134               // If the file had WORKBOOK instead of Workbook, we'll write it
 1135               //  out correctly shortly, so don't include the old one
 1136               excepts.add("WORKBOOK");
 1137   
 1138               // Copy over all the other nodes to our new poifs
 1139               copyNodes(this.filesystem,fs,excepts);
 1140           }
 1141           fs.writeFilesystem(stream);
 1142           //poifs.writeFilesystem(stream);
 1143       }
 1144   
 1145       /**
 1146        * Method getBytes - get the bytes of just the HSSF portions of the XLS file.
 1147        * Use this to construct a POI POIFSFileSystem yourself.
 1148        *
 1149        *
 1150        * @return byte[] array containing the binary representation of this workbook and all contained
 1151        *         sheets, rows, cells, etc.
 1152        *
 1153        * @see org.apache.poi.hssf.model.Workbook
 1154        * @see org.apache.poi.hssf.model.Sheet
 1155        */
 1156   
 1157       public byte[] getBytes()
 1158       {
 1159           if (log.check( POILogger.DEBUG )) {
 1160               log.log(DEBUG, "HSSFWorkbook.getBytes()");
 1161           }
 1162           
 1163           HSSFSheet[] sheets = getSheets();
 1164           int nSheets = sheets.length;
 1165   
 1166           // before getting the workbook size we must tell the sheets that
 1167           // serialization is about to occur.
 1168           for (int i = 0; i < nSheets; i++) {
 1169               sheets[i].getSheet().preSerialize();
 1170           }
 1171   
 1172           int totalsize = workbook.getSize();
 1173   
 1174           // pre-calculate all the sheet sizes and set BOF indexes
 1175           int[] estimatedSheetSizes = new int[nSheets];
 1176           for (int k = 0; k < nSheets; k++) {
 1177               workbook.setSheetBof(k, totalsize);
 1178               int sheetSize = sheets[k].getSheet().getSize();
 1179               estimatedSheetSizes[k] = sheetSize;
 1180               totalsize += sheetSize;
 1181           }
 1182   
 1183   
 1184           byte[] retval = new byte[totalsize];
 1185           int pos = workbook.serialize(0, retval);
 1186   
 1187           for (int k = 0; k < nSheets; k++) {
 1188               int serializedSize = sheets[k].getSheet().serialize(pos, retval);
 1189               if (serializedSize != estimatedSheetSizes[k]) {
 1190                   // Wrong offset values have been passed in the call to setSheetBof() above.
 1191                   // For books with more than one sheet, this discrepancy would cause excel 
 1192                   // to report errors and loose data while reading the workbook
 1193                   throw new IllegalStateException("Actual serialized sheet size (" + serializedSize 
 1194                           + ") differs from pre-calculated size (" + estimatedSheetSizes[k] 
 1195                           + ") for sheet (" + k + ")");
 1196                   // TODO - add similar sanity check to ensure that Sheet.serializeIndexRecord() does not write mis-aligned offsets either
 1197               }
 1198               pos += serializedSize;
 1199           }
 1200           return retval;
 1201       }
 1202   
 1203       /** @deprecated Do not call this method from your applications. Use the methods
 1204        *  available in the HSSFRow to add string HSSFCells
 1205        */
 1206       public int addSSTString(String string)
 1207       {
 1208           return workbook.addSSTString(new UnicodeString(string));
 1209       }
 1210   
 1211       /** @deprecated Do not call this method from your applications. Use the methods
 1212        *  available in the HSSFRow to get string HSSFCells
 1213        */
 1214       public String getSSTString(int index)
 1215       {
 1216           return workbook.getSSTString(index).getString();
 1217       }
 1218   
 1219       protected Workbook getWorkbook()
 1220       {
 1221           return workbook;
 1222       }
 1223   
 1224       /** gets the total number of named ranges in the workboko
 1225        * @return number of named ranges
 1226        */
 1227       public int getNumberOfNames(){
 1228           int result = names.size();
 1229           return result;
 1230       }
 1231   
 1232       /** gets the Named range
 1233        * @param index position of the named range
 1234        * @return named range high level
 1235        */
 1236       public HSSFName getNameAt(int index){
 1237           HSSFName result = (HSSFName) names.get(index);
 1238   
 1239           return result;
 1240       }
 1241   
 1242       /** gets the named range name
 1243        * @param index the named range index (0 based)
 1244        * @return named range name
 1245        */
 1246       public String getNameName(int index){
 1247           String result = getNameAt(index).getNameName();
 1248   
 1249           return result;
 1250       }
 1251   
 1252       /**
 1253        * TODO - make this less cryptic / move elsewhere
 1254        * @param refIndex Index to REF entry in EXTERNSHEET record in the Link Table
 1255        * @param definedNameIndex zero-based to DEFINEDNAME or EXTERNALNAME record
 1256        * @return the string representation of the defined or external name
 1257        */
 1258       public String resolveNameXText(int refIndex, int definedNameIndex) {
 1259           return workbook.resolveNameXText(refIndex, definedNameIndex);
 1260       }
 1261   
 1262   
 1263       /**
 1264        * Sets the printarea for the sheet provided
 1265        * <p>
 1266        * i.e. Reference = $A$1:$B$2
 1267        * @param sheetIndex Zero-based sheet index (0 Represents the first sheet to keep consistent with java)
 1268        * @param reference Valid name Reference for the Print Area
 1269        */
 1270       public void setPrintArea(int sheetIndex, String reference)
 1271       {
 1272           NameRecord name = workbook.getSpecificBuiltinRecord(NameRecord.BUILTIN_PRINT_AREA, sheetIndex+1);
 1273   
 1274   
 1275           if (name == null)
 1276               name = workbook.createBuiltInName(NameRecord.BUILTIN_PRINT_AREA, sheetIndex+1);
 1277          //adding one here because 0 indicates a global named region; doesnt make sense for print areas
 1278   
 1279           short externSheetIndex = getWorkbook().checkExternSheet(sheetIndex);
 1280           name.setExternSheetNumber(externSheetIndex);
 1281           name.setAreaReference(reference);
 1282   
 1283   
 1284       }
 1285   
 1286       /**
 1287        * For the Convenience of Java Programmers maintaining pointers.
 1288        * @see #setPrintArea(int, String)
 1289        * @param sheetIndex Zero-based sheet index (0 = First Sheet)
 1290        * @param startColumn Column to begin printarea
 1291        * @param endColumn Column to end the printarea
 1292        * @param startRow Row to begin the printarea
 1293        * @param endRow Row to end the printarea
 1294        */
 1295       public void setPrintArea(int sheetIndex, int startColumn, int endColumn,
 1296                                 int startRow, int endRow) {
 1297   
 1298           //using absolute references because they don't get copied and pasted anyway
 1299           CellReference cell = new CellReference(startRow, startColumn, true, true);
 1300           String reference = cell.formatAsString();
 1301   
 1302           cell = new CellReference(endRow, endColumn, true, true);
 1303           reference = reference+":"+cell.formatAsString();
 1304   
 1305           setPrintArea(sheetIndex, reference);
 1306       }
 1307   
 1308   
 1309       /**
 1310        * Retrieves the reference for the printarea of the specified sheet, the sheet name is appended to the reference even if it was not specified.
 1311        * @param sheetIndex Zero-based sheet index (0 Represents the first sheet to keep consistent with java)
 1312        * @return String Null if no print area has been defined
 1313        */
 1314       public String getPrintArea(int sheetIndex)
 1315       {
 1316           NameRecord name = workbook.getSpecificBuiltinRecord(NameRecord.BUILTIN_PRINT_AREA, sheetIndex+1);
 1317           if (name == null) return null;
 1318           //adding one here because 0 indicates a global named region; doesnt make sense for print areas
 1319   
 1320           return name.getAreaReference(this);
 1321       }
 1322   
 1323       /**
 1324        * Delete the printarea for the sheet specified
 1325        * @param sheetIndex Zero-based sheet index (0 = First Sheet)
 1326        */
 1327       public void removePrintArea(int sheetIndex) {
 1328           getWorkbook().removeBuiltinRecord(NameRecord.BUILTIN_PRINT_AREA, sheetIndex+1);
 1329       }
 1330   
 1331       /** creates a new named range and add it to the model
 1332        * @return named range high level
 1333        */
 1334       public HSSFName createName(){
 1335           NameRecord nameRecord = workbook.createName();
 1336   
 1337           HSSFName newName = new HSSFName(this, nameRecord);
 1338   
 1339           names.add(newName);
 1340   
 1341           return newName;
 1342       }
 1343   
 1344       /** gets the named range index by his name
 1345        * <i>Note:</i>Excel named ranges are case-insensitive and
 1346        * this method performs a case-insensitive search.
 1347        *
 1348        * @param name named range name
 1349        * @return named range index
 1350        */
 1351       public int getNameIndex(String name)
 1352       {
 1353           int retval = -1;
 1354   
 1355           for (int k = 0; k < names.size(); k++)
 1356           {
 1357               String nameName = getNameName(k);
 1358   
 1359               if (nameName.equalsIgnoreCase(name))
 1360               {
 1361                   retval = k;
 1362                   break;
 1363               }
 1364           }
 1365           return retval;
 1366       }
 1367   
 1368   
 1369       /** remove the named range by his index
 1370        * @param index named range index (0 based)
 1371        */
 1372       public void removeName(int index){
 1373           names.remove(index);
 1374           workbook.removeName(index);
 1375       }
 1376   
 1377       /**
 1378        * Returns the instance of HSSFDataFormat for this workbook.
 1379        * @return the HSSFDataFormat object
 1380        * @see org.apache.poi.hssf.record.FormatRecord
 1381        * @see org.apache.poi.hssf.record.Record
 1382        */
 1383       public HSSFDataFormat createDataFormat() {
 1384       if (formatter == null)
 1385           formatter = new HSSFDataFormat(workbook);
 1386       return formatter;
 1387       }
 1388   
 1389       /** remove the named range by his name
 1390        * @param name named range name
 1391        */
 1392       public void removeName(String name){
 1393           int index = getNameIndex(name);
 1394   
 1395           removeName(index);
 1396   
 1397       }
 1398   
 1399       public HSSFPalette getCustomPalette()
 1400       {
 1401           return new HSSFPalette(workbook.getCustomPalette());
 1402       }
 1403   
 1404       /** Test only. Do not use */
 1405       public void insertChartRecord()
 1406       {
 1407           int loc = workbook.findFirstRecordLocBySid(SSTRecord.sid);
 1408           byte[] data = {
 1409              (byte)0x0F, (byte)0x00, (byte)0x00, (byte)0xF0, (byte)0x52,
 1410              (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
 1411              (byte)0x06, (byte)0xF0, (byte)0x18, (byte)0x00, (byte)0x00,
 1412              (byte)0x00, (byte)0x01, (byte)0x08, (byte)0x00, (byte)0x00,
 1413              (byte)0x02, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x02,
 1414              (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x01, (byte)0x00,
 1415              (byte)0x00, (byte)0x00, (byte)0x01, (byte)0x00, (byte)0x00,
 1416              (byte)0x00, (byte)0x03, (byte)0x00, (byte)0x00, (byte)0x00,
 1417              (byte)0x33, (byte)0x00, (byte)0x0B, (byte)0xF0, (byte)0x12,
 1418              (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xBF, (byte)0x00,
 1419              (byte)0x08, (byte)0x00, (byte)0x08, (byte)0x00, (byte)0x81,
 1420              (byte)0x01, (byte)0x09, (byte)0x00, (byte)0x00, (byte)0x08,
 1421              (byte)0xC0, (byte)0x01, (byte)0x40, (byte)0x00, (byte)0x00,
 1422              (byte)0x08, (byte)0x40, (byte)0x00, (byte)0x1E, (byte)0xF1,
 1423              (byte)0x10, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x0D,
 1424              (byte)0x00, (byte)0x00, (byte)0x08, (byte)0x0C, (byte)0x00,
 1425              (byte)0x00, (byte)0x08, (byte)0x17, (byte)0x00, (byte)0x00,
 1426              (byte)0x08, (byte)0xF7, (byte)0x00, (byte)0x00, (byte)0x10,
 1427           };
 1428           UnknownRecord r = new UnknownRecord((short)0x00EB, data);
 1429           workbook.getRecords().add(loc, r);
 1430       }
 1431   
 1432       /**
 1433        * Spits out a list of all the drawing records in the workbook.
 1434        */
 1435       public void dumpDrawingGroupRecords(boolean fat)
 1436       {
 1437           DrawingGroupRecord r = (DrawingGroupRecord) workbook.findFirstRecordBySid( DrawingGroupRecord.sid );
 1438           r.decode();
 1439           List escherRecords = r.getEscherRecords();
 1440           PrintWriter w = new PrintWriter(System.out);
 1441           for ( Iterator iterator = escherRecords.iterator(); iterator.hasNext(); )
 1442           {
 1443               EscherRecord escherRecord = (EscherRecord) iterator.next();
 1444               if (fat)
 1445                   System.out.println(escherRecord.toString());
 1446               else
 1447                   escherRecord.display(w, 0);
 1448           }
 1449           w.flush();
 1450       }
 1451   
 1452       /**
 1453        * Adds a picture to the workbook.
 1454        *
 1455        * @param pictureData       The bytes of the picture
 1456        * @param format            The format of the picture.  One of <code>PICTURE_TYPE_*</code>
 1457        *
 1458        * @return the index to this picture (1 based).
 1459        */
 1460       public int addPicture(byte[] pictureData, int format)
 1461       {
 1462           byte[] uid = newUID();
 1463           EscherBitmapBlip blipRecord = new EscherBitmapBlip();
 1464           blipRecord.setRecordId( (short) ( EscherBitmapBlip.RECORD_ID_START + format ) );
 1465           switch (format)
 1466           {
 1467               case PICTURE_TYPE_EMF:
 1468                   blipRecord.setOptions(HSSFPictureData.MSOBI_EMF);
 1469                   break;
 1470               case PICTURE_TYPE_WMF:
 1471                   blipRecord.setOptions(HSSFPictureData.MSOBI_WMF);
 1472                   break;
 1473               case PICTURE_TYPE_PICT:
 1474                   blipRecord.setOptions(HSSFPictureData.MSOBI_PICT);
 1475                   break;
 1476               case PICTURE_TYPE_PNG:
 1477                   blipRecord.setOptions(HSSFPictureData.MSOBI_PNG);
 1478                   break;
 1479               case HSSFWorkbook.PICTURE_TYPE_JPEG:
 1480                   blipRecord.setOptions(HSSFPictureData.MSOBI_JPEG);
 1481                   break;
 1482               case HSSFWorkbook.PICTURE_TYPE_DIB:
 1483                   blipRecord.setOptions(HSSFPictureData.MSOBI_DIB);
 1484                   break;
 1485           }
 1486   
 1487           blipRecord.setUID( uid );
 1488           blipRecord.setMarker( (byte) 0xFF );
 1489           blipRecord.setPictureData( pictureData );
 1490   
 1491           EscherBSERecord r = new EscherBSERecord();
 1492           r.setRecordId( EscherBSERecord.RECORD_ID );
 1493           r.setOptions( (short) ( 0x0002 | ( format << 4 ) ) );
 1494           r.setBlipTypeMacOS( (byte) format );
 1495           r.setBlipTypeWin32( (byte) format );
 1496           r.setUid( uid );
 1497           r.setTag( (short) 0xFF );
 1498           r.setSize( pictureData.length + 25 );
 1499           r.setRef( 1 );
 1500           r.setOffset( 0 );
 1501           r.setBlipRecord( blipRecord );
 1502   
 1503           return workbook.addBSERecord( r );
 1504       }
 1505   
 1506       /**
 1507        * Gets all pictures from the Workbook.
 1508        *
 1509        * @return the list of pictures (a list of {@link HSSFPictureData} objects.)
 1510        */
 1511       public List getAllPictures()
 1512       {
 1513           // The drawing group record always exists at the top level, so we won't need to do this recursively.
 1514           List pictures = new ArrayList();
 1515           Iterator recordIter = workbook.getRecords().iterator();
 1516           while (recordIter.hasNext())
 1517           {
 1518               Object obj = recordIter.next();
 1519               if (obj instanceof AbstractEscherHolderRecord)
 1520               {
 1521                   ((AbstractEscherHolderRecord) obj).decode();
 1522                   List escherRecords = ((AbstractEscherHolderRecord) obj).getEscherRecords();
 1523                   searchForPictures(escherRecords, pictures);
 1524               }
 1525           }
 1526           return pictures;
 1527       }
 1528   
 1529       /**
 1530        * Performs a recursive search for pictures in the given list of escher records.
 1531        *
 1532        * @param escherRecords the escher records.
 1533        * @param pictures the list to populate with the pictures.
 1534        */
 1535       private void searchForPictures(List escherRecords, List pictures)
 1536       {
 1537           Iterator recordIter = escherRecords.iterator();
 1538           while (recordIter.hasNext())
 1539           {
 1540               Object obj = recordIter.next();
 1541               if (obj instanceof EscherRecord)
 1542               {
 1543                   EscherRecord escherRecord = (EscherRecord) obj;
 1544   
 1545                   if (escherRecord instanceof EscherBSERecord)
 1546                   {
 1547                       EscherBlipRecord blip = ((EscherBSERecord) escherRecord).getBlipRecord();
 1548                       if (blip != null)
 1549                       {
 1550                           // TODO: Some kind of structure.
 1551                           pictures.add(new HSSFPictureData(blip));
 1552                       }
 1553                   }
 1554   
 1555                   // Recursive call.
 1556                   searchForPictures(escherRecord.getChildRecords(), pictures);
 1557               }
 1558           }
 1559       }
 1560   
 1561       /**
 1562        * Is the workbook protected with a password (not encrypted)?
 1563        */
 1564       public boolean isWriteProtected() {
 1565           return this.workbook.isWriteProtected();
 1566       }
 1567   
 1568       /**
 1569        * protect a workbook with a password (not encypted, just sets writeprotect
 1570        * flags and the password.
 1571        * @param password to set
 1572        */
 1573       public void writeProtectWorkbook( String password, String username ) {
 1574          this.workbook.writeProtectWorkbook(password, username);
 1575       }
 1576   
 1577       /**
 1578        * removes the write protect flag
 1579        */
 1580       public void unwriteProtectWorkbook() {
 1581          this.workbook.unwriteProtectWorkbook();
 1582       }
 1583   
 1584       /**
 1585        * Gets all embedded OLE2 objects from the Workbook.
 1586        *
 1587        * @return the list of embedded objects (a list of {@link HSSFObjectData} objects.)
 1588        */
 1589       public List getAllEmbeddedObjects()
 1590       {
 1591           List objects = new ArrayList();
 1592           for (int i = 0; i < getNumberOfSheets(); i++)
 1593           {
 1594               getAllEmbeddedObjects(getSheetAt(i).getSheet().getRecords(), objects);
 1595           }
 1596           return objects;
 1597       }
 1598   
 1599       /**
 1600        * Gets all embedded OLE2 objects from the Workbook.
 1601        *
 1602        * @param records the list of records to search.
 1603        * @param objects the list of embedded objects to populate.
 1604        */
 1605       private void getAllEmbeddedObjects(List records, List objects)
 1606       {
 1607           Iterator recordIter = records.iterator();
 1608           while (recordIter.hasNext())
 1609           {
 1610               Object obj = recordIter.next();
 1611               if (obj instanceof ObjRecord)
 1612               {
 1613                   // TODO: More convenient way of determining if there is stored binary.
 1614                   // TODO: Link to the data stored in the other stream.
 1615                   Iterator subRecordIter = ((ObjRecord) obj).getSubRecords().iterator();
 1616                   while (subRecordIter.hasNext())
 1617                   {
 1618                       Object sub = subRecordIter.next();
 1619                       if (sub instanceof EmbeddedObjectRefSubRecord)
 1620                       {
 1621                           objects.add(new HSSFObjectData((ObjRecord) obj, filesystem));
 1622                       }
 1623                   }
 1624               }
 1625           }
 1626       }
 1627   
 1628       private byte[] newUID()
 1629       {
 1630           byte[] bytes = new byte[16];
 1631           return bytes;
 1632       }
 1633   }

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