572 lines
16 KiB
Java
572 lines
16 KiB
Java
/* ====================================================================
|
|
Licensed to the Apache Software Foundation (ASF) under one or more
|
|
contributor license agreements. See the NOTICE file distributed with
|
|
this work for additional information regarding copyright ownership.
|
|
The ASF licenses this file to You under the Apache License, Version 2.0
|
|
(the "License"); you may not use this file except in compliance with
|
|
the License. You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
==================================================================== */
|
|
|
|
package org.apache.poi.hssf.usermodel;
|
|
|
|
import java.util.Iterator;
|
|
import java.util.NoSuchElementException;
|
|
|
|
import org.apache.poi.hssf.model.Sheet;
|
|
import org.apache.poi.hssf.model.Workbook;
|
|
import org.apache.poi.hssf.record.CellValueRecordInterface;
|
|
import org.apache.poi.hssf.record.RowRecord;
|
|
|
|
/**
|
|
* High level representation of a row of a spreadsheet.
|
|
*
|
|
* Only rows that have cells should be added to a Sheet.
|
|
* @version 1.0-pre
|
|
* @author Andrew C. Oliver (acoliver at apache dot org)
|
|
* @author Glen Stampoultzis (glens at apache.org)
|
|
*/
|
|
public final class HSSFRow implements Comparable {
|
|
|
|
// used for collections
|
|
public final static int INITIAL_CAPACITY = 5;
|
|
//private short rowNum;
|
|
private int rowNum;
|
|
private HSSFCell[] cells=new HSSFCell[INITIAL_CAPACITY];
|
|
// private short firstcell = -1;
|
|
// private short lastcell = -1;
|
|
|
|
/**
|
|
* reference to low level representation
|
|
*/
|
|
|
|
private RowRecord row;
|
|
|
|
/**
|
|
* reference to containing low level Workbook
|
|
*/
|
|
|
|
private HSSFWorkbook book;
|
|
|
|
/**
|
|
* reference to containing Sheet
|
|
*/
|
|
|
|
private Sheet sheet;
|
|
|
|
protected HSSFRow()
|
|
{
|
|
}
|
|
|
|
/**
|
|
* Creates new HSSFRow from scratch. Only HSSFSheet should do this.
|
|
*
|
|
* @param book low-level Workbook object containing the sheet that contains this row
|
|
* @param sheet low-level Sheet object that contains this Row
|
|
* @param rowNum the row number of this row (0 based)
|
|
* @see org.apache.poi.hssf.usermodel.HSSFSheet#createRow(int)
|
|
*/
|
|
|
|
//protected HSSFRow(Workbook book, Sheet sheet, short rowNum)
|
|
protected HSSFRow(HSSFWorkbook book, Sheet sheet, int rowNum)
|
|
{
|
|
this.rowNum = rowNum;
|
|
this.book = book;
|
|
this.sheet = sheet;
|
|
row = new RowRecord();
|
|
row.setOptionFlags( (short)0x100 ); // seems necessary for outlining to work.
|
|
row.setHeight((short) 0xff);
|
|
row.setLastCol((short) -1);
|
|
row.setFirstCol((short) -1);
|
|
|
|
setRowNum(rowNum);
|
|
}
|
|
|
|
/**
|
|
* Creates an HSSFRow from a low level RowRecord object. Only HSSFSheet should do
|
|
* this. HSSFSheet uses this when an existing file is read in.
|
|
*
|
|
* @param book low-level Workbook object containing the sheet that contains this row
|
|
* @param sheet low-level Sheet object that contains this Row
|
|
* @param record the low level api object this row should represent
|
|
* @see org.apache.poi.hssf.usermodel.HSSFSheet#createRow(int)
|
|
*/
|
|
|
|
protected HSSFRow(HSSFWorkbook book, Sheet sheet, RowRecord record)
|
|
{
|
|
this.book = book;
|
|
this.sheet = sheet;
|
|
row = record;
|
|
|
|
setRowNum(record.getRowNumber());
|
|
}
|
|
|
|
/**
|
|
* Use this to create new cells within the row and return it.
|
|
* <p>
|
|
* The cell that is returned is a CELL_TYPE_BLANK. The type can be changed
|
|
* either through calling <code>setCellValue</code> or <code>setCellType</code>.
|
|
*
|
|
* @param column - the column number this cell represents
|
|
*
|
|
* @return HSSFCell a high level representation of the created cell.
|
|
*/
|
|
|
|
public HSSFCell createCell(short column)
|
|
{
|
|
return this.createCell(column,HSSFCell.CELL_TYPE_BLANK);
|
|
}
|
|
|
|
/**
|
|
* Use this to create new cells within the row and return it.
|
|
* <p>
|
|
* The cell that is returned is a CELL_TYPE_BLANK. The type can be changed
|
|
* either through calling setCellValue or setCellType.
|
|
*
|
|
* @param column - the column number this cell represents
|
|
*
|
|
* @return HSSFCell a high level representation of the created cell.
|
|
*/
|
|
|
|
public HSSFCell createCell(short column, int type)
|
|
{
|
|
HSSFCell cell = new HSSFCell(book, sheet, getRowNum(), column, type);
|
|
|
|
addCell(cell);
|
|
sheet.addValueRecord(getRowNum(), cell.getCellValueRecord());
|
|
return cell;
|
|
}
|
|
|
|
/**
|
|
* remove the HSSFCell from this row.
|
|
* @param cell to remove
|
|
*/
|
|
public void removeCell(HSSFCell cell) {
|
|
if(cell == null) {
|
|
throw new IllegalArgumentException("cell must not be null");
|
|
}
|
|
removeCell(cell, true);
|
|
}
|
|
private void removeCell(HSSFCell cell, boolean alsoRemoveRecords) {
|
|
|
|
short column=cell.getCellNum();
|
|
if(column < 0) {
|
|
throw new RuntimeException("Negative cell indexes not allowed");
|
|
}
|
|
if(column >= cells.length || cell != cells[column]) {
|
|
throw new RuntimeException("Specified cell is not from this row");
|
|
}
|
|
cells[column]=null;
|
|
|
|
if(alsoRemoveRecords) {
|
|
CellValueRecordInterface cval = cell.getCellValueRecord();
|
|
sheet.removeValueRecord(getRowNum(), cval);
|
|
}
|
|
|
|
if (cell.getCellNum()+1 == row.getLastCol()) {
|
|
row.setLastCol((short) (findLastCell(row.getLastCol())+1));
|
|
}
|
|
if (cell.getCellNum() == row.getFirstCol()) {
|
|
row.setFirstCol(findFirstCell(row.getFirstCol()));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* create a high level HSSFCell object from an existing low level record. Should
|
|
* only be called from HSSFSheet or HSSFRow itself.
|
|
* @param cell low level cell to create the high level representation from
|
|
* @return HSSFCell representing the low level record passed in
|
|
*/
|
|
|
|
protected HSSFCell createCellFromRecord(CellValueRecordInterface cell)
|
|
{
|
|
HSSFCell hcell = new HSSFCell(book, sheet, getRowNum(), cell);
|
|
|
|
addCell(hcell);
|
|
|
|
// sheet.addValueRecord(getRowNum(),cell.getCellValueRecord());
|
|
return hcell;
|
|
}
|
|
|
|
/**
|
|
* set the row number of this row.
|
|
* @param rowNum the row number (0-based)
|
|
* @throws IndexOutOfBoundsException if the row number is not within the range 0-65535.
|
|
*/
|
|
|
|
//public void setRowNum(short rowNum)
|
|
public void setRowNum(int rowNum)
|
|
{
|
|
if ((rowNum < 0) || (rowNum > RowRecord.MAX_ROW_NUMBER))
|
|
throw new IndexOutOfBoundsException("Row number must be between 0 and "+RowRecord.MAX_ROW_NUMBER+", was <"+rowNum+">");
|
|
this.rowNum = rowNum;
|
|
if (row != null)
|
|
{
|
|
row.setRowNumber(rowNum); // used only for KEY comparison (HSSFRow)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* get row number this row represents
|
|
* @return the row number (0 based)
|
|
*/
|
|
|
|
//public short getRowNum()
|
|
public int getRowNum()
|
|
{
|
|
return rowNum;
|
|
}
|
|
|
|
/**
|
|
* Returns the rows outline level. Increased as you
|
|
* put it into more groups (outlines), reduced as
|
|
* you take it out of them.
|
|
* TODO - Should this really be public?
|
|
*/
|
|
protected int getOutlineLevel() {
|
|
return row.getOutlineLevel();
|
|
}
|
|
|
|
/**
|
|
* Moves the supplied cell to a new column, which
|
|
* must not already have a cell there!
|
|
* @param cell The cell to move
|
|
* @param newColumn The new column number (0 based)
|
|
*/
|
|
public void moveCell(HSSFCell cell, short newColumn) {
|
|
// Ensure the destination is free
|
|
if(cells.length > newColumn && cells[newColumn] != null) {
|
|
throw new IllegalArgumentException("Asked to move cell to column " + newColumn + " but there's already a cell there");
|
|
}
|
|
|
|
// Check it's one of ours
|
|
if(! cells[cell.getCellNum()].equals(cell)) {
|
|
throw new IllegalArgumentException("Asked to move a cell, but it didn't belong to our row");
|
|
}
|
|
|
|
// Move the cell to the new position
|
|
// (Don't remove the records though)
|
|
removeCell(cell, false);
|
|
cell.updateCellNum(newColumn);
|
|
addCell(cell);
|
|
}
|
|
|
|
/**
|
|
* used internally to add a cell.
|
|
*/
|
|
private void addCell(HSSFCell cell) {
|
|
|
|
short column=cell.getCellNum();
|
|
// re-allocate cells array as required.
|
|
if(column>=cells.length) {
|
|
HSSFCell[] oldCells=cells;
|
|
int newSize=oldCells.length*2;
|
|
if(newSize<column+1) {
|
|
newSize=column+1;
|
|
}
|
|
cells=new HSSFCell[newSize];
|
|
System.arraycopy(oldCells,0,cells,0,oldCells.length);
|
|
}
|
|
cells[column]=cell;
|
|
|
|
// fix up firstCol and lastCol indexes
|
|
if (row.getFirstCol() == -1 || column < row.getFirstCol()) {
|
|
row.setFirstCol(column);
|
|
}
|
|
|
|
if (row.getLastCol() == -1 || column >= row.getLastCol()) {
|
|
row.setLastCol((short) (column+1)); // +1 -> for one past the last index
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get the hssfcell representing a given column (logical cell)
|
|
* 0-based. If you ask for a cell that is not defined....
|
|
* you get a null.
|
|
* Short method signature provided to retain binary
|
|
* compatibility.
|
|
*
|
|
* @param cellnum 0 based column number
|
|
* @return HSSFCell representing that column or null if undefined.
|
|
*/
|
|
public HSSFCell getCell(short cellnum) {
|
|
int ushortCellNum = cellnum & 0x0000FFFF; // avoid sign extension
|
|
return getCell(ushortCellNum);
|
|
}
|
|
/**
|
|
* Get the hssfcell representing a given column (logical cell)
|
|
* 0-based. If you ask for a cell that is not defined....
|
|
* you get a null.
|
|
*
|
|
* @param cellnum 0 based column number
|
|
* @return HSSFCell representing that column or null if undefined.
|
|
*/
|
|
public HSSFCell getCell(int cellnum) {
|
|
if(cellnum<0||cellnum>=cells.length) return null;
|
|
return cells[cellnum];
|
|
}
|
|
|
|
/**
|
|
* get the number of the first cell contained in this row.
|
|
* @return short representing the first logical cell in the row, or -1 if the row does not contain any cells.
|
|
*/
|
|
|
|
public short getFirstCellNum()
|
|
{
|
|
if (getPhysicalNumberOfCells() == 0)
|
|
return -1;
|
|
else
|
|
return row.getFirstCol();
|
|
}
|
|
|
|
/**
|
|
* Gets the index of the last cell contained in this row <b>PLUS ONE</b>. The result also
|
|
* happens to be the 1-based column number of the last cell. This value can be used as a
|
|
* standard upper bound when iterating over cells:
|
|
* <pre>
|
|
* short minColIx = row.getFirstCellNum();
|
|
* short maxColIx = row.getLastCellNum();
|
|
* for(short colIx=minColIx; colIx<maxColIx; colIx++) {
|
|
* HSSFCell cell = row.getCell(colIx);
|
|
* if(cell == null) {
|
|
* continue;
|
|
* }
|
|
* //... do something with cell
|
|
* }
|
|
* </pre>
|
|
*
|
|
* @return short representing the last logical cell in the row <b>PLUS ONE</b>, or -1 if the
|
|
* row does not contain any cells.
|
|
*/
|
|
public short getLastCellNum() {
|
|
if (getPhysicalNumberOfCells() == 0) {
|
|
return -1;
|
|
}
|
|
return row.getLastCol();
|
|
}
|
|
|
|
|
|
/**
|
|
* gets the number of defined cells (NOT number of cells in the actual row!).
|
|
* That is to say if only columns 0,4,5 have values then there would be 3.
|
|
* @return int representing the number of defined cells in the row.
|
|
*/
|
|
|
|
public int getPhysicalNumberOfCells()
|
|
{
|
|
int count=0;
|
|
for(int i=0;i<cells.length;i++)
|
|
{
|
|
if(cells[i]!=null) count++;
|
|
}
|
|
return count;
|
|
}
|
|
|
|
/**
|
|
* set the row's height or set to ff (-1) for undefined/default-height. Set the height in "twips" or
|
|
* 1/20th of a point.
|
|
* @param height rowheight or 0xff for undefined (use sheet default)
|
|
*/
|
|
|
|
public void setHeight(short height)
|
|
{
|
|
|
|
// row.setOptionFlags(
|
|
row.setBadFontHeight(true);
|
|
row.setHeight(height);
|
|
}
|
|
|
|
/**
|
|
* set whether or not to display this row with 0 height
|
|
* @param zHeight height is zero or not.
|
|
*/
|
|
public void setZeroHeight(boolean zHeight) {
|
|
row.setZeroHeight(zHeight);
|
|
}
|
|
|
|
/**
|
|
* get whether or not to display this row with 0 height
|
|
* @return - zHeight height is zero or not.
|
|
*/
|
|
public boolean getZeroHeight() {
|
|
return row.getZeroHeight();
|
|
}
|
|
|
|
/**
|
|
* set the row's height in points.
|
|
* @param height row height in points
|
|
*/
|
|
|
|
public void setHeightInPoints(float height)
|
|
{
|
|
|
|
// row.setOptionFlags(
|
|
row.setBadFontHeight(true);
|
|
row.setHeight((short) (height * 20));
|
|
}
|
|
|
|
/**
|
|
* get the row's height or ff (-1) for undefined/default-height in twips (1/20th of a point)
|
|
* @return rowheight or 0xff for undefined (use sheet default)
|
|
*/
|
|
|
|
public short getHeight()
|
|
{
|
|
return row.getHeight();
|
|
}
|
|
|
|
/**
|
|
* get the row's height or ff (-1) for undefined/default-height in points (20*getHeight())
|
|
* @return rowheight or 0xff for undefined (use sheet default)
|
|
*/
|
|
|
|
public float getHeightInPoints()
|
|
{
|
|
return (row.getHeight() / 20);
|
|
}
|
|
|
|
/**
|
|
* get the lowlevel RowRecord represented by this object - should only be called
|
|
* by other parts of the high level API
|
|
*
|
|
* @return RowRecord this row represents
|
|
*/
|
|
|
|
protected RowRecord getRowRecord()
|
|
{
|
|
return row;
|
|
}
|
|
|
|
/**
|
|
* used internally to refresh the "last cell" when the last cell is removed.
|
|
*/
|
|
|
|
private short findLastCell(short lastcell)
|
|
{
|
|
short cellnum = (short) (lastcell - 1);
|
|
HSSFCell r = getCell(cellnum);
|
|
|
|
while (r == null && cellnum >= 0)
|
|
{
|
|
r = getCell(--cellnum);
|
|
}
|
|
return cellnum;
|
|
}
|
|
|
|
/**
|
|
* used internally to refresh the "first cell" when the first cell is removed.
|
|
*/
|
|
|
|
private short findFirstCell(short firstcell)
|
|
{
|
|
short cellnum = (short) (firstcell + 1);
|
|
HSSFCell r = getCell(cellnum);
|
|
|
|
while (r == null && cellnum <= getLastCellNum())
|
|
{
|
|
r = getCell(++cellnum);
|
|
}
|
|
if (cellnum > getLastCellNum())
|
|
return -1;
|
|
return cellnum;
|
|
}
|
|
|
|
/**
|
|
* @return cell iterator of the physically defined cells. Note element 4 may
|
|
* actually be row cell depending on how many are defined!
|
|
*/
|
|
public Iterator cellIterator()
|
|
{
|
|
return new CellIterator();
|
|
}
|
|
/**
|
|
* Alias for {@link CellIterator} to allow
|
|
* foreach loops
|
|
*/
|
|
public Iterator iterator() {
|
|
return cellIterator();
|
|
}
|
|
|
|
private class CellIterator implements Iterator
|
|
{
|
|
int thisId=-1;
|
|
int nextId=-1;
|
|
|
|
public CellIterator()
|
|
{
|
|
findNext();
|
|
}
|
|
|
|
public boolean hasNext() {
|
|
return nextId<cells.length;
|
|
}
|
|
|
|
public Object next() {
|
|
if (!hasNext())
|
|
throw new NoSuchElementException("At last element");
|
|
HSSFCell cell=cells[nextId];
|
|
thisId=nextId;
|
|
findNext();
|
|
return cell;
|
|
}
|
|
|
|
public void remove() {
|
|
if (thisId == -1)
|
|
throw new IllegalStateException("remove() called before next()");
|
|
cells[thisId]=null;
|
|
}
|
|
|
|
private void findNext()
|
|
{
|
|
int i=nextId+1;
|
|
for(;i<cells.length;i++)
|
|
{
|
|
if(cells[i]!=null) break;
|
|
}
|
|
nextId=i;
|
|
}
|
|
|
|
}
|
|
|
|
public int compareTo(Object obj)
|
|
{
|
|
HSSFRow loc = (HSSFRow) obj;
|
|
|
|
if (this.getRowNum() == loc.getRowNum())
|
|
{
|
|
return 0;
|
|
}
|
|
if (this.getRowNum() < loc.getRowNum())
|
|
{
|
|
return -1;
|
|
}
|
|
if (this.getRowNum() > loc.getRowNum())
|
|
{
|
|
return 1;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
public boolean equals(Object obj)
|
|
{
|
|
if (!(obj instanceof HSSFRow))
|
|
{
|
|
return false;
|
|
}
|
|
HSSFRow loc = (HSSFRow) obj;
|
|
|
|
if (this.getRowNum() == loc.getRowNum())
|
|
{
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
}
|