Switch XSSF onto BaseFormulaEvaluator, reducing code duplication and fixing XSSFFormulaEvaluator.evaluateAll() to mirror HSSF and use any setup referenced workbooks (stackoverflow #38706562)

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1755079 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Nick Burch 2016-08-03 11:53:43 +00:00
parent 60b2586c3f
commit a1f6d9c832
5 changed files with 23 additions and 134 deletions

View File

@ -33,8 +33,6 @@ import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellType; import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.ss.usermodel.CellValue; import org.apache.poi.ss.usermodel.CellValue;
import org.apache.poi.ss.usermodel.FormulaEvaluator; 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.usermodel.Workbook;
import org.apache.poi.util.Internal; import org.apache.poi.util.Internal;
@ -248,21 +246,7 @@ public class HSSFFormulaEvaluator extends BaseFormulaEvaluator {
* cells, and calling evaluateFormulaCell on each one. * cells, and calling evaluateFormulaCell on each one.
*/ */
public static void evaluateAllFormulaCells(Workbook wb) { public static void evaluateAllFormulaCells(Workbook wb) {
FormulaEvaluator evaluator = wb.getCreationHelper().createFormulaEvaluator(); BaseFormulaEvaluator.evaluateAllFormulaCells(wb);
evaluateAllFormulaCells(wb, evaluator);
}
private static void evaluateAllFormulaCells(Workbook wb, FormulaEvaluator evaluator) {
for(int i=0; i<wb.getNumberOfSheets(); i++) {
Sheet sheet = wb.getSheetAt(i);
for(Row r : sheet) {
for (Cell c : r) {
if (c.getCellTypeEnum() == CellType.FORMULA) {
evaluator.evaluateFormulaCellEnum(c);
}
}
}
}
} }
/** /**

View File

@ -142,8 +142,10 @@ public abstract class BaseFormulaEvaluator implements FormulaEvaluator, Workbook
return; return;
case BLANK: case BLANK:
// never happens - blanks eventually get translated to zero // never happens - blanks eventually get translated to zero
throw new IllegalArgumentException("This should never happen. Blanks eventually get translated to zero.");
case FORMULA: case FORMULA:
// this will never happen, we have already evaluated the formula // this will never happen, we have already evaluated the formula
throw new IllegalArgumentException("This should never happen. Formulas should have already been evaluated.");
default: default:
throw new IllegalStateException("Unexpected cell value type (" + cellType + ")"); throw new IllegalStateException("Unexpected cell value type (" + cellType + ")");
} }
@ -164,7 +166,7 @@ public abstract class BaseFormulaEvaluator implements FormulaEvaluator, Workbook
FormulaEvaluator evaluator = wb.getCreationHelper().createFormulaEvaluator(); FormulaEvaluator evaluator = wb.getCreationHelper().createFormulaEvaluator();
evaluateAllFormulaCells(wb, evaluator); evaluateAllFormulaCells(wb, evaluator);
} }
private static void evaluateAllFormulaCells(Workbook wb, FormulaEvaluator evaluator) { protected static void evaluateAllFormulaCells(Workbook wb, FormulaEvaluator evaluator) {
for(int i=0; i<wb.getNumberOfSheets(); i++) { for(int i=0; i<wb.getNumberOfSheets(); i++) {
Sheet sheet = wb.getSheetAt(i); Sheet sheet = wb.getSheetAt(i);

View File

@ -17,12 +17,9 @@
package org.apache.poi.xssf.usermodel; package org.apache.poi.xssf.usermodel;
import java.util.Map; import org.apache.poi.ss.formula.BaseFormulaEvaluator;
import org.apache.poi.ss.formula.CollaboratingWorkbooksEnvironment;
import org.apache.poi.ss.formula.EvaluationCell; import org.apache.poi.ss.formula.EvaluationCell;
import org.apache.poi.ss.formula.WorkbookEvaluator; import org.apache.poi.ss.formula.WorkbookEvaluator;
import org.apache.poi.ss.formula.WorkbookEvaluatorProvider;
import org.apache.poi.ss.formula.eval.BoolEval; import org.apache.poi.ss.formula.eval.BoolEval;
import org.apache.poi.ss.formula.eval.ErrorEval; import org.apache.poi.ss.formula.eval.ErrorEval;
import org.apache.poi.ss.formula.eval.NumberEval; import org.apache.poi.ss.formula.eval.NumberEval;
@ -31,28 +28,16 @@ import org.apache.poi.ss.formula.eval.ValueEval;
import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellType; import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.ss.usermodel.CellValue; import org.apache.poi.ss.usermodel.CellValue;
import org.apache.poi.ss.usermodel.FormulaEvaluator;
import org.apache.poi.util.Internal; import org.apache.poi.util.Internal;
/** /**
* Internal POI use only - parent of XSSF and SXSSF formula evaluators * Internal POI use only - parent of XSSF and SXSSF formula evaluators
*/ */
public abstract class BaseXSSFFormulaEvaluator implements FormulaEvaluator, WorkbookEvaluatorProvider { public abstract class BaseXSSFFormulaEvaluator extends BaseFormulaEvaluator {
private WorkbookEvaluator _bookEvaluator;
protected BaseXSSFFormulaEvaluator(WorkbookEvaluator bookEvaluator) { protected BaseXSSFFormulaEvaluator(WorkbookEvaluator bookEvaluator) {
_bookEvaluator = bookEvaluator; super(bookEvaluator);
} }
/**
* Should be called whenever there are major changes (e.g. moving sheets) to input cells
* in the evaluated workbook.
* Failure to call this method after changing cell values will cause incorrect behaviour
* of the evaluate~ methods of this class
*/
public void clearAllCachedResultValues() {
_bookEvaluator.clearAllCachedResultValues();
}
public void notifySetFormula(Cell cell) { public void notifySetFormula(Cell cell) {
_bookEvaluator.notifyUpdateCell(new XSSFEvaluationCell((XSSFCell)cell)); _bookEvaluator.notifyUpdateCell(new XSSFEvaluationCell((XSSFCell)cell));
} }
@ -63,60 +48,6 @@ public abstract class BaseXSSFFormulaEvaluator implements FormulaEvaluator, Work
_bookEvaluator.notifyUpdateCell(new XSSFEvaluationCell((XSSFCell)cell)); _bookEvaluator.notifyUpdateCell(new XSSFEvaluationCell((XSSFCell)cell));
} }
/**
* If cell contains a formula, the formula is evaluated and returned,
* else the CellValue simply copies the appropriate cell value from
* the cell and also its cell type. This method should be preferred over
* evaluateInCell() when the call should not modify the contents of the
* original cell.
* @param cell
*/
public CellValue evaluate(Cell cell) {
if (cell == null) {
return null;
}
switch (cell.getCellTypeEnum()) {
case BOOLEAN:
return CellValue.valueOf(cell.getBooleanCellValue());
case ERROR:
return CellValue.getError(cell.getErrorCellValue());
case FORMULA:
return evaluateFormulaCellValue(cell);
case NUMERIC:
return new CellValue(cell.getNumericCellValue());
case STRING:
return new CellValue(cell.getRichStringCellValue().getString());
case BLANK:
return null;
default:
throw new IllegalStateException("Bad cell type (" + cell.getCellTypeEnum() + ")");
}
}
/**
* If cell contains formula, it evaluates the formula,
* and saves the result of the formula. The cell
* remains as a formula cell.
* Else if cell does not contain formula, this method leaves
* the cell unchanged.
* Note that the type of the formula result is returned,
* so you know what kind of value is also stored with
* the formula.
* <pre>
* int evaluatedCellType = evaluator.evaluateFormulaCell(cell);
* </pre>
* Be aware that your cell will hold both the formula,
* and the result. If you want the cell replaced with
* the result of the formula, use {@link #evaluate(org.apache.poi.ss.usermodel.Cell)} }
* @param cell The cell to evaluate
* @return The type of the formula result (the cell's type remains as CellType.FORMULA however)
*/
public int evaluateFormulaCell(Cell cell) {
return evaluateFormulaCellEnum(cell).getCode();
}
/** /**
* If cell contains formula, it evaluates the formula, * If cell contains formula, it evaluates the formula,
* and saves the result of the formula. The cell * and saves the result of the formula. The cell
@ -164,27 +95,6 @@ public abstract class BaseXSSFFormulaEvaluator implements FormulaEvaluator, Work
setCellValue(cell, cv); setCellValue(cell, cv);
} }
} }
private static void setCellType(Cell cell, CellValue cv) {
CellType cellType = cv.getCellType();
switch (cellType) {
case BOOLEAN:
case ERROR:
case NUMERIC:
case STRING:
cell.setCellType(cellType);
return;
case BLANK:
// never happens - blanks eventually get translated to zero
throw new IllegalArgumentException("This should never happen. Blanks eventually get translated to zero.");
case FORMULA:
// this will never happen, we have already evaluated the formula
throw new IllegalArgumentException("This should never happen. Formulas should have already been evaluated.");
default:
throw new IllegalStateException("Unexpected cell value type (" + cellType + ")");
}
}
private static void setCellValue(Cell cell, CellValue cv) { private static void setCellValue(Cell cell, CellValue cv) {
CellType cellType = cv.getCellType(); CellType cellType = cv.getCellType();
@ -218,7 +128,7 @@ public abstract class BaseXSSFFormulaEvaluator implements FormulaEvaluator, Work
/** /**
* Returns a CellValue wrapper around the supplied ValueEval instance. * Returns a CellValue wrapper around the supplied ValueEval instance.
*/ */
private CellValue evaluateFormulaCellValue(Cell cell) { protected CellValue evaluateFormulaCellValue(Cell cell) {
EvaluationCell evalCell = toEvaluationCell(cell); EvaluationCell evalCell = toEvaluationCell(cell);
ValueEval eval = _bookEvaluator.evaluate(evalCell); ValueEval eval = _bookEvaluator.evaluate(evalCell);
if (eval instanceof NumberEval) { if (eval instanceof NumberEval) {
@ -238,22 +148,4 @@ public abstract class BaseXSSFFormulaEvaluator implements FormulaEvaluator, Work
} }
throw new RuntimeException("Unexpected eval class (" + eval.getClass().getName() + ")"); throw new RuntimeException("Unexpected eval class (" + eval.getClass().getName() + ")");
} }
public void setupReferencedWorkbooks(Map<String, FormulaEvaluator> evaluators) {
CollaboratingWorkbooksEnvironment.setupFormulaEvaluator(evaluators);
}
public WorkbookEvaluator _getWorkbookEvaluator() {
return _bookEvaluator;
}
/** {@inheritDoc} */
public void setIgnoreMissingWorkbooks(boolean ignore){
_bookEvaluator.setIgnoreMissingWorkbooks(ignore);
}
/** {@inheritDoc} */
public void setDebugEvaluationOutputForNextEval(boolean value){
_bookEvaluator.setDebugEvaluationOutputForNextEval(value);
}
} }

View File

@ -17,7 +17,7 @@
package org.apache.poi.xssf.usermodel; package org.apache.poi.xssf.usermodel;
import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator; import org.apache.poi.ss.formula.BaseFormulaEvaluator;
import org.apache.poi.ss.formula.EvaluationCell; import org.apache.poi.ss.formula.EvaluationCell;
import org.apache.poi.ss.formula.IStabilityClassifier; import org.apache.poi.ss.formula.IStabilityClassifier;
import org.apache.poi.ss.formula.WorkbookEvaluator; import org.apache.poi.ss.formula.WorkbookEvaluator;
@ -88,7 +88,7 @@ public final class XSSFFormulaEvaluator extends BaseXSSFFormulaEvaluator {
* cells, and calling evaluateFormulaCell on each one. * cells, and calling evaluateFormulaCell on each one.
*/ */
public static void evaluateAllFormulaCells(XSSFWorkbook wb) { public static void evaluateAllFormulaCells(XSSFWorkbook wb) {
HSSFFormulaEvaluator.evaluateAllFormulaCells(wb); BaseFormulaEvaluator.evaluateAllFormulaCells(wb);
} }
/** /**
* Loops over all cells in all sheets of the supplied * Loops over all cells in all sheets of the supplied
@ -102,7 +102,7 @@ public final class XSSFFormulaEvaluator extends BaseXSSFFormulaEvaluator {
* cells, and calling evaluateFormulaCell on each one. * cells, and calling evaluateFormulaCell on each one.
*/ */
public void evaluateAll() { public void evaluateAll() {
HSSFFormulaEvaluator.evaluateAllFormulaCells(_book); evaluateAllFormulaCells(_book, this);
} }
/** /**

View File

@ -173,8 +173,19 @@ public final class TestXSSFFormulaEvaluation extends BaseTestFormulaEvaluator {
evaluator.evaluate(c); evaluator.evaluate(c);
} }
} }
// And evaluate the other way too
evaluator.evaluateAll();
// Evaluate and check results // Static evaluator won't work, as no references passed in
try {
XSSFFormulaEvaluator.evaluateAllFormulaCells(wb);
fail("Static method lacks references, shouldn't work");
} catch(Exception e) {
// expected here
}
// Evaluate specific cells and check results
assertEquals("\"Hello!\"", evaluator.evaluate(cXSLX_cell).formatAsString()); assertEquals("\"Hello!\"", evaluator.evaluate(cXSLX_cell).formatAsString());
assertEquals("\"Test A1\"", evaluator.evaluate(cXSLX_sNR).formatAsString()); assertEquals("\"Test A1\"", evaluator.evaluate(cXSLX_sNR).formatAsString());
assertEquals("142.0", evaluator.evaluate(cXSLX_gNR).formatAsString()); assertEquals("142.0", evaluator.evaluate(cXSLX_gNR).formatAsString());