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    * HSSFRow.java
57    *
58    * Created on September 30, 2001, 3:44 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.CellValueRecordInterface;
65   import org.apache.poi.hssf.record.RowRecord;
66   
67   import java.util.HashMap;
68   import java.util.Iterator;
69   
70   /**
71    * High level representation of a row of a spreadsheet.
72    *
73    * Only rows that have cells should be added to a Sheet.
74    * @version 1.0-pre
75    * @author  Andrew C. Oliver (acoliver at apache dot org)
76    * @author Glen Stampoultzis (glens at apache.org)
77    */
78   
79   public class HSSFRow
80           implements Comparable
81   {
82   
83       // used for collections
84       public final static int INITIAL_CAPACITY = 5;
85       //private short rowNum;
86       private int rowNum;
87       private HashMap cells;
88   //    private short firstcell = -1;
89   //    private short lastcell = -1;
90   
91       /**
92        * reference to low level representation
93        */
94   
95       private RowRecord row;
96   
97       /**
98        * reference to containing low level Workbook
99        */
100  
101      private Workbook book;
102  
103      /**
104       * reference to containing Sheet
105       */
106  
107      private Sheet sheet;
108  
109      protected HSSFRow()
110      {
111      }
112  
113      /**
114       * Creates new HSSFRow from scratch. Only HSSFSheet should do this.
115       *
116       * @param book low-level Workbook object containing the sheet that contains this row
117       * @param sheet low-level Sheet object that contains this Row
118       * @param rowNum the row number of this row (0 based)
119       * @see org.apache.poi.hssf.usermodel.HSSFSheet#createRow(short)
120       */
121  
122      //protected HSSFRow(Workbook book, Sheet sheet, short rowNum)
123      protected HSSFRow(Workbook book, Sheet sheet, int rowNum)
124      {
125          this.rowNum = rowNum;
126          cells = new HashMap(10);   // new ArrayList(INITIAL_CAPACITY);
127          this.book = book;
128          this.sheet = sheet;
129          row = new RowRecord();
130          row.setHeight((short) 0xff);
131          row.setLastCol((short) -1);
132          row.setFirstCol((short) -1);
133  
134          // row.setRowNumber(rowNum);
135          setRowNum(rowNum);
136      }
137  
138      /**
139       * Creates an HSSFRow from a low level RowRecord object.  Only HSSFSheet should do
140       * this.  HSSFSheet uses this when an existing file is read in.
141       *
142       * @param book low-level Workbook object containing the sheet that contains this row
143       * @param sheet low-level Sheet object that contains this Row
144       * @param record the low level api object this row should represent
145       * @see org.apache.poi.hssf.usermodel.HSSFSheet#createRow(short)
146       */
147  
148      protected HSSFRow(Workbook book, Sheet sheet, RowRecord record)
149      {
150          //this.rowNum = rowNum;
151          cells = new HashMap();   // ArrayList(INITIAL_CAPACITY);
152          this.book = book;
153          this.sheet = sheet;
154          row = record;
155  
156          // row.setHeight(record.getHeight());
157          // row.setRowNumber(rowNum);
158          setRowNum(record.getRowNumber());
159  
160  //        addColumns(book, sheet, record);
161      }
162  
163      /**
164       * Use this to create new cells within the row and return it.
165       * <p>
166       * The cell that is returned is a CELL_TYPE_BLANK. The type can be changed
167       * either through calling <code>setCellValue</code> or <code>setCellType</code>.
168       *
169       * @param column - the column number this cell represents
170       *
171       * @return HSSFCell a high level representation of the created cell.
172       */
173  
174      public HSSFCell createCell(short column)
175      {
176          HSSFCell cell = new HSSFCell(book, sheet, getRowNum(), column);
177  
178          addCell(cell);
179          sheet.addValueRecord(getRowNum(), cell.getCellValueRecord());
180          return cell;
181      }
182  
183      /**
184       * Use this to create new cells within the row and return it.
185       * <p>
186       * The cell that is returned is a CELL_TYPE_BLANK. The type can be changed
187       * either through calling setCellValue or setCellType.
188       *
189       * @param column - the column number this cell represents
190       *
191       * @return HSSFCell a high level representation of the created cell.
192       * @deprecated As of 22-Jan-2002 use createCell(short) and use setCellValue to
193       * specify the type lazily.
194       */
195  
196      public HSSFCell createCell(short column, int type)
197      {
198          HSSFCell cell = new HSSFCell(book, sheet, getRowNum(), column, type);
199  
200          addCell(cell);
201          sheet.addValueRecord(getRowNum(), cell.getCellValueRecord());
202          return cell;
203      }
204  
205      /**
206       * remove the HSSFCell from this row.
207       * @param cell to remove
208       */
209      public void removeCell(HSSFCell cell)
210      {
211          CellValueRecordInterface cval = cell.getCellValueRecord();
212  
213          sheet.removeValueRecord(getRowNum(), cval);
214          cells.remove(new Integer(cell.getCellNum()));
215  
216          if (cell.getCellNum() == row.getLastCol())
217          {
218              row.setLastCol(findLastCell(row.getLastCol()));
219          }
220          if (cell.getCellNum() == row.getFirstCol())
221          {
222              row.setFirstCol(findFirstCell(row.getFirstCol()));
223          }
224      }
225  
226      /**
227       * create a high level HSSFCell object from an existing low level record.  Should
228       * only be called from HSSFSheet or HSSFRow itself.
229       * @param cell low level cell to create the high level representation from
230       * @return HSSFCell representing the low level record passed in
231       */
232  
233      protected HSSFCell createCellFromRecord(CellValueRecordInterface cell)
234      {
235          HSSFCell hcell = new HSSFCell(book, sheet, getRowNum(), cell);
236  
237          addCell(hcell);
238  
239          // sheet.addValueRecord(getRowNum(),cell.getCellValueRecord());
240          return hcell;
241      }
242  
243      /**
244       * set the row number of this row.
245       * @param rowNum  the row number (0-based)
246       */
247  
248      //public void setRowNum(short rowNum)
249      public void setRowNum(int rowNum)
250      {
251          this.rowNum = rowNum;
252          if (row != null)
253          {
254              row.setRowNumber(rowNum);   // used only for KEY comparison (HSSFRow)
255          }
256      }
257  
258      /**
259       * get row number this row represents
260       * @return the row number (0 based)
261       */
262  
263      //public short getRowNum()
264      public int getRowNum()
265      {
266          return rowNum;
267      }
268  
269      /**
270       * used internally to add a cell.
271       */
272  
273      private void addCell(HSSFCell cell)
274      {
275          if (row.getFirstCol() == -1)
276          {
277              row.setFirstCol(cell.getCellNum());
278          }
279          if (row.getLastCol() == -1)
280          {
281              row.setLastCol(cell.getCellNum());
282          }
283          cells.put(new Integer(cell.getCellNum()), cell);
284  
285          if (cell.getCellNum() < row.getFirstCol())
286          {
287              row.setFirstCol(cell.getCellNum());
288          }
289          if (cell.getCellNum() > row.getLastCol())
290          {
291              row.setLastCol(cell.getCellNum());
292          }
293      }
294  
295      /**
296       * get the hssfcell representing a given column (logical cell) 0-based.  If you
297       * ask for a cell that is not defined....you get a null.
298       *
299       * @param cellnum  0 based column number
300       * @return HSSFCell representing that column or null if undefined.
301       */
302  
303      public HSSFCell getCell(short cellnum)
304      {
305  
306  /*        for (int k = 0; k < cells.size(); k++)
307          {
308              HSSFCell cell = ( HSSFCell ) cells.get(k);
309  
310              if (cell.getCellNum() == cellnum)
311              {
312                  return cell;
313              }
314          }*/
315          return (HSSFCell) cells.get(new Integer(cellnum));
316      }
317  
318      /**
319       * get the number of the first cell contained in this row.
320       * @return short representing the first logical cell in the row
321       */
322  
323      public short getFirstCellNum()
324      {
325          if (getPhysicalNumberOfCells() == 0)
326              return -1;
327          else
328              return row.getFirstCol();
329      }
330  
331      /**
332       * get the number of the last cell contained in this row.
333       * @return short representing the last logical cell in the row
334       */
335  
336      public short getLastCellNum()
337      {
338          if (getPhysicalNumberOfCells() == 0)
339              return -1;
340          else
341              return row.getLastCol();
342      }
343  
344  
345      /**
346       * gets the number of defined cells (NOT number of cells in the actual row!).
347       * That is to say if only columns 0,4,5 have values then there would be 3.
348       * @return int representing the number of defined cells in the row.
349       */
350  
351      public int getPhysicalNumberOfCells()
352      {
353          if (cells == null)
354          {
355              return 0;   // shouldn't be possible but it is due to missing API support for BLANK/MULBLANK
356          }
357          return cells.size();
358      }
359  
360      /**
361       * set the row's height or set to ff (-1) for undefined/default-height.  Set the height in "twips" or
362       * 1/20th of a point.
363       * @param height  rowheight or 0xff for undefined (use sheet default)
364       */
365  
366      public void setHeight(short height)
367      {
368  
369          // row.setOptionFlags(
370          row.setBadFontHeight(true);
371          row.setHeight(height);
372      }
373  
374      /**
375       * set the row's height in points.
376       * @param height  row height in points
377       */
378  
379      public void setHeightInPoints(float height)
380      {
381  
382          // row.setOptionFlags(
383          row.setBadFontHeight(true);
384          row.setHeight((short) (height * 20));
385      }
386  
387      /**
388       * get the row's height or ff (-1) for undefined/default-height in twips (1/20th of a point)
389       * @return rowheight or 0xff for undefined (use sheet default)
390       */
391  
392      public short getHeight()
393      {
394          return row.getHeight();
395      }
396  
397      /**
398       * get the row's height or ff (-1) for undefined/default-height in points (20*getHeight())
399       * @return rowheight or 0xff for undefined (use sheet default)
400       */
401  
402      public float getHeightInPoints()
403      {
404          return (row.getHeight() / 20);
405      }
406  
407      /**
408       * get the lowlevel RowRecord represented by this object - should only be called
409       * by other parts of the high level API
410       *
411       * @return RowRecord this row represents
412       */
413  
414      protected RowRecord getRowRecord()
415      {
416          return row;
417      }
418  
419      /**
420       * used internally to refresh the "last cell" when the last cell is removed.
421       */
422  
423      private short findLastCell(short lastcell)
424      {
425          short cellnum = (short) (lastcell - 1);
426          HSSFCell r = getCell(cellnum);
427  
428          while (r == null && cellnum >= 0)
429          {
430              r = getCell(--cellnum);
431          }
432          return cellnum;
433      }
434  
435      /**
436       * used internally to refresh the "first cell" when the first cell is removed.
437       */
438  
439      private short findFirstCell(short firstcell)
440      {
441          short cellnum = (short) (firstcell + 1);
442          HSSFCell r = getCell(cellnum);
443  
444          while (r == null && cellnum <= getLastCellNum())
445          {
446              r = getCell(++cellnum);
447          }
448          if (cellnum > getLastCellNum())
449              return -1;
450          return cellnum;
451      }
452  
453      /**
454       * @return cell iterator of the physically defined cells.  Note element 4 may
455       * actually be row cell depending on how many are defined!
456       */
457  
458      public Iterator cellIterator()
459      {
460          return cells.values().iterator();
461      }
462  
463      public int compareTo(Object obj)
464      {
465          HSSFRow loc = (HSSFRow) obj;
466  
467          if (this.getRowNum() == loc.getRowNum())
468          {
469              return 0;
470          }
471          if (this.getRowNum() < loc.getRowNum())
472          {
473              return -1;
474          }
475          if (this.getRowNum() > loc.getRowNum())
476          {
477              return 1;
478          }
479          return -1;
480      }
481  
482      public boolean equals(Object obj)
483      {
484          if (!(obj instanceof HSSFRow))
485          {
486              return false;
487          }
488          HSSFRow loc = (HSSFRow) obj;
489  
490          if (this.getRowNum() == loc.getRowNum())
491          {
492              return true;
493          }
494          return false;
495      }
496  }
497