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