Bug 59132: Adjust implementation of COUNTBLANK to be conforming to Excel, empty strings are counted as blank as well

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1737009 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Dominik Stadler 2016-03-29 14:53:44 +00:00
parent 57aa67b524
commit c23c89a3da
4 changed files with 68 additions and 28 deletions

View File

@ -18,10 +18,7 @@
package org.apache.poi.ss.formula.functions; package org.apache.poi.ss.formula.functions;
import org.apache.poi.ss.formula.ThreeDEval; import org.apache.poi.ss.formula.ThreeDEval;
import org.apache.poi.ss.formula.eval.BlankEval; import org.apache.poi.ss.formula.eval.*;
import org.apache.poi.ss.formula.eval.NumberEval;
import org.apache.poi.ss.formula.eval.RefEval;
import org.apache.poi.ss.formula.eval.ValueEval;
import org.apache.poi.ss.formula.functions.CountUtils.I_MatchPredicate; import org.apache.poi.ss.formula.functions.CountUtils.I_MatchPredicate;
/** /**
@ -54,7 +51,10 @@ public final class Countblank extends Fixed1ArgFunction {
public boolean matches(ValueEval valueEval) { public boolean matches(ValueEval valueEval) {
// Note - only BlankEval counts // Note - only BlankEval counts
return valueEval == BlankEval.instance; return valueEval == BlankEval.instance ||
// see https://support.office.com/en-us/article/COUNTBLANK-function-6a92d772-675c-4bee-b346-24af6bd3ac22
// "Cells with formulas that return "" (empty text) are also counted."
(valueEval instanceof StringEval && "".equals(((StringEval)valueEval).getStringValue()));
} }
}; };
} }

View File

@ -362,9 +362,11 @@ public final class TestXSSFBugs extends BaseTestBugzillaIssues {
assertEquals(true, cs.getCoreXf().getApplyFill()); assertEquals(true, cs.getCoreXf().getApplyFill());
XSSFCellFill fg = wb.getStylesSource().getFillAt(2); XSSFCellFill fg = wb.getStylesSource().getFillAt(2);
assertNotNull(fg.getFillForegroundColor());
assertEquals(0, fg.getFillForegroundColor().getIndexed()); assertEquals(0, fg.getFillForegroundColor().getIndexed());
assertEquals(0.0, fg.getFillForegroundColor().getTint(), 0); assertEquals(0.0, fg.getFillForegroundColor().getTint(), 0);
assertEquals("FFFF0000", fg.getFillForegroundColor().getARGBHex()); assertEquals("FFFF0000", fg.getFillForegroundColor().getARGBHex());
assertNotNull(fg.getFillBackgroundColor());
assertEquals(64, fg.getFillBackgroundColor().getIndexed()); assertEquals(64, fg.getFillBackgroundColor().getIndexed());
// Now look higher up // Now look higher up
@ -1374,6 +1376,7 @@ public final class TestXSSFBugs extends BaseTestBugzillaIssues {
* DISABLED As we can't currently evaluate these * DISABLED As we can't currently evaluate these
*/ */
@Ignore @Ignore
@Test
public void bug48703() throws IOException { public void bug48703() throws IOException {
XSSFWorkbook wb = XSSFTestDataSamples.openSampleWorkbook("48703.xlsx"); XSSFWorkbook wb = XSSFTestDataSamples.openSampleWorkbook("48703.xlsx");
XSSFSheet sheet = wb.getSheetAt(0); XSSFSheet sheet = wb.getSheetAt(0);
@ -1675,16 +1678,14 @@ public final class TestXSSFBugs extends BaseTestBugzillaIssues {
saveAndReloadReport(wb, xlsOutput); saveAndReloadReport(wb, xlsOutput);
Row newRow = null;
Cell newCell = null;
// 2) attempt to create a new row IN PLACE of a removed row by a negative shift causes corrupted // 2) attempt to create a new row IN PLACE of a removed row by a negative shift causes corrupted
// xlsx file with unreadable data in the negative shifted row. // xlsx file with unreadable data in the negative shifted row.
// NOTE it's ok to create any other row. // NOTE it's ok to create any other row.
newRow = testSheet.createRow(3); Row newRow = testSheet.createRow(3);
saveAndReloadReport(wb, xlsOutput); saveAndReloadReport(wb, xlsOutput);
newCell = newRow.createCell(0); Cell newCell = newRow.createCell(0);
saveAndReloadReport(wb, xlsOutput); saveAndReloadReport(wb, xlsOutput);
@ -1962,8 +1963,6 @@ public final class TestXSSFBugs extends BaseTestBugzillaIssues {
/** /**
* .xlsb files are not supported, but we should generate a helpful * .xlsb files are not supported, but we should generate a helpful
* error message if given one * error message if given one
* @throws InvalidFormatException
* @throws
*/ */
@Test @Test
public void bug56800_xlsb() throws IOException, InvalidFormatException { public void bug56800_xlsb() throws IOException, InvalidFormatException {
@ -2226,11 +2225,7 @@ public final class TestXSSFBugs extends BaseTestBugzillaIssues {
//saveWorkbook(wb, fileName); //saveWorkbook(wb, fileName);
XSSFWorkbook wbBack = XSSFTestDataSamples.writeOutAndReadBack(wb); XSSFWorkbook wbBack = XSSFTestDataSamples.writeOutAndReadBack(wb);
try { wbBack.close();
} finally {
wbBack.close();
}
} finally { } finally {
wb.close(); wb.close();
} }
@ -2246,11 +2241,7 @@ public final class TestXSSFBugs extends BaseTestBugzillaIssues {
//saveWorkbook(wb, fileName); //saveWorkbook(wb, fileName);
XSSFWorkbook wbBack = XSSFTestDataSamples.writeOutAndReadBack(wb); XSSFWorkbook wbBack = XSSFTestDataSamples.writeOutAndReadBack(wb);
try { wbBack.close();
} finally {
wbBack.close();
}
} finally { } finally {
wb.close(); wb.close();
} }
@ -2511,6 +2502,7 @@ public final class TestXSSFBugs extends BaseTestBugzillaIssues {
// avoid OOM in gump run // avoid OOM in gump run
File file = XSSFTestDataSamples.writeOutAndClose(wb, "bug57880"); File file = XSSFTestDataSamples.writeOutAndClose(wb, "bug57880");
//noinspection UnusedAssignment
wb = null; wb = null;
// Garbage collection may happen here // Garbage collection may happen here
@ -2968,4 +2960,54 @@ public final class TestXSSFBugs extends BaseTestBugzillaIssues {
createXls(); createXls();
createXlsx(); createXlsx();
} }
@Test
public void test59132() throws IOException {
Workbook workbook = XSSFTestDataSamples.openSampleWorkbook("59132.xlsx");
Sheet worksheet = workbook.getSheet("sheet1");
// B3
Row row = worksheet.getRow(2);
Cell cell = row.getCell(1);
cell.setCellValue((String)null);
FormulaEvaluator evaluator = workbook.getCreationHelper().createFormulaEvaluator();
// B3
row = worksheet.getRow(2);
cell = row.getCell(1);
assertEquals(Cell.CELL_TYPE_BLANK, cell.getCellType());
assertEquals(-1, evaluator.evaluateFormulaCell(cell));
// A3
row = worksheet.getRow(2);
cell = row.getCell(0);
assertEquals(Cell.CELL_TYPE_FORMULA, cell.getCellType());
assertEquals("IF(ISBLANK(B3),\"\",B3)", cell.getCellFormula());
assertEquals(Cell.CELL_TYPE_STRING, evaluator.evaluateFormulaCell(cell));
CellValue value = evaluator.evaluate(cell);
assertEquals("", value.getStringValue());
// A5
row = worksheet.getRow(4);
cell = row.getCell(0);
assertEquals(Cell.CELL_TYPE_FORMULA, cell.getCellType());
assertEquals("COUNTBLANK(A1:A4)", cell.getCellFormula());
assertEquals(Cell.CELL_TYPE_NUMERIC, evaluator.evaluateFormulaCell(cell));
value = evaluator.evaluate(cell);
assertEquals(1.0, value.getNumberValue(), 0.1);
/*FileOutputStream output = new FileOutputStream("C:\\temp\\59132.xlsx");
try {
workbook.write(output);
} finally {
output.close();
}*/
workbook.close();
}
} }

View File

@ -47,35 +47,33 @@ public final class TestCountFuncs extends TestCase {
private static final String NULL = null; private static final String NULL = null;
public void testCountBlank() { public void testCountBlank() {
AreaEval range; AreaEval range;
ValueEval[] values; ValueEval[] values;
values = new ValueEval[] { values = new ValueEval[] {
new NumberEval(0), new NumberEval(0),
new StringEval(""), // note - does not match blank new StringEval(""), // note - does match blank
BoolEval.TRUE, BoolEval.TRUE,
BoolEval.FALSE, BoolEval.FALSE,
ErrorEval.DIV_ZERO, ErrorEval.DIV_ZERO,
BlankEval.instance, BlankEval.instance,
}; };
range = EvalFactory.createAreaEval("A1:B3", values); range = EvalFactory.createAreaEval("A1:B3", values);
confirmCountBlank(1, range); confirmCountBlank(2, range);
values = new ValueEval[] { values = new ValueEval[] {
new NumberEval(0), new NumberEval(0),
new StringEval(""), // note - does not match blank new StringEval(""), // does match blank
BlankEval.instance, BlankEval.instance,
BoolEval.FALSE, BoolEval.FALSE,
BoolEval.TRUE, BoolEval.TRUE,
BlankEval.instance, BlankEval.instance,
}; };
range = EvalFactory.createAreaEval("A1:B3", values); range = EvalFactory.createAreaEval("A1:B3", values);
confirmCountBlank(2, range); confirmCountBlank(3, range);
} }
public void testCountA() { public void testCountA() {
ValueEval[] args; ValueEval[] args;
args = new ValueEval[] { args = new ValueEval[] {
@ -105,7 +103,6 @@ public final class TestCountFuncs extends TestCase {
} }
public void testCountIf() { public void testCountIf() {
AreaEval range; AreaEval range;
ValueEval[] values; ValueEval[] values;
@ -143,6 +140,7 @@ public final class TestCountFuncs extends TestCase {
public void testCriteriaPredicateNe_Bug46647() { public void testCriteriaPredicateNe_Bug46647() {
I_MatchPredicate mp = Countif.createCriteriaPredicate(new StringEval("<>aa"), 0, 0); I_MatchPredicate mp = Countif.createCriteriaPredicate(new StringEval("<>aa"), 0, 0);
assertNotNull(mp);
StringEval seA = new StringEval("aa"); // this should not match the criteria '<>aa' StringEval seA = new StringEval("aa"); // this should not match the criteria '<>aa'
StringEval seB = new StringEval("bb"); // this should match StringEval seB = new StringEval("bb"); // this should match
if (mp.matches(seA) && !mp.matches(seB)) { if (mp.matches(seA) && !mp.matches(seB)) {

Binary file not shown.