From 6b433ae8b34b8c32a0457d74fe3b023222fe7945 Mon Sep 17 00:00:00 2001
From: Greg Woolsey
Date: Fri, 19 Oct 2018 01:11:47 +0000
Subject: [PATCH] #62834 FormulaEvaluator.evaluateInCell() throws Exception
added cell type = formula check when looping through the shared formula range, to ignore any non-formula cells.
Also refactored a bit to enable passing in the evaluation context, as getCellFormula() uses it behind the scenes when evaluating a shared formula cell (has to shift the formula references based on the master cell). Review of these changes is welcome, as always.
Checked all other code referencing the "SHARED" enum, and didn't see anything else that dealt with formula cell values and thus would need to notice non-formula cells.
Added unit test based on the failing file from Bugzilla. Test failed until the fixed code was in place.
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1844295 13f79535-47bb-0310-9956-ffa450edef68
---
.../poi/ss/formula/BaseFormulaEvaluator.java | 26 ++++++++++-
.../org/apache/poi/ss/usermodel/Sheet.java | 5 +-
.../usermodel/BaseXSSFFormulaEvaluator.java | 9 ++++
.../apache/poi/xssf/usermodel/XSSFCell.java | 23 ++++++++--
.../apache/poi/xssf/usermodel/XSSFSheet.java | 8 ++--
.../usermodel/TestXSSFFormulaEvaluation.java | 43 +++++++++++++++++-
test-data/spreadsheet/62834.xlsx | Bin 0 -> 9629 bytes
7 files changed, 102 insertions(+), 12 deletions(-)
create mode 100644 test-data/spreadsheet/62834.xlsx
diff --git a/src/java/org/apache/poi/ss/formula/BaseFormulaEvaluator.java b/src/java/org/apache/poi/ss/formula/BaseFormulaEvaluator.java
index 51a99d5950..ee0615057d 100644
--- a/src/java/org/apache/poi/ss/formula/BaseFormulaEvaluator.java
+++ b/src/java/org/apache/poi/ss/formula/BaseFormulaEvaluator.java
@@ -64,6 +64,14 @@ public abstract class BaseFormulaEvaluator implements FormulaEvaluator, Workbook
return _bookEvaluator;
}
+ /**
+ * internal use
+ * @return evaluation workbook
+ */
+ protected EvaluationWorkbook getEvaluationWorkbook() {
+ return _bookEvaluator.getWorkbook();
+ }
+
/**
* Should be called whenever there are major changes (e.g. moving sheets) to input cells
* in the evaluated workbook. If performance is not critical, a single call to this method
@@ -208,14 +216,19 @@ public abstract class BaseFormulaEvaluator implements FormulaEvaluator, Workbook
return evaluateFormulaCell(cell);
}
- protected static void setCellType(Cell cell, CellValue cv) {
+ /**
+ * set the cell type
+ * @param cell
+ * @param cv
+ */
+ protected void setCellType(Cell cell, CellValue cv) {
CellType cellType = cv.getCellType();
switch (cellType) {
case BOOLEAN:
case ERROR:
case NUMERIC:
case STRING:
- cell.setCellType(cellType);
+ setCellType(cell, cellType);
return;
case BLANK:
// never happens - blanks eventually get translated to zero
@@ -227,6 +240,15 @@ public abstract class BaseFormulaEvaluator implements FormulaEvaluator, Workbook
throw new IllegalStateException("Unexpected cell value type (" + cellType + ")");
}
}
+
+ /**
+ * Override if a different variation is needed, e.g. passing the evaluator to the cell method
+ * @param cell
+ * @param cellType
+ */
+ protected void setCellType(Cell cell, CellType cellType) {
+ cell.setCellType(cellType);
+ }
protected abstract RichTextString createRichTextString(String str);
diff --git a/src/java/org/apache/poi/ss/usermodel/Sheet.java b/src/java/org/apache/poi/ss/usermodel/Sheet.java
index 7e3eb3c574..d9dd4ec132 100644
--- a/src/java/org/apache/poi/ss/usermodel/Sheet.java
+++ b/src/java/org/apache/poi/ss/usermodel/Sheet.java
@@ -1006,7 +1006,10 @@ public interface Sheet extends Iterable {
/**
* Sets array formula to specified region for result.
- *
+ *
+ * Note if there are shared formulas this will invalidate any
+ * {@link FormulaEvaluator} instances based on this workbook
+ *
* @param formula text representation of the formula
* @param range Region of array formula for result.
* @return the {@link CellRange} of cells affected by this change
diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/BaseXSSFFormulaEvaluator.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/BaseXSSFFormulaEvaluator.java
index adb951d77a..0e41e50cb5 100644
--- a/src/ooxml/java/org/apache/poi/xssf/usermodel/BaseXSSFFormulaEvaluator.java
+++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/BaseXSSFFormulaEvaluator.java
@@ -19,6 +19,7 @@ package org.apache.poi.xssf.usermodel;
import org.apache.poi.ss.formula.BaseFormulaEvaluator;
import org.apache.poi.ss.formula.EvaluationCell;
+import org.apache.poi.ss.formula.EvaluationWorkbook;
import org.apache.poi.ss.formula.WorkbookEvaluator;
import org.apache.poi.ss.formula.eval.BoolEval;
import org.apache.poi.ss.formula.eval.ErrorEval;
@@ -26,6 +27,7 @@ import org.apache.poi.ss.formula.eval.NumberEval;
import org.apache.poi.ss.formula.eval.StringEval;
import org.apache.poi.ss.formula.eval.ValueEval;
import org.apache.poi.ss.usermodel.Cell;
+import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.ss.usermodel.CellValue;
import org.apache.poi.ss.usermodel.RichTextString;
@@ -69,4 +71,11 @@ public abstract class BaseXSSFFormulaEvaluator extends BaseFormulaEvaluator {
}
throw new RuntimeException("Unexpected eval class (" + eval.getClass().getName() + ")");
}
+
+ protected void setCellType(Cell cell, CellType cellType) {
+ EvaluationWorkbook evaluationWorkbook = getEvaluationWorkbook();
+ BaseXSSFEvaluationWorkbook xewb = BaseXSSFEvaluationWorkbook.class.isAssignableFrom(evaluationWorkbook.getClass()) ? (BaseXSSFEvaluationWorkbook) evaluationWorkbook : null;
+
+ ((XSSFCell) cell).setCellType(cellType, xewb);
+ }
}
diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFCell.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFCell.java
index b3113203a6..a9490694c0 100644
--- a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFCell.java
+++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFCell.java
@@ -37,6 +37,7 @@ import org.apache.poi.ss.usermodel.Comment;
import org.apache.poi.ss.usermodel.DataFormatter;
import org.apache.poi.ss.usermodel.DateUtil;
import org.apache.poi.ss.usermodel.FormulaError;
+import org.apache.poi.ss.usermodel.FormulaEvaluator;
import org.apache.poi.ss.usermodel.Hyperlink;
import org.apache.poi.ss.usermodel.RichTextString;
import org.apache.poi.ss.usermodel.Row.MissingCellPolicy;
@@ -477,7 +478,7 @@ public final class XSSFCell implements Cell {
* @return a formula for the cell
* @throws IllegalStateException if the cell type returned by {@link #getCellType()} is not {@link CellType#FORMULA}
*/
- protected String getCellFormula(XSSFEvaluationWorkbook fpb) {
+ protected String getCellFormula(BaseXSSFEvaluationWorkbook fpb) {
CellType cellType = getCellType();
if(cellType != CellType.FORMULA) {
throw typeMismatch(CellType.FORMULA, cellType, false);
@@ -506,7 +507,7 @@ public final class XSSFCell implements Cell {
* @param si Shared Group Index
* @return non shared formula created for the given shared formula and this cell
*/
- private String convertSharedFormula(int si, XSSFEvaluationWorkbook fpb){
+ private String convertSharedFormula(int si, BaseXSSFEvaluationWorkbook fpb){
XSSFSheet sheet = getSheet();
CTCellFormula f = sheet.getSharedFormula(si);
@@ -536,6 +537,10 @@ public final class XSSFCell implements Cell {
* Note, this method only sets the formula string and does not calculate the formula value.
* To set the precalculated value use {@link #setCellValue(double)} or {@link #setCellValue(String)}
*
+ *
+ * Note, if there are any shared formulas, his will invalidate any
+ * {@link FormulaEvaluator} instances based on this workbook.
+ *
*
* @param formula the formula to set, e.g. "SUM(C4:E4)"
.
* If the argument is null
then the current formula is removed.
@@ -563,7 +568,7 @@ public final class XSSFCell implements Cell {
if (formula == null) {
wb.onDeleteFormula(this);
if (_cell.isSetF()) {
- _row.getSheet().onDeleteFormula(this);
+ _row.getSheet().onDeleteFormula(this, null);
_cell.unsetF();
}
return;
@@ -962,6 +967,16 @@ public final class XSSFCell implements Cell {
*/
@Override
public void setCellType(CellType cellType) {
+ setCellType(cellType, null);
+ }
+
+ /**
+ * Needed by bug #62834, which points out getCellFormula() expects an evaluation context or creates a new one,
+ * so if there is one in use, it needs to be carried on through.
+ * @param cellType
+ * @param evalWb BaseXSSFEvaluationWorkbook already in use, or null if a new implicit one should be used
+ */
+ protected void setCellType(CellType cellType, BaseXSSFEvaluationWorkbook evalWb) {
CellType prevType = getCellType();
if(isPartOfArrayFormulaGroup()){
@@ -969,7 +984,7 @@ public final class XSSFCell implements Cell {
}
if(prevType == CellType.FORMULA && cellType != CellType.FORMULA) {
if (_cell.isSetF()) {
- _row.getSheet().onDeleteFormula(this);
+ _row.getSheet().onDeleteFormula(this, evalWb);
}
getSheet().getWorkbook().onDeleteFormula(this);
}
diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFSheet.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFSheet.java
index 423561b2ce..cd9620d79c 100644
--- a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFSheet.java
+++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFSheet.java
@@ -4621,8 +4621,10 @@ public class XSSFSheet extends POIXMLDocumentPart implements Sheet {
/**
* when a cell with a 'master' shared formula is removed, the next cell in the range becomes the master
+ * @param cell
+ * @param evalWb BaseXSSFEvaluationWorkbook in use, if one exists
*/
- protected void onDeleteFormula(XSSFCell cell){
+ protected void onDeleteFormula(XSSFCell cell, BaseXSSFEvaluationWorkbook evalWb){
CTCellFormula f = cell.getCTCell().getF();
if (f != null && f.getT() == STCellFormulaType.SHARED && f.isSetRef() && f.getStringValue() != null) {
@@ -4634,9 +4636,9 @@ public class XSSFSheet extends POIXMLDocumentPart implements Sheet {
XSSFRow row = getRow(i);
if(row != null) for(int j = cell.getColumnIndex(); j <= ref.getLastColumn(); j++){
XSSFCell nextCell = row.getCell(j);
- if(nextCell != null && nextCell != cell){
+ if(nextCell != null && nextCell != cell && nextCell.getCellType() == CellType.FORMULA){
CTCellFormula nextF = nextCell.getCTCell().getF();
- nextF.setStringValue(nextCell.getCellFormula());
+ nextF.setStringValue(nextCell.getCellFormula(evalWb));
CellRangeAddress nextRef = new CellRangeAddress(
nextCell.getRowIndex(), ref.getLastRow(),
nextCell.getColumnIndex(), ref.getLastColumn());
diff --git a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFFormulaEvaluation.java b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFFormulaEvaluation.java
index f8f71686fa..418eb268e8 100644
--- a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFFormulaEvaluation.java
+++ b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFFormulaEvaluation.java
@@ -17,14 +17,24 @@
package org.apache.poi.xssf.usermodel;
-import static org.junit.Assert.*;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.fail;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import org.apache.poi.hssf.HSSFTestDataSamples;
-import org.apache.poi.ss.usermodel.*;
+import org.apache.poi.ss.usermodel.BaseTestFormulaEvaluator;
+import org.apache.poi.ss.usermodel.Cell;
+import org.apache.poi.ss.usermodel.CellValue;
+import org.apache.poi.ss.usermodel.CreationHelper;
+import org.apache.poi.ss.usermodel.FormulaEvaluator;
+import org.apache.poi.ss.usermodel.Row;
+import org.apache.poi.ss.usermodel.Sheet;
+import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.util.CellReference;
import org.apache.poi.xssf.XSSFITestDataProvider;
import org.apache.poi.xssf.XSSFTestDataSamples;
@@ -437,6 +447,10 @@ public final class TestXSSFFormulaEvaluation extends BaseTestFormulaEvaluator {
assertEquals("D 0,068", evaluator.evaluate(wb.getSheetAt(0).getRow(1).getCell(1)));
}
+ /**
+ * see bug 62275
+ * @throws IOException
+ */
@Test
public void testBug62275() throws IOException {
try (Workbook wb = new XSSFWorkbook()) {
@@ -451,4 +465,29 @@ public final class TestXSSFFormulaEvaluation extends BaseTestFormulaEvaluator {
eval.evaluate(cell);
}
}
+
+ /**
+ * see bug 62834, handle when a shared formula range doesn't contain only formula cells
+ * @throws IOException
+ */
+ @Test
+ public void testBug62834() throws IOException {
+ try (Workbook wb = XSSFTestDataSamples.openSampleWorkbook("62834.xlsx")) {
+ FormulaEvaluator evaluator = wb.getCreationHelper().createFormulaEvaluator();
+
+ Cell a2 = wb.getSheetAt(0).getRow(1).getCell(0);
+ Cell value = evaluator.evaluateInCell(a2);
+ assertEquals("wrong value A2", "a value", value.getStringCellValue());
+
+// evaluator.clearAllCachedResultValues();
+
+ Cell a3 = wb.getSheetAt(0).getRow(2).getCell(0);
+ value = evaluator.evaluateInCell(a3);
+ assertEquals("wrong value A3", "a value", value.getStringCellValue());
+
+ Cell a5 = wb.getSheetAt(0).getRow(4).getCell(0);
+ value = evaluator.evaluateInCell(a5);
+ assertEquals("wrong value A5", "another value", value.getStringCellValue());
+ }
+ }
}
diff --git a/test-data/spreadsheet/62834.xlsx b/test-data/spreadsheet/62834.xlsx
new file mode 100644
index 0000000000000000000000000000000000000000..2f265e69c2be2d756a6eba0c11219c0f1114bea4
GIT binary patch
literal 9629
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
literal 0
HcmV?d00001
--
2.39.5