Fix for bug 44708. XSSFCell.getCellType() now returns CELL_TYPE_BLANK for numeric cells with no value.

git-svn-id: https://svn.apache.org/repos/asf/poi/branches/ooxml@645298 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Josh Micich 2008-04-06 20:27:40 +00:00
parent f3aa6517c0
commit 4e01cda4ae
7 changed files with 327 additions and 156 deletions

View File

@ -236,6 +236,7 @@ under the License.
<path refid="ooxml.classpath"/>
<pathelement location="${ooxml.output.dir}"/>
<pathelement location="${ooxml.output.test.dir}"/>
<pathelement location="${main.output.test.dir}"/> <!-- ooxml tests use some utilities from main tests -->
<pathelement location="${junit.jar1.dir}"/>
</path>
@ -790,7 +791,7 @@ under the License.
<batchtest todir="${ooxml.reports.test}">
<fileset dir="${ooxml.src.test}">
<include name="**/Test*.java"/>
<exclude name="**/AllTests.java"/>
<exclude name="**/All*Tests.java"/>
</fileset>
</batchtest>
</junit>

View File

@ -117,11 +117,12 @@ public interface Cell {
void setCellType(int cellType);
/**
* get the cells type (numeric, formula or string)
* @return the cell's type (e.g. numeric, formula or string)
* @see #CELL_TYPE_STRING
* @see #CELL_TYPE_NUMERIC
* @see #CELL_TYPE_FORMULA
* @see #CELL_TYPE_BOOLEAN
* @see #CELL_TYPE_BLANK
* @see #CELL_TYPE_ERROR
*/

View File

@ -35,8 +35,10 @@ import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTCell;
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTCellFormula;
import org.openxmlformats.schemas.spreadsheetml.x2006.main.STCellType;
public class XSSFCell implements Cell {
/**
*
*/
public final class XSSFCell implements Cell {
private static final String FALSE_AS_STRING = "0";
private static final String TRUE_AS_STRING = "1";
@ -70,7 +72,7 @@ public class XSSFCell implements Cell {
return this.sharedStringSource;
}
protected StylesSource getStylesSource() {
return this.stylesSource;
return this.stylesSource;
}
public boolean getBooleanCellValue() {
@ -85,7 +87,7 @@ public class XSSFCell implements Cell {
}
public Comment getCellComment() {
return row.getSheet().getCellComment(row.getRowNum(), getCellNum());
return row.getSheet().getCellComment(row.getRowNum(), getCellNum());
}
public String getCellFormula() {
@ -100,24 +102,32 @@ public class XSSFCell implements Cell {
}
public CellStyle getCellStyle() {
// Zero is the empty default
if(this.cell.getS() > 0) {
return stylesSource.getStyleAt(this.cell.getS());
}
return null;
// Zero is the empty default
if(this.cell.getS() > 0) {
return stylesSource.getStyleAt(this.cell.getS());
}
return null;
}
public int getCellType() {
// Detecting formulas is quite pesky,
// as they don't get their type set
if(this.cell.getF() != null) {
return CELL_TYPE_FORMULA;
}
// Detecting formulas is quite pesky,
// as they don't get their type set
if(this.cell.getF() != null) {
return CELL_TYPE_FORMULA;
}
switch (this.cell.getT().intValue()) {
case STCellType.INT_B:
return CELL_TYPE_BOOLEAN;
case STCellType.INT_N:
if(!cell.isSetV()) {
// ooxml does have a separate cell type of 'blank'. A blank cell gets encoded as
// (either not present or) a numeric cell with no value set.
// The formula evaluator (and perhaps other clients of this interface) needs to
// distinguish blank values which sometimes get translated into zero and sometimes
// empty string, depending on context
return CELL_TYPE_BLANK;
}
return CELL_TYPE_NUMERIC;
case STCellType.INT_E:
return CELL_TYPE_ERROR;
@ -152,7 +162,7 @@ public class XSSFCell implements Cell {
throw new NumberFormatException("You cannot get a error value from a non-error cell");
}
if (this.cell.isSetV()) {
return this.cell.getV();
return this.cell.getV();
}
return null;
}
@ -165,26 +175,26 @@ public class XSSFCell implements Cell {
throw new NumberFormatException("You cannot get a error value from a non-error cell");
}
if (this.cell.isSetV()) {
String errS = this.cell.getV();
if(errS.equals(Cell.ERROR_NULL.getStringRepr())) {
return Cell.ERROR_NULL.getType();
}
if(errS.equals(Cell.ERROR_DIV0.getStringRepr())) {
return Cell.ERROR_DIV0.getType();
}
if(errS.equals(Cell.ERROR_VALUE.getStringRepr())) {
return Cell.ERROR_VALUE.getType();
}
if(errS.equals(Cell.ERROR_REF.getStringRepr())) {
return Cell.ERROR_REF.getType();
}
if(errS.equals(Cell.ERROR_NAME.getStringRepr())) {
return Cell.ERROR_NAME.getType();
}
if(errS.equals(Cell.ERROR_NUM.getStringRepr())) {
return Cell.ERROR_NUM.getType();
}
return Cell.ERROR_NA.getType();
String errS = this.cell.getV();
if(errS.equals(Cell.ERROR_NULL.getStringRepr())) {
return Cell.ERROR_NULL.getType();
}
if(errS.equals(Cell.ERROR_DIV0.getStringRepr())) {
return Cell.ERROR_DIV0.getType();
}
if(errS.equals(Cell.ERROR_VALUE.getStringRepr())) {
return Cell.ERROR_VALUE.getType();
}
if(errS.equals(Cell.ERROR_REF.getStringRepr())) {
return Cell.ERROR_REF.getType();
}
if(errS.equals(Cell.ERROR_NAME.getStringRepr())) {
return Cell.ERROR_NAME.getType();
}
if(errS.equals(Cell.ERROR_NUM.getStringRepr())) {
return Cell.ERROR_NUM.getType();
}
return Cell.ERROR_NA.getType();
}
return 0;
}
@ -196,7 +206,24 @@ public class XSSFCell implements Cell {
if (this.cell.isSetV()) {
return Double.parseDouble(this.cell.getV());
}
return Double.NaN;
// else - cell is blank.
// TODO - behaviour in the case of blank cells.
// Revise spec, choose best alternative below, and comment why.
if (true) {
// returning NaN from a blank cell seems wrong
// there are a few junits which assert this behaviour, though.
return Double.NaN;
}
if (true) {
// zero is probably a more reasonable value.
return 0.0;
} else {
// or perhaps disallow reading value from blank cell.
throw new RuntimeException("You cannot get a numeric value from a blank cell");
}
// Note - it would be nice if the behaviour is consistent with getRichStringCellValue
// (i.e. whether to return empty string or throw exception).
}
public RichTextString getRichStringCellValue() {
@ -219,34 +246,34 @@ public class XSSFCell implements Cell {
}
public void setAsActiveCell() {
row.getSheet().setActiveCell(cell.getR());
row.getSheet().setActiveCell(cell.getR());
}
public void setCellComment(Comment comment) {
String cellRef =
new CellReference(row.getRowNum(), getCellNum()).formatAsString();
row.getSheet().setCellComment(cellRef, (XSSFComment)comment);
String cellRef =
new CellReference(row.getRowNum(), getCellNum()).formatAsString();
row.getSheet().setCellComment(cellRef, (XSSFComment)comment);
}
public void setCellErrorValue(byte value) {
if(value == Cell.ERROR_DIV0.getType()) {
setCellErrorValue(Cell.ERROR_DIV0);
} else if(value == Cell.ERROR_NA.getType()) {
setCellErrorValue(Cell.ERROR_NA);
} else if(value == Cell.ERROR_NAME.getType()) {
setCellErrorValue(Cell.ERROR_NAME);
} else if(value == Cell.ERROR_NULL.getType()) {
setCellErrorValue(Cell.ERROR_NULL);
} else if(value == Cell.ERROR_NUM.getType()) {
setCellErrorValue(Cell.ERROR_NUM);
} else if(value == Cell.ERROR_REF.getType()) {
setCellErrorValue(Cell.ERROR_REF);
} else if(value == Cell.ERROR_VALUE.getType()) {
setCellErrorValue(Cell.ERROR_VALUE);
} else {
logger.log(POILogger.WARN, "Unknown error type " + value + " specified, treating as #N/A");
setCellErrorValue(Cell.ERROR_NA);
}
if(value == Cell.ERROR_DIV0.getType()) {
setCellErrorValue(Cell.ERROR_DIV0);
} else if(value == Cell.ERROR_NA.getType()) {
setCellErrorValue(Cell.ERROR_NA);
} else if(value == Cell.ERROR_NAME.getType()) {
setCellErrorValue(Cell.ERROR_NAME);
} else if(value == Cell.ERROR_NULL.getType()) {
setCellErrorValue(Cell.ERROR_NULL);
} else if(value == Cell.ERROR_NUM.getType()) {
setCellErrorValue(Cell.ERROR_NUM);
} else if(value == Cell.ERROR_REF.getType()) {
setCellErrorValue(Cell.ERROR_REF);
} else if(value == Cell.ERROR_VALUE.getType()) {
setCellErrorValue(Cell.ERROR_VALUE);
} else {
logger.log(POILogger.WARN, "Unknown error type " + value + " specified, treating as #N/A");
setCellErrorValue(Cell.ERROR_NA);
}
}
public void setCellErrorValue(CELL_ERROR_TYPE errorType) {
if ((this.cell.getT() != STCellType.E) && (this.cell.getT() != STCellType.STR))
@ -273,7 +300,7 @@ public class XSSFCell implements Cell {
}
public void setCellNum(int num) {
setCellNum((short)num);
setCellNum((short)num);
}
public void setCellNum(short num) {
checkBounds(num);
@ -303,13 +330,13 @@ public class XSSFCell implements Cell {
}
public void setCellStyle(CellStyle style) {
if(style == null) {
this.cell.setS(0);
} else {
this.cell.setS(
row.getSheet().getWorkbook().getStylesSource().putStyle(style)
);
}
if(style == null) {
this.cell.setS(0);
} else {
this.cell.setS(
row.getSheet().getWorkbook().getStylesSource().putStyle(style)
);
}
}
public void setCellType(int cellType) {
@ -377,7 +404,7 @@ public class XSSFCell implements Cell {
* Returns the raw, underlying ooxml value for the cell
*/
public String getRawValue() {
return this.cell.getV();
return this.cell.getV();
}
/**
@ -396,14 +423,14 @@ public class XSSFCell implements Cell {
/**
* Creates an XSSFRichTextString for you.
*/
public RichTextString createRichTextString(String text) {
return new XSSFRichTextString(text);
}
public RichTextString createRichTextString(String text) {
return new XSSFRichTextString(text);
}
public Hyperlink getHyperlink() {
return row.getSheet().getHyperlink(row.getRowNum(), cellNum);
}
public void setHyperlink(Hyperlink hyperlink) {
row.getSheet().setCellHyperlink((XSSFHyperlink)hyperlink);
}
public Hyperlink getHyperlink() {
return row.getSheet().getHyperlink(row.getRowNum(), cellNum);
}
public void setHyperlink(Hyperlink hyperlink) {
row.getSheet().setCellHyperlink((XSSFHyperlink)hyperlink);
}
}

View File

@ -0,0 +1,54 @@
/* ====================================================================
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.xssf;
import junit.framework.Test;
import junit.framework.TestSuite;
import org.apache.poi.xssf.eventusermodel.TestXSSFReader;
import org.apache.poi.xssf.extractor.TestXSSFExcelExtractor;
import org.apache.poi.xssf.io.TestLoadSaveXSSF;
import org.apache.poi.xssf.model.TestCommentsTable;
import org.apache.poi.xssf.model.TestStylesTable;
import org.apache.poi.xssf.usermodel.AllXSSFUsermodelTests;
import org.apache.poi.xssf.util.TestCTColComparator;
import org.apache.poi.xssf.util.TestCellReference;
import org.apache.poi.xssf.util.TestNumericRanges;
/**
* Collects all tests for <tt>org.apache.poi.xssf</tt> and sub-packages.
*
* @author Josh Micich
*/
public final class AllXSSFTests {
public static Test suite() {
TestSuite result = new TestSuite(AllXSSFTests.class.getName());
result.addTest(AllXSSFUsermodelTests.suite());
result.addTestSuite(TestXSSFReader.class);
result.addTestSuite(TestXSSFExcelExtractor.class);
result.addTestSuite(TestLoadSaveXSSF.class);
result.addTestSuite(TestCommentsTable.class);
result.addTestSuite(TestStylesTable.class);
result.addTestSuite(TestCellReference.class);
result.addTestSuite(TestCTColComparator.class);
result.addTestSuite(TestNumericRanges.class);
return result;
}
}

View File

@ -0,0 +1,56 @@
/* ====================================================================
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.xssf.usermodel;
import junit.framework.Test;
import junit.framework.TestSuite;
import org.apache.poi.xssf.usermodel.extensions.TestXSSFBorder;
import org.apache.poi.xssf.usermodel.extensions.TestXSSFCellFill;
import org.apache.poi.xssf.usermodel.extensions.TestXSSFSheetComments;
import org.apache.poi.xssf.usermodel.helpers.TestColumnHelper;
import org.apache.poi.xssf.usermodel.helpers.TestHeaderFooterHelper;
/**
* Collects all tests for <tt>org.apache.poi.xssf.usermodel</tt> and sub-packages.
*
* @author Josh Micich
*/
public final class AllXSSFUsermodelTests {
public static Test suite() {
TestSuite result = new TestSuite(AllXSSFUsermodelTests.class.getName());
result.addTestSuite(TestXSSFBorder.class);
result.addTestSuite(TestXSSFCellFill.class);
result.addTestSuite(TestXSSFHeaderFooter.class);
result.addTestSuite(TestXSSFSheetComments.class);
result.addTestSuite(TestColumnHelper.class);
result.addTestSuite(TestHeaderFooterHelper.class);
result.addTestSuite(TestFormulaEvaluatorOnXSSF.class);
result.addTestSuite(TestXSSFCell.class);
result.addTestSuite(TestXSSFCellStyle.class);
result.addTestSuite(TestXSSFComment.class);
result.addTestSuite(TestXSSFDialogSheet.class);
result.addTestSuite(TestXSSFFormulaEvaluation.class);
result.addTestSuite(TestXSSFHeaderFooter.class);
result.addTestSuite(TestXSSFRow.class);
result.addTestSuite(TestXSSFSheet.class);
result.addTestSuite(TestXSSFWorkbook.class);
return result;
}
}

View File

@ -41,7 +41,6 @@ import org.openxml4j.opc.Package;
* Periodically, you should open FormulaEvalTestData.xls in
* Excel 2007, and re-save it as FormulaEvalTestData_Copy.xlsx
*
* Currently disabled, as doesn't work
*/
public final class TestFormulaEvaluatorOnXSSF extends TestCase {
@ -178,7 +177,7 @@ public final class TestFormulaEvaluatorOnXSSF extends TestCase {
* Disabled for now, as many things seem to break
* for XSSF, which is a shame
*/
public void DISABLEDtestFunctionsFromTestSpreadsheet() {
public void testFunctionsFromTestSpreadsheet() {
processFunctionGroup(SS.START_OPERATORS_ROW_INDEX, null);
processFunctionGroup(SS.START_FUNCTIONS_ROW_INDEX, null);
@ -261,8 +260,19 @@ public final class TestFormulaEvaluatorOnXSSF extends TestCase {
if (c == null || c.getCellType() != Cell.CELL_TYPE_FORMULA) {
continue;
}
if(isIgnoredFormulaTestCase(c.getCellFormula())) {
continue;
}
FormulaEvaluator.CellValue actualValue = evaluator.evaluate(c);
FormulaEvaluator.CellValue actualValue;
try {
actualValue = evaluator.evaluate(c);
} catch (RuntimeException e) {
_evaluationFailureCount ++;
printShortStackTrace(System.err, e);
result = Result.SOME_EVALUATIONS_FAILED;
continue;
}
Cell expectedValueCell = getExpectedValueCell(expectedValuesRow, colnum);
try {
@ -281,10 +291,29 @@ public final class TestFormulaEvaluatorOnXSSF extends TestCase {
return result;
}
/*
* TODO - these are all formulas which currently (Apr-2008) break on ooxml
*/
private static boolean isIgnoredFormulaTestCase(String cellFormula) {
if ("COLUMN(1:2)".equals(cellFormula) || "ROW(2:3)".equals(cellFormula)) {
// full row ranges are not parsed properly yet.
// These cases currently work in svn trunk because of another bug which causes the
// formula to get rendered as COLUMN($A$1:$IV$2) or ROW($A$2:$IV$3)
return true;
}
if ("ISREF(currentcell())".equals(cellFormula)) {
// currently throws NPE because unknown function "currentcell" causes name lookup
// Name lookup requires some equivalent object of the Workbook within xSSFWorkbook.
return true;
}
return false;
}
/**
* Useful to keep output concise when expecting many failures to be reported by this test case
*/
private static void printShortStackTrace(PrintStream ps, AssertionFailedError e) {
private static void printShortStackTrace(PrintStream ps, Throwable e) {
StackTraceElement[] stes = e.getStackTrace();
int startIx = 0;

View File

@ -24,8 +24,10 @@ import junit.framework.TestCase;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.xssf.usermodel.TestXSSFCell.DummySharedStringSource;
public class TestXSSFRow extends TestCase {
/**
* Tests for XSSFRow
*/
public final class TestXSSFRow extends TestCase {
/**
* Test adding cells to a row in various places and see if we can find them again.
@ -84,23 +86,25 @@ public class TestXSSFRow extends TestCase {
assertEquals(Cell.CELL_TYPE_STRING, cell5.getCellType());
}
public void testGetCell() throws Exception {
public void testGetCell() {
XSSFRow row = getSampleRow();
assertNotNull(row.getCell((short) 2));
assertNotNull(row.getCell((short) 3));
assertNotNull(row.getCell((short) 4));
assertEquals(Cell.CELL_TYPE_NUMERIC, row.getCell((short) 3).getCellType());
// cell3 may have been created as CELL_TYPE_NUMERIC, but since there is no numeric
// value set yet, its cell type is classified as 'blank'
assertEquals(Cell.CELL_TYPE_BLANK, row.getCell((short) 3).getCellType());
assertNull(row.getCell((short) 5));
}
public void testGetPhysicalNumberOfCells() throws Exception {
public void testGetPhysicalNumberOfCells() {
XSSFRow row = getSampleRow();
assertEquals(7, row.getPhysicalNumberOfCells());
}
public void testGetFirstCellNum() throws Exception {
// Test a row with some cells
public void testGetFirstCellNum() {
// Test a row with some cells
XSSFRow row = getSampleRow();
assertFalse(row.getFirstCellNum() == (short) 0);
assertEquals((short) 2, row.getFirstCellNum());
@ -115,7 +119,7 @@ public class TestXSSFRow extends TestCase {
assertEquals(-1, emptyRow.getFirstCellNum());
}
public void testLastCellNum() throws Exception {
public void testLastCellNum() {
XSSFRow row = getSampleRow();
assertEquals(100, row.getLastCellNum());
@ -124,25 +128,24 @@ public class TestXSSFRow extends TestCase {
assertFalse(row.getLastCellNum() == (short) 100);
}
public void testRemoveCell() throws Exception {
XSSFRow row = getSampleRow();
public void testRemoveCell() {
XSSFRow row = getSampleRow();
// Test removing the first cell
Cell firstCell = row.getCell((short) 2);
assertNotNull(firstCell);
assertEquals(7, row.getPhysicalNumberOfCells());
row.removeCell(firstCell);
assertEquals(6, row.getPhysicalNumberOfCells());
firstCell = row.getCell((short) 2);
assertNull(firstCell);
// Test removing the last cell
Cell lastCell = row.getCell((short) 100);
row.removeCell(lastCell);
// Test removing the first cell
Cell firstCell = row.getCell((short) 2);
assertNotNull(firstCell);
assertEquals(7, row.getPhysicalNumberOfCells());
row.removeCell(firstCell);
assertEquals(6, row.getPhysicalNumberOfCells());
firstCell = row.getCell((short) 2);
assertNull(firstCell);
// Test removing the last cell
Cell lastCell = row.getCell((short) 100);
row.removeCell(lastCell);
}
public void testGetSetHeight() throws Exception {
public void testGetSetHeight() {
XSSFRow row = getSampleRow();
// I assume that "ht" attribute value is in 'points', please verify that
// Test that no rowHeight is set
@ -150,9 +153,9 @@ public class TestXSSFRow extends TestCase {
// Set a rowHeight in twips (1/20th of a point) and test the new value
row.setHeight((short) 240);
assertEquals((short) 240, row.getHeight());
assertEquals((float) 12, row.getHeightInPoints());
assertEquals(12F, row.getHeightInPoints());
// Set a new rowHeight in points and test the new value
row.setHeightInPoints((float) 13);
row.setHeightInPoints(13F);
assertEquals((float) 13, row.getHeightInPoints());
assertEquals((short) 260, row.getHeight());
}
@ -168,19 +171,19 @@ public class TestXSSFRow extends TestCase {
* Method that returns a row with some sample cells
* @return row
*/
public XSSFRow getSampleRow() {
XSSFRow row = new XSSFRow(createParentObjects());
row.createCell((short) 2);
row.createCell((short) 3, Cell.CELL_TYPE_NUMERIC);
row.createCell((short) 4);
row.createCell((short) 6);
row.createCell((short) 7);
row.createCell((short) 8);
row.createCell((short) 100);
return row;
private static XSSFRow getSampleRow() {
XSSFRow row = new XSSFRow(createParentObjects());
row.createCell((short) 2);
row.createCell((short) 3, Cell.CELL_TYPE_NUMERIC);
row.createCell((short) 4);
row.createCell((short) 6);
row.createCell((short) 7);
row.createCell((short) 8);
row.createCell((short) 100);
return row;
}
private XSSFSheet createParentObjects() {
private static XSSFSheet createParentObjects() {
XSSFWorkbook wb = new XSSFWorkbook();
wb.setSharedStringSource(new DummySharedStringSource());
return new XSSFSheet(wb);