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    * HSSFWorkbook.java
57    *
58    * Created on September 30, 2001, 3:37 PM
59    */
60   package org.apache.poi.hssf.usermodel;
61   
62   import org.apache.poi.util.POILogFactory;
63   import org.apache.poi.hssf.model.Sheet;
64   import org.apache.poi.hssf.model.Workbook;
65   import org.apache.poi.hssf.record.*;
66   import org.apache.poi.poifs.filesystem.POIFSFileSystem;
67   import org.apache.poi.util.POILogger;
68   
69   import java.io.ByteArrayInputStream;
70   import java.io.IOException;
71   import java.io.InputStream;
72   import java.io.OutputStream;
73   import java.util.ArrayList;
74   import java.util.List;
75   
76   /**
77    * High level representation of a workbook.  This is the first object most users
78    * will construct whether they are reading or writing a workbook.  It is also the
79    * top level object for creating new sheets/etc.
80    *
81    * @see org.apache.poi.hssf.model.Workbook
82    * @see org.apache.poi.hssf.usermodel.HSSFSheet
83    * @author  Andrew C. Oliver (acoliver at apache dot org)
84    * @author  Glen Stampoultzis (glens at apache.org)
85    * @version 2.0-pre
86    */
87   
88   public class HSSFWorkbook
89           extends java.lang.java.lang.Objectvate static final int DEBUG = POILogger.DEBUG;
90   
91       /**
92        * used for compile-time performance/memory optimization.  This determines the
93        * initial capacity for the sheet collection.  Its currently set to 3.
94        * Changing it in this release will decrease performance
95        * since you're never allowed to have more or less than three sheets!
96        */
97   
98       public final static int INITIAL_CAPACITY = 3;
99   
100      /**
101       * this is the reference to the low level Workbook object
102       */
103  
104      private Workbook workbook;
105  
106      /**
107       * this holds the HSSFSheet objects attached to this workbook
108       */
109  
110      private ArrayList sheets;
111      private static POILogger log = POILogFactory.getLogger(HSSFWorkbook.class);
112  
113      /**
114       * Creates new HSSFWorkbook from scratch (start here!)
115       *
116       */
117  
118      public HSSFWorkbook()
119      {
120          workbook = Workbook.createWorkbook();
121          sheets = new ArrayList(INITIAL_CAPACITY);
122      }
123  
124      /**
125       * given a POI POIFSFileSystem object, read in its Workbook and populate the high and
126       * low level models.  If you're reading in a workbook...start here.
127       *
128       * @param fs the POI filesystem that contains the Workbook stream.
129       * @see org.apache.poi.poifs.filesystem.POIFSFileSystem
130       * @exception IOException if the stream cannot be read
131       */
132  
133      public HSSFWorkbook(POIFSFileSystem fs)
134              throws IOException
135      {
136          sheets = new ArrayList(INITIAL_CAPACITY);
137          InputStream stream = fs.createDocumentInputStream("Workbook");
138          List records = RecordFactory.createRecords(stream);
139  
140          workbook = Workbook.createWorkbook(records);
141          setPropertiesFromWorkbook(workbook);
142          int numRecords = workbook.getNumRecords();
143          int sheetNum = 0;
144  
145          while (numRecords < records.size())
146          {
147              Sheet sheet = Sheet.createSheet(records, sheetNum++, numRecords);
148  
149              numRecords += sheet.getNumRecords();
150              sheet.convertLabelRecords(
151                      workbook);   // convert all LabelRecord records to LabelSSTRecord
152              HSSFSheet hsheet = new HSSFSheet(workbook, sheet);
153  
154              sheets.add(hsheet);
155  
156              // workbook.setSheetName(sheets.size() -1, "Sheet"+sheets.size());
157          }
158      }
159  
160      /**
161       * Companion to HSSFWorkbook(POIFSFileSystem), this constructs the POI filesystem around your
162       * inputstream.
163       *
164       * @param s  the POI filesystem that contains the Workbook stream.
165       * @see org.apache.poi.poifs.filesystem.POIFSFileSystem
166       * @see #HSSFWorkbook(POIFSFileSystem)
167       * @exception IOException if the stream cannot be read
168       */
169  
170      public HSSFWorkbook(InputStream s)
171              throws IOException
172      {
173          this((new POIFSFileSystem(s)));
174      }
175  
176      /**
177       * used internally to set the workbook properties.
178       */
179  
180      private void setPropertiesFromWorkbook(Workbook book)
181      {
182          this.workbook = book;
183  
184          // none currently
185      }
186  
187      /**
188       * set the sheet name.
189       * @param sheet number (0 based)
190       * @param sheet name
191       */
192  
193      public void setSheetName(int sheet, String name)
194      {
195          if (sheet > (sheets.size() - 1))
196          {
197              throw new RuntimeException("Sheet out of bounds");
198          }
199          workbook.setSheetName(sheet, name);
200      }
201  
202      /**
203       * get the sheet name
204       * @param sheet Number
205       * @return Sheet name
206       */
207  
208      public String getSheetName(int sheet)
209      {
210          if (sheet > (sheets.size() - 1))
211          {
212              throw new RuntimeException("Sheet out of bounds");
213          }
214          return workbook.getSheetName(sheet);
215      }
216  
217      /**
218       * get the sheet's index
219       * @param name  sheet name
220       * @return sheet index or -1 if it was not found.
221       */
222  
223      public int getSheetIndex(String name)
224      {
225          int retval = -1;
226  
227          for (int k = 0; k < sheets.size(); k++)
228          {
229              String sheet = workbook.getSheetName(k);
230  
231              if (sheet.equals(name))
232              {
233                  retval = k;
234                  break;
235              }
236          }
237          return retval;
238      }
239  
240      /**
241       * create an HSSFSheet for this HSSFWorkbook, adds it to the sheets and returns
242       * the high level representation.  Use this to create new sheets.
243       *
244       * @return HSSFSheet representing the new sheet.
245       */
246  
247      public HSSFSheet createSheet()
248      {
249  
250  //        if (getNumberOfSheets() == 3)
251  //            throw new RuntimeException("You cannot have more than three sheets in HSSF 1.0");
252          HSSFSheet sheet = new HSSFSheet(workbook);
253  
254          sheets.add(sheet);
255          workbook.setSheetName(sheets.size() - 1,
256                  "Sheet" + (sheets.size() - 1));
257          WindowTwoRecord windowTwo = (WindowTwoRecord) sheet.getSheet().findFirstRecordBySid(WindowTwoRecord.sid);
258          windowTwo.setSelected(sheets.size() == 1);
259          windowTwo.setPaged(sheets.size() == 1);
260          return sheet;
261      }
262  
263      /**
264       * create an HSSFSheet for this HSSFWorkbook, adds it to the sheets and returns
265       * the high level representation.  Use this to create new sheets.
266       *
267       * @param sheetname     sheetname to set for the sheet.
268       * @return HSSFSheet representing the new sheet.
269       */
270  
271      public HSSFSheet createSheet(String sheetname)
272      {
273  
274  //        if (getNumberOfSheets() == 3)
275  //            throw new RuntimeException("You cannot have more than three sheets in HSSF 1.0");
276          HSSFSheet sheet = new HSSFSheet(workbook);
277  
278          sheets.add(sheet);
279          workbook.setSheetName(sheets.size() - 1, sheetname);
280          WindowTwoRecord windowTwo = (WindowTwoRecord) sheet.getSheet().findFirstRecordBySid(WindowTwoRecord.sid);
281          windowTwo.setSelected(sheets.size() == 1);
282          windowTwo.setPaged(sheets.size() == 1);
283          return sheet;
284      }
285  
286      /**
287       * get the number of spreadsheets in the workbook (this will be three after serialization)
288       * @return number of sheets
289       */
290  
291      public int getNumberOfSheets()
292      {
293          return sheets.size();
294      }
295  
296      /**
297       * Get the HSSFSheet object at the given index.
298       * @param index of the sheet number (0-based physical & logical)
299       * @return HSSFSheet at the provided index
300       */
301  
302      public HSSFSheet getSheetAt(int index)
303      {
304          return (HSSFSheet) sheets.get(index);
305      }
306  
307      /**
308       * Get sheet with the given name
309       * @param name of the sheet
310       * @return HSSFSheet with the name provided or null if it does not exist
311       */
312  
313      public HSSFSheet getSheet(String name)
314      {
315          HSSFSheet retval = null;
316  
317          for (int k = 0; k < sheets.size(); k++)
318          {
319              String sheetname = workbook.getSheetName(k);
320  
321              if (sheetname.equals(name))
322              {
323                  retval = (HSSFSheet) sheets.get(k);
324              }
325          }
326          return retval;
327      }
328  
329      /**
330       * removes sheet at the given index
331       * @param index of the sheet  (0-based)
332       */
333  
334      public void removeSheetAt(int index)
335      {
336          sheets.remove(index);
337          workbook.removeSheet(index);
338      }
339  
340      /**
341       * determine whether the Excel GUI will backup the workbook when saving.
342       *
343       * @param backupValue   true to indicate a backup will be performed.
344       */
345  
346      public void setBackupFlag(boolean backupValue)
347      {
348          BackupRecord backupRecord = workbook.getBackupRecord();
349  
350          backupRecord.setBackup(backupValue ? (short) 1
351                  : (short) 0);
352      }
353  
354      /**
355       * determine whether the Excel GUI will backup the workbook when saving.
356       *
357       * @return the current setting for backups.
358       */
359  
360      public boolean getBackupFlag()
361      {
362          BackupRecord backupRecord = workbook.getBackupRecord();
363  
364          return (backupRecord.getBackup() == 0) ? false
365                  : true;
366      }
367  
368      /**
369       * create a new Font and add it to the workbook's font table
370       * @return new font object
371       */
372  
373      public HSSFFont createFont()
374      {
375          FontRecord font = workbook.createNewFont();
376          short fontindex = (short) (getNumberOfFonts() - 1);
377  
378          if (fontindex > 3)
379          {
380              fontindex++;   // THERE IS NO FOUR!!
381          }
382          HSSFFont retval = new HSSFFont(fontindex, font);
383  
384          return retval;
385      }
386  
387      /**
388       * get the number of fonts in the font table
389       * @return number of fonts
390       */
391  
392      public short getNumberOfFonts()
393      {
394          return (short) workbook.getNumberOfFontRecords();
395      }
396  
397      /**
398       * get the font at the given index number
399       * @param idx  index number
400       * @return HSSFFont at the index
401       */
402  
403      public HSSFFont getFontAt(short idx)
404      {
405          FontRecord font = workbook.getFontRecordAt(idx);
406          HSSFFont retval = new HSSFFont(idx, font);
407  
408          return retval;
409      }
410  
411      /**
412       * create a new Cell style and add it to the workbook's style table
413       * @return the new Cell Style object
414       */
415  
416      public HSSFCellStyle createCellStyle()
417      {
418          ExtendedFormatRecord xfr = workbook.createCellXF();
419          short index = (short) (getNumCellStyles() - 1);
420          HSSFCellStyle style = new HSSFCellStyle(index, xfr);
421  
422          return style;
423      }
424  
425      /**
426       * get the number of styles the workbook contains
427       * @return count of cell styles
428       */
429  
430      public short getNumCellStyles()
431      {
432          return (short) workbook.getNumExFormats();
433      }
434  
435      /**
436       * get the cell style object at the given index
437       * @param idx  index within the set of styles
438       * @return HSSFCellStyle object at the index
439       */
440  
441      public HSSFCellStyle getCellStyleAt(short idx)
442      {
443          ExtendedFormatRecord xfr = workbook.getExFormatAt(idx);
444          HSSFCellStyle style = new HSSFCellStyle(idx, xfr);
445  
446          return style;
447      }
448  
449      /**
450       * Method write - write out this workbook to an Outputstream.  Constructs
451       * a new POI POIFSFileSystem, passes in the workbook binary representation  and
452       * writes it out.
453       *
454       * @param stream - the java OutputStream you wish to write the XLS to
455       *
456       * @exception IOException if anything can't be written.
457       * @see org.apache.poi.poifs.filesystem.POIFSFileSystem
458       */
459  
460      public void write(OutputStream stream)
461              throws IOException
462      {
463          byte[] bytes = getBytes();
464          POIFSFileSystem fs = new POIFSFileSystem();
465  
466          fs.createDocument(new ByteArrayInputStream(bytes), "Workbook");
467          fs.writeFilesystem(stream);
468      }
469  
470      /**
471       * Method getBytes - get the bytes of just the HSSF portions of the XLS file.
472       * Use this to construct a POI POIFSFileSystem yourself.
473       *
474       *
475       * @return byte[] array containing the binary representation of this workbook and all contained
476       *         sheets, rows, cells, etc.
477       *
478       * @see org.apache.poi.hssf.model.Workbook
479       * @see org.apache.poi.hssf.model.Sheet
480       */
481  
482      public byte[] getBytes()
483      {
484          log.log(DEBUG, "HSSFWorkbook.getBytes()");
485          int wbsize = workbook.getSize();
486  
487          // log.debug("REMOVEME: old sizing method "+workbook.serialize().length);
488          // ArrayList sheetbytes = new ArrayList(sheets.size());
489          int totalsize = wbsize;
490  
491          for (int k = 0; k < sheets.size(); k++)
492          {
493              workbook.setSheetBof(k, totalsize);
494  
495              // sheetbytes.add((( HSSFSheet ) sheets.get(k)).getSheet().getSize());
496              totalsize += ((HSSFSheet) sheets.get(k)).getSheet().getSize();
497          }
498          if (totalsize < 4096)
499          {
500              totalsize = 4096;
501          }
502          byte[] retval = new byte[totalsize];
503          int pos = workbook.serialize(0, retval);
504  
505          // System.arraycopy(wb, 0, retval, 0, wb.length);
506          for (int k = 0; k < sheets.size(); k++)
507          {
508  
509              // byte[] sb = (byte[])sheetbytes.get(k);
510              // System.arraycopy(sb, 0, retval, pos, sb.length);
511              pos += ((HSSFSheet) sheets.get(k)).getSheet().serialize(pos,
512                      retval);   // sb.length;
513          }
514          for (int k = pos; k < totalsize; k++)
515          {
516              retval[k] = 0;
517          }
518          return retval;
519      }
520  
521      public int addSSTString(String string)
522      {
523          return workbook.addSSTString(string);
524      }
525  
526      public String getSSTString(int index)
527      {
528          return workbook.getSSTString(index);
529      }
530  
531      Workbook getWorkbook()
532      {
533          return workbook;
534      }
535  }
536