Fix inconsistent whitespace/indents

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1695604 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Nick Burch 2015-08-12 19:49:25 +00:00
parent 83bf96035c
commit a87cdf35cd

View File

@ -48,409 +48,405 @@ import org.apache.poi.ss.util.CellReference;
* *
*/ */
public final class TestFormulaEvaluatorBugs extends TestCase { public final class TestFormulaEvaluatorBugs extends TestCase {
private static final boolean OUTPUT_TEST_FILES = false;
private String tmpDirName;
private static final boolean OUTPUT_TEST_FILES = false; protected void setUp() {
private String tmpDirName; tmpDirName = System.getProperty("java.io.tmpdir");
}
protected void setUp() { /**
tmpDirName = System.getProperty("java.io.tmpdir"); * An odd problem with evaluateFormulaCell giving the
} * right values when file is opened, but changes
* to the source data in some versions of excel
* doesn't cause them to be updated. However, other
* versions of excel, and gnumeric, work just fine
* WARNING - tedious bug where you actually have to
* open up excel
*/
public void test44636() throws Exception {
// Open the existing file, tweak one value and
// re-calculate
/** HSSFWorkbook wb = HSSFTestDataSamples.openSampleWorkbook("44636.xls");
* An odd problem with evaluateFormulaCell giving the HSSFSheet sheet = wb.getSheetAt(0);
* right values when file is opened, but changes HSSFRow row = sheet.getRow(0);
* to the source data in some versions of excel
* doesn't cause them to be updated. However, other
* versions of excel, and gnumeric, work just fine
* WARNING - tedious bug where you actually have to
* open up excel
*/
public void test44636() throws Exception {
// Open the existing file, tweak one value and
// re-calculate
HSSFWorkbook wb = HSSFTestDataSamples.openSampleWorkbook("44636.xls"); row.getCell(0).setCellValue(4.2);
HSSFSheet sheet = wb.getSheetAt(0); row.getCell(2).setCellValue(25);
HSSFRow row = sheet.getRow(0);
row.getCell(0).setCellValue(4.2); HSSFFormulaEvaluator.evaluateAllFormulaCells(wb);
row.getCell(2).setCellValue(25); assertEquals(4.2 * 25, row.getCell(3).getNumericCellValue(), 0.0001);
HSSFFormulaEvaluator.evaluateAllFormulaCells(wb); FileOutputStream out;
assertEquals(4.2 * 25, row.getCell(3).getNumericCellValue(), 0.0001); if (OUTPUT_TEST_FILES) {
// Save
File existing = new File(tmpDirName, "44636-existing.xls");
out = new FileOutputStream(existing);
wb.write(out);
out.close();
System.err.println("Existing file for bug #44636 written to " + existing.toString());
}
// Now, do a new file from scratch
wb = new HSSFWorkbook();
sheet = wb.createSheet();
FileOutputStream out; row = sheet.createRow(0);
if (OUTPUT_TEST_FILES) { row.createCell(0).setCellValue(1.2);
// Save row.createCell(1).setCellValue(4.2);
File existing = new File(tmpDirName, "44636-existing.xls");
out = new FileOutputStream(existing);
wb.write(out);
out.close();
System.err.println("Existing file for bug #44636 written to " + existing.toString());
}
// Now, do a new file from scratch
wb = new HSSFWorkbook();
sheet = wb.createSheet();
row = sheet.createRow(0); row = sheet.createRow(1);
row.createCell(0).setCellValue(1.2); row.createCell(0).setCellFormula("SUM(A1:B1)");
row.createCell(1).setCellValue(4.2);
row = sheet.createRow(1); HSSFFormulaEvaluator.evaluateAllFormulaCells(wb);
row.createCell(0).setCellFormula("SUM(A1:B1)"); assertEquals(5.4, row.getCell(0).getNumericCellValue(), 0.0001);
HSSFFormulaEvaluator.evaluateAllFormulaCells(wb); if (OUTPUT_TEST_FILES) {
assertEquals(5.4, row.getCell(0).getNumericCellValue(), 0.0001); // Save
File scratch = new File(tmpDirName, "44636-scratch.xls");
out = new FileOutputStream(scratch);
wb.write(out);
out.close();
System.err.println("New file for bug #44636 written to " + scratch.toString());
}
}
if (OUTPUT_TEST_FILES) { /**
// Save * Bug 44297: 32767+32768 is evaluated to -1
File scratch = new File(tmpDirName, "44636-scratch.xls"); * Fix: IntPtg must operate with unsigned short. Reading signed short results in incorrect formula calculation
out = new FileOutputStream(scratch); * if a formula has values in the interval [Short.MAX_VALUE, (Short.MAX_VALUE+1)*2]
wb.write(out); *
out.close(); * @author Yegor Kozlov
System.err.println("New file for bug #44636 written to " + scratch.toString()); */
} public void test44297() {
}
/** HSSFWorkbook wb = HSSFTestDataSamples.openSampleWorkbook("44297.xls");
* Bug 44297: 32767+32768 is evaluated to -1
* Fix: IntPtg must operate with unsigned short. Reading signed short results in incorrect formula calculation
* if a formula has values in the interval [Short.MAX_VALUE, (Short.MAX_VALUE+1)*2]
*
* @author Yegor Kozlov
*/
public void test44297() {
HSSFWorkbook wb = HSSFTestDataSamples.openSampleWorkbook("44297.xls"); HSSFRow row;
HSSFCell cell;
HSSFRow row; HSSFSheet sheet = wb.getSheetAt(0);
HSSFCell cell;
HSSFSheet sheet = wb.getSheetAt(0); HSSFFormulaEvaluator eva = new HSSFFormulaEvaluator(wb);
HSSFFormulaEvaluator eva = new HSSFFormulaEvaluator(wb); row = sheet.getRow(0);
cell = row.getCell(0);
assertEquals("31+46", cell.getCellFormula());
assertEquals(77, eva.evaluate(cell).getNumberValue(), 0);
row = sheet.getRow(0); row = sheet.getRow(1);
cell = row.getCell(0); cell = row.getCell(0);
assertEquals("31+46", cell.getCellFormula()); assertEquals("30+53", cell.getCellFormula());
assertEquals(77, eva.evaluate(cell).getNumberValue(), 0); assertEquals(83, eva.evaluate(cell).getNumberValue(), 0);
row = sheet.getRow(1); row = sheet.getRow(2);
cell = row.getCell(0); cell = row.getCell(0);
assertEquals("30+53", cell.getCellFormula()); assertEquals("SUM(A1:A2)", cell.getCellFormula());
assertEquals(83, eva.evaluate(cell).getNumberValue(), 0); assertEquals(160, eva.evaluate(cell).getNumberValue(), 0);
row = sheet.getRow(2); row = sheet.getRow(4);
cell = row.getCell(0); cell = row.getCell(0);
assertEquals("SUM(A1:A2)", cell.getCellFormula()); assertEquals("32767+32768", cell.getCellFormula());
assertEquals(160, eva.evaluate(cell).getNumberValue(), 0); assertEquals(65535, eva.evaluate(cell).getNumberValue(), 0);
row = sheet.getRow(4); row = sheet.getRow(7);
cell = row.getCell(0); cell = row.getCell(0);
assertEquals("32767+32768", cell.getCellFormula()); assertEquals("32744+42333", cell.getCellFormula());
assertEquals(65535, eva.evaluate(cell).getNumberValue(), 0); assertEquals(75077, eva.evaluate(cell).getNumberValue(), 0);
row = sheet.getRow(7); row = sheet.getRow(8);
cell = row.getCell(0); cell = row.getCell(0);
assertEquals("32744+42333", cell.getCellFormula()); assertEquals("327680/32768", cell.getCellFormula());
assertEquals(75077, eva.evaluate(cell).getNumberValue(), 0); assertEquals(10, eva.evaluate(cell).getNumberValue(), 0);
row = sheet.getRow(8); row = sheet.getRow(9);
cell = row.getCell(0); cell = row.getCell(0);
assertEquals("327680/32768", cell.getCellFormula()); assertEquals("32767+32769", cell.getCellFormula());
assertEquals(10, eva.evaluate(cell).getNumberValue(), 0); assertEquals(65536, eva.evaluate(cell).getNumberValue(), 0);
row = sheet.getRow(9); row = sheet.getRow(10);
cell = row.getCell(0); cell = row.getCell(0);
assertEquals("32767+32769", cell.getCellFormula()); assertEquals("35000+36000", cell.getCellFormula());
assertEquals(65536, eva.evaluate(cell).getNumberValue(), 0); assertEquals(71000, eva.evaluate(cell).getNumberValue(), 0);
row = sheet.getRow(10); row = sheet.getRow(11);
cell = row.getCell(0); cell = row.getCell(0);
assertEquals("35000+36000", cell.getCellFormula()); assertEquals("-1000000-3000000", cell.getCellFormula());
assertEquals(71000, eva.evaluate(cell).getNumberValue(), 0); assertEquals(-4000000, eva.evaluate(cell).getNumberValue(), 0);
}
row = sheet.getRow(11); /**
cell = row.getCell(0); * Bug 44410: SUM(C:C) is valid in excel, and means a sum
assertEquals("-1000000-3000000", cell.getCellFormula()); * of all the rows in Column C
assertEquals(-4000000, eva.evaluate(cell).getNumberValue(), 0); */
} public void test44410() {
HSSFWorkbook wb = HSSFTestDataSamples.openSampleWorkbook("SingleLetterRanges.xls");
/** HSSFSheet sheet = wb.getSheetAt(0);
* Bug 44410: SUM(C:C) is valid in excel, and means a sum
* of all the rows in Column C
*
* @author Nick Burch
*/
public void test44410() {
HSSFWorkbook wb = HSSFTestDataSamples.openSampleWorkbook("SingleLetterRanges.xls"); HSSFFormulaEvaluator eva = new HSSFFormulaEvaluator(wb);
HSSFSheet sheet = wb.getSheetAt(0); // =index(C:C,2,1) -> 2
HSSFRow rowIDX = sheet.getRow(3);
// =sum(C:C) -> 6
HSSFRow rowSUM = sheet.getRow(4);
// =sum(C:D) -> 66
HSSFRow rowSUM2D = sheet.getRow(5);
HSSFFormulaEvaluator eva = new HSSFFormulaEvaluator(wb); // Test the sum
HSSFCell cellSUM = rowSUM.getCell(0);
// =index(C:C,2,1) -> 2 FormulaRecordAggregate frec = (FormulaRecordAggregate) cellSUM.getCellValueRecord();
HSSFRow rowIDX = sheet.getRow(3); Ptg[] ops = frec.getFormulaRecord().getParsedExpression();
// =sum(C:C) -> 6 assertEquals(2, ops.length);
HSSFRow rowSUM = sheet.getRow(4); assertEquals(AreaPtg.class, ops[0].getClass());
// =sum(C:D) -> 66 assertEquals(FuncVarPtg.class, ops[1].getClass());
HSSFRow rowSUM2D = sheet.getRow(5);
// Test the sum // Actually stored as C1 to C65536
HSSFCell cellSUM = rowSUM.getCell(0); // (last row is -1 === 65535)
AreaPtg ptg = (AreaPtg) ops[0];
assertEquals(2, ptg.getFirstColumn());
assertEquals(2, ptg.getLastColumn());
assertEquals(0, ptg.getFirstRow());
assertEquals(65535, ptg.getLastRow());
assertEquals("C:C", ptg.toFormulaString());
FormulaRecordAggregate frec = (FormulaRecordAggregate) cellSUM.getCellValueRecord(); // Will show as C:C, but won't know how many
Ptg[] ops = frec.getFormulaRecord().getParsedExpression(); // rows it covers as we don't have the sheet
assertEquals(2, ops.length); // to hand when turning the Ptgs into a string
assertEquals(AreaPtg.class, ops[0].getClass()); assertEquals("SUM(C:C)", cellSUM.getCellFormula());
assertEquals(FuncVarPtg.class, ops[1].getClass());
// Actually stored as C1 to C65536 // But the evaluator knows the sheet, so it
// (last row is -1 === 65535) // can do it properly
AreaPtg ptg = (AreaPtg) ops[0]; assertEquals(6, eva.evaluate(cellSUM).getNumberValue(), 0);
assertEquals(2, ptg.getFirstColumn());
assertEquals(2, ptg.getLastColumn());
assertEquals(0, ptg.getFirstRow());
assertEquals(65535, ptg.getLastRow());
assertEquals("C:C", ptg.toFormulaString());
// Will show as C:C, but won't know how many // Test the index
// rows it covers as we don't have the sheet // Again, the formula string will be right but
// to hand when turning the Ptgs into a string // lacking row count, evaluated will be right
assertEquals("SUM(C:C)", cellSUM.getCellFormula()); HSSFCell cellIDX = rowIDX.getCell(0);
assertEquals("INDEX(C:C,2,1)", cellIDX.getCellFormula());
assertEquals(2, eva.evaluate(cellIDX).getNumberValue(), 0);
// But the evaluator knows the sheet, so it // Across two colums
// can do it properly HSSFCell cellSUM2D = rowSUM2D.getCell(0);
assertEquals(6, eva.evaluate(cellSUM).getNumberValue(), 0); assertEquals("SUM(C:D)", cellSUM2D.getCellFormula());
assertEquals(66, eva.evaluate(cellSUM2D).getNumberValue(), 0);
}
// Test the index /**
// Again, the formula string will be right but * Tests that we can evaluate boolean cells properly
// lacking row count, evaluated will be right */
HSSFCell cellIDX = rowIDX.getCell(0); public void testEvaluateBooleanInCell_bug44508() {
assertEquals("INDEX(C:C,2,1)", cellIDX.getCellFormula()); HSSFWorkbook wb = new HSSFWorkbook();
assertEquals(2, eva.evaluate(cellIDX).getNumberValue(), 0); HSSFSheet sheet = wb.createSheet();
wb.setSheetName(0, "Sheet1");
HSSFRow row = sheet.createRow(0);
HSSFCell cell = row.createCell(0);
// Across two colums cell.setCellFormula("1=1");
HSSFCell cellSUM2D = rowSUM2D.getCell(0);
assertEquals("SUM(C:D)", cellSUM2D.getCellFormula());
assertEquals(66, eva.evaluate(cellSUM2D).getNumberValue(), 0);
}
/** HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(wb);
* Tests that we can evaluate boolean cells properly try {
*/ fe.evaluateInCell(cell);
public void testEvaluateBooleanInCell_bug44508() { } catch (NumberFormatException e) {
HSSFWorkbook wb = new HSSFWorkbook(); fail("Identified bug 44508");
HSSFSheet sheet = wb.createSheet(); }
wb.setSheetName(0, "Sheet1"); assertEquals(true, cell.getBooleanCellValue());
HSSFRow row = sheet.createRow(0); }
HSSFCell cell = row.createCell(0);
cell.setCellFormula("1=1"); public void testClassCast_bug44861() {
HSSFWorkbook wb = HSSFTestDataSamples.openSampleWorkbook("44861.xls");
HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(wb); // Check direct
try { HSSFFormulaEvaluator.evaluateAllFormulaCells(wb);
fe.evaluateInCell(cell);
} catch (NumberFormatException e) {
fail("Identified bug 44508");
}
assertEquals(true, cell.getBooleanCellValue());
}
public void testClassCast_bug44861() { // And via calls
HSSFWorkbook wb = HSSFTestDataSamples.openSampleWorkbook("44861.xls"); int numSheets = wb.getNumberOfSheets();
for (int i = 0; i < numSheets; i++) {
HSSFSheet s = wb.getSheetAt(i);
HSSFFormulaEvaluator eval = new HSSFFormulaEvaluator(wb);
// Check direct for (Iterator<Row> rows = s.rowIterator(); rows.hasNext();) {
HSSFFormulaEvaluator.evaluateAllFormulaCells(wb); HSSFRow r = (HSSFRow)rows.next();
for (Iterator<Cell> cells = r.cellIterator(); cells.hasNext();) {
HSSFCell c = (HSSFCell)cells.next();
eval.evaluateFormulaCell(c);
}
}
}
}
// And via calls public void testEvaluateInCellWithErrorCode_bug44950() {
int numSheets = wb.getNumberOfSheets(); HSSFWorkbook wb = new HSSFWorkbook();
for (int i = 0; i < numSheets; i++) { HSSFSheet sheet = wb.createSheet("Sheet1");
HSSFSheet s = wb.getSheetAt(i); HSSFRow row = sheet.createRow(1);
HSSFFormulaEvaluator eval = new HSSFFormulaEvaluator(wb); HSSFCell cell = row.createCell(0);
cell.setCellFormula("na()"); // this formula evaluates to an Excel error code '#N/A'
HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(wb);
try {
fe.evaluateInCell(cell);
} catch (NumberFormatException e) {
if (e.getMessage().equals("You cannot get an error value from a non-error cell")) {
throw new AssertionFailedError("Identified bug 44950 b");
}
throw e;
}
}
for (Iterator<Row> rows = s.rowIterator(); rows.hasNext();) { public void testDateWithNegativeParts_bug48528() {
HSSFRow r = (HSSFRow)rows.next(); HSSFWorkbook wb = new HSSFWorkbook();
for (Iterator<Cell> cells = r.cellIterator(); cells.hasNext();) { HSSFSheet sheet = wb.createSheet("Sheet1");
HSSFCell c = (HSSFCell)cells.next(); HSSFRow row = sheet.createRow(1);
eval.evaluateFormulaCell(c); HSSFCell cell = row.createCell(0);
} HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(wb);
}
}
}
public void testEvaluateInCellWithErrorCode_bug44950() { // 5th Feb 2012 = 40944
HSSFWorkbook wb = new HSSFWorkbook(); // 1st Feb 2012 = 40940
HSSFSheet sheet = wb.createSheet("Sheet1"); // 5th Jan 2012 = 40913
HSSFRow row = sheet.createRow(1); // 5th Dec 2011 = 40882
HSSFCell cell = row.createCell(0); // 5th Feb 2011 = 40579
cell.setCellFormula("na()"); // this formula evaluates to an Excel error code '#N/A'
HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(wb);
try {
fe.evaluateInCell(cell);
} catch (NumberFormatException e) {
if (e.getMessage().equals("You cannot get an error value from a non-error cell")) {
throw new AssertionFailedError("Identified bug 44950 b");
}
throw e;
}
}
public void testDateWithNegativeParts_bug48528() {
HSSFWorkbook wb = new HSSFWorkbook();
HSSFSheet sheet = wb.createSheet("Sheet1");
HSSFRow row = sheet.createRow(1);
HSSFCell cell = row.createCell(0);
HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(wb);
// 5th Feb 2012 = 40944
// 1st Feb 2012 = 40940
// 5th Jan 2012 = 40913
// 5th Dec 2011 = 40882
// 5th Feb 2011 = 40579
cell.setCellFormula("DATE(2012,2,1)");
fe.notifyUpdateCell(cell);
assertEquals(40940.0, fe.evaluate(cell).getNumberValue());
cell.setCellFormula("DATE(2012,2,1+4)");
fe.notifyUpdateCell(cell);
assertEquals(40944.0, fe.evaluate(cell).getNumberValue());
cell.setCellFormula("DATE(2012,2-1,1+4)");
fe.notifyUpdateCell(cell);
assertEquals(40913.0, fe.evaluate(cell).getNumberValue());
cell.setCellFormula("DATE(2012,2,1-27)");
fe.notifyUpdateCell(cell);
assertEquals(40913.0, fe.evaluate(cell).getNumberValue());
cell.setCellFormula("DATE(2012,2-2,1+4)");
fe.notifyUpdateCell(cell);
assertEquals(40882.0, fe.evaluate(cell).getNumberValue());
cell.setCellFormula("DATE(2012,2,1-58)");
fe.notifyUpdateCell(cell);
assertEquals(40882.0, fe.evaluate(cell).getNumberValue());
cell.setCellFormula("DATE(2012,2-12,1+4)");
fe.notifyUpdateCell(cell);
assertEquals(40579.0, fe.evaluate(cell).getNumberValue());
}
private static final class EvalListener extends EvaluationListener { cell.setCellFormula("DATE(2012,2,1)");
private int _countCacheHits; fe.notifyUpdateCell(cell);
private int _countCacheMisses; assertEquals(40940.0, fe.evaluate(cell).getNumberValue());
public EvalListener() { cell.setCellFormula("DATE(2012,2,1+4)");
_countCacheHits = 0; fe.notifyUpdateCell(cell);
_countCacheMisses = 0; assertEquals(40944.0, fe.evaluate(cell).getNumberValue());
}
public int getCountCacheHits() {
return _countCacheHits;
}
public int getCountCacheMisses() {
return _countCacheMisses;
}
public void onCacheHit(int sheetIndex, int srcRowNum, int srcColNum, ValueEval result) { cell.setCellFormula("DATE(2012,2-1,1+4)");
_countCacheHits++; fe.notifyUpdateCell(cell);
} assertEquals(40913.0, fe.evaluate(cell).getNumberValue());
public void onStartEvaluate(EvaluationCell cell, ICacheEntry entry) {
_countCacheMisses++;
}
}
/** cell.setCellFormula("DATE(2012,2,1-27)");
* The HSSFFormula evaluator performance benefits greatly from caching of intermediate cell values fe.notifyUpdateCell(cell);
*/ assertEquals(40913.0, fe.evaluate(cell).getNumberValue());
public void testSlowEvaluate45376() {
/*
* Note - to observe behaviour without caching, disable the call to
* updateValue() from FormulaCellCacheEntry.updateFormulaResult().
*/
// Firstly set up a sequence of formula cells where each depends on the previous multiple cell.setCellFormula("DATE(2012,2-2,1+4)");
// times. Without caching, each subsequent cell take about 4 times longer to evaluate. fe.notifyUpdateCell(cell);
HSSFWorkbook wb = new HSSFWorkbook(); assertEquals(40882.0, fe.evaluate(cell).getNumberValue());
HSSFSheet sheet = wb.createSheet("Sheet1");
HSSFRow row = sheet.createRow(0);
for(int i=1; i<10; i++) {
HSSFCell cell = row.createCell(i);
char prevCol = (char) ('A' + i-1);
String prevCell = prevCol + "1";
// this formula is inspired by the offending formula of the attachment for bug 45376
// IF(DATE(YEAR(A1),MONTH(A1)+1,1)<=$D$3,DATE(YEAR(A1),MONTH(A1)+1,1),NA()) etc
String formula = "IF(DATE(YEAR(" + prevCell + "),MONTH(" + prevCell + ")+1,1)<=$D$3," +
"DATE(YEAR(" + prevCell + "),MONTH(" + prevCell + ")+1,1),NA())";
cell.setCellFormula(formula);
}
Calendar cal = new GregorianCalendar(2000, 0, 1, 0, 0, 0);
row.createCell(0).setCellValue(cal);
// Choose cell A9 instead of A10, so that the failing test case doesn't take too long to execute. cell.setCellFormula("DATE(2012,2,1-58)");
HSSFCell cell = row.getCell(8); fe.notifyUpdateCell(cell);
EvalListener evalListener = new EvalListener(); assertEquals(40882.0, fe.evaluate(cell).getNumberValue());
WorkbookEvaluator evaluator = WorkbookEvaluatorTestHelper.createEvaluator(wb, evalListener);
ValueEval ve = evaluator.evaluate(HSSFEvaluationTestHelper.wrapCell(cell));
int evalCount = evalListener.getCountCacheMisses();
if (evalCount > 10) {
// Without caching, evaluating cell 'A9' takes 21845 evaluations which consumes
// much time (~3 sec on Core 2 Duo 2.2GHz)
// short-circuit-if optimisation cuts this down to 255 evaluations which is still too high
System.err.println("Cell A9 took " + evalCount + " intermediate evaluations");
throw new AssertionFailedError("Identifed bug 45376 - Formula evaluator should cache values");
}
// With caching, the evaluationCount is 8 which is exactly the
// number of formula cells that needed to be evaluated.
assertEquals(8, evalCount);
// The cache hits would be 24 if fully evaluating all arguments of the cell.setCellFormula("DATE(2012,2-12,1+4)");
// "IF()" functions (Each of the 8 formulas has 4 refs to formula cells fe.notifyUpdateCell(cell);
// which result in 1 cache miss and 3 cache hits). However with the assertEquals(40579.0, fe.evaluate(cell).getNumberValue());
// short-circuit-if optimisation, 2 of the cell refs get skipped }
// reducing this metric 8.
assertEquals(8, evalListener.getCountCacheHits());
// confirm the evaluation result too private static final class EvalListener extends EvaluationListener {
assertEquals(ErrorEval.NA, ve); private int _countCacheHits;
} private int _countCacheMisses;
@SuppressWarnings("resource") public EvalListener() {
_countCacheHits = 0;
_countCacheMisses = 0;
}
public int getCountCacheHits() {
return _countCacheHits;
}
public int getCountCacheMisses() {
return _countCacheMisses;
}
public void onCacheHit(int sheetIndex, int srcRowNum, int srcColNum, ValueEval result) {
_countCacheHits++;
}
public void onStartEvaluate(EvaluationCell cell, ICacheEntry entry) {
_countCacheMisses++;
}
}
/**
* The HSSFFormula evaluator performance benefits greatly from caching of intermediate cell values
*/
public void testSlowEvaluate45376() {
/*
* Note - to observe behaviour without caching, disable the call to
* updateValue() from FormulaCellCacheEntry.updateFormulaResult().
*/
// Firstly set up a sequence of formula cells where each depends on the previous multiple
// times. Without caching, each subsequent cell take about 4 times longer to evaluate.
HSSFWorkbook wb = new HSSFWorkbook();
HSSFSheet sheet = wb.createSheet("Sheet1");
HSSFRow row = sheet.createRow(0);
for(int i=1; i<10; i++) {
HSSFCell cell = row.createCell(i);
char prevCol = (char) ('A' + i-1);
String prevCell = prevCol + "1";
// this formula is inspired by the offending formula of the attachment for bug 45376
// IF(DATE(YEAR(A1),MONTH(A1)+1,1)<=$D$3,DATE(YEAR(A1),MONTH(A1)+1,1),NA()) etc
String formula = "IF(DATE(YEAR(" + prevCell + "),MONTH(" + prevCell + ")+1,1)<=$D$3," +
"DATE(YEAR(" + prevCell + "),MONTH(" + prevCell + ")+1,1),NA())";
cell.setCellFormula(formula);
}
Calendar cal = new GregorianCalendar(2000, 0, 1, 0, 0, 0);
row.createCell(0).setCellValue(cal);
// Choose cell A9 instead of A10, so that the failing test case doesn't take too long to execute.
HSSFCell cell = row.getCell(8);
EvalListener evalListener = new EvalListener();
WorkbookEvaluator evaluator = WorkbookEvaluatorTestHelper.createEvaluator(wb, evalListener);
ValueEval ve = evaluator.evaluate(HSSFEvaluationTestHelper.wrapCell(cell));
int evalCount = evalListener.getCountCacheMisses();
if (evalCount > 10) {
// Without caching, evaluating cell 'A9' takes 21845 evaluations which consumes
// much time (~3 sec on Core 2 Duo 2.2GHz)
// short-circuit-if optimisation cuts this down to 255 evaluations which is still too high
System.err.println("Cell A9 took " + evalCount + " intermediate evaluations");
throw new AssertionFailedError("Identifed bug 45376 - Formula evaluator should cache values");
}
// With caching, the evaluationCount is 8 which is exactly the
// number of formula cells that needed to be evaluated.
assertEquals(8, evalCount);
// The cache hits would be 24 if fully evaluating all arguments of the
// "IF()" functions (Each of the 8 formulas has 4 refs to formula cells
// which result in 1 cache miss and 3 cache hits). However with the
// short-circuit-if optimisation, 2 of the cell refs get skipped
// reducing this metric 8.
assertEquals(8, evalListener.getCountCacheHits());
// confirm the evaluation result too
assertEquals(ErrorEval.NA, ve);
}
@SuppressWarnings("resource")
public void test55747_55324() throws Exception { public void test55747_55324() throws Exception {
HSSFWorkbook wb = new HSSFWorkbook(); HSSFWorkbook wb = new HSSFWorkbook();
HSSFFormulaEvaluator ev = wb.getCreationHelper().createFormulaEvaluator(); HSSFFormulaEvaluator ev = wb.getCreationHelper().createFormulaEvaluator();
HSSFSheet ws = wb.createSheet(); HSSFSheet ws = wb.createSheet();
HSSFRow row = ws.createRow(0); HSSFRow row = ws.createRow(0);
HSSFCell cell; HSSFCell cell;
// Our test value // Our test value
cell = row.createCell(0); cell = row.createCell(0);
cell.setCellValue("abc"); cell.setCellValue("abc");
// Lots of IF cases // Lots of IF cases
cell = row.createCell(1); cell = row.createCell(1);
cell.setCellFormula("IF(A1<>\"\",MID(A1,1,2),\"X\")");//if(expr,func,val) cell.setCellFormula("IF(A1<>\"\",MID(A1,1,2),\"X\")");//if(expr,func,val)
cell = row.createCell(2); cell = row.createCell(2);
cell.setCellFormula("IF(A1<>\"\",\"A\",\"B\")");// if(expr,val,val) cell.setCellFormula("IF(A1<>\"\",\"A\",\"B\")");// if(expr,val,val)
cell = row.createCell(3); cell = row.createCell(3);
cell.setCellFormula("IF(A1=\"\",\"X\",MID(A1,1,2))");//if(expr,val,func), cell.setCellFormula("IF(A1=\"\",\"X\",MID(A1,1,2))");//if(expr,val,func),
cell = row.createCell(4); cell = row.createCell(4);
cell.setCellFormula("IF(A1<>\"\",\"X\",MID(A1,1,2))");//if(expr,val,func), cell.setCellFormula("IF(A1<>\"\",\"X\",MID(A1,1,2))");//if(expr,val,func),
cell = row.createCell(5); cell = row.createCell(5);
cell.setCellFormula("IF(A1=\"\",MID(A1,1,2),MID(A1,2,2))");//if(exp,func,func) cell.setCellFormula("IF(A1=\"\",MID(A1,1,2),MID(A1,2,2))");//if(exp,func,func)
cell = row.createCell(6); cell = row.createCell(6);
cell.setCellFormula("IF(A1<>\"\",MID(A1,1,2),MID(A1,2,2))");//if(exp,func,func) cell.setCellFormula("IF(A1<>\"\",MID(A1,1,2),MID(A1,2,2))");//if(exp,func,func)
cell = row.createCell(7); cell = row.createCell(7);
cell.setCellFormula("IF(MID(A1,1,2)<>\"\",\"A\",\"B\")");//if(func_expr,val,val) cell.setCellFormula("IF(MID(A1,1,2)<>\"\",\"A\",\"B\")");//if(func_expr,val,val)
// And some MID ones just to check // And some MID ones just to check
row = ws.createRow(1); row = ws.createRow(1);
cell = row.createCell(1); cell = row.createCell(1);
@ -461,14 +457,14 @@ public final class TestFormulaEvaluatorBugs extends TestCase {
cell.setCellFormula("MID(A1,2,1)"); cell.setCellFormula("MID(A1,2,1)");
cell = row.createCell(4); cell = row.createCell(4);
cell.setCellFormula("MID(A1,3,1)"); cell.setCellFormula("MID(A1,3,1)");
// Evaluate // Evaluate
ev.evaluateAll(); ev.evaluateAll();
// Save and re-load // Save and re-load
wb = HSSFTestDataSamples.writeOutAndReadBack(wb); wb = HSSFTestDataSamples.writeOutAndReadBack(wb);
ws = wb.getSheetAt(0); ws = wb.getSheetAt(0);
// Check the MID Ptgs in Row 2 have V RefPtgs for A1 // Check the MID Ptgs in Row 2 have V RefPtgs for A1
row = ws.getRow(1); row = ws.getRow(1);
for (int i=1; i<=4; i++) { for (int i=1; i<=4; i++) {
@ -479,21 +475,21 @@ public final class TestFormulaEvaluatorBugs extends TestCase {
assertEquals("MID", ((FuncPtg)ptgs[3]).getName()); assertEquals("MID", ((FuncPtg)ptgs[3]).getName());
assertRefPtgA1('V', ptgs, 0); assertRefPtgA1('V', ptgs, 0);
} }
// Now check the IF formulas // Now check the IF formulas
row = ws.getRow(0); row = ws.getRow(0);
// H1, MID is used in the expression IF checks, so A1 should be V // H1, MID is used in the expression IF checks, so A1 should be V
cell = row.getCell(CellReference.convertColStringToIndex("H")); cell = row.getCell(CellReference.convertColStringToIndex("H"));
assertRefPtgA1('V', getPtgs(cell), 0); assertRefPtgA1('V', getPtgs(cell), 0);
// E1, MID is used in the FALSE route, so: // E1, MID is used in the FALSE route, so:
// A1 should be V in the IF check // A1 should be V in the IF check
// A1 should be R in the FALSE route // A1 should be R in the FALSE route
cell = row.getCell(CellReference.convertColStringToIndex("E")); cell = row.getCell(CellReference.convertColStringToIndex("E"));
assertRefPtgA1('V', getPtgs(cell), 0); assertRefPtgA1('V', getPtgs(cell), 0);
assertRefPtgA1('R', getPtgs(cell), 6); assertRefPtgA1('R', getPtgs(cell), 6);
// Check that, for B1, D1, F1 and G1, the references to A1 // Check that, for B1, D1, F1 and G1, the references to A1
// from all of IF check, True and False are V // from all of IF check, True and False are V
cell = row.getCell(CellReference.convertColStringToIndex("B")); cell = row.getCell(CellReference.convertColStringToIndex("B"));
@ -537,19 +533,19 @@ public final class TestFormulaEvaluatorBugs extends TestCase {
//FileOutputStream out = new FileOutputStream("/tmp/test.xls"); //FileOutputStream out = new FileOutputStream("/tmp/test.xls");
//wb.write(out); //wb.write(out);
//out.close(); //out.close();
} }
private Ptg[] getPtgs(HSSFCell cell) { private Ptg[] getPtgs(HSSFCell cell) {
assertEquals(HSSFCell.CELL_TYPE_FORMULA, cell.getCellType()); assertEquals(HSSFCell.CELL_TYPE_FORMULA, cell.getCellType());
assertEquals(FormulaRecordAggregate.class, cell.getCellValueRecord().getClass()); assertEquals(FormulaRecordAggregate.class, cell.getCellValueRecord().getClass());
FormulaRecordAggregate agg = (FormulaRecordAggregate)cell.getCellValueRecord(); FormulaRecordAggregate agg = (FormulaRecordAggregate)cell.getCellValueRecord();
FormulaRecord rec = agg.getFormulaRecord(); FormulaRecord rec = agg.getFormulaRecord();
return rec.getParsedExpression(); return rec.getParsedExpression();
} }
private void assertRefPtgA1(char rv, Ptg[] ptgs, int at) { private void assertRefPtgA1(char rv, Ptg[] ptgs, int at) {
Ptg ptg = ptgs[at]; Ptg ptg = ptgs[at];
assertEquals(RefPtg.class, ptg.getClass()); assertEquals(RefPtg.class, ptg.getClass());
assertEquals(0, ((RefPtg)ptg).getRow()); assertEquals(0, ((RefPtg)ptg).getRow());
assertEquals(0, ((RefPtg)ptg).getColumn()); assertEquals(0, ((RefPtg)ptg).getColumn());
assertEquals(rv, ((RefPtg)ptg).getRVAType()); assertEquals(rv, ((RefPtg)ptg).getRVAType());
} }
} }