1. important algorithmic improvements of XSSFRow and XSSFSheet, removed LinkedList in favor of TreeMap, that allowed O(Log(N)) performance instead of O(N) when adding new rows and cells2. Revised cell value accessors in XSSFCell. Now both HSSF and XSSF handle various cell types equally. The same exceptions are thrown in case of type mismatch, same behaviour when setting nulls, etc.

3. Moved FormulaError codes out of the Cell interface into enum. Interface isn't a proper place for it
4. Finally finished javadoc on XSSFRow and XSSFCell


git-svn-id: https://svn.apache.org/repos/asf/poi/branches/ooxml@707445 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Yegor Kozlov 2008-10-23 18:57:28 +00:00
parent b15942d4a8
commit dfb6d9f84f
9 changed files with 1095 additions and 636 deletions

View File

@ -70,26 +70,6 @@ public interface Cell {
public final static int CELL_TYPE_ERROR = 5;
public final static class CELL_ERROR_TYPE {
private final byte type;
private final String repr;
private CELL_ERROR_TYPE(int type, String repr) {
this.type = (byte)type;
this.repr = repr;
}
public byte getType() { return type; }
public String getStringRepr() { return repr; }
}
public static final CELL_ERROR_TYPE ERROR_NULL = new CELL_ERROR_TYPE(0, "#NULL!");
public static final CELL_ERROR_TYPE ERROR_DIV0 = new CELL_ERROR_TYPE(7, "#DIV/0!");
public static final CELL_ERROR_TYPE ERROR_VALUE = new CELL_ERROR_TYPE(15, "#VALUE!");
public static final CELL_ERROR_TYPE ERROR_REF = new CELL_ERROR_TYPE(23, "#REF!");
public static final CELL_ERROR_TYPE ERROR_NAME = new CELL_ERROR_TYPE(29, "#NAME?");
public static final CELL_ERROR_TYPE ERROR_NUM = new CELL_ERROR_TYPE(36, "#NUM!");
public static final CELL_ERROR_TYPE ERROR_NA = new CELL_ERROR_TYPE(42, "#N/A");
/**
* set the cell's number within the row (0 based)
* @param num short the cell number
@ -239,12 +219,12 @@ public interface Cell {
byte getErrorCellValue();
/**
* set the style for the cell. The style should be an HSSFCellStyle created/retreived from
* the HSSFWorkbook.
* set the style for the cell. The style should be an CellStyle created/retreived from
* the Workbook.
*
* @param style reference contained in the workbook
* @see org.apache.poi.hssf.usermodel.HSSFWorkbook#createCellStyle()
* @see org.apache.poi.hssf.usermodel.HSSFWorkbook#getCellStyleAt(short)
* @see Workbook#createCellStyle()
* @see Workbook#getCellStyleAt(short)
*/
void setCellStyle(CellStyle style);
@ -252,7 +232,7 @@ public interface Cell {
/**
* get the style for the cell. This is a reference to a cell style contained in the workbook
* object.
* @see org.apache.poi.hssf.usermodel.HSSFWorkbook#getCellStyleAt(short)
* @see Workbook#getCellStyleAt(short)
*/
CellStyle getCellStyle();
@ -262,20 +242,6 @@ public interface Cell {
*/
void setAsActiveCell();
/**
* Returns a string representation of the cell
*
* This method returns a simple representation,
* anthing more complex should be in user code, with
* knowledge of the semantics of the sheet being processed.
*
* Formula cells return the formula string,
* rather than the formula result.
* Dates are displayed in dd-MMM-yyyy format
* Errors are displayed as #ERR<errIdx>
*/
String toString();
/**
* Assign a comment to this cell
*
@ -295,12 +261,12 @@ public interface Cell {
*
* @return hyperlink associated with this cell or null if not found
*/
public Hyperlink getHyperlink();
Hyperlink getHyperlink();
/**
* Assign a hypelrink to this cell
*
* @param link hypelrink associated with this cell
*/
public void setHyperlink(Hyperlink link);
void setHyperlink(Hyperlink link);
}

View File

@ -0,0 +1,140 @@
/* ====================================================================
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.ss.usermodel;
import java.util.Map;
import java.util.HashMap;
/**
* Enumerates error values in SpreadsheetML formula calculations.
*
* @author Yegor Kozlov
*/
public enum FormulaError {
/**
* Intended to indicate when two areas are required to intersect, but do not.
* <p>Example:
* In the case of SUM(B1 C1), the space between B1 and C1 is treated as the binary
* intersection operator, when a comma was intended. end example]
* </p>
*/
NULL(0x00, "#NULL!"),
/**
* Intended to indicate when any number, including zero, is divided by zero.
* Note: However, any error code divided by zero results in that error code.
*/
DIV0(0x07, "#DIV/0!"),
/**
* Intended to indicate when an incompatible type argument is passed to a function, or
* an incompatible type operand is used with an operator.
* <p>Example:
* In the case of a function argument, text was expected, but a number was provided
* </p>
*/
VALUE(0x0F, "#VALUE!"),
/**
* Intended to indicate when a cell reference is invalid.
* <p>Example:
* If a formula contains a reference to a cell, and then the row or column containing that cell is deleted,
* a #REF! error results. If a worksheet does not support 20,001 columns,
* OFFSET(A1,0,20000) will result in a #REF! error.
* </p>
*/
REF(0x1D, "#REF!"),
/**
* Intended to indicate when what looks like a name is used, but no such name has been defined.
* <p>Example:
* XYZ/3, where XYZ is not a defined name. Total is & A10,
* where neither Total nor is is a defined name. Presumably, "Total is " & A10
* was intended. SUM(A1C10), where the range A1:C10 was intended.
* </p>
*/
NAME(0x1D, "#NAME?"),
/**
* Intended to indicate when an argument to a function has a compatible type, but has a
* value that is outside the domain over which that function is defined. (This is known as
* a domain error.)
* <p>Example:
* Certain calls to ASIN, ATANH, FACT, and SQRT might result in domain errors.
* </p>
* Intended to indicate that the result of a function cannot be represented in a value of
* the specified type, typically due to extreme magnitude. (This is known as a range
* error.)
* <p>Example: FACT(1000) might result in a range error. </p>
*/
NUM(0x24, "#NUM!"),
/**
* Intended to indicate when a designated value is not available.
* <p>Example:
* Some functions, such as SUMX2MY2, perform a series of operations on corresponding
* elements in two arrays. If those arrays do not have the same number of elements, then
* for some elements in the longer array, there are no corresponding elements in the
* shorter one; that is, one or more values in the shorter array are not available.
* </p>
* This error value can be produced by calling the function NA
*/
NA(0x2A, "#N/A");
private byte type;
private String repr;
private FormulaError(int type, String repr) {
this.type = (byte) type;
this.repr = repr;
}
/**
* @return numeric code of the error
*/
public int getCode() {
return type;
}
/**
* @return string representation of the error
*/
public String getString() {
return repr;
}
private static Map<String, FormulaError> smap = new HashMap<String, FormulaError>();
private static Map<Integer, FormulaError> imap = new HashMap<Integer, FormulaError>();
static{
for (FormulaError error : values()) {
imap.put(error.getCode(), error);
smap.put(error.getString(), error);
}
}
public static FormulaError forInt(int type){
FormulaError err = imap.get(type);
if(err == null) throw new IllegalArgumentException("Unknown error type: " + type);
return err;
}
public static FormulaError forString(String code){
FormulaError err = smap.get(code);
if(err == null) throw new IllegalArgumentException("Unknown error code: " + code);
return err;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -866,6 +866,7 @@ public class XSSFCellStyle implements CellStyle, Cloneable {
* @param fmt the index of a data format
*/
public void setDataFormat(short fmt) {
cellXf.setApplyNumberFormat(true);
cellXf.setNumFmtId((long)fmt);
}

View File

@ -17,11 +17,8 @@
package org.apache.poi.xssf.usermodel;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.*;
import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTCell;
@ -30,28 +27,38 @@ import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTRow;
/**
* High level representation of a row of a spreadsheet.
*/
public class XSSFRow implements Row, Comparable {
private CTRow row;
private List<Cell> cells;
private XSSFSheet sheet;
public class XSSFRow implements Row, Comparable<XSSFRow> {
/**
* Create a new XSSFRow.
*
* @param row The underlying XMLBeans row.
* @param sheet The parent sheet.
* the xml bean containing all cell definitions for this row
*/
public XSSFRow(CTRow row, XSSFSheet sheet) {
private final CTRow row;
/**
* Cells of this row keyed by their column indexes.
* The TreeMap ensures that the cells are ordered by columnIndex in the ascending order.
*/
private final TreeMap<Integer, Cell> cells;
/**
* the parent sheet
*/
private final XSSFSheet sheet;
/**
* Construct a XSSFRow.
*
* @param row the xml bean containing all cell definitions for this row.
* @param sheet the parent sheet.
*/
protected XSSFRow(CTRow row, XSSFSheet sheet) {
this.row = row;
this.sheet = sheet;
this.cells = new LinkedList<Cell>();
this.cells = new TreeMap<Integer, Cell>();
for (CTCell c : row.getCArray()) {
this.cells.add(new XSSFCell(this, c));
XSSFCell cell = new XSSFCell(this, c);
this.cells.put(cell.getColumnIndex(), cell);
}
}
/**
@ -62,25 +69,39 @@ public class XSSFRow implements Row, Comparable {
public XSSFSheet getSheet() {
return this.sheet;
}
/**
* @return Cell iterator of the physically defined cells. Note element 4 may
* actually be row cell depending on how many are defined!
* Cell iterator over the physically defined cells:
* <blockquote><pre>
* for (Iterator<Cell> it = row.cellIterator(); it.hasNext(); ) {
* Cell cell = it.next();
* ...
* }
* </pre></blockquote>
*
* @return an iterator over cells in this row.
*/
public Iterator<Cell> cellIterator() {
return cells.iterator();
return cells.values().iterator();
}
/**
* Alias for {@link #cellIterator()} to allow
* foreach loops
* Alias for {@link #cellIterator()} to allow foreach loops:
* <blockquote><pre>
* for(Cell cell : row){
* ...
* }
* </pre></blockquote>
*
* @return an iterator over cells in this row.
*/
public Iterator<Cell> iterator() {
return cellIterator();
}
/**
* Compares two <code>XSSFRow</code> objects.
* Compares two <code>XSSFRow</code> objects. Two rows are equal if they belong to the same worksheet and
* their row indexes are equal.
*
* @param row the <code>XSSFRow</code> to be compared.
* @return the value <code>0</code> if the row number of this <code>XSSFRow</code> is
@ -89,84 +110,57 @@ public class XSSFRow implements Row, Comparable {
* than the row number of the argument <code>XSSFRow</code>; and a value greater
* than <code>0</code> if the row number of this this <code>XSSFRow</code> is numerically
* greater than the row number of the argument <code>XSSFRow</code>.
* @throws IllegalArgumentException if the argument row belongs to a different worksheet
*/
public int compareTo(Object row) {
public int compareTo(XSSFRow row) {
int thisVal = this.getRowNum();
int anotherVal = ((XSSFRow)row).getRowNum();
if(row.getSheet() != getSheet()) throw new IllegalArgumentException("The compared rows must belong to the same XSSFSheet");
int anotherVal = row.getRowNum();
return (thisVal < anotherVal ? -1 : (thisVal == anotherVal ? 0 : 1));
}
/**
* 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
* The cell that is returned is a {@link Cell#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
* </p>
* @param columnIndex - the column number this cell represents
* @return Cell a high level representation of the created cell.
* @throws IllegalArgumentException if columnIndex < 0
*/
public XSSFCell createCell(int column) {
return createCell(column, Cell.CELL_TYPE_BLANK);
}
/**
* Add a new empty cell to this row.
*
* @param column Cell column number.
* @param index Position where to insert cell.
* @param type cell type, one of Cell.CELL_TYPE_*
* @return The new cell.
*/
protected XSSFCell addCell(int column, int index, int type) {
CTCell ctcell = row.insertNewC(index);
XSSFCell xcell = new XSSFCell(this, ctcell);
xcell.setCellNum(column);
if (type != Cell.CELL_TYPE_BLANK) {
xcell.setCellType(type);
}
return xcell;
public XSSFCell createCell(int columnIndex) {
return createCell(columnIndex, Cell.CELL_TYPE_BLANK);
}
/**
* Use this to create new cells within the row and return it.
*
* @param column - the column number this cell represents
* @param columnIndex - the column number this cell represents
* @param type - the cell's data type
*
* @return XSSFCell a high level representation of the created cell.
* @throws IllegalArgumentException if columnIndex < 0 or if the specified cell type is invalid
* @see Cell#CELL_TYPE_BLANK
* @see Cell#CELL_TYPE_BOOLEAN
* @see Cell#CELL_TYPE_ERROR
* @see Cell#CELL_TYPE_FORMULA
* @see Cell#CELL_TYPE_NUMERIC
* @see Cell#CELL_TYPE_STRING
*/
public XSSFCell createCell(int column, int type) {
int index = 0;
for (Cell c : this.cells) {
if (c.getColumnIndex() == column) {
// Replace c with new Cell
XSSFCell xcell = addCell(column, index, type);
cells.set(index, xcell);
return xcell;
}
if (c.getColumnIndex() > column) {
XSSFCell xcell = addCell(column, index, type);
cells.add(index, xcell);
return xcell;
}
++index;
public XSSFCell createCell(int columnIndex, int type) {
if(columnIndex < 0) throw new IllegalArgumentException("columnIndex must be >= 0, was " + columnIndex);
CTCell ctcell = CTCell.Factory.newInstance();
XSSFCell xcell = new XSSFCell(this, ctcell);
xcell.setCellNum(columnIndex);
if (type != Cell.CELL_TYPE_BLANK) {
xcell.setCellType(type);
}
XSSFCell xcell = addCell(column, index, type);
cells.add(xcell);
cells.put(columnIndex, xcell);
return xcell;
}
private XSSFCell retrieveCell(int cellnum) {
Iterator<Cell> it = cellIterator();
for ( ; it.hasNext() ; ) {
Cell cell = it.next();
if (cell.getColumnIndex() == cellnum) {
return (XSSFCell)cell;
}
}
return null;
}
/**
* Returns the cell at the given (0 based) index,
* with the {@link MissingCellPolicy} from the parent Workbook.
@ -176,28 +170,33 @@ public class XSSFRow implements Row, Comparable {
public XSSFCell getCell(int cellnum) {
return getCell(cellnum, sheet.getWorkbook().getMissingCellPolicy());
}
/**
* Returns the cell at the given (0 based) index,
* with the specified {@link MissingCellPolicy}
* Returns the cell at the given (0 based) index, with the specified {@link MissingCellPolicy}
*
* @return the cell at the given (0 based) index
* @throws IllegalArgumentException if cellnum < 0 or the specified MissingCellPolicy is invalid
* @see Row#RETURN_NULL_AND_BLANK
* @see Row#RETURN_BLANK_AS_NULL
* @see Row#CREATE_NULL_AS_BLANK
*/
public XSSFCell getCell(int cellnum, MissingCellPolicy policy) {
XSSFCell cell = retrieveCell(cellnum);
if(cellnum < 0) throw new IllegalArgumentException("Cell index must be >= 0");
XSSFCell cell = (XSSFCell)cells.get(cellnum);
if(policy == RETURN_NULL_AND_BLANK) {
return cell;
}
if(policy == RETURN_BLANK_AS_NULL) {
if(cell == null) return cell;
if(cell.getCellType() == HSSFCell.CELL_TYPE_BLANK) {
if(cell.getCellType() == Cell.CELL_TYPE_BLANK) {
return null;
}
return cell;
}
if(policy == CREATE_NULL_AS_BLANK) {
if(cell == null) {
return createCell((short)cellnum, HSSFCell.CELL_TYPE_BLANK);
return createCell((short)cellnum, Cell.CELL_TYPE_BLANK);
}
return cell;
}
@ -207,16 +206,34 @@ public class XSSFRow implements Row, Comparable {
/**
* 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.
* @return short representing the first logical cell in the row,
* or -1 if the row does not contain any cells.
*/
public short getFirstCellNum() {
for (Iterator<Cell> it = cellIterator() ; it.hasNext() ; ) {
Cell cell = it.next();
if (cell != null) {
return (short)cell.getColumnIndex();
}
}
return -1;
return (short)(cells.size() == 0 ? -1 : cells.firstKey());
}
/**
* 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&lt;maxColIx; colIx++) {
* XSSFCell 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() {
return (short)(cells.size() == 0 ? -1 : (cells.lastKey() + 1));
}
/**
@ -244,88 +261,6 @@ public class XSSFRow implements Row, Comparable {
}
}
/**
* 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&lt;maxColIx; colIx++) {
* XSSFCell 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() {
short lastCellNum = -1;
for (Iterator<Cell> it = cellIterator() ; it.hasNext() ; ) {
Cell cell = it.next();
if (cell != null) {
lastCellNum = (short)(cell.getColumnIndex() + 1);
}
}
return lastCellNum;
}
/**
* 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 (Iterator<Cell> it = cellIterator() ; it.hasNext() ; ) {
if (it.next() != null) {
count++;
}
}
return count;
}
/**
* Get row number this row represents
*
* @return the row number (0 based)
*/
public int getRowNum() {
return (int) (row.getR() - 1);
}
/**
* Get whether or not to display this row with 0 height
*
* @return - height is zero or not.
*/
public boolean getZeroHeight() {
return this.row.getHidden();
}
/**
* Remove the Cell from this row.
*
* @param cell to remove
*/
public void removeCell(Cell cell) {
int counter = 0;
for (Iterator<Cell> it = cellIterator(); it.hasNext(); ) {
Cell c = it.next();
if (c.getColumnIndex() == cell.getColumnIndex()) {
it.remove();
row.removeC(counter);
continue;
}
counter++;
}
}
/**
* Set the height in "twips" or 1/20th of a point.
*
@ -350,14 +285,44 @@ public class XSSFRow implements Row, Comparable {
setHeight((short)(height*20));
}
/**
* 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() {
return cells.size();
}
/**
* Get row number this row represents
*
* @return the row number (0 based)
*/
public int getRowNum() {
return (int) (row.getR() - 1);
}
/**
* Set the row number of this row.
*
* @param rowNum the row number (0-based)
* @throws IllegalArgumentException if rowNum < 0
*/
public void setRowNum(int rowNum) {
this.row.setR(rowNum + 1);
if(rowNum < 0) throw new IllegalArgumentException("Row number must be >= 0");
this.row.setR(rowNum + 1);
}
/**
* Get whether or not to display this row with 0 height
*
* @return - height is zero or not.
*/
public boolean getZeroHeight() {
return this.row.getHidden();
}
/**
@ -369,14 +334,48 @@ public class XSSFRow implements Row, Comparable {
this.row.setHidden(height);
}
/**
* Returns the underlying CTRow xml bean representing this row
* Remove the Cell from this row.
*
* @return the underlying CTRow bean
* @param cell the cell to remove
*/
public CTRow getCTRow(){
return this.row;
public void removeCell(Cell cell) {
cells.remove(cell.getColumnIndex());
}
/**
* Returns the underlying CTRow xml bean containing all cell definitions in this row
*
* @return the underlying CTRow xml bean
*/
public CTRow getCTRow(){
return row;
}
/**
* Fired when the document is written to an output stream.
* <p>
* Attaches CTCell beans to the underlying CTRow bean
* </p>
* @see org.apache.poi.xssf.usermodel.XSSFSheet#commit()
*/
protected void onDocumentWrite(){
ArrayList<CTCell> cArray = new ArrayList<CTCell>(cells.size());
//create array of CTCell objects.
//TreeMap's value iterator ensures that the cells are ordered by columnIndex in the ascending order
for (Cell cell : cells.values()) {
XSSFCell c = (XSSFCell)cell;
cArray.add(c.getCTCell());
}
row.setCArray(cArray.toArray(new CTCell[cArray.size()]));
}
/**
* @return formatted xml representation of this row
*/
@Override
public String toString(){
return row.toString();
}
}

View File

@ -71,7 +71,7 @@ public class XSSFSheet extends POIXMLDocumentPart implements Sheet {
protected CTSheet sheet;
protected CTWorksheet worksheet;
protected List<Row> rows;
protected TreeMap<Integer, Row> rows;
protected List<XSSFHyperlink> hyperlinks;
protected ColumnHelper columnHelper;
private CommentsSource sheetComments;
@ -156,9 +156,10 @@ public class XSSFSheet extends POIXMLDocumentPart implements Sheet {
}
protected void initRows(CTWorksheet worksheet) {
this.rows = new LinkedList<Row>();
this.rows = new TreeMap<Integer, Row>();
for (CTRow row : worksheet.getSheetData().getRowArray()) {
this.rows.add(new XSSFRow(row, this));
XSSFRow r = new XSSFRow(row, this);
this.rows.put(r.getRowNum(), r);
}
}
@ -307,13 +308,6 @@ public class XSSFSheet extends POIXMLDocumentPart implements Sheet {
return (XSSFComment)sheetComments.addComment();
}
protected XSSFRow addRow(int index, int rownum) {
CTRow row = this.worksheet.getSheetData().insertNewRow(index);
XSSFRow xrow = new XSSFRow(row, this);
xrow.setRowNum(rownum);
return xrow;
}
/**
* Create a new row within the sheet and return the high level representation
*
@ -322,24 +316,11 @@ public class XSSFSheet extends POIXMLDocumentPart implements Sheet {
* @see #removeRow(org.apache.poi.ss.usermodel.Row)
*/
public XSSFRow createRow(int rownum) {
int index = 0;
for (Row r : this.rows) {
if (r.getRowNum() == rownum) {
// Replace r with new row
XSSFRow xrow = addRow(index, rownum);
rows.set(index, xrow);
return xrow;
}
if (r.getRowNum() > rownum) {
XSSFRow xrow = addRow(index, rownum);
rows.add(index, xrow);
return xrow;
}
++index;
}
XSSFRow xrow = addRow(index, rownum);
rows.add(xrow);
return xrow;
CTRow ctRow = CTRow.Factory.newInstance();
XSSFRow r = new XSSFRow(ctRow, this);
r.setRowNum(rownum);
rows.put(r.getRowNum(), r);
return r;
}
/**
@ -737,14 +718,7 @@ public class XSSFSheet extends POIXMLDocumentPart implements Sheet {
* @return <code>XSSFRow</code> representing the rownumber or <code>null</code> if its not defined on the sheet
*/
public XSSFRow getRow(int rownum) {
//TODO current implemenation is expensive, it should take O(1), not O(N)
for (Iterator<Row> it = rowIterator() ; it.hasNext() ; ) {
Row row = it.next();
if (row.getRowNum() == rownum) {
return (XSSFRow)row;
}
}
return null;
return (XSSFRow)rows.get(rownum);
}
/**
@ -926,7 +900,7 @@ public class XSSFSheet extends POIXMLDocumentPart implements Sheet {
private short getMaxOutlineLevelRows(){
short outlineLevel=0;
for(Row r:rows){
for(Row r : rows.values()){
XSSFRow xrow=(XSSFRow)r;
outlineLevel=xrow.getCTRow().getOutlineLevel()>outlineLevel? xrow.getCTRow().getOutlineLevel(): outlineLevel;
}
@ -1067,16 +1041,8 @@ public class XSSFSheet extends POIXMLDocumentPart implements Sheet {
}
public void removeRow(Row row) {
int counter = 0;
int rowNum=row.getRowNum();
for (Iterator<Row> it = rowIterator() ; it.hasNext() ; ) {
Row r = it.next();
if (r.getRowNum() == rowNum) {
it.remove();
worksheet.getSheetData().removeRow(counter);
}
counter++;
}
rows.remove(row.getRowNum());
}
/**
@ -1093,7 +1059,7 @@ public class XSSFSheet extends POIXMLDocumentPart implements Sheet {
}
public Iterator<Row> rowIterator() {
return rows.iterator();
return rows.values().iterator();
}
/**
@ -1444,6 +1410,10 @@ public class XSSFSheet extends POIXMLDocumentPart implements Sheet {
row.setRowNum(row.getRowNum() + n);
}
}
//rebuild the rows map
TreeMap<Integer, Row> map = new TreeMap<Integer, Row>();
for(Row r : this) map.put(r.getRowNum(), r);
rows = map;
}
/**
@ -1680,6 +1650,15 @@ public class XSSFSheet extends POIXMLDocumentPart implements Sheet {
worksheet.getHyperlinks().setHyperlinkArray(ctHls);
}
CTSheetData sheetData = worksheet.getSheetData();
ArrayList<CTRow> rArray = new ArrayList<CTRow>(rows.size());
for(Row row : rows.values()){
XSSFRow r = (XSSFRow)row;
r.onDocumentWrite();
rArray.add(r.getCTRow());
}
sheetData.setRowArray(rArray.toArray(new CTRow[rArray.size()]));
XmlOptions xmlOptions = new XmlOptions(DEFAULT_XML_OPTIONS);
xmlOptions.setSaveSyntheticDocumentElement(new QName(CTWorksheet.type.getName().getNamespaceURI(), "worksheet"));

View File

@ -25,13 +25,7 @@ import junit.framework.TestCase;
import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.hssf.usermodel.HSSFRichTextString;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.CreationHelper;
import org.apache.poi.ss.usermodel.DataFormat;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.XSSFTestDataSamples;
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTCell;
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTWorksheet;
@ -51,18 +45,39 @@ public final class TestXSSFCell extends TestCase {
public void testSetGetBoolean() throws Exception {
XSSFRow row = createParentObjects();
XSSFCell cell = row.createCell(0);
//for blank cells getBooleanCellValue returns false
assertFalse(cell.getBooleanCellValue());
cell.setCellValue(true);
assertEquals(Cell.CELL_TYPE_BOOLEAN, cell.getCellType());
assertTrue(cell.getBooleanCellValue());
cell.setCellValue(false);
assertFalse(cell.getBooleanCellValue());
cell.setCellType(Cell.CELL_TYPE_NUMERIC);
try {
cell.getBooleanCellValue();
fail("Exception expected");
} catch (NumberFormatException e) {
} catch (IllegalStateException e) {
// success
assertEquals("Cannot get a boolean value from a numeric cell", e.getMessage());
}
cell.setCellValue("1");
assertEquals(Cell.CELL_TYPE_STRING, cell.getCellType());
try {
cell.getBooleanCellValue();
fail("Exception expected");
} catch (IllegalStateException e) {
// success
assertEquals("Cannot get a boolean value from a text cell", e.getMessage());
}
//reverted to a blank cell
cell.setCellType(Cell.CELL_TYPE_BLANK);
assertFalse(cell.getBooleanCellValue());
}
/**
@ -71,15 +86,54 @@ public final class TestXSSFCell extends TestCase {
public void testSetGetNumeric() throws Exception {
XSSFRow row = createParentObjects();
XSSFCell cell = row.createCell(0);
cell.setCellValue(10d);
assertEquals(0.0, cell.getNumericCellValue());
cell.setCellValue(10.0);
assertEquals(Cell.CELL_TYPE_NUMERIC, cell.getCellType());
assertEquals(10d, cell.getNumericCellValue());
assertEquals(10.0, cell.getNumericCellValue());
cell.setCellValue(-23.76);
assertEquals(-23.76, cell.getNumericCellValue());
}
cell.setCellValue("string");
try {
cell.getNumericCellValue();
fail("Exception expected");
} catch (IllegalStateException e) {
// success
assertEquals("Cannot get a numeric value from a text cell", e.getMessage());
}
cell.setCellValue(true);
try {
cell.getNumericCellValue();
fail("Exception expected");
} catch (IllegalStateException e) {
// success
assertEquals("Cannot get a numeric value from a boolean cell", e.getMessage());
}
//reverted to a blank cell
cell.setCellType(Cell.CELL_TYPE_BLANK);
assertEquals(0.0, cell.getNumericCellValue());
//setting numeric value for a formula cell does not change the cell type
XSSFCell fcell = row.createCell(1);
fcell.setCellFormula("SUM(C4:E4)");
assertEquals(Cell.CELL_TYPE_FORMULA, fcell.getCellType());
fcell.setCellValue(36.6);
assertEquals(Cell.CELL_TYPE_FORMULA, fcell.getCellType());
assertEquals(36.6, fcell.getNumericCellValue());
//the said above is true for error cells
fcell.setCellType(Cell.CELL_TYPE_ERROR);
assertEquals(36.6, fcell.getNumericCellValue());
fcell.setCellValue(16.6);
assertEquals(Cell.CELL_TYPE_FORMULA, fcell.getCellType());
assertEquals(16.6, fcell.getNumericCellValue());
}
/**
* Test setting and getting numeric values.
* Test setting and getting date values.
*/
public void testSetGetDate() throws Exception {
XSSFRow row = createParentObjects();
@ -100,8 +154,9 @@ public final class TestXSSFCell extends TestCase {
try {
cell.getDateCellValue();
fail("Exception expected");
} catch (NumberFormatException e) {
} catch (IllegalStateException e) {
// success
assertEquals("Cannot get a numeric value from a boolean cell", e.getMessage());
}
cell.setCellValue(cal);
@ -109,6 +164,34 @@ public final class TestXSSFCell extends TestCase {
}
/**
* Test setting and getting date values.
*/
public void testSetGetType() throws Exception {
XSSFRow row = createParentObjects();
XSSFCell cell = row.createCell(0);
cell.setCellType(Cell.CELL_TYPE_BLANK);
assertEquals(Cell.CELL_TYPE_BLANK, cell.getCellType());
cell.setCellType(Cell.CELL_TYPE_STRING);
assertEquals(Cell.CELL_TYPE_STRING, cell.getCellType());
cell.setCellType(Cell.CELL_TYPE_FORMULA);
assertEquals(Cell.CELL_TYPE_FORMULA, cell.getCellType());
cell.setCellFormula(null);
//number cell w/o value is treated as a Blank cell
cell.setCellType(Cell.CELL_TYPE_NUMERIC);
assertFalse(cell.getCTCell().isSetV());
assertEquals(Cell.CELL_TYPE_BLANK, cell.getCellType());
//normal number cells have set values
cell.setCellType(Cell.CELL_TYPE_NUMERIC);
cell.getCTCell().setV("0");
assertEquals(Cell.CELL_TYPE_NUMERIC, cell.getCellType());
cell.setCellType(Cell.CELL_TYPE_BOOLEAN);
assertEquals(Cell.CELL_TYPE_BOOLEAN, cell.getCellType());
}
public void testSetGetError() throws Exception {
XSSFRow row = createParentObjects();
XSSFCell cell = row.createCell(0);
@ -116,14 +199,15 @@ public final class TestXSSFCell extends TestCase {
cell.setCellErrorValue((byte)0);
assertEquals(Cell.CELL_TYPE_ERROR, cell.getCellType());
assertEquals((byte)0, cell.getErrorCellValue());
//YK setting numeric value of a error cell does not change the cell type
cell.setCellValue(2.2);
assertEquals(Cell.CELL_TYPE_NUMERIC, cell.getCellType());
cell.setCellErrorValue(Cell.ERROR_NAME);
assertEquals(Cell.CELL_TYPE_ERROR, cell.getCellType());
assertEquals(Cell.ERROR_NAME.getType(), cell.getErrorCellValue());
assertEquals(Cell.ERROR_NAME.getStringRepr(), cell.getErrorCellString());
cell.setCellErrorValue(FormulaError.NAME);
assertEquals(Cell.CELL_TYPE_ERROR, cell.getCellType());
assertEquals(FormulaError.NAME.getCode(), cell.getErrorCellValue());
assertEquals(FormulaError.NAME.getString(), cell.getErrorCellString());
}
public void testSetGetFormula() throws Exception {
@ -135,7 +219,13 @@ public final class TestXSSFCell extends TestCase {
assertEquals(Cell.CELL_TYPE_FORMULA, cell.getCellType());
assertEquals(formula, cell.getCellFormula());
assertTrue( Double.isNaN( cell.getNumericCellValue() ));
assertEquals(0.0, cell.getNumericCellValue());
cell.setCellValue(44.5); //set precalculated value
assertEquals(Cell.CELL_TYPE_FORMULA, cell.getCellType());
assertEquals(44.5, cell.getNumericCellValue());
cell.setCellValue(""); //set precalculated value
}
public void testSetGetStringInline() throws Exception {
@ -166,14 +256,27 @@ public final class TestXSSFCell extends TestCase {
public void testSetGetStringShared() {
XSSFRow row = createParentObjects();
XSSFCell cell = row.createCell(0);
//we return empty string for blank cells
assertEquals("", cell.getStringCellValue());
cell.setCellValue(new XSSFRichTextString(""));
cell.setCellValue(new XSSFRichTextString("test"));
assertEquals(Cell.CELL_TYPE_STRING, cell.getCellType());
assertEquals("", cell.getRichStringCellValue().getString());
assertEquals("test", cell.getRichStringCellValue().getString());
cell.setCellValue(new XSSFRichTextString("Foo"));
assertEquals(Cell.CELL_TYPE_STRING, cell.getCellType());
assertEquals("Foo", cell.getRichStringCellValue().getString());
cell.setCellValue((String)null);
assertEquals(Cell.CELL_TYPE_BLANK, cell.getCellType());
XSSFCell fcell = row.createCell(1);
fcell.setCellFormula("SUM(C4:E4)");
assertEquals(Cell.CELL_TYPE_FORMULA, fcell.getCellType());
fcell.setCellValue("36.6");
assertEquals(Cell.CELL_TYPE_FORMULA, fcell.getCellType());
assertEquals("36.6", fcell.getStringCellValue());
}
/**
@ -185,7 +288,7 @@ public final class TestXSSFCell extends TestCase {
cell.setCellType(Cell.CELL_TYPE_BOOLEAN);
assertFalse(cell.getBooleanCellValue());
cell.setCellType(Cell.CELL_TYPE_NUMERIC);
assertTrue(Double.isNaN( cell.getNumericCellValue() ));
assertEquals(0.0, cell.getNumericCellValue() );
assertNull(cell.getDateCellValue());
cell.setCellType(Cell.CELL_TYPE_ERROR);
assertEquals(0, cell.getErrorCellValue());
@ -246,17 +349,15 @@ public final class TestXSSFCell extends TestCase {
XSSFComment comment = sheet.createComment();
comment.setAuthor(TEST_C10_AUTHOR);
CTWorksheet ctWorksheet = sheet.getWorksheet();
// Create C10 cell
Row row = sheet.createRow(9);
Cell cell = row.createCell(2);
XSSFRow row = sheet.createRow(9);
XSSFCell cell = row.createCell(2);
row.createCell(3);
// Set a comment for C10 cell
cell.setCellComment(comment);
CTCell ctCell = ctWorksheet.getSheetData().getRowArray(0).getCArray(0);
CTCell ctCell = cell.getCTCell();
assertNotNull(ctCell);
assertEquals("C10", ctCell.getR());
assertEquals(TEST_C10_AUTHOR, comment.getAuthor());
@ -407,7 +508,7 @@ public final class TestXSSFCell extends TestCase {
assertEquals(hcell.toString(),xcell.toString());
//ERROR
xcell.setCellErrorValue(Cell.ERROR_VALUE);
xcell.setCellErrorValue(FormulaError.VALUE);
xcell.setCellType(Cell.CELL_TYPE_ERROR);
hcell.setCellErrorValue((byte)0);

View File

@ -42,12 +42,12 @@ public final class TestXSSFFormulaEvaluation extends TestCase {
Cell c1 = r.createCell(0);
c1.setCellFormula("1+5");
assertTrue( Double.isNaN(c1.getNumericCellValue()) );
assertEquals(0.0, c1.getNumericCellValue() );
Cell c2 = r.createCell(1);
c2.setCellFormula("10/2");
assertTrue( Double.isNaN(c2.getNumericCellValue()) );
assertEquals(0.0, c2.getNumericCellValue() );
FormulaEvaluator fe = new XSSFFormulaEvaluator(wb);
fe.evaluateFormulaCell(c1);
@ -70,19 +70,19 @@ public final class TestXSSFFormulaEvaluation extends TestCase {
Cell c1 = r.createCell(0);
c1.setCellFormula("SUM(A1:B1)");
assertTrue( Double.isNaN(c1.getNumericCellValue()) );
assertEquals(0.0, c1.getNumericCellValue() );
Cell c2 = r.createCell(1);
c2.setCellFormula("SUM(A1:E1)");
assertTrue( Double.isNaN(c2.getNumericCellValue()) );
assertEquals(0.0, c2.getNumericCellValue() );
Cell c3 = r.createCell(2);
c3.setCellFormula("COUNT(A1:A1)");
assertTrue( Double.isNaN(c3.getNumericCellValue()) );
assertEquals(0.0, c3.getNumericCellValue() );
Cell c4 = r.createCell(3);
c4.setCellFormula("COUNTA(A1:E1)");
assertTrue( Double.isNaN(c4.getNumericCellValue()) );
assertEquals(0.0, c4.getNumericCellValue() );
// Evaluate and test

View File

@ -793,9 +793,7 @@ public class TestXSSFSheet extends TestCase {
//one level
sheet.groupRow(9,10);
assertEquals(2,sheet.rows.size());
CTRow[]rowArray=sheet.getWorksheet().getSheetData().getRowArray();
assertEquals(2,rowArray.length);
CTRow ctrow=rowArray[0];
CTRow ctrow = sheet.getRow(8).getCTRow();
assertNotNull(ctrow);
assertEquals(9,ctrow.getR());
@ -804,10 +802,8 @@ public class TestXSSFSheet extends TestCase {
//two level
sheet.groupRow(10,13);
rowArray=sheet.getWorksheet().getSheetData().getRowArray();
assertEquals(5,rowArray.length);
assertEquals(5,sheet.rows.size());
ctrow=rowArray[1];
ctrow = sheet.getRow(9).getCTRow();
assertNotNull(ctrow);
assertEquals(10,ctrow.getR());
assertEquals(2, ctrow.getOutlineLevel());
@ -815,14 +811,11 @@ public class TestXSSFSheet extends TestCase {
sheet.ungroupRow(8, 10);
rowArray=sheet.getWorksheet().getSheetData().getRowArray();
assertEquals(4,rowArray.length);
assertEquals(4,sheet.rows.size());
assertEquals(1,sheet.getSheetTypeSheetFormatPr().getOutlineLevelRow());
sheet.ungroupRow(10,10);
rowArray=sheet.getWorksheet().getSheetData().getRowArray();
assertEquals(3,rowArray.length);
assertEquals(3,sheet.rows.size());
assertEquals(3,sheet.rows.size());
assertEquals(1,sheet.getSheetTypeSheetFormatPr().getOutlineLevelRow());
}