Refactor the core part of the XSSF Formula Evaluator, to make SXSSF overrides easier #58200

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1693661 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Nick Burch 2015-07-31 22:04:16 +00:00
parent 4782319fd1
commit 1761949b02
3 changed files with 228 additions and 187 deletions

View File

@ -20,9 +20,7 @@ package org.apache.poi.xssf.usermodel;
import java.util.List; import java.util.List;
import org.apache.poi.ss.SpreadsheetVersion; import org.apache.poi.ss.SpreadsheetVersion;
import org.apache.poi.ss.formula.EvaluationCell;
import org.apache.poi.ss.formula.EvaluationName; import org.apache.poi.ss.formula.EvaluationName;
import org.apache.poi.ss.formula.EvaluationSheet;
import org.apache.poi.ss.formula.EvaluationWorkbook; import org.apache.poi.ss.formula.EvaluationWorkbook;
import org.apache.poi.ss.formula.FormulaParser; import org.apache.poi.ss.formula.FormulaParser;
import org.apache.poi.ss.formula.FormulaParsingWorkbook; import org.apache.poi.ss.formula.FormulaParsingWorkbook;
@ -44,7 +42,7 @@ import org.apache.poi.xssf.model.ExternalLinksTable;
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTDefinedName; import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTDefinedName;
/** /**
* Internal POI use only * Internal POI use only - parent of XSSF and SXSSF evaluation workbooks
*/ */
public abstract class BaseXSSFEvaluationWorkbook implements FormulaRenderingWorkbook, EvaluationWorkbook, FormulaParsingWorkbook { public abstract class BaseXSSFEvaluationWorkbook implements FormulaRenderingWorkbook, EvaluationWorkbook, FormulaParsingWorkbook {
protected final XSSFWorkbook _uBook; protected final XSSFWorkbook _uBook;

View File

@ -0,0 +1,225 @@
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
package org.apache.poi.xssf.usermodel;
import java.util.Map;
import org.apache.poi.ss.formula.CollaboratingWorkbooksEnvironment;
import org.apache.poi.ss.formula.EvaluationCell;
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.ErrorEval;
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.CellValue;
import org.apache.poi.ss.usermodel.FormulaEvaluator;
/**
* Internal POI use only - parent of XSSF and SXSSF formula evaluators
*/
public abstract class BaseXSSFFormulaEvaluator implements FormulaEvaluator, WorkbookEvaluatorProvider {
private WorkbookEvaluator _bookEvaluator;
protected BaseXSSFFormulaEvaluator(WorkbookEvaluator bookEvaluator) {
_bookEvaluator = 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) {
_bookEvaluator.notifyUpdateCell(new XSSFEvaluationCell((XSSFCell)cell));
}
public void notifyDeleteCell(Cell cell) {
_bookEvaluator.notifyDeleteCell(new XSSFEvaluationCell((XSSFCell)cell));
}
public void notifyUpdateCell(Cell 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.getCellType()) {
case XSSFCell.CELL_TYPE_BOOLEAN:
return CellValue.valueOf(cell.getBooleanCellValue());
case XSSFCell.CELL_TYPE_ERROR:
return CellValue.getError(cell.getErrorCellValue());
case XSSFCell.CELL_TYPE_FORMULA:
return evaluateFormulaCellValue(cell);
case XSSFCell.CELL_TYPE_NUMERIC:
return new CellValue(cell.getNumericCellValue());
case XSSFCell.CELL_TYPE_STRING:
return new CellValue(cell.getRichStringCellValue().getString());
case XSSFCell.CELL_TYPE_BLANK:
return null;
}
throw new IllegalStateException("Bad cell type (" + cell.getCellType() + ")");
}
/**
* 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 HSSFCell.CELL_TYPE_FORMULA however)
*/
public int evaluateFormulaCell(Cell cell) {
if (cell == null || cell.getCellType() != XSSFCell.CELL_TYPE_FORMULA) {
return -1;
}
CellValue cv = evaluateFormulaCellValue(cell);
// cell remains a formula cell, but the cached value is changed
setCellValue(cell, cv);
return cv.getCellType();
}
/**
* If cell contains formula, it evaluates the formula, and
* puts the formula result back into the cell, in place
* of the old formula.
* Else if cell does not contain formula, this method leaves
* the cell unchanged.
*/
protected void doEvaluateInCell(Cell cell) {
if (cell == null) return;
if (cell.getCellType() == XSSFCell.CELL_TYPE_FORMULA) {
CellValue cv = evaluateFormulaCellValue(cell);
setCellType(cell, cv); // cell will no longer be a formula cell
setCellValue(cell, cv);
}
}
private static void setCellType(Cell cell, CellValue cv) {
int cellType = cv.getCellType();
switch (cellType) {
case XSSFCell.CELL_TYPE_BOOLEAN:
case XSSFCell.CELL_TYPE_ERROR:
case XSSFCell.CELL_TYPE_NUMERIC:
case XSSFCell.CELL_TYPE_STRING:
cell.setCellType(cellType);
return;
case XSSFCell.CELL_TYPE_BLANK:
// never happens - blanks eventually get translated to zero
case XSSFCell.CELL_TYPE_FORMULA:
// this will never happen, we have already evaluated the formula
}
throw new IllegalStateException("Unexpected cell value type (" + cellType + ")");
}
private static void setCellValue(Cell cell, CellValue cv) {
int cellType = cv.getCellType();
switch (cellType) {
case XSSFCell.CELL_TYPE_BOOLEAN:
cell.setCellValue(cv.getBooleanValue());
break;
case XSSFCell.CELL_TYPE_ERROR:
cell.setCellErrorValue(cv.getErrorValue());
break;
case XSSFCell.CELL_TYPE_NUMERIC:
cell.setCellValue(cv.getNumberValue());
break;
case XSSFCell.CELL_TYPE_STRING:
cell.setCellValue(new XSSFRichTextString(cv.getStringValue()));
break;
case XSSFCell.CELL_TYPE_BLANK:
// never happens - blanks eventually get translated to zero
case XSSFCell.CELL_TYPE_FORMULA:
// this will never happen, we have already evaluated the formula
default:
throw new IllegalStateException("Unexpected cell value type (" + cellType + ")");
}
}
/**
* Turns a XSSFCell / SXSSFCell into a XSSFEvaluationCell
*/
protected abstract EvaluationCell toEvaluationCell(Cell cell);
/**
* Returns a CellValue wrapper around the supplied ValueEval instance.
*/
private CellValue evaluateFormulaCellValue(Cell cell) {
EvaluationCell evalCell = toEvaluationCell(cell);
ValueEval eval = _bookEvaluator.evaluate(evalCell);
if (eval instanceof NumberEval) {
NumberEval ne = (NumberEval) eval;
return new CellValue(ne.getNumberValue());
}
if (eval instanceof BoolEval) {
BoolEval be = (BoolEval) eval;
return CellValue.valueOf(be.getBooleanValue());
}
if (eval instanceof StringEval) {
StringEval ne = (StringEval) eval;
return new CellValue(ne.getStringValue());
}
if (eval instanceof ErrorEval) {
return CellValue.getError(((ErrorEval)eval).getErrorCode());
}
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,23 +17,12 @@
package org.apache.poi.xssf.usermodel; package org.apache.poi.xssf.usermodel;
import java.util.Map;
import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator; import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator;
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.IStabilityClassifier; import org.apache.poi.ss.formula.IStabilityClassifier;
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.ErrorEval;
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.formula.udf.UDFFinder; import org.apache.poi.ss.formula.udf.UDFFinder;
import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellValue;
import org.apache.poi.ss.usermodel.FormulaEvaluator;
/** /**
* Evaluates formula cells.<p/> * Evaluates formula cells.<p/>
@ -42,9 +31,7 @@ import org.apache.poi.ss.usermodel.FormulaEvaluator;
* cell values. Be sure to call {@link #clearAllCachedResultValues()} if any workbook cells are changed between * cell values. Be sure to call {@link #clearAllCachedResultValues()} if any workbook cells are changed between
* calls to evaluate~ methods on this class. * calls to evaluate~ methods on this class.
*/ */
public class XSSFFormulaEvaluator implements FormulaEvaluator, WorkbookEvaluatorProvider { public final class XSSFFormulaEvaluator extends BaseXSSFFormulaEvaluator {
private WorkbookEvaluator _bookEvaluator;
private XSSFWorkbook _book; private XSSFWorkbook _book;
public XSSFFormulaEvaluator(XSSFWorkbook workbook) { public XSSFFormulaEvaluator(XSSFWorkbook workbook) {
@ -54,7 +41,7 @@ public class XSSFFormulaEvaluator implements FormulaEvaluator, WorkbookEvaluator
this(workbook, new WorkbookEvaluator(XSSFEvaluationWorkbook.create(workbook), stabilityClassifier, udfFinder)); this(workbook, new WorkbookEvaluator(XSSFEvaluationWorkbook.create(workbook), stabilityClassifier, udfFinder));
} }
protected XSSFFormulaEvaluator(XSSFWorkbook workbook, WorkbookEvaluator bookEvaluator) { protected XSSFFormulaEvaluator(XSSFWorkbook workbook, WorkbookEvaluator bookEvaluator) {
_bookEvaluator = bookEvaluator; super(bookEvaluator);
_book = workbook; _book = workbook;
} }
@ -68,85 +55,6 @@ public class XSSFFormulaEvaluator implements FormulaEvaluator, WorkbookEvaluator
return new XSSFFormulaEvaluator(workbook, stabilityClassifier, udfFinder); return new XSSFFormulaEvaluator(workbook, stabilityClassifier, udfFinder);
} }
/**
* 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) {
_bookEvaluator.notifyUpdateCell(new XSSFEvaluationCell((XSSFCell)cell));
}
public void notifyDeleteCell(Cell cell) {
_bookEvaluator.notifyDeleteCell(new XSSFEvaluationCell((XSSFCell)cell));
}
public void notifyUpdateCell(Cell 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.getCellType()) {
case XSSFCell.CELL_TYPE_BOOLEAN:
return CellValue.valueOf(cell.getBooleanCellValue());
case XSSFCell.CELL_TYPE_ERROR:
return CellValue.getError(cell.getErrorCellValue());
case XSSFCell.CELL_TYPE_FORMULA:
return evaluateFormulaCellValue(cell);
case XSSFCell.CELL_TYPE_NUMERIC:
return new CellValue(cell.getNumericCellValue());
case XSSFCell.CELL_TYPE_STRING:
return new CellValue(cell.getRichStringCellValue().getString());
case XSSFCell.CELL_TYPE_BLANK:
return null;
}
throw new IllegalStateException("Bad cell type (" + cell.getCellType() + ")");
}
/**
* 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 HSSFCell.CELL_TYPE_FORMULA however)
*/
public int evaluateFormulaCell(Cell cell) {
if (cell == null || cell.getCellType() != XSSFCell.CELL_TYPE_FORMULA) {
return -1;
}
CellValue cv = evaluateFormulaCellValue(cell);
// cell remains a formula cell, but the cached value is changed
setCellValue(cell, cv);
return cv.getCellType();
}
/** /**
* If cell contains formula, it evaluates the formula, and * If cell contains formula, it evaluates the formula, and
* puts the formula result back into the cell, in place * puts the formula result back into the cell, in place
@ -167,54 +75,6 @@ public class XSSFFormulaEvaluator implements FormulaEvaluator, WorkbookEvaluator
doEvaluateInCell(cell); doEvaluateInCell(cell);
return (XSSFCell)cell; return (XSSFCell)cell;
} }
protected void doEvaluateInCell(Cell cell) {
if (cell == null) return;
if (cell.getCellType() == XSSFCell.CELL_TYPE_FORMULA) {
CellValue cv = evaluateFormulaCellValue(cell);
setCellType(cell, cv); // cell will no longer be a formula cell
setCellValue(cell, cv);
}
}
private static void setCellType(Cell cell, CellValue cv) {
int cellType = cv.getCellType();
switch (cellType) {
case XSSFCell.CELL_TYPE_BOOLEAN:
case XSSFCell.CELL_TYPE_ERROR:
case XSSFCell.CELL_TYPE_NUMERIC:
case XSSFCell.CELL_TYPE_STRING:
cell.setCellType(cellType);
return;
case XSSFCell.CELL_TYPE_BLANK:
// never happens - blanks eventually get translated to zero
case XSSFCell.CELL_TYPE_FORMULA:
// this will never happen, we have already evaluated the formula
}
throw new IllegalStateException("Unexpected cell value type (" + cellType + ")");
}
private static void setCellValue(Cell cell, CellValue cv) {
int cellType = cv.getCellType();
switch (cellType) {
case XSSFCell.CELL_TYPE_BOOLEAN:
cell.setCellValue(cv.getBooleanValue());
break;
case XSSFCell.CELL_TYPE_ERROR:
cell.setCellErrorValue(cv.getErrorValue());
break;
case XSSFCell.CELL_TYPE_NUMERIC:
cell.setCellValue(cv.getNumberValue());
break;
case XSSFCell.CELL_TYPE_STRING:
cell.setCellValue(new XSSFRichTextString(cv.getStringValue()));
break;
case XSSFCell.CELL_TYPE_BLANK:
// never happens - blanks eventually get translated to zero
case XSSFCell.CELL_TYPE_FORMULA:
// this will never happen, we have already evaluated the formula
default:
throw new IllegalStateException("Unexpected cell value type (" + cellType + ")");
}
}
/** /**
* Loops over all cells in all sheets of the supplied * Loops over all cells in all sheets of the supplied
@ -256,46 +116,4 @@ public class XSSFFormulaEvaluator implements FormulaEvaluator, WorkbookEvaluator
return new XSSFEvaluationCell((XSSFCell)cell); return new XSSFEvaluationCell((XSSFCell)cell);
} }
/**
* Returns a CellValue wrapper around the supplied ValueEval instance.
*/
private CellValue evaluateFormulaCellValue(Cell cell) {
EvaluationCell evalCell = toEvaluationCell(cell);
ValueEval eval = _bookEvaluator.evaluate(evalCell);
if (eval instanceof NumberEval) {
NumberEval ne = (NumberEval) eval;
return new CellValue(ne.getNumberValue());
}
if (eval instanceof BoolEval) {
BoolEval be = (BoolEval) eval;
return CellValue.valueOf(be.getBooleanValue());
}
if (eval instanceof StringEval) {
StringEval ne = (StringEval) eval;
return new CellValue(ne.getStringValue());
}
if (eval instanceof ErrorEval) {
return CellValue.getError(((ErrorEval)eval).getErrorCode());
}
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);
}
} }