improved test coverage for SXSSF

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1124698 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Yegor Kozlov 2011-05-19 12:00:10 +00:00
parent 1ed61c54f0
commit f90fcee530
9 changed files with 285 additions and 164 deletions

View File

@ -17,19 +17,16 @@
package org.apache.poi.xssf.streaming; package org.apache.poi.xssf.streaming;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar; import java.util.Calendar;
import java.util.Date; import java.util.Date;
import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.formula.eval.ErrorEval;
import org.apache.poi.ss.usermodel.CellStyle; import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.RichTextString;
import org.apache.poi.ss.usermodel.Comment;
import org.apache.poi.ss.usermodel.Hyperlink;
import org.apache.poi.ss.usermodel.DateUtil;
import org.apache.poi.ss.formula.FormulaParseException; import org.apache.poi.ss.formula.FormulaParseException;
import org.apache.poi.ss.util.CellRangeAddress; import org.apache.poi.ss.util.CellRangeAddress;
import org.openxmlformats.schemas.spreadsheetml.x2006.main.STCellType;
/** /**
* Streaming version of XSSFRow implementing the "BigGridDemo" strategy. * Streaming version of XSSFRow implementing the "BigGridDemo" strategy.
@ -132,9 +129,11 @@ public class SXSSFCell implements Cell
*/ */
public int getCachedFormulaResultType() public int getCachedFormulaResultType()
{ {
//TODO: Implement this correctly if (_value.getType() != CELL_TYPE_FORMULA) {
assert false; throw new IllegalStateException("Only formula cells have cached results");
return CELL_TYPE_NUMERIC; }
return ((FormulaValue)_value).getFormulaType();
} }
/** /**
@ -146,11 +145,19 @@ public class SXSSFCell implements Cell
*/ */
public void setCellValue(double value) public void setCellValue(double value)
{ {
ensureTypeOrFormulaType(CELL_TYPE_NUMERIC); if(Double.isInfinite(value)) {
if(_value.getType()==CELL_TYPE_FORMULA) // Excel does not support positive/negative infinities,
((NumericFormulaValue)_value).setPreEvaluatedValue(value); // rather, it gives a #DIV/0! error in these cases.
else setCellErrorValue(FormulaError.DIV0.getCode());
((NumericValue)_value).setValue(value); } else if (Double.isNaN(value)){
setCellErrorValue(FormulaError.NUM.getCode());
} else {
ensureTypeOrFormulaType(CELL_TYPE_NUMERIC);
if(_value.getType()==CELL_TYPE_FORMULA)
((NumericFormulaValue)_value).setPreEvaluatedValue(value);
else
((NumericValue)_value).setValue(value);
}
} }
/** /**
@ -244,6 +251,11 @@ public class SXSSFCell implements Cell
*/ */
public void setCellFormula(String formula) throws FormulaParseException public void setCellFormula(String formula) throws FormulaParseException
{ {
if(formula == null) {
setType(Cell.CELL_TYPE_BLANK);
return;
}
ensureFormulaType(computeTypeFromFormula(formula)); ensureFormulaType(computeTypeFromFormula(formula));
((FormulaValue)_value).setValue(formula); ((FormulaValue)_value).setValue(formula);
} }
@ -328,9 +340,16 @@ public class SXSSFCell implements Cell
public RichTextString getRichStringCellValue() public RichTextString getRichStringCellValue()
{ {
int cellType = getCellType(); int cellType = getCellType();
if(!(getCellType()==CELL_TYPE_STRING&&((StringValue)_value).isRichText())) if(getCellType() != CELL_TYPE_STRING)
throw typeMismatch(CELL_TYPE_STRING, cellType, false); throw typeMismatch(CELL_TYPE_STRING, cellType, false);
return ((RichTextValue)_value).getValue();
StringValue sval = (StringValue)_value;
if(sval.isRichText())
return ((RichTextValue)_value).getValue();
else {
String plainText = getStringCellValue();
return getSheet().getWorkbook().getCreationHelper().createRichTextString(plainText);
}
} }
@ -490,7 +509,12 @@ public class SXSSFCell implements Cell
*/ */
public CellStyle getCellStyle() public CellStyle getCellStyle()
{ {
return _style; if(_style == null){
SXSSFWorkbook wb = (SXSSFWorkbook)getRow().getSheet().getWorkbook();
return wb.getCellStyleAt((short)0);
} else {
return _style;
}
} }
/** /**
@ -569,6 +593,37 @@ public class SXSSFCell implements Cell
} }
//end of interface implementation //end of interface implementation
/**
* Returns a string representation of the cell
* <p>
* Formula cells return the formula string, rather than the formula result.
* Dates are displayed in dd-MMM-yyyy format
* Errors are displayed as #ERR&lt;errIdx&gt;
* </p>
*/
public String toString() {
switch (getCellType()) {
case CELL_TYPE_BLANK:
return "";
case CELL_TYPE_BOOLEAN:
return getBooleanCellValue() ? "TRUE" : "FALSE";
case CELL_TYPE_ERROR:
return ErrorEval.getText(getErrorCellValue());
case CELL_TYPE_FORMULA:
return getCellFormula();
case CELL_TYPE_NUMERIC:
if (DateUtil.isCellDateFormatted(this)) {
DateFormat sdf = new SimpleDateFormat("dd-MMM-yyyy");
return sdf.format(getDateCellValue());
}
return getNumericCellValue() + "";
case CELL_TYPE_STRING:
return getRichStringCellValue().toString();
default:
return "Unknown Cell Type: " + getCellType();
}
}
void removeProperty(int type) void removeProperty(int type)
{ {
Property current=_firstProperty; Property current=_firstProperty;
@ -693,7 +748,13 @@ public class SXSSFCell implements Cell
} }
case CELL_TYPE_STRING: case CELL_TYPE_STRING:
{ {
_value=new PlainStringValue(); PlainStringValue sval = new PlainStringValue();
if(_value != null){
// if a cell is not blank then convert the old value to string
String str = convertCellValueToString();
sval.setValue(str);
}
_value = sval;
break; break;
} }
case CELL_TYPE_FORMULA: case CELL_TYPE_FORMULA:
@ -708,7 +769,13 @@ public class SXSSFCell implements Cell
} }
case CELL_TYPE_BOOLEAN: case CELL_TYPE_BOOLEAN:
{ {
_value=new BooleanValue(); BooleanValue bval = new BooleanValue();
if(_value != null){
// if a cell is not blank then convert the old value to string
boolean val = convertCellValueToBoolean();
bval.setValue(val);
}
_value = bval;
break; break;
} }
case CELL_TYPE_ERROR: case CELL_TYPE_ERROR:
@ -781,6 +848,49 @@ public class SXSSFCell implements Cell
} }
return "#unknown cell type (" + cellTypeCode + ")#"; return "#unknown cell type (" + cellTypeCode + ")#";
} }
private boolean convertCellValueToBoolean() {
int cellType = getCellType();
if (cellType == CELL_TYPE_FORMULA) {
cellType = getCachedFormulaResultType();
}
switch (cellType) {
case CELL_TYPE_BOOLEAN:
return getBooleanCellValue();
case CELL_TYPE_STRING:
String text = getStringCellValue();
return Boolean.parseBoolean(text);
case CELL_TYPE_NUMERIC:
return getNumericCellValue() != 0;
case CELL_TYPE_ERROR:
case CELL_TYPE_BLANK:
return false;
}
throw new RuntimeException("Unexpected cell type (" + cellType + ")");
}
private String convertCellValueToString() {
int cellType = getCellType();
switch (cellType) {
case CELL_TYPE_BLANK:
return "";
case CELL_TYPE_BOOLEAN:
return getBooleanCellValue() ? "TRUE" : "FALSE";
case CELL_TYPE_STRING:
return getStringCellValue();
case CELL_TYPE_NUMERIC:
case CELL_TYPE_ERROR:
byte errVal = getErrorCellValue();
return FormulaError.forInt(errVal).getString();
case CELL_TYPE_FORMULA:
return "";
default:
throw new IllegalStateException("Unexpected cell type (" + cellType + ")");
}
}
//END OF COPIED CODE //END OF COPIED CODE
static abstract class Property static abstract class Property
@ -870,7 +980,7 @@ public class SXSSFCell implements Cell
return false; return false;
} }
} }
static class RichTextValue implements Value static class RichTextValue extends StringValue
{ {
RichTextString _value; RichTextString _value;
public int getType() public int getType()

View File

@ -35,8 +35,7 @@ public class SXSSFRow implements Row
SXSSFCell[] _cells; SXSSFCell[] _cells;
int _maxColumn=-1; int _maxColumn=-1;
short _height=-1; short _height=-1;
//TODO: Need to set the correct default value for _zHeight boolean _zHeight = false;
boolean _zHeight;
public SXSSFRow(SXSSFSheet sheet, int initialSize) public SXSSFRow(SXSSFSheet sheet, int initialSize)
{ {
@ -150,9 +149,29 @@ public class SXSSFRow implements Row
* @return Cell representing that column or null if undefined. * @return Cell representing that column or null if undefined.
* @see #getCell(int, org.apache.poi.ss.usermodel.Row.MissingCellPolicy) * @see #getCell(int, org.apache.poi.ss.usermodel.Row.MissingCellPolicy)
*/ */
public Cell getCell(int cellnum) public Cell getCell(int cellnum) {
{ if(cellnum < 0) throw new IllegalArgumentException("Cell index must be >= 0");
return cellnum>_maxColumn?null:_cells[cellnum];
Cell cell = cellnum > _maxColumn ? null : _cells[cellnum];
MissingCellPolicy policy = _sheet.getWorkbook().getMissingCellPolicy();
if(policy == RETURN_NULL_AND_BLANK) {
return cell;
}
if (policy == RETURN_BLANK_AS_NULL) {
if (cell == null) return cell;
if (cell.getCellType() == Cell.CELL_TYPE_BLANK) {
return null;
}
return cell;
}
if (policy == CREATE_NULL_AS_BLANK) {
if (cell == null) {
return createCell((short) cellnum, Cell.CELL_TYPE_BLANK);
}
return cell;
}
throw new IllegalArgumentException("Illegal policy " + policy + " (" + policy.id + ")");
} }
/** /**
@ -226,7 +245,7 @@ public class SXSSFRow implements Row
*/ */
public short getLastCellNum() public short getLastCellNum()
{ {
return (short)(_maxColumn+1); return _maxColumn == -1 ? -1 : (short)(_maxColumn+1);
} }
/** /**
@ -337,6 +356,16 @@ public class SXSSFRow implements Row
public class FilledCellIterator implements Iterator<Cell> public class FilledCellIterator implements Iterator<Cell>
{ {
int pos=0; int pos=0;
FilledCellIterator(){
for (int i = 0; i <= _maxColumn; i++) {
if (_cells[i] != null) {
pos = i;
break;
}
}
}
public boolean hasNext() public boolean hasNext()
{ {
return pos <= _maxColumn; return pos <= _maxColumn;

View File

@ -27,20 +27,7 @@ import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.FileInputStream; import java.io.FileInputStream;
import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.PrintSetup;
import org.apache.poi.ss.usermodel.Header;
import org.apache.poi.ss.usermodel.Footer;
import org.apache.poi.ss.usermodel.Comment;
import org.apache.poi.ss.usermodel.Drawing;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.usermodel.DataValidation;
import org.apache.poi.ss.usermodel.CellRange;
import org.apache.poi.ss.usermodel.DataValidationHelper;
import org.apache.poi.ss.usermodel.AutoFilter;
import org.apache.poi.ss.util.CellReference; import org.apache.poi.ss.util.CellReference;
import org.apache.poi.xssf.usermodel.XSSFSheet; import org.apache.poi.xssf.usermodel.XSSFSheet;
@ -125,6 +112,10 @@ public class SXSSFSheet implements Sheet, Cloneable
*/ */
public void removeRow(Row row) public void removeRow(Row row)
{ {
if (row.getSheet() != this) {
throw new IllegalArgumentException("Specified row does not belong to this sheet");
}
for(Iterator<Map.Entry<Integer,SXSSFRow>> iter=_rows.entrySet().iterator();iter.hasNext();) for(Iterator<Map.Entry<Integer,SXSSFRow>> iter=_rows.entrySet().iterator();iter.hasNext();)
{ {
Map.Entry<Integer,SXSSFRow> entry=iter.next(); Map.Entry<Integer,SXSSFRow> entry=iter.next();
@ -165,10 +156,9 @@ public class SXSSFSheet implements Sheet, Cloneable
*/ */
public int getFirstRowNum() public int getFirstRowNum()
{ {
if(_writer.getNumberOfFlushedRows()>0) if(_writer.getNumberOfFlushedRows() > 0)
return _writer.getLowestIndexOfFlushedRows(); return _writer.getLowestIndexOfFlushedRows();
Integer firstKey=_rows.firstKey(); return _rows.size() == 0 ? 0 : _rows.firstKey();
return firstKey==null?-1:firstKey.intValue();
} }
/** /**
@ -178,8 +168,7 @@ public class SXSSFSheet implements Sheet, Cloneable
*/ */
public int getLastRowNum() public int getLastRowNum()
{ {
Integer lastKey=_rows.lastKey(); return _rows.size() == 0 ? 0 : _rows.lastKey();
return lastKey==null?-1:lastKey.intValue();
} }
/** /**
@ -1306,6 +1295,8 @@ public class SXSSFSheet implements Sheet, Cloneable
_out.write("<row r=\""+(rownum+1)+"\""); _out.write("<row r=\""+(rownum+1)+"\"");
if(row.hasCustomHeight()) if(row.hasCustomHeight())
_out.write(" customHeight=\"true\" ht=\""+row.getHeightInPoints()+"\""); _out.write(" customHeight=\"true\" ht=\""+row.getHeightInPoints()+"\"");
if(row.getZeroHeight())
_out.write(" hidden=\"true\"");
_out.write(">\n"); _out.write(">\n");
this._rownum = rownum; this._rownum = rownum;
_rowContainedNullCells=false; _rowContainedNullCells=false;
@ -1326,17 +1317,30 @@ public class SXSSFSheet implements Sheet, Cloneable
String ref = new CellReference(_rownum, columnIndex).formatAsString(); String ref = new CellReference(_rownum, columnIndex).formatAsString();
_out.write("<c r=\""+ref+"\""); _out.write("<c r=\""+ref+"\"");
CellStyle cellStyle=cell.getCellStyle(); CellStyle cellStyle=cell.getCellStyle();
if(cellStyle!=null) _out.write(" s=\""+cellStyle.getIndex()+"\""); if(cellStyle.getIndex() != 0) _out.write(" s=\""+cellStyle.getIndex()+"\"");
int cellType=cell.getCellType(); int cellType=cell.getCellType();
switch(cellType) switch(cellType)
{ {
case Cell.CELL_TYPE_BLANK: case Cell.CELL_TYPE_BLANK:
{ {
//TODO: What needs to be done here? _out.write(">");
break;
} }
case Cell.CELL_TYPE_FORMULA: case Cell.CELL_TYPE_FORMULA:
{ {
//TODO: What needs to be done here? _out.write(">");
_out.write("<f>");
outputQuotedString(cell.getCellFormula());
_out.write("</f>");
switch (cell.getCachedFormulaResultType()){
case Cell.CELL_TYPE_NUMERIC:
double nval = cell.getNumericCellValue();
if(!Double.isNaN(nval)){
_out.write("<v>"+nval+"</v>");
}
break;
}
break;
} }
case Cell.CELL_TYPE_STRING: case Cell.CELL_TYPE_STRING:
{ {
@ -1354,15 +1358,16 @@ public class SXSSFSheet implements Sheet, Cloneable
} }
case Cell.CELL_TYPE_BOOLEAN: case Cell.CELL_TYPE_BOOLEAN:
{ {
_out.write(" t=\"n\">"); _out.write(" t=\"b\">");
_out.write("<v>"+(cell.getBooleanCellValue()?"1":"0")+"</v>"); _out.write("<v>"+(cell.getBooleanCellValue()?"1":"0")+"</v>");
break; break;
} }
case Cell.CELL_TYPE_ERROR: case Cell.CELL_TYPE_ERROR:
{ {
//TODO: What needs to be done here? FormulaError error = FormulaError.forInt(cell.getErrorCellValue());
_out.write(" t=\"inlineStr\">");
_out.write("<is><t></t></is>"); _out.write(" t=\"e\">");
_out.write("<v>" + error.getString() +"</v>");
break; break;
} }
default: default:

View File

@ -327,7 +327,7 @@ public class SXSSFWorkbook implements Workbook
*/ */
public Sheet cloneSheet(int sheetNum) public Sheet cloneSheet(int sheetNum)
{ {
return createAndRegisterSXSSFSheet(_wb.cloneSheet(sheetNum)); throw new RuntimeException("NotImplemented");
} }

View File

@ -256,6 +256,11 @@ public class XSSFFormulaEvaluator implements FormulaEvaluator {
* Returns a CellValue wrapper around the supplied ValueEval instance. * Returns a CellValue wrapper around the supplied ValueEval instance.
*/ */
private CellValue evaluateFormulaCellValue(Cell cell) { private CellValue evaluateFormulaCellValue(Cell cell) {
if(!(cell instanceof XSSFCell)){
throw new IllegalArgumentException("Unexpected type of cell: " + cell.getClass() + "." +
" Only XSSFCells can be evaluated.");
}
ValueEval eval = _bookEvaluator.evaluate(new XSSFEvaluationCell((XSSFCell) cell)); ValueEval eval = _bookEvaluator.evaluate(new XSSFEvaluationCell((XSSFCell) cell));
if (eval instanceof NumberEval) { if (eval instanceof NumberEval) {
NumberEval ne = (NumberEval) eval; NumberEval ne = (NumberEval) eval;

View File

@ -31,63 +31,33 @@ public class TestSXSSFCell extends BaseTestCell {
super(SXSSFITestDataProvider.instance); super(SXSSFITestDataProvider.instance);
} }
@Override /**
public void testSetValues() { * this test involves evaluation of formulas which isn't supported for SXSSF
// TODO fix me */
}
@Override
public void testBoolErr() {
// TODO fix me
}
@Override
public void testFormulaStyle() {
// TODO fix me
}
@Override
public void testToString() {
// TODO fix me
}
@Override
public void testSetFormulaValue() {
// TODO fix me
}
@Override
public void testChangeTypeStringToBool() {
// TODO fix me
}
@Override
public void testChangeTypeBoolToString() {
// TODO fix me
}
@Override @Override
public void testConvertStringFormulaCell() { public void testConvertStringFormulaCell() {
// TODO fix me try {
super.testConvertStringFormulaCell();
fail("expected exception");
} catch (IllegalArgumentException e){
assertEquals(
"Unexpected type of cell: class org.apache.poi.xssf.streaming.SXSSFCell. " +
"Only XSSFCells can be evaluated.", e.getMessage());
}
} }
/**
* this test involves evaluation of formulas which isn't supported for SXSSF
*/
@Override @Override
public void testSetTypeStringOnFormulaCell() { public void testSetTypeStringOnFormulaCell() {
// TODO fix me try {
} super.testSetTypeStringOnFormulaCell();
fail("expected exception");
@Override } catch (IllegalArgumentException e){
public void testChangeTypeFormulaToBoolean() { assertEquals(
// TODO fix me "Unexpected type of cell: class org.apache.poi.xssf.streaming.SXSSFCell. " +
} "Only XSSFCells can be evaluated.", e.getMessage());
}
@Override
public void test40296() {
// TODO fix me
}
@Override
public void testNanAndInfinity() {
// TODO fix me
} }
} }

View File

@ -19,10 +19,8 @@
package org.apache.poi.xssf.usermodel.streaming; package org.apache.poi.xssf.usermodel.streaming;
import org.apache.poi.ss.SpreadsheetVersion;
import org.apache.poi.ss.usermodel.BaseTestRow; import org.apache.poi.ss.usermodel.BaseTestRow;
import org.apache.poi.xssf.SXSSFITestDataProvider; import org.apache.poi.xssf.SXSSFITestDataProvider;
import org.apache.poi.xssf.XSSFITestDataProvider;
/** /**
* Tests for XSSFRow * Tests for XSSFRow
@ -32,44 +30,4 @@ public final class TestSXSSFRow extends BaseTestRow {
public TestSXSSFRow() { public TestSXSSFRow() {
super(SXSSFITestDataProvider.instance); super(SXSSFITestDataProvider.instance);
} }
public void testRowBounds() {
//TODO fix me
//baseTestRowBounds(SpreadsheetVersion.EXCEL2007.getLastRowIndex());
}
public void testCellBounds() {
//TODO fix me
//baseTestCellBounds(SpreadsheetVersion.EXCEL2007.getLastColumnIndex());
}
@Override
public void testLastAndFirstColumns() {
//TODO fix me
}
@Override
public void testRemoveCell() {
//TODO fix me
}
@Override
public void testLastCellNumIsCorrectAfterAddCell_bug43901() {
//TODO fix me
}
@Override
public void testGetCellPolicy() {
//TODO fix me
}
@Override
public void testRowHeight() {
//TODO fix me
}
@Override
public void testCellIterator() {
//TODO fix me
}
} }

View File

@ -29,28 +29,58 @@ public class TestSXSSFSheet extends BaseTestSheet {
super(SXSSFITestDataProvider.instance); super(SXSSFITestDataProvider.instance);
} }
/**
* cloning of sheets is not supported in SXSSF
*/
@Override @Override
public void testRemoveRow(){ public void testCloneSheet() {
// TODO fix me try {
super.testCloneSheet();
fail("expected exception");
} catch (RuntimeException e){
assertEquals("NotImplemented", e.getMessage());
}
} }
@Override @Override
public void testCloneSheet(){ public void testCloneSheetMultipleTimes() {
// TODO fix me try {
super.testCloneSheetMultipleTimes();
fail("expected exception");
} catch (RuntimeException e){
assertEquals("NotImplemented", e.getMessage());
}
} }
/**
* shifting rows is not supported in SXSSF
*/
@Override @Override
public void testShiftMerged(){ public void testShiftMerged(){
// TODO fix me try {
super.testShiftMerged();
fail("expected exception");
} catch (RuntimeException e){
assertEquals("NotImplemented", e.getMessage());
}
} }
/**
* Bug 35084: cloning cells with formula
*
* The test is disabled because cloning of sheets is not supported in SXSSF
*/
@Override @Override
public void test35084(){ public void test35084(){
// TODO fix me try {
super.test35084();
fail("expected exception");
} catch (RuntimeException e){
assertEquals("NotImplemented", e.getMessage());
}
} }
@Override @Override
public void testDefaultColumnStyle() { public void testDefaultColumnStyle() {
// TODO fix me //TODO column styles are not yet supported by XSSF
} }
} }

View File

@ -28,18 +28,32 @@ public final class TestSXSSFWorkbook extends BaseTestWorkbook {
super(SXSSFITestDataProvider.instance); super(SXSSFITestDataProvider.instance);
} }
/**
* cloning of sheets is not supported in SXSSF
*/
@Override @Override
public void testCloneSheet() { public void testCloneSheet() {
// TODO figure out why the base class failes and remove me try {
} super.testCloneSheet();
fail("expected exception");
@Override } catch (RuntimeException e){
public void testUnicodeInAll() { assertEquals("NotImplemented", e.getMessage());
// TODO figure out why the base class failes and remove me }
} }
/**
* this test involves evaluation of formulas which isn't supported for SXSSF
*/
@Override @Override
public void testSetSheetName() { public void testSetSheetName() {
// this test involves formula evaluation which isn't supportd by SXSSF try {
super.testSetSheetName();
fail("expected exception");
} catch (Exception e){
assertEquals(
"Unexpected type of cell: class org.apache.poi.xssf.streaming.SXSSFCell. " +
"Only XSSFCells can be evaluated.", e.getMessage());
}
} }
} }