public void notifyUpdateCell(int bookIndex, int sheetIndex, EvaluationCell cell) {
FormulaCellCacheEntry fcce = _formulaCellCache.get(cell);
-
+
Loc loc = new Loc(bookIndex, sheetIndex, cell.getRowIndex(), cell.getColumnIndex());
PlainValueCellCacheEntry pcce = _plainCellCache.get(loc);
if (cell.getCellType() == HSSFCell.CELL_TYPE_FORMULA) {
if (fcce == null) {
+ fcce = new FormulaCellCacheEntry();
if (pcce == null) {
- updateAnyBlankReferencingFormulas(bookIndex, sheetIndex, cell.getRowIndex(), cell.getColumnIndex());
+ if (_evaluationListener != null) {
+ _evaluationListener.onChangeFromBlankValue(sheetIndex, cell.getRowIndex(),
+ cell.getColumnIndex(), cell, fcce);
+ }
+ updateAnyBlankReferencingFormulas(bookIndex, sheetIndex, cell.getRowIndex(),
+ cell.getColumnIndex());
}
- fcce = new FormulaCellCacheEntry();
_formulaCellCache.put(cell, fcce);
} else {
fcce.recurseClearCachedFormulaResults(_evaluationListener);
} else {
ValueEval value = WorkbookEvaluator.getValueFromNonFormulaCell(cell);
if (pcce == null) {
- if (fcce == null) {
- updateAnyBlankReferencingFormulas(bookIndex, sheetIndex, cell.getRowIndex(), cell.getColumnIndex());
- }
- pcce = new PlainValueCellCacheEntry(value);
- _plainCellCache.put(loc, pcce);
- if (_evaluationListener != null) {
- _evaluationListener.onReadPlainValue(sheetIndex, cell.getRowIndex(), cell.getColumnIndex(), pcce);
+ if (value != BlankEval.INSTANCE) {
+ // only cache non-blank values in the plain cell cache
+ // (dependencies on blank cells are managed by
+ // FormulaCellCacheEntry._usedBlankCellGroup)
+ pcce = new PlainValueCellCacheEntry(value);
+ if (fcce == null) {
+ if (_evaluationListener != null) {
+ _evaluationListener.onChangeFromBlankValue(sheetIndex, cell
+ .getRowIndex(), cell.getColumnIndex(), cell, pcce);
+ }
+ updateAnyBlankReferencingFormulas(bookIndex, sheetIndex,
+ cell.getRowIndex(), cell.getColumnIndex());
+ }
+ _plainCellCache.put(loc, pcce);
}
} else {
- if(pcce.updateValue(value)) {
+ if (pcce.updateValue(value)) {
pcce.recurseClearCachedFormulaResults(_evaluationListener);
}
+ if (value == BlankEval.INSTANCE) {
+ _plainCellCache.remove(loc);
+ }
}
if (fcce == null) {
// was plain cell before - no change of type
import org.apache.poi.hssf.record.formula.eval.ValueEval;
import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.hssf.usermodel.HSSFEvaluationTestHelper;
+import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator;
import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator.CellValue;
import org.apache.poi.hssf.util.CellReference;
import org.apache.poi.ss.formula.PlainCellCache.Loc;
}
public void onClearDependentCachedValue(ICacheEntry entry, int depth) {
EvaluationCell cell = (EvaluationCell) _formulaCellsByCacheEntry.get(entry);
- log("clear" + depth, cell.getRowIndex(), cell.getColumnIndex(), entry.getValue());
+ log("clear" + depth, cell.getRowIndex(), cell.getColumnIndex(), entry.getValue());
+ }
+
+ public void onChangeFromBlankValue(int sheetIndex, int rowIndex, int columnIndex,
+ EvaluationCell cell, ICacheEntry entry) {
+ log("changeFromBlank", rowIndex, columnIndex, entry.getValue());
+ if (entry.getValue() == null) { // hack to tell the difference between formula and plain value
+ // perhaps the API could be improved: onChangeFromBlankToValue, onChangeFromBlankToFormula
+ _formulaCellsByCacheEntry.put(entry, cell);
+ } else {
+ Loc loc = new Loc(0, sheetIndex, rowIndex, columnIndex);
+ _plainCellLocsByCacheEntry.put(entry, loc);
+ }
}
private void log(String tag, int rowIndex, int columnIndex, Object value) {
StringBuffer sb = new StringBuffer(64);
cell.setCellValue(value);
_evaluator.notifyUpdateCell(wrapCell(cell));
}
+ public void clearCell(String cellRefText) {
+ HSSFCell cell = getOrCreateCell(cellRefText);
+ cell.setCellType(HSSFCell.CELL_TYPE_BLANK);
+ _evaluator.notifyUpdateCell(wrapCell(cell));
+ }
public void setCellFormula(String cellRefText, String formulaText) {
HSSFCell cell = getOrCreateCell(cellRefText);
});
}
+ /**
+ * Make sure that when blank cells are changed to value/formula cells, any dependent formulas
+ * have their cached results cleared.
+ */
+ public void testBlankCellChangedToValueCell_bug46053() {
+ HSSFWorkbook wb = new HSSFWorkbook();
+ HSSFSheet sheet = wb.createSheet("Sheet1");
+ HSSFRow row = sheet.createRow(0);
+ HSSFCell cellA1 = row.createCell(0);
+ HSSFCell cellB1 = row.createCell(1);
+ HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(wb);
+
+ cellA1.setCellFormula("B1+2.2");
+ cellB1.setCellValue(1.5);
+
+ fe.notifyUpdateCell(cellA1);
+ fe.notifyUpdateCell(cellB1);
+
+ CellValue cv;
+ cv = fe.evaluate(cellA1);
+ assertEquals(3.7, cv.getNumberValue(), 0.0);
+
+ cellB1.setCellType(HSSFCell.CELL_TYPE_BLANK);
+ fe.notifyUpdateCell(cellB1);
+ cv = fe.evaluate(cellA1); // B1 was used to evaluate A1
+ assertEquals(2.2, cv.getNumberValue(), 0.0);
+
+ cellB1.setCellValue(0.4); // changing B1, so A1 cached result should be cleared
+ fe.notifyUpdateCell(cellB1);
+ cv = fe.evaluate(cellA1);
+ if (cv.getNumberValue() == 2.2) {
+ // looks like left-over cached result from before change to B1
+ throw new AssertionFailedError("Identified bug 46053");
+ }
+ assertEquals(2.6, cv.getNumberValue(), 0.0);
+ }
+
+ /**
+ * same use-case as the test for bug 46053, but checking trace values too
+ */
+ public void testBlankCellChangedToValueCell() {
+
+ MySheet ms = new MySheet();
+
+ ms.setCellFormula("A1", "B1+2.2");
+ ms.setCellValue("B1", 1.5);
+ ms.clearAllCachedResultValues();
+ ms.clearCell("B1");
+ ms.getAndClearLog();
+
+ confirmEvaluate(ms, "A1", 2.2);
+ confirmLog(ms, new String[] {
+ "start A1 B1+2.2",
+ "end A1 2.2",
+ });
+ ms.setCellValue("B1", 0.4);
+ confirmLog(ms, new String[] {
+ "changeFromBlank B1 0.4",
+ "clear A1",
+ });
+
+ confirmEvaluate(ms, "A1", 2.6);
+ confirmLog(ms, new String[] {
+ "start A1 B1+2.2",
+ "hit B1 0.4",
+ "end A1 2.6",
+ });
+ }
+
private static void confirmEvaluate(MySheet ms, String cellRefText, double expectedValue) {
ValueEval v = ms.evaluateCell(cellRefText);
assertEquals(NumberEval.class, v.getClass());