Shuffle the FormulaParser stuff about, and get it all working with the new interfaces, and not just with the hard coded HSSF classes
git-svn-id: https://svn.apache.org/repos/asf/poi/branches/ooxml@642624 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
df3577e39b
commit
3d9e7d5bf5
@ -18,8 +18,8 @@
|
|||||||
package org.apache.poi.hssf.record.formula.eval;
|
package org.apache.poi.hssf.record.formula.eval;
|
||||||
|
|
||||||
import org.apache.poi.hssf.record.formula.functions.FreeRefFunction;
|
import org.apache.poi.hssf.record.formula.functions.FreeRefFunction;
|
||||||
import org.apache.poi.hssf.usermodel.HSSFSheet;
|
import org.apache.poi.ss.usermodel.Sheet;
|
||||||
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
|
import org.apache.poi.ss.usermodel.Workbook;
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Common entry point for all external functions (where
|
* Common entry point for all external functions (where
|
||||||
@ -29,7 +29,7 @@ import org.apache.poi.hssf.usermodel.HSSFWorkbook;
|
|||||||
*/
|
*/
|
||||||
final class ExternalFunction implements FreeRefFunction {
|
final class ExternalFunction implements FreeRefFunction {
|
||||||
|
|
||||||
public ValueEval evaluate(Eval[] args, int srcCellRow, short srcCellCol, HSSFWorkbook workbook, HSSFSheet sheet) {
|
public ValueEval evaluate(Eval[] args, int srcCellRow, short srcCellCol, Workbook workbook, Sheet sheet) {
|
||||||
|
|
||||||
int nIncomingArgs = args.length;
|
int nIncomingArgs = args.length;
|
||||||
if(nIncomingArgs < 1) {
|
if(nIncomingArgs < 1) {
|
||||||
@ -56,7 +56,7 @@ final class ExternalFunction implements FreeRefFunction {
|
|||||||
return targetFunc.evaluate(outGoingArgs, srcCellRow, srcCellCol, workbook, sheet);
|
return targetFunc.evaluate(outGoingArgs, srcCellRow, srcCellCol, workbook, sheet);
|
||||||
}
|
}
|
||||||
|
|
||||||
private FreeRefFunction findTargetFunction(HSSFWorkbook workbook, NameEval functionNameEval) throws EvaluationException {
|
private FreeRefFunction findTargetFunction(Workbook workbook, NameEval functionNameEval) throws EvaluationException {
|
||||||
|
|
||||||
int numberOfNames = workbook.getNumberOfNames();
|
int numberOfNames = workbook.getNumberOfNames();
|
||||||
|
|
||||||
|
@ -19,8 +19,8 @@ package org.apache.poi.hssf.record.formula.functions;
|
|||||||
|
|
||||||
import org.apache.poi.hssf.record.formula.eval.Eval;
|
import org.apache.poi.hssf.record.formula.eval.Eval;
|
||||||
import org.apache.poi.hssf.record.formula.eval.ValueEval;
|
import org.apache.poi.hssf.record.formula.eval.ValueEval;
|
||||||
import org.apache.poi.hssf.usermodel.HSSFSheet;
|
import org.apache.poi.ss.usermodel.Sheet;
|
||||||
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
|
import org.apache.poi.ss.usermodel.Workbook;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -53,5 +53,5 @@ public interface FreeRefFunction {
|
|||||||
* a specified Excel error (Exceptions are never thrown to represent Excel errors).
|
* a specified Excel error (Exceptions are never thrown to represent Excel errors).
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
ValueEval evaluate(Eval[] args, int srcCellRow, short srcCellCol, HSSFWorkbook workbook, HSSFSheet sheet);
|
ValueEval evaluate(Eval[] args, int srcCellRow, short srcCellCol, Workbook workbook, Sheet sheet);
|
||||||
}
|
}
|
||||||
|
@ -20,8 +20,8 @@ package org.apache.poi.hssf.record.formula.functions;
|
|||||||
import org.apache.poi.hssf.record.formula.eval.ErrorEval;
|
import org.apache.poi.hssf.record.formula.eval.ErrorEval;
|
||||||
import org.apache.poi.hssf.record.formula.eval.Eval;
|
import org.apache.poi.hssf.record.formula.eval.Eval;
|
||||||
import org.apache.poi.hssf.record.formula.eval.ValueEval;
|
import org.apache.poi.hssf.record.formula.eval.ValueEval;
|
||||||
import org.apache.poi.hssf.usermodel.HSSFSheet;
|
import org.apache.poi.ss.usermodel.Sheet;
|
||||||
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
|
import org.apache.poi.ss.usermodel.Workbook;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implementation for Excel function INDIRECT<p/>
|
* Implementation for Excel function INDIRECT<p/>
|
||||||
@ -41,7 +41,7 @@ import org.apache.poi.hssf.usermodel.HSSFWorkbook;
|
|||||||
*/
|
*/
|
||||||
public final class Indirect implements FreeRefFunction {
|
public final class Indirect implements FreeRefFunction {
|
||||||
|
|
||||||
public ValueEval evaluate(Eval[] args, int srcCellRow, short srcCellCol, HSSFWorkbook workbook, HSSFSheet sheet) {
|
public ValueEval evaluate(Eval[] args, int srcCellRow, short srcCellCol, Workbook workbook, Sheet sheet) {
|
||||||
// TODO - implement INDIRECT()
|
// TODO - implement INDIRECT()
|
||||||
return ErrorEval.FUNCTION_NOT_IMPLEMENTED;
|
return ErrorEval.FUNCTION_NOT_IMPLEMENTED;
|
||||||
}
|
}
|
||||||
|
@ -31,8 +31,8 @@ import org.apache.poi.hssf.record.formula.eval.RefEval;
|
|||||||
import org.apache.poi.hssf.record.formula.eval.StringEval;
|
import org.apache.poi.hssf.record.formula.eval.StringEval;
|
||||||
import org.apache.poi.hssf.record.formula.eval.ValueEval;
|
import org.apache.poi.hssf.record.formula.eval.ValueEval;
|
||||||
import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator;
|
import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator;
|
||||||
import org.apache.poi.hssf.usermodel.HSSFSheet;
|
import org.apache.poi.ss.usermodel.Sheet;
|
||||||
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
|
import org.apache.poi.ss.usermodel.Workbook;
|
||||||
/**
|
/**
|
||||||
* Implementation for Excel function OFFSET()<p/>
|
* Implementation for Excel function OFFSET()<p/>
|
||||||
*
|
*
|
||||||
@ -201,7 +201,7 @@ public final class Offset implements FreeRefFunction {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public ValueEval evaluate(Eval[] args, int srcCellRow, short srcCellCol, HSSFWorkbook workbook, HSSFSheet sheet) {
|
public ValueEval evaluate(Eval[] args, int srcCellRow, short srcCellCol, Workbook workbook, Sheet sheet) {
|
||||||
|
|
||||||
if(args.length < 3 || args.length > 5) {
|
if(args.length < 3 || args.length > 5) {
|
||||||
return ErrorEval.VALUE_INVALID;
|
return ErrorEval.VALUE_INVALID;
|
||||||
@ -235,7 +235,7 @@ public final class Offset implements FreeRefFunction {
|
|||||||
|
|
||||||
private static AreaEval createOffset(BaseRef baseRef,
|
private static AreaEval createOffset(BaseRef baseRef,
|
||||||
LinearOffsetRange rowOffsetRange, LinearOffsetRange colOffsetRange,
|
LinearOffsetRange rowOffsetRange, LinearOffsetRange colOffsetRange,
|
||||||
HSSFWorkbook workbook, HSSFSheet sheet) throws EvalEx {
|
Workbook workbook, Sheet sheet) throws EvalEx {
|
||||||
|
|
||||||
LinearOffsetRange rows = rowOffsetRange.normaliseAndTranslate(baseRef.getFirstRowIndex());
|
LinearOffsetRange rows = rowOffsetRange.normaliseAndTranslate(baseRef.getFirstRowIndex());
|
||||||
LinearOffsetRange cols = colOffsetRange.normaliseAndTranslate(baseRef.getFirstColumnIndex());
|
LinearOffsetRange cols = colOffsetRange.normaliseAndTranslate(baseRef.getFirstColumnIndex());
|
||||||
|
@ -17,96 +17,25 @@
|
|||||||
|
|
||||||
package org.apache.poi.hssf.usermodel;
|
package org.apache.poi.hssf.usermodel;
|
||||||
|
|
||||||
import java.lang.reflect.Constructor;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Stack;
|
|
||||||
|
|
||||||
import org.apache.poi.hssf.model.FormulaParser;
|
import org.apache.poi.hssf.model.FormulaParser;
|
||||||
import org.apache.poi.hssf.model.Workbook;
|
|
||||||
import org.apache.poi.hssf.record.formula.Area3DPtg;
|
|
||||||
import org.apache.poi.hssf.record.formula.AreaPtg;
|
|
||||||
import org.apache.poi.hssf.record.formula.AttrPtg;
|
|
||||||
import org.apache.poi.hssf.record.formula.BoolPtg;
|
|
||||||
import org.apache.poi.hssf.record.formula.ControlPtg;
|
|
||||||
import org.apache.poi.hssf.record.formula.IntPtg;
|
|
||||||
import org.apache.poi.hssf.record.formula.MemErrPtg;
|
|
||||||
import org.apache.poi.hssf.record.formula.MissingArgPtg;
|
|
||||||
import org.apache.poi.hssf.record.formula.NamePtg;
|
|
||||||
import org.apache.poi.hssf.record.formula.NameXPtg;
|
|
||||||
import org.apache.poi.hssf.record.formula.NumberPtg;
|
|
||||||
import org.apache.poi.hssf.record.formula.OperationPtg;
|
import org.apache.poi.hssf.record.formula.OperationPtg;
|
||||||
import org.apache.poi.hssf.record.formula.ParenthesisPtg;
|
|
||||||
import org.apache.poi.hssf.record.formula.Ptg;
|
import org.apache.poi.hssf.record.formula.Ptg;
|
||||||
import org.apache.poi.hssf.record.formula.Ref3DPtg;
|
import org.apache.poi.ss.usermodel.Cell;
|
||||||
import org.apache.poi.hssf.record.formula.ReferencePtg;
|
import org.apache.poi.ss.usermodel.CreationHelper;
|
||||||
import org.apache.poi.hssf.record.formula.StringPtg;
|
import org.apache.poi.ss.usermodel.FormulaEvaluator;
|
||||||
import org.apache.poi.hssf.record.formula.UnionPtg;
|
import org.apache.poi.ss.usermodel.Sheet;
|
||||||
import org.apache.poi.hssf.record.formula.UnknownPtg;
|
import org.apache.poi.ss.usermodel.Workbook;
|
||||||
import org.apache.poi.hssf.record.formula.eval.Area2DEval;
|
import org.apache.poi.ss.usermodel.FormulaEvaluator.CellValue;
|
||||||
import org.apache.poi.hssf.record.formula.eval.Area3DEval;
|
|
||||||
import org.apache.poi.hssf.record.formula.eval.AreaEval;
|
|
||||||
import org.apache.poi.hssf.record.formula.eval.BlankEval;
|
|
||||||
import org.apache.poi.hssf.record.formula.eval.BoolEval;
|
|
||||||
import org.apache.poi.hssf.record.formula.eval.ErrorEval;
|
|
||||||
import org.apache.poi.hssf.record.formula.eval.Eval;
|
|
||||||
import org.apache.poi.hssf.record.formula.eval.FunctionEval;
|
|
||||||
import org.apache.poi.hssf.record.formula.eval.NameEval;
|
|
||||||
import org.apache.poi.hssf.record.formula.eval.NumberEval;
|
|
||||||
import org.apache.poi.hssf.record.formula.eval.OperationEval;
|
|
||||||
import org.apache.poi.hssf.record.formula.eval.Ref2DEval;
|
|
||||||
import org.apache.poi.hssf.record.formula.eval.Ref3DEval;
|
|
||||||
import org.apache.poi.hssf.record.formula.eval.RefEval;
|
|
||||||
import org.apache.poi.hssf.record.formula.eval.StringEval;
|
|
||||||
import org.apache.poi.hssf.record.formula.eval.ValueEval;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Amol S. Deshmukh < amolweb at ya hoo dot com >
|
* @author Amol S. Deshmukh < amolweb at ya hoo dot com >
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class HSSFFormulaEvaluator {
|
public class HSSFFormulaEvaluator extends FormulaEvaluator {
|
||||||
|
|
||||||
// params to lookup the right constructor using reflection
|
|
||||||
private static final Class[] VALUE_CONTRUCTOR_CLASS_ARRAY = new Class[] { Ptg.class };
|
|
||||||
|
|
||||||
private static final Class[] AREA3D_CONSTRUCTOR_CLASS_ARRAY = new Class[] { Ptg.class, ValueEval[].class };
|
|
||||||
|
|
||||||
private static final Class[] REFERENCE_CONSTRUCTOR_CLASS_ARRAY = new Class[] { Ptg.class, ValueEval.class };
|
|
||||||
|
|
||||||
private static final Class[] REF3D_CONSTRUCTOR_CLASS_ARRAY = new Class[] { Ptg.class, ValueEval.class };
|
|
||||||
|
|
||||||
// Maps for mapping *Eval to *Ptg
|
|
||||||
private static final Map VALUE_EVALS_MAP = new HashMap();
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Following is the mapping between the Ptg tokens returned
|
|
||||||
* by the FormulaParser and the *Eval classes that are used
|
|
||||||
* by the FormulaEvaluator
|
|
||||||
*/
|
|
||||||
static {
|
|
||||||
VALUE_EVALS_MAP.put(BoolPtg.class, BoolEval.class);
|
|
||||||
VALUE_EVALS_MAP.put(IntPtg.class, NumberEval.class);
|
|
||||||
VALUE_EVALS_MAP.put(NumberPtg.class, NumberEval.class);
|
|
||||||
VALUE_EVALS_MAP.put(StringPtg.class, StringEval.class);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
protected HSSFRow row;
|
|
||||||
protected HSSFSheet sheet;
|
|
||||||
protected HSSFWorkbook workbook;
|
|
||||||
|
|
||||||
public HSSFFormulaEvaluator(HSSFSheet sheet, HSSFWorkbook workbook) {
|
public HSSFFormulaEvaluator(HSSFSheet sheet, HSSFWorkbook workbook) {
|
||||||
this.sheet = sheet;
|
super(sheet, workbook);
|
||||||
this.workbook = workbook;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setCurrentRow(HSSFRow row) {
|
|
||||||
this.row = row;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns an underlying FormulaParser, for the specified
|
* Returns an underlying FormulaParser, for the specified
|
||||||
* Formula String and HSSFWorkbook.
|
* Formula String and HSSFWorkbook.
|
||||||
@ -118,639 +47,6 @@ public class HSSFFormulaEvaluator {
|
|||||||
return new FormulaParser(formula, workbook.getWorkbook());
|
return new FormulaParser(formula, workbook.getWorkbook());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 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(HSSFCell cell) {
|
|
||||||
CellValue retval = null;
|
|
||||||
if (cell != null) {
|
|
||||||
switch (cell.getCellType()) {
|
|
||||||
case HSSFCell.CELL_TYPE_BLANK:
|
|
||||||
retval = new CellValue(HSSFCell.CELL_TYPE_BLANK);
|
|
||||||
break;
|
|
||||||
case HSSFCell.CELL_TYPE_BOOLEAN:
|
|
||||||
retval = new CellValue(HSSFCell.CELL_TYPE_BOOLEAN);
|
|
||||||
retval.setBooleanValue(cell.getBooleanCellValue());
|
|
||||||
break;
|
|
||||||
case HSSFCell.CELL_TYPE_ERROR:
|
|
||||||
retval = new CellValue(HSSFCell.CELL_TYPE_ERROR);
|
|
||||||
retval.setErrorValue(cell.getErrorCellValue());
|
|
||||||
break;
|
|
||||||
case HSSFCell.CELL_TYPE_FORMULA:
|
|
||||||
retval = getCellValueForEval(internalEvaluate(cell, row, sheet, workbook));
|
|
||||||
break;
|
|
||||||
case HSSFCell.CELL_TYPE_NUMERIC:
|
|
||||||
retval = new CellValue(HSSFCell.CELL_TYPE_NUMERIC);
|
|
||||||
retval.setNumberValue(cell.getNumericCellValue());
|
|
||||||
break;
|
|
||||||
case HSSFCell.CELL_TYPE_STRING:
|
|
||||||
retval = new CellValue(HSSFCell.CELL_TYPE_STRING);
|
|
||||||
retval.setRichTextStringValue(cell.getRichStringCellValue());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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 #evaluateInCell(HSSFCell)}
|
|
||||||
* @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(HSSFCell cell) {
|
|
||||||
if (cell != null) {
|
|
||||||
switch (cell.getCellType()) {
|
|
||||||
case HSSFCell.CELL_TYPE_FORMULA:
|
|
||||||
CellValue cv = getCellValueForEval(internalEvaluate(cell, row, sheet, workbook));
|
|
||||||
switch (cv.getCellType()) {
|
|
||||||
case HSSFCell.CELL_TYPE_BOOLEAN:
|
|
||||||
cell.setCellValue(cv.getBooleanValue());
|
|
||||||
break;
|
|
||||||
case HSSFCell.CELL_TYPE_ERROR:
|
|
||||||
cell.setCellValue(cv.getErrorValue());
|
|
||||||
break;
|
|
||||||
case HSSFCell.CELL_TYPE_NUMERIC:
|
|
||||||
cell.setCellValue(cv.getNumberValue());
|
|
||||||
break;
|
|
||||||
case HSSFCell.CELL_TYPE_STRING:
|
|
||||||
cell.setCellValue(cv.getRichTextStringValue());
|
|
||||||
break;
|
|
||||||
case HSSFCell.CELL_TYPE_BLANK:
|
|
||||||
break;
|
|
||||||
case HSSFCell.CELL_TYPE_FORMULA: // this will never happen, we have already evaluated the formula
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return cv.getCellType();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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.
|
|
||||||
* Note that the same instance of HSSFCell is returned to
|
|
||||||
* allow chained calls like:
|
|
||||||
* <pre>
|
|
||||||
* int evaluatedCellType = evaluator.evaluateInCell(cell).getCellType();
|
|
||||||
* </pre>
|
|
||||||
* Be aware that your cell value will be changed to hold the
|
|
||||||
* result of the formula. If you simply want the formula
|
|
||||||
* value computed for you, use {@link #evaluateFormulaCell(HSSFCell)}
|
|
||||||
* @param cell
|
|
||||||
*/
|
|
||||||
public HSSFCell evaluateInCell(HSSFCell cell) {
|
|
||||||
if (cell != null) {
|
|
||||||
switch (cell.getCellType()) {
|
|
||||||
case HSSFCell.CELL_TYPE_FORMULA:
|
|
||||||
CellValue cv = getCellValueForEval(internalEvaluate(cell, row, sheet, workbook));
|
|
||||||
switch (cv.getCellType()) {
|
|
||||||
case HSSFCell.CELL_TYPE_BOOLEAN:
|
|
||||||
cell.setCellType(HSSFCell.CELL_TYPE_BOOLEAN);
|
|
||||||
cell.setCellValue(cv.getBooleanValue());
|
|
||||||
break;
|
|
||||||
case HSSFCell.CELL_TYPE_ERROR:
|
|
||||||
cell.setCellType(HSSFCell.CELL_TYPE_ERROR);
|
|
||||||
cell.setCellValue(cv.getErrorValue());
|
|
||||||
break;
|
|
||||||
case HSSFCell.CELL_TYPE_NUMERIC:
|
|
||||||
cell.setCellType(HSSFCell.CELL_TYPE_NUMERIC);
|
|
||||||
cell.setCellValue(cv.getNumberValue());
|
|
||||||
break;
|
|
||||||
case HSSFCell.CELL_TYPE_STRING:
|
|
||||||
cell.setCellType(HSSFCell.CELL_TYPE_STRING);
|
|
||||||
cell.setCellValue(cv.getRichTextStringValue());
|
|
||||||
break;
|
|
||||||
case HSSFCell.CELL_TYPE_BLANK:
|
|
||||||
break;
|
|
||||||
case HSSFCell.CELL_TYPE_FORMULA: // this will never happen, we have already evaluated the formula
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return cell;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Loops over all cells in all sheets of the supplied
|
|
||||||
* workbook.
|
|
||||||
* For cells that contain formulas, their formulas are
|
|
||||||
* evaluated, and the results are saved. These cells
|
|
||||||
* remain as formula cells.
|
|
||||||
* For cells that do not contain formulas, no changes
|
|
||||||
* are made.
|
|
||||||
* This is a helpful wrapper around looping over all
|
|
||||||
* cells, and calling evaluateFormulaCell on each one.
|
|
||||||
*/
|
|
||||||
public static void evaluateAllFormulaCells(HSSFWorkbook wb) {
|
|
||||||
for(int i=0; i<wb.getNumberOfSheets(); i++) {
|
|
||||||
HSSFSheet sheet = wb.getSheetAt(i);
|
|
||||||
HSSFFormulaEvaluator evaluator = new HSSFFormulaEvaluator(sheet, wb);
|
|
||||||
|
|
||||||
for (Iterator rit = sheet.rowIterator(); rit.hasNext();) {
|
|
||||||
HSSFRow r = (HSSFRow)rit.next();
|
|
||||||
evaluator.setCurrentRow(r);
|
|
||||||
|
|
||||||
for (Iterator cit = r.cellIterator(); cit.hasNext();) {
|
|
||||||
HSSFCell c = (HSSFCell)cit.next();
|
|
||||||
if (c.getCellType() == HSSFCell.CELL_TYPE_FORMULA)
|
|
||||||
evaluator.evaluateFormulaCell(c);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a CellValue wrapper around the supplied ValueEval instance.
|
|
||||||
* @param eval
|
|
||||||
*/
|
|
||||||
protected static CellValue getCellValueForEval(ValueEval eval) {
|
|
||||||
CellValue retval = null;
|
|
||||||
if (eval != null) {
|
|
||||||
if (eval instanceof NumberEval) {
|
|
||||||
NumberEval ne = (NumberEval) eval;
|
|
||||||
retval = new CellValue(HSSFCell.CELL_TYPE_NUMERIC);
|
|
||||||
retval.setNumberValue(ne.getNumberValue());
|
|
||||||
}
|
|
||||||
else if (eval instanceof BoolEval) {
|
|
||||||
BoolEval be = (BoolEval) eval;
|
|
||||||
retval = new CellValue(HSSFCell.CELL_TYPE_BOOLEAN);
|
|
||||||
retval.setBooleanValue(be.getBooleanValue());
|
|
||||||
}
|
|
||||||
else if (eval instanceof StringEval) {
|
|
||||||
StringEval ne = (StringEval) eval;
|
|
||||||
retval = new CellValue(HSSFCell.CELL_TYPE_STRING);
|
|
||||||
retval.setStringValue(ne.getStringValue());
|
|
||||||
}
|
|
||||||
else if (eval instanceof BlankEval) {
|
|
||||||
retval = new CellValue(HSSFCell.CELL_TYPE_BLANK);
|
|
||||||
}
|
|
||||||
else if (eval instanceof ErrorEval) {
|
|
||||||
retval = new CellValue(HSSFCell.CELL_TYPE_ERROR);
|
|
||||||
retval.setErrorValue((byte)((ErrorEval)eval).getErrorCode());
|
|
||||||
// retval.setRichTextStringValue(new HSSFRichTextString("#An error occurred. check cell.getErrorCode()"));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
retval = new CellValue(HSSFCell.CELL_TYPE_ERROR);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Dev. Note: Internal evaluate must be passed only a formula cell
|
|
||||||
* else a runtime exception will be thrown somewhere inside the method.
|
|
||||||
* (Hence this is a private method.)
|
|
||||||
*/
|
|
||||||
private static ValueEval internalEvaluate(HSSFCell srcCell, HSSFRow srcRow, HSSFSheet sheet, HSSFWorkbook workbook) {
|
|
||||||
int srcRowNum = srcRow.getRowNum();
|
|
||||||
short srcColNum = srcCell.getCellNum();
|
|
||||||
|
|
||||||
|
|
||||||
EvaluationCycleDetector tracker = EvaluationCycleDetectorManager.getTracker();
|
|
||||||
|
|
||||||
if(!tracker.startEvaluate(workbook, sheet, srcRowNum, srcColNum)) {
|
|
||||||
return ErrorEval.CIRCULAR_REF_ERROR;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
return evaluateCell(workbook, sheet, srcRowNum, srcColNum, srcCell.getCellFormula());
|
|
||||||
} finally {
|
|
||||||
tracker.endEvaluate(workbook, sheet, srcRowNum, srcColNum);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private static ValueEval evaluateCell(HSSFWorkbook workbook, HSSFSheet sheet,
|
|
||||||
int srcRowNum, short srcColNum, String cellFormulaText) {
|
|
||||||
FormulaParser parser = new FormulaParser(cellFormulaText, workbook.getWorkbook());
|
|
||||||
parser.parse();
|
|
||||||
Ptg[] ptgs = parser.getRPNPtg();
|
|
||||||
// -- parsing over --
|
|
||||||
|
|
||||||
|
|
||||||
Stack stack = new Stack();
|
|
||||||
for (int i = 0, iSize = ptgs.length; i < iSize; i++) {
|
|
||||||
|
|
||||||
// since we don't know how to handle these yet :(
|
|
||||||
Ptg ptg = ptgs[i];
|
|
||||||
if (ptg instanceof ControlPtg) { continue; }
|
|
||||||
if (ptg instanceof MemErrPtg) { continue; }
|
|
||||||
if (ptg instanceof MissingArgPtg) { continue; }
|
|
||||||
if (ptg instanceof NamePtg) {
|
|
||||||
// named ranges, macro functions
|
|
||||||
NamePtg namePtg = (NamePtg) ptg;
|
|
||||||
stack.push(new NameEval(namePtg.getIndex()));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (ptg instanceof NameXPtg) {
|
|
||||||
// TODO - external functions
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (ptg instanceof UnknownPtg) { continue; }
|
|
||||||
|
|
||||||
if (ptg instanceof OperationPtg) {
|
|
||||||
OperationPtg optg = (OperationPtg) ptg;
|
|
||||||
|
|
||||||
// parens can be ignored since we have RPN tokens
|
|
||||||
if (optg instanceof ParenthesisPtg) { continue; }
|
|
||||||
if (optg instanceof AttrPtg) { continue; }
|
|
||||||
if (optg instanceof UnionPtg) { continue; }
|
|
||||||
|
|
||||||
OperationEval operation = OperationEvaluatorFactory.create(optg);
|
|
||||||
|
|
||||||
int numops = operation.getNumberOfOperands();
|
|
||||||
Eval[] ops = new Eval[numops];
|
|
||||||
|
|
||||||
// storing the ops in reverse order since they are popping
|
|
||||||
for (int j = numops - 1; j >= 0; j--) {
|
|
||||||
Eval p = (Eval) stack.pop();
|
|
||||||
ops[j] = p;
|
|
||||||
}
|
|
||||||
Eval opresult = invokeOperation(operation, ops, srcRowNum, srcColNum, workbook, sheet);
|
|
||||||
stack.push(opresult);
|
|
||||||
}
|
|
||||||
else if (ptg instanceof ReferencePtg) {
|
|
||||||
ReferencePtg refPtg = (ReferencePtg) ptg;
|
|
||||||
int colIx = refPtg.getColumn();
|
|
||||||
int rowIx = refPtg.getRow();
|
|
||||||
HSSFRow row = sheet.getRow(rowIx);
|
|
||||||
HSSFCell cell = (row != null) ? row.getCell(colIx) : null;
|
|
||||||
stack.push(createRef2DEval(refPtg, cell, row, sheet, workbook));
|
|
||||||
}
|
|
||||||
else if (ptg instanceof Ref3DPtg) {
|
|
||||||
Ref3DPtg refPtg = (Ref3DPtg) ptg;
|
|
||||||
int colIx = refPtg.getColumn();
|
|
||||||
int rowIx = refPtg.getRow();
|
|
||||||
Workbook wb = workbook.getWorkbook();
|
|
||||||
HSSFSheet xsheet = workbook.getSheetAt(wb.getSheetIndexFromExternSheetIndex(refPtg.getExternSheetIndex()));
|
|
||||||
HSSFRow row = xsheet.getRow(rowIx);
|
|
||||||
HSSFCell cell = (row != null) ? row.getCell(colIx) : null;
|
|
||||||
stack.push(createRef3DEval(refPtg, cell, row, xsheet, workbook));
|
|
||||||
}
|
|
||||||
else if (ptg instanceof AreaPtg) {
|
|
||||||
AreaPtg ap = (AreaPtg) ptg;
|
|
||||||
AreaEval ae = evaluateAreaPtg(sheet, workbook, ap);
|
|
||||||
stack.push(ae);
|
|
||||||
}
|
|
||||||
else if (ptg instanceof Area3DPtg) {
|
|
||||||
Area3DPtg a3dp = (Area3DPtg) ptg;
|
|
||||||
AreaEval ae = evaluateArea3dPtg(workbook, a3dp);
|
|
||||||
stack.push(ae);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
Eval ptgEval = getEvalForPtg(ptg);
|
|
||||||
stack.push(ptgEval);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ValueEval value = ((ValueEval) stack.pop());
|
|
||||||
if (!stack.isEmpty()) {
|
|
||||||
throw new IllegalStateException("evaluation stack not empty");
|
|
||||||
}
|
|
||||||
value = dereferenceValue(value, srcRowNum, srcColNum);
|
|
||||||
if (value instanceof BlankEval) {
|
|
||||||
// Note Excel behaviour here. A blank final final value is converted to zero.
|
|
||||||
return NumberEval.ZERO;
|
|
||||||
// Formulas _never_ evaluate to blank. If a formula appears to have evaluated to
|
|
||||||
// blank, the actual value is empty string. This can be verified with ISBLANK().
|
|
||||||
}
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Dereferences a single value from any AreaEval or RefEval evaluation result.
|
|
||||||
* If the supplied evaluationResult is just a plain value, it is returned as-is.
|
|
||||||
* @return a <tt>NumberEval</tt>, <tt>StringEval</tt>, <tt>BoolEval</tt>,
|
|
||||||
* <tt>BlankEval</tt> or <tt>ErrorEval</tt>. Never <code>null</code>.
|
|
||||||
*/
|
|
||||||
private static ValueEval dereferenceValue(ValueEval evaluationResult, int srcRowNum, short srcColNum) {
|
|
||||||
if (evaluationResult instanceof RefEval) {
|
|
||||||
RefEval rv = (RefEval) evaluationResult;
|
|
||||||
return rv.getInnerValueEval();
|
|
||||||
}
|
|
||||||
if (evaluationResult instanceof AreaEval) {
|
|
||||||
AreaEval ae = (AreaEval) evaluationResult;
|
|
||||||
if (ae.isRow()) {
|
|
||||||
if(ae.isColumn()) {
|
|
||||||
return ae.getValues()[0];
|
|
||||||
}
|
|
||||||
return ae.getValueAt(ae.getFirstRow(), srcColNum);
|
|
||||||
}
|
|
||||||
if (ae.isColumn()) {
|
|
||||||
return ae.getValueAt(srcRowNum, ae.getFirstColumn());
|
|
||||||
}
|
|
||||||
return ErrorEval.VALUE_INVALID;
|
|
||||||
}
|
|
||||||
return evaluationResult;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Eval invokeOperation(OperationEval operation, Eval[] ops, int srcRowNum, short srcColNum,
|
|
||||||
HSSFWorkbook workbook, HSSFSheet sheet) {
|
|
||||||
|
|
||||||
if(operation instanceof FunctionEval) {
|
|
||||||
FunctionEval fe = (FunctionEval) operation;
|
|
||||||
if(fe.isFreeRefFunction()) {
|
|
||||||
return fe.getFreeRefFunction().evaluate(ops, srcRowNum, srcColNum, workbook, sheet);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return operation.evaluate(ops, srcRowNum, srcColNum);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static AreaEval evaluateAreaPtg(HSSFSheet sheet, HSSFWorkbook workbook, AreaPtg ap) {
|
|
||||||
int row0 = ap.getFirstRow();
|
|
||||||
int col0 = ap.getFirstColumn();
|
|
||||||
int row1 = ap.getLastRow();
|
|
||||||
int col1 = ap.getLastColumn();
|
|
||||||
|
|
||||||
// If the last row is -1, then the
|
|
||||||
// reference is for the rest of the column
|
|
||||||
// (eg C:C)
|
|
||||||
// TODO: Handle whole column ranges properly
|
|
||||||
if(row1 == -1 && row0 >= 0) {
|
|
||||||
row1 = (short)sheet.getLastRowNum();
|
|
||||||
}
|
|
||||||
ValueEval[] values = evalArea(workbook, sheet, row0, col0, row1, col1);
|
|
||||||
return new Area2DEval(ap, values);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static AreaEval evaluateArea3dPtg(HSSFWorkbook workbook, Area3DPtg a3dp) {
|
|
||||||
int row0 = a3dp.getFirstRow();
|
|
||||||
int col0 = a3dp.getFirstColumn();
|
|
||||||
int row1 = a3dp.getLastRow();
|
|
||||||
int col1 = a3dp.getLastColumn();
|
|
||||||
Workbook wb = workbook.getWorkbook();
|
|
||||||
HSSFSheet xsheet = workbook.getSheetAt(wb.getSheetIndexFromExternSheetIndex(a3dp.getExternSheetIndex()));
|
|
||||||
|
|
||||||
// If the last row is -1, then the
|
|
||||||
// reference is for the rest of the column
|
|
||||||
// (eg C:C)
|
|
||||||
// TODO: Handle whole column ranges properly
|
|
||||||
if(row1 == -1 && row0 >= 0) {
|
|
||||||
row1 = (short)xsheet.getLastRowNum();
|
|
||||||
}
|
|
||||||
|
|
||||||
ValueEval[] values = evalArea(workbook, xsheet, row0, col0, row1, col1);
|
|
||||||
return new Area3DEval(a3dp, values);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static ValueEval[] evalArea(HSSFWorkbook workbook, HSSFSheet sheet,
|
|
||||||
int row0, int col0, int row1, int col1) {
|
|
||||||
ValueEval[] values = new ValueEval[(row1 - row0 + 1) * (col1 - col0 + 1)];
|
|
||||||
for (int x = row0; sheet != null && x < row1 + 1; x++) {
|
|
||||||
HSSFRow row = sheet.getRow(x);
|
|
||||||
for (int y = col0; y < col1 + 1; y++) {
|
|
||||||
ValueEval cellEval;
|
|
||||||
if(row == null) {
|
|
||||||
cellEval = BlankEval.INSTANCE;
|
|
||||||
} else {
|
|
||||||
cellEval = getEvalForCell(row.getCell(y), row, sheet, workbook);
|
|
||||||
}
|
|
||||||
values[(x - row0) * (col1 - col0 + 1) + (y - col0)] = cellEval;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return values;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* returns an appropriate Eval impl instance for the Ptg. The Ptg must be
|
|
||||||
* one of: Area3DPtg, AreaPtg, ReferencePtg, Ref3DPtg, IntPtg, NumberPtg,
|
|
||||||
* StringPtg, BoolPtg <br/>special Note: OperationPtg subtypes cannot be
|
|
||||||
* passed here!
|
|
||||||
*
|
|
||||||
* @param ptg
|
|
||||||
*/
|
|
||||||
protected static Eval getEvalForPtg(Ptg ptg) {
|
|
||||||
Eval retval = null;
|
|
||||||
|
|
||||||
Class clazz = (Class) VALUE_EVALS_MAP.get(ptg.getClass());
|
|
||||||
try {
|
|
||||||
if (ptg instanceof Area3DPtg) {
|
|
||||||
Constructor constructor = clazz.getConstructor(AREA3D_CONSTRUCTOR_CLASS_ARRAY);
|
|
||||||
retval = (OperationEval) constructor.newInstance(new Ptg[] { ptg });
|
|
||||||
}
|
|
||||||
else if (ptg instanceof AreaPtg) {
|
|
||||||
Constructor constructor = clazz.getConstructor(AREA3D_CONSTRUCTOR_CLASS_ARRAY);
|
|
||||||
retval = (OperationEval) constructor.newInstance(new Ptg[] { ptg });
|
|
||||||
}
|
|
||||||
else if (ptg instanceof ReferencePtg) {
|
|
||||||
Constructor constructor = clazz.getConstructor(REFERENCE_CONSTRUCTOR_CLASS_ARRAY);
|
|
||||||
retval = (OperationEval) constructor.newInstance(new Ptg[] { ptg });
|
|
||||||
}
|
|
||||||
else if (ptg instanceof Ref3DPtg) {
|
|
||||||
Constructor constructor = clazz.getConstructor(REF3D_CONSTRUCTOR_CLASS_ARRAY);
|
|
||||||
retval = (OperationEval) constructor.newInstance(new Ptg[] { ptg });
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (ptg instanceof IntPtg || ptg instanceof NumberPtg || ptg instanceof StringPtg
|
|
||||||
|| ptg instanceof BoolPtg) {
|
|
||||||
Constructor constructor = clazz.getConstructor(VALUE_CONTRUCTOR_CLASS_ARRAY);
|
|
||||||
retval = (ValueEval) constructor.newInstance(new Ptg[] { ptg });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception e) {
|
|
||||||
throw new RuntimeException("Fatal Error: ", e);
|
|
||||||
}
|
|
||||||
return retval;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Given a cell, find its type and from that create an appropriate ValueEval
|
|
||||||
* impl instance and return that. Since the cell could be an external
|
|
||||||
* reference, we need the sheet that this belongs to.
|
|
||||||
* Non existent cells are treated as empty.
|
|
||||||
* @param cell
|
|
||||||
* @param sheet
|
|
||||||
* @param workbook
|
|
||||||
*/
|
|
||||||
protected static ValueEval getEvalForCell(HSSFCell cell, HSSFRow row, HSSFSheet sheet, HSSFWorkbook workbook) {
|
|
||||||
|
|
||||||
if (cell == null) {
|
|
||||||
return BlankEval.INSTANCE;
|
|
||||||
}
|
|
||||||
switch (cell.getCellType()) {
|
|
||||||
case HSSFCell.CELL_TYPE_NUMERIC:
|
|
||||||
return new NumberEval(cell.getNumericCellValue());
|
|
||||||
case HSSFCell.CELL_TYPE_STRING:
|
|
||||||
return new StringEval(cell.getRichStringCellValue().getString());
|
|
||||||
case HSSFCell.CELL_TYPE_FORMULA:
|
|
||||||
return internalEvaluate(cell, row, sheet, workbook);
|
|
||||||
case HSSFCell.CELL_TYPE_BOOLEAN:
|
|
||||||
return BoolEval.valueOf(cell.getBooleanCellValue());
|
|
||||||
case HSSFCell.CELL_TYPE_BLANK:
|
|
||||||
return BlankEval.INSTANCE;
|
|
||||||
case HSSFCell.CELL_TYPE_ERROR:
|
|
||||||
return ErrorEval.valueOf(cell.getErrorCellValue());
|
|
||||||
}
|
|
||||||
throw new RuntimeException("Unexpected cell type (" + cell.getCellType() + ")");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a Ref2DEval for ReferencePtg.
|
|
||||||
* Non existent cells are treated as RefEvals containing BlankEval.
|
|
||||||
*/
|
|
||||||
private static Ref2DEval createRef2DEval(ReferencePtg ptg, HSSFCell cell,
|
|
||||||
HSSFRow row, HSSFSheet sheet, HSSFWorkbook workbook) {
|
|
||||||
if (cell == null) {
|
|
||||||
return new Ref2DEval(ptg, BlankEval.INSTANCE);
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (cell.getCellType()) {
|
|
||||||
case HSSFCell.CELL_TYPE_NUMERIC:
|
|
||||||
return new Ref2DEval(ptg, new NumberEval(cell.getNumericCellValue()));
|
|
||||||
case HSSFCell.CELL_TYPE_STRING:
|
|
||||||
return new Ref2DEval(ptg, new StringEval(cell.getRichStringCellValue().getString()));
|
|
||||||
case HSSFCell.CELL_TYPE_FORMULA:
|
|
||||||
return new Ref2DEval(ptg, internalEvaluate(cell, row, sheet, workbook));
|
|
||||||
case HSSFCell.CELL_TYPE_BOOLEAN:
|
|
||||||
return new Ref2DEval(ptg, BoolEval.valueOf(cell.getBooleanCellValue()));
|
|
||||||
case HSSFCell.CELL_TYPE_BLANK:
|
|
||||||
return new Ref2DEval(ptg, BlankEval.INSTANCE);
|
|
||||||
case HSSFCell.CELL_TYPE_ERROR:
|
|
||||||
return new Ref2DEval(ptg, ErrorEval.valueOf(cell.getErrorCellValue()));
|
|
||||||
}
|
|
||||||
throw new RuntimeException("Unexpected cell type (" + cell.getCellType() + ")");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* create a Ref3DEval for Ref3DPtg.
|
|
||||||
*/
|
|
||||||
private static Ref3DEval createRef3DEval(Ref3DPtg ptg, HSSFCell cell,
|
|
||||||
HSSFRow row, HSSFSheet sheet, HSSFWorkbook workbook) {
|
|
||||||
if (cell == null) {
|
|
||||||
return new Ref3DEval(ptg, BlankEval.INSTANCE);
|
|
||||||
}
|
|
||||||
switch (cell.getCellType()) {
|
|
||||||
case HSSFCell.CELL_TYPE_NUMERIC:
|
|
||||||
return new Ref3DEval(ptg, new NumberEval(cell.getNumericCellValue()));
|
|
||||||
case HSSFCell.CELL_TYPE_STRING:
|
|
||||||
return new Ref3DEval(ptg, new StringEval(cell.getRichStringCellValue().getString()));
|
|
||||||
case HSSFCell.CELL_TYPE_FORMULA:
|
|
||||||
return new Ref3DEval(ptg, internalEvaluate(cell, row, sheet, workbook));
|
|
||||||
case HSSFCell.CELL_TYPE_BOOLEAN:
|
|
||||||
return new Ref3DEval(ptg, BoolEval.valueOf(cell.getBooleanCellValue()));
|
|
||||||
case HSSFCell.CELL_TYPE_BLANK:
|
|
||||||
return new Ref3DEval(ptg, BlankEval.INSTANCE);
|
|
||||||
case HSSFCell.CELL_TYPE_ERROR:
|
|
||||||
return new Ref3DEval(ptg, ErrorEval.valueOf(cell.getErrorCellValue()));
|
|
||||||
}
|
|
||||||
throw new RuntimeException("Unexpected cell type (" + cell.getCellType() + ")");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Mimics the 'data view' of a cell. This allows formula evaluator
|
|
||||||
* to return a CellValue instead of precasting the value to String
|
|
||||||
* or Number or boolean type.
|
|
||||||
* @author Amol S. Deshmukh < amolweb at ya hoo dot com >
|
|
||||||
*/
|
|
||||||
public static final class CellValue {
|
|
||||||
private int cellType;
|
|
||||||
private HSSFRichTextString richTextStringValue;
|
|
||||||
private double numberValue;
|
|
||||||
private boolean booleanValue;
|
|
||||||
private byte errorValue;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* CellType should be one of the types defined in HSSFCell
|
|
||||||
* @param cellType
|
|
||||||
*/
|
|
||||||
public CellValue(int cellType) {
|
|
||||||
super();
|
|
||||||
this.cellType = cellType;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* @return Returns the booleanValue.
|
|
||||||
*/
|
|
||||||
public boolean getBooleanValue() {
|
|
||||||
return booleanValue;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* @param booleanValue The booleanValue to set.
|
|
||||||
*/
|
|
||||||
public void setBooleanValue(boolean booleanValue) {
|
|
||||||
this.booleanValue = booleanValue;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* @return Returns the numberValue.
|
|
||||||
*/
|
|
||||||
public double getNumberValue() {
|
|
||||||
return numberValue;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* @param numberValue The numberValue to set.
|
|
||||||
*/
|
|
||||||
public void setNumberValue(double numberValue) {
|
|
||||||
this.numberValue = numberValue;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* @return Returns the stringValue. This method is deprecated, use
|
|
||||||
* getRichTextStringValue instead
|
|
||||||
* @deprecated
|
|
||||||
*/
|
|
||||||
public String getStringValue() {
|
|
||||||
return richTextStringValue.getString();
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* @param stringValue The stringValue to set. This method is deprecated, use
|
|
||||||
* getRichTextStringValue instead.
|
|
||||||
* @deprecated
|
|
||||||
*/
|
|
||||||
public void setStringValue(String stringValue) {
|
|
||||||
this.richTextStringValue = new HSSFRichTextString(stringValue);
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* @return Returns the cellType.
|
|
||||||
*/
|
|
||||||
public int getCellType() {
|
|
||||||
return cellType;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* @return Returns the errorValue.
|
|
||||||
*/
|
|
||||||
public byte getErrorValue() {
|
|
||||||
return errorValue;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* @param errorValue The errorValue to set.
|
|
||||||
*/
|
|
||||||
public void setErrorValue(byte errorValue) {
|
|
||||||
this.errorValue = errorValue;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* @return Returns the richTextStringValue.
|
|
||||||
*/
|
|
||||||
public HSSFRichTextString getRichTextStringValue() {
|
|
||||||
return richTextStringValue;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* @param richTextStringValue The richTextStringValue to set.
|
|
||||||
*/
|
|
||||||
public void setRichTextStringValue(HSSFRichTextString richTextStringValue) {
|
|
||||||
this.richTextStringValue = richTextStringValue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* debug method
|
* debug method
|
||||||
@ -760,7 +56,8 @@ public class HSSFFormulaEvaluator {
|
|||||||
* @param workbook
|
* @param workbook
|
||||||
*/
|
*/
|
||||||
void inspectPtgs(String formula) {
|
void inspectPtgs(String formula) {
|
||||||
FormulaParser fp = new FormulaParser(formula, workbook.getWorkbook());
|
HSSFWorkbook hssfWb = (HSSFWorkbook)workbook;
|
||||||
|
FormulaParser fp = new FormulaParser(formula, hssfWb.getWorkbook());
|
||||||
fp.parse();
|
fp.parse();
|
||||||
Ptg[] ptgs = fp.getRPNPtg();
|
Ptg[] ptgs = fp.getRPNPtg();
|
||||||
System.out.println("<ptg-group>");
|
System.out.println("<ptg-group>");
|
||||||
@ -775,4 +72,13 @@ public class HSSFFormulaEvaluator {
|
|||||||
System.out.println("</ptg-group>");
|
System.out.println("</ptg-group>");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compatibility class.
|
||||||
|
* Seems to do more harm than good though
|
||||||
|
*/
|
||||||
|
// public static class CellValue extends FormulaEvaluator.CellValue {
|
||||||
|
// public CellValue(int cellType, CreationHelper creationHelper) {
|
||||||
|
// super(cellType, creationHelper);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
@ -610,7 +610,11 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm
|
|||||||
return sheets.size();
|
return sheets.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public int getSheetIndexFromExternSheetIndex(int externSheetNumber) {
|
||||||
|
return workbook.getSheetIndexFromExternSheetIndex(externSheetNumber);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
* Get the HSSFSheet object at the given index.
|
* Get the HSSFSheet object at the given index.
|
||||||
* @param index of the sheet number (0-based physical & logical)
|
* @param index of the sheet number (0-based physical & logical)
|
||||||
* @return HSSFSheet at the provided index
|
* @return HSSFSheet at the provided index
|
||||||
|
@ -15,11 +15,14 @@
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
==================================================================== */
|
==================================================================== */
|
||||||
|
|
||||||
package org.apache.poi.hssf.usermodel;
|
package org.apache.poi.ss.usermodel;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.apache.poi.ss.usermodel.Sheet;
|
||||||
|
import org.apache.poi.ss.usermodel.Workbook;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Instances of this class keep track of multiple dependent cell evaluations due
|
* Instances of this class keep track of multiple dependent cell evaluations due
|
||||||
* to recursive calls to <tt>HSSFFormulaEvaluator.internalEvaluate()</tt>.
|
* to recursive calls to <tt>HSSFFormulaEvaluator.internalEvaluate()</tt>.
|
||||||
@ -36,12 +39,12 @@ final class EvaluationCycleDetector {
|
|||||||
*/
|
*/
|
||||||
private static final class CellEvaluationFrame {
|
private static final class CellEvaluationFrame {
|
||||||
|
|
||||||
private final HSSFWorkbook _workbook;
|
private final Workbook _workbook;
|
||||||
private final HSSFSheet _sheet;
|
private final Sheet _sheet;
|
||||||
private final int _srcRowNum;
|
private final int _srcRowNum;
|
||||||
private final int _srcColNum;
|
private final int _srcColNum;
|
||||||
|
|
||||||
public CellEvaluationFrame(HSSFWorkbook workbook, HSSFSheet sheet, int srcRowNum, int srcColNum) {
|
public CellEvaluationFrame(Workbook workbook, Sheet sheet, int srcRowNum, int srcColNum) {
|
||||||
if (workbook == null) {
|
if (workbook == null) {
|
||||||
throw new IllegalArgumentException("workbook must not be null");
|
throw new IllegalArgumentException("workbook must not be null");
|
||||||
}
|
}
|
||||||
@ -108,7 +111,7 @@ final class EvaluationCycleDetector {
|
|||||||
* @return <code>true</code> if the specified cell has not been visited yet in the current
|
* @return <code>true</code> if the specified cell has not been visited yet in the current
|
||||||
* evaluation. <code>false</code> if the specified cell is already being evaluated.
|
* evaluation. <code>false</code> if the specified cell is already being evaluated.
|
||||||
*/
|
*/
|
||||||
public boolean startEvaluate(HSSFWorkbook workbook, HSSFSheet sheet, int srcRowNum, int srcColNum) {
|
public boolean startEvaluate(Workbook workbook, Sheet sheet, int srcRowNum, int srcColNum) {
|
||||||
CellEvaluationFrame cef = new CellEvaluationFrame(workbook, sheet, srcRowNum, srcColNum);
|
CellEvaluationFrame cef = new CellEvaluationFrame(workbook, sheet, srcRowNum, srcColNum);
|
||||||
if (_evaluationFrames.contains(cef)) {
|
if (_evaluationFrames.contains(cef)) {
|
||||||
return false;
|
return false;
|
||||||
@ -129,7 +132,7 @@ final class EvaluationCycleDetector {
|
|||||||
* required. However, they have been included to assert correct behaviour,
|
* required. However, they have been included to assert correct behaviour,
|
||||||
* and form more meaningful error messages.
|
* and form more meaningful error messages.
|
||||||
*/
|
*/
|
||||||
public void endEvaluate(HSSFWorkbook workbook, HSSFSheet sheet, int srcRowNum, int srcColNum) {
|
public void endEvaluate(Workbook workbook, Sheet sheet, int srcRowNum, int srcColNum) {
|
||||||
int nFrames = _evaluationFrames.size();
|
int nFrames = _evaluationFrames.size();
|
||||||
if (nFrames < 1) {
|
if (nFrames < 1) {
|
||||||
throw new IllegalStateException("Call to endEvaluate without matching call to startEvaluate");
|
throw new IllegalStateException("Call to endEvaluate without matching call to startEvaluate");
|
@ -15,7 +15,7 @@
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
==================================================================== */
|
==================================================================== */
|
||||||
|
|
||||||
package org.apache.poi.hssf.usermodel;
|
package org.apache.poi.ss.usermodel;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class makes an <tt>EvaluationCycleDetector</tt> instance available to
|
* This class makes an <tt>EvaluationCycleDetector</tt> instance available to
|
759
src/java/org/apache/poi/ss/usermodel/FormulaEvaluator.java
Normal file
759
src/java/org/apache/poi/ss/usermodel/FormulaEvaluator.java
Normal file
@ -0,0 +1,759 @@
|
|||||||
|
/*
|
||||||
|
* 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.ss.usermodel;
|
||||||
|
|
||||||
|
import java.lang.reflect.Constructor;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Stack;
|
||||||
|
|
||||||
|
import org.apache.poi.hssf.model.FormulaParser;
|
||||||
|
import org.apache.poi.hssf.record.formula.Area3DPtg;
|
||||||
|
import org.apache.poi.hssf.record.formula.AreaPtg;
|
||||||
|
import org.apache.poi.hssf.record.formula.AttrPtg;
|
||||||
|
import org.apache.poi.hssf.record.formula.BoolPtg;
|
||||||
|
import org.apache.poi.hssf.record.formula.ControlPtg;
|
||||||
|
import org.apache.poi.hssf.record.formula.IntPtg;
|
||||||
|
import org.apache.poi.hssf.record.formula.MemErrPtg;
|
||||||
|
import org.apache.poi.hssf.record.formula.MissingArgPtg;
|
||||||
|
import org.apache.poi.hssf.record.formula.NamePtg;
|
||||||
|
import org.apache.poi.hssf.record.formula.NameXPtg;
|
||||||
|
import org.apache.poi.hssf.record.formula.NumberPtg;
|
||||||
|
import org.apache.poi.hssf.record.formula.OperationPtg;
|
||||||
|
import org.apache.poi.hssf.record.formula.ParenthesisPtg;
|
||||||
|
import org.apache.poi.hssf.record.formula.Ptg;
|
||||||
|
import org.apache.poi.hssf.record.formula.Ref3DPtg;
|
||||||
|
import org.apache.poi.hssf.record.formula.ReferencePtg;
|
||||||
|
import org.apache.poi.hssf.record.formula.StringPtg;
|
||||||
|
import org.apache.poi.hssf.record.formula.UnionPtg;
|
||||||
|
import org.apache.poi.hssf.record.formula.UnknownPtg;
|
||||||
|
import org.apache.poi.hssf.record.formula.eval.Area2DEval;
|
||||||
|
import org.apache.poi.hssf.record.formula.eval.Area3DEval;
|
||||||
|
import org.apache.poi.hssf.record.formula.eval.AreaEval;
|
||||||
|
import org.apache.poi.hssf.record.formula.eval.BlankEval;
|
||||||
|
import org.apache.poi.hssf.record.formula.eval.BoolEval;
|
||||||
|
import org.apache.poi.hssf.record.formula.eval.ErrorEval;
|
||||||
|
import org.apache.poi.hssf.record.formula.eval.Eval;
|
||||||
|
import org.apache.poi.hssf.record.formula.eval.FunctionEval;
|
||||||
|
import org.apache.poi.hssf.record.formula.eval.NameEval;
|
||||||
|
import org.apache.poi.hssf.record.formula.eval.NumberEval;
|
||||||
|
import org.apache.poi.hssf.record.formula.eval.OperationEval;
|
||||||
|
import org.apache.poi.hssf.record.formula.eval.Ref2DEval;
|
||||||
|
import org.apache.poi.hssf.record.formula.eval.Ref3DEval;
|
||||||
|
import org.apache.poi.hssf.record.formula.eval.RefEval;
|
||||||
|
import org.apache.poi.hssf.record.formula.eval.StringEval;
|
||||||
|
import org.apache.poi.hssf.record.formula.eval.ValueEval;
|
||||||
|
import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator;
|
||||||
|
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Amol S. Deshmukh < amolweb at ya hoo dot com >
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class FormulaEvaluator {
|
||||||
|
|
||||||
|
// params to lookup the right constructor using reflection
|
||||||
|
private static final Class[] VALUE_CONTRUCTOR_CLASS_ARRAY = new Class[] { Ptg.class };
|
||||||
|
|
||||||
|
private static final Class[] AREA3D_CONSTRUCTOR_CLASS_ARRAY = new Class[] { Ptg.class, ValueEval[].class };
|
||||||
|
|
||||||
|
private static final Class[] REFERENCE_CONSTRUCTOR_CLASS_ARRAY = new Class[] { Ptg.class, ValueEval.class };
|
||||||
|
|
||||||
|
private static final Class[] REF3D_CONSTRUCTOR_CLASS_ARRAY = new Class[] { Ptg.class, ValueEval.class };
|
||||||
|
|
||||||
|
// Maps for mapping *Eval to *Ptg
|
||||||
|
private static final Map VALUE_EVALS_MAP = new HashMap();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Following is the mapping between the Ptg tokens returned
|
||||||
|
* by the FormulaParser and the *Eval classes that are used
|
||||||
|
* by the FormulaEvaluator
|
||||||
|
*/
|
||||||
|
static {
|
||||||
|
VALUE_EVALS_MAP.put(BoolPtg.class, BoolEval.class);
|
||||||
|
VALUE_EVALS_MAP.put(IntPtg.class, NumberEval.class);
|
||||||
|
VALUE_EVALS_MAP.put(NumberPtg.class, NumberEval.class);
|
||||||
|
VALUE_EVALS_MAP.put(StringPtg.class, StringEval.class);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected Row row;
|
||||||
|
protected Sheet sheet;
|
||||||
|
protected Workbook workbook;
|
||||||
|
|
||||||
|
public FormulaEvaluator(Sheet sheet, Workbook workbook) {
|
||||||
|
this.sheet = sheet;
|
||||||
|
this.workbook = workbook;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCurrentRow(Row row) {
|
||||||
|
this.row = row;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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) {
|
||||||
|
CellValue retval = null;
|
||||||
|
if (cell != null) {
|
||||||
|
switch (cell.getCellType()) {
|
||||||
|
case Cell.CELL_TYPE_BLANK:
|
||||||
|
retval = new CellValue(Cell.CELL_TYPE_BLANK, workbook.getCreationHelper());
|
||||||
|
break;
|
||||||
|
case Cell.CELL_TYPE_BOOLEAN:
|
||||||
|
retval = new CellValue(Cell.CELL_TYPE_BOOLEAN, workbook.getCreationHelper());
|
||||||
|
retval.setBooleanValue(cell.getBooleanCellValue());
|
||||||
|
break;
|
||||||
|
case Cell.CELL_TYPE_ERROR:
|
||||||
|
retval = new CellValue(Cell.CELL_TYPE_ERROR, workbook.getCreationHelper());
|
||||||
|
retval.setErrorValue(cell.getErrorCellValue());
|
||||||
|
break;
|
||||||
|
case Cell.CELL_TYPE_FORMULA:
|
||||||
|
retval = getCellValueForEval(internalEvaluate(cell, row, sheet, workbook), workbook.getCreationHelper());
|
||||||
|
break;
|
||||||
|
case Cell.CELL_TYPE_NUMERIC:
|
||||||
|
retval = new CellValue(Cell.CELL_TYPE_NUMERIC, workbook.getCreationHelper());
|
||||||
|
retval.setNumberValue(cell.getNumericCellValue());
|
||||||
|
break;
|
||||||
|
case Cell.CELL_TYPE_STRING:
|
||||||
|
retval = new CellValue(Cell.CELL_TYPE_STRING, workbook.getCreationHelper());
|
||||||
|
retval.setRichTextStringValue(cell.getRichStringCellValue());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 #evaluateInCell(HSSFCell)}
|
||||||
|
* @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) {
|
||||||
|
switch (cell.getCellType()) {
|
||||||
|
case Cell.CELL_TYPE_FORMULA:
|
||||||
|
CellValue cv = getCellValueForEval(internalEvaluate(cell, row, sheet, workbook), workbook.getCreationHelper());
|
||||||
|
switch (cv.getCellType()) {
|
||||||
|
case Cell.CELL_TYPE_BOOLEAN:
|
||||||
|
cell.setCellValue(cv.getBooleanValue());
|
||||||
|
break;
|
||||||
|
case Cell.CELL_TYPE_ERROR:
|
||||||
|
cell.setCellValue(cv.getErrorValue());
|
||||||
|
break;
|
||||||
|
case Cell.CELL_TYPE_NUMERIC:
|
||||||
|
cell.setCellValue(cv.getNumberValue());
|
||||||
|
break;
|
||||||
|
case Cell.CELL_TYPE_STRING:
|
||||||
|
cell.setCellValue(cv.getRichTextStringValue());
|
||||||
|
break;
|
||||||
|
case Cell.CELL_TYPE_BLANK:
|
||||||
|
break;
|
||||||
|
case Cell.CELL_TYPE_FORMULA: // this will never happen, we have already evaluated the formula
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return cv.getCellType();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
* Note that the same instance of HSSFCell is returned to
|
||||||
|
* allow chained calls like:
|
||||||
|
* <pre>
|
||||||
|
* int evaluatedCellType = evaluator.evaluateInCell(cell).getCellType();
|
||||||
|
* </pre>
|
||||||
|
* Be aware that your cell value will be changed to hold the
|
||||||
|
* result of the formula. If you simply want the formula
|
||||||
|
* value computed for you, use {@link #evaluateFormulaCell(HSSFCell)}
|
||||||
|
* @param cell
|
||||||
|
*/
|
||||||
|
public Cell evaluateInCell(Cell cell) {
|
||||||
|
if (cell != null) {
|
||||||
|
switch (cell.getCellType()) {
|
||||||
|
case Cell.CELL_TYPE_FORMULA:
|
||||||
|
CellValue cv = getCellValueForEval(internalEvaluate(cell, row, sheet, workbook), workbook.getCreationHelper());
|
||||||
|
switch (cv.getCellType()) {
|
||||||
|
case Cell.CELL_TYPE_BOOLEAN:
|
||||||
|
cell.setCellType(Cell.CELL_TYPE_BOOLEAN);
|
||||||
|
cell.setCellValue(cv.getBooleanValue());
|
||||||
|
break;
|
||||||
|
case Cell.CELL_TYPE_ERROR:
|
||||||
|
cell.setCellType(Cell.CELL_TYPE_ERROR);
|
||||||
|
cell.setCellValue(cv.getErrorValue());
|
||||||
|
break;
|
||||||
|
case Cell.CELL_TYPE_NUMERIC:
|
||||||
|
cell.setCellType(Cell.CELL_TYPE_NUMERIC);
|
||||||
|
cell.setCellValue(cv.getNumberValue());
|
||||||
|
break;
|
||||||
|
case Cell.CELL_TYPE_STRING:
|
||||||
|
cell.setCellType(Cell.CELL_TYPE_STRING);
|
||||||
|
cell.setCellValue(cv.getRichTextStringValue());
|
||||||
|
break;
|
||||||
|
case Cell.CELL_TYPE_BLANK:
|
||||||
|
break;
|
||||||
|
case Cell.CELL_TYPE_FORMULA: // this will never happen, we have already evaluated the formula
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return cell;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loops over all cells in all sheets of the supplied
|
||||||
|
* workbook.
|
||||||
|
* For cells that contain formulas, their formulas are
|
||||||
|
* evaluated, and the results are saved. These cells
|
||||||
|
* remain as formula cells.
|
||||||
|
* For cells that do not contain formulas, no changes
|
||||||
|
* are made.
|
||||||
|
* This is a helpful wrapper around looping over all
|
||||||
|
* cells, and calling evaluateFormulaCell on each one.
|
||||||
|
*/
|
||||||
|
public static void evaluateAllFormulaCells(Workbook wb) {
|
||||||
|
for(int i=0; i<wb.getNumberOfSheets(); i++) {
|
||||||
|
Sheet sheet = wb.getSheetAt(i);
|
||||||
|
FormulaEvaluator evaluator = new FormulaEvaluator(sheet, wb);
|
||||||
|
|
||||||
|
for (Iterator rit = sheet.rowIterator(); rit.hasNext();) {
|
||||||
|
Row r = (Row)rit.next();
|
||||||
|
evaluator.setCurrentRow(r);
|
||||||
|
|
||||||
|
for (Iterator cit = r.cellIterator(); cit.hasNext();) {
|
||||||
|
Cell c = (Cell)cit.next();
|
||||||
|
if (c.getCellType() == Cell.CELL_TYPE_FORMULA)
|
||||||
|
evaluator.evaluateFormulaCell(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a CellValue wrapper around the supplied ValueEval instance.
|
||||||
|
* @param eval
|
||||||
|
*/
|
||||||
|
protected static CellValue getCellValueForEval(ValueEval eval, CreationHelper cHelper) {
|
||||||
|
CellValue retval = null;
|
||||||
|
if (eval != null) {
|
||||||
|
if (eval instanceof NumberEval) {
|
||||||
|
NumberEval ne = (NumberEval) eval;
|
||||||
|
retval = new CellValue(Cell.CELL_TYPE_NUMERIC, cHelper);
|
||||||
|
retval.setNumberValue(ne.getNumberValue());
|
||||||
|
}
|
||||||
|
else if (eval instanceof BoolEval) {
|
||||||
|
BoolEval be = (BoolEval) eval;
|
||||||
|
retval = new CellValue(Cell.CELL_TYPE_BOOLEAN, cHelper);
|
||||||
|
retval.setBooleanValue(be.getBooleanValue());
|
||||||
|
}
|
||||||
|
else if (eval instanceof StringEval) {
|
||||||
|
StringEval ne = (StringEval) eval;
|
||||||
|
retval = new CellValue(Cell.CELL_TYPE_STRING, cHelper);
|
||||||
|
retval.setStringValue(ne.getStringValue());
|
||||||
|
}
|
||||||
|
else if (eval instanceof BlankEval) {
|
||||||
|
retval = new CellValue(Cell.CELL_TYPE_BLANK, cHelper);
|
||||||
|
}
|
||||||
|
else if (eval instanceof ErrorEval) {
|
||||||
|
retval = new CellValue(Cell.CELL_TYPE_ERROR, cHelper);
|
||||||
|
retval.setErrorValue((byte)((ErrorEval)eval).getErrorCode());
|
||||||
|
// retval.setRichTextStringValue(new HSSFRichTextString("#An error occurred. check cell.getErrorCode()"));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
retval = new CellValue(Cell.CELL_TYPE_ERROR, cHelper);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dev. Note: Internal evaluate must be passed only a formula cell
|
||||||
|
* else a runtime exception will be thrown somewhere inside the method.
|
||||||
|
* (Hence this is a private method.)
|
||||||
|
*/
|
||||||
|
private static ValueEval internalEvaluate(Cell srcCell, Row srcRow, Sheet sheet, Workbook workbook) {
|
||||||
|
int srcRowNum = srcRow.getRowNum();
|
||||||
|
short srcColNum = srcCell.getCellNum();
|
||||||
|
|
||||||
|
|
||||||
|
EvaluationCycleDetector tracker = EvaluationCycleDetectorManager.getTracker();
|
||||||
|
|
||||||
|
if(!tracker.startEvaluate(workbook, sheet, srcRowNum, srcColNum)) {
|
||||||
|
return ErrorEval.CIRCULAR_REF_ERROR;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return evaluateCell(workbook, sheet, srcRowNum, srcColNum, srcCell.getCellFormula());
|
||||||
|
} finally {
|
||||||
|
tracker.endEvaluate(workbook, sheet, srcRowNum, srcColNum);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private static ValueEval evaluateCell(Workbook workbook, Sheet sheet,
|
||||||
|
int srcRowNum, short srcColNum, String cellFormulaText) {
|
||||||
|
|
||||||
|
FormulaParser parser;
|
||||||
|
if(workbook instanceof HSSFWorkbook) {
|
||||||
|
parser = HSSFFormulaEvaluator.getUnderlyingParser(
|
||||||
|
(HSSFWorkbook)workbook,
|
||||||
|
cellFormulaText
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// Hope for the best...
|
||||||
|
parser = new FormulaParser(cellFormulaText, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
parser.parse();
|
||||||
|
Ptg[] ptgs = parser.getRPNPtg();
|
||||||
|
// -- parsing over --
|
||||||
|
|
||||||
|
|
||||||
|
Stack stack = new Stack();
|
||||||
|
for (int i = 0, iSize = ptgs.length; i < iSize; i++) {
|
||||||
|
|
||||||
|
// since we don't know how to handle these yet :(
|
||||||
|
Ptg ptg = ptgs[i];
|
||||||
|
if (ptg instanceof ControlPtg) { continue; }
|
||||||
|
if (ptg instanceof MemErrPtg) { continue; }
|
||||||
|
if (ptg instanceof MissingArgPtg) { continue; }
|
||||||
|
if (ptg instanceof NamePtg) {
|
||||||
|
// named ranges, macro functions
|
||||||
|
NamePtg namePtg = (NamePtg) ptg;
|
||||||
|
stack.push(new NameEval(namePtg.getIndex()));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (ptg instanceof NameXPtg) {
|
||||||
|
// TODO - external functions
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (ptg instanceof UnknownPtg) { continue; }
|
||||||
|
|
||||||
|
if (ptg instanceof OperationPtg) {
|
||||||
|
OperationPtg optg = (OperationPtg) ptg;
|
||||||
|
|
||||||
|
// parens can be ignored since we have RPN tokens
|
||||||
|
if (optg instanceof ParenthesisPtg) { continue; }
|
||||||
|
if (optg instanceof AttrPtg) { continue; }
|
||||||
|
if (optg instanceof UnionPtg) { continue; }
|
||||||
|
|
||||||
|
OperationEval operation = OperationEvaluatorFactory.create(optg);
|
||||||
|
|
||||||
|
int numops = operation.getNumberOfOperands();
|
||||||
|
Eval[] ops = new Eval[numops];
|
||||||
|
|
||||||
|
// storing the ops in reverse order since they are popping
|
||||||
|
for (int j = numops - 1; j >= 0; j--) {
|
||||||
|
Eval p = (Eval) stack.pop();
|
||||||
|
ops[j] = p;
|
||||||
|
}
|
||||||
|
Eval opresult = invokeOperation(operation, ops, srcRowNum, srcColNum, workbook, sheet);
|
||||||
|
stack.push(opresult);
|
||||||
|
}
|
||||||
|
else if (ptg instanceof ReferencePtg) {
|
||||||
|
ReferencePtg refPtg = (ReferencePtg) ptg;
|
||||||
|
int colIx = refPtg.getColumn();
|
||||||
|
int rowIx = refPtg.getRow();
|
||||||
|
Row row = sheet.getRow(rowIx);
|
||||||
|
Cell cell = (row != null) ? row.getCell(colIx) : null;
|
||||||
|
stack.push(createRef2DEval(refPtg, cell, row, sheet, workbook));
|
||||||
|
}
|
||||||
|
else if (ptg instanceof Ref3DPtg) {
|
||||||
|
Ref3DPtg refPtg = (Ref3DPtg) ptg;
|
||||||
|
int colIx = refPtg.getColumn();
|
||||||
|
int rowIx = refPtg.getRow();
|
||||||
|
Sheet xsheet = workbook.getSheetAt(
|
||||||
|
workbook.getSheetIndexFromExternSheetIndex(refPtg.getExternSheetIndex())
|
||||||
|
);
|
||||||
|
Row row = xsheet.getRow(rowIx);
|
||||||
|
Cell cell = (row != null) ? row.getCell(colIx) : null;
|
||||||
|
stack.push(createRef3DEval(refPtg, cell, row, xsheet, workbook));
|
||||||
|
}
|
||||||
|
else if (ptg instanceof AreaPtg) {
|
||||||
|
AreaPtg ap = (AreaPtg) ptg;
|
||||||
|
AreaEval ae = evaluateAreaPtg(sheet, workbook, ap);
|
||||||
|
stack.push(ae);
|
||||||
|
}
|
||||||
|
else if (ptg instanceof Area3DPtg) {
|
||||||
|
Area3DPtg a3dp = (Area3DPtg) ptg;
|
||||||
|
AreaEval ae = evaluateArea3dPtg(workbook, a3dp);
|
||||||
|
stack.push(ae);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Eval ptgEval = getEvalForPtg(ptg);
|
||||||
|
stack.push(ptgEval);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ValueEval value = ((ValueEval) stack.pop());
|
||||||
|
if (!stack.isEmpty()) {
|
||||||
|
throw new IllegalStateException("evaluation stack not empty");
|
||||||
|
}
|
||||||
|
value = dereferenceValue(value, srcRowNum, srcColNum);
|
||||||
|
if (value instanceof BlankEval) {
|
||||||
|
// Note Excel behaviour here. A blank final final value is converted to zero.
|
||||||
|
return NumberEval.ZERO;
|
||||||
|
// Formulas _never_ evaluate to blank. If a formula appears to have evaluated to
|
||||||
|
// blank, the actual value is empty string. This can be verified with ISBLANK().
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dereferences a single value from any AreaEval or RefEval evaluation result.
|
||||||
|
* If the supplied evaluationResult is just a plain value, it is returned as-is.
|
||||||
|
* @return a <tt>NumberEval</tt>, <tt>StringEval</tt>, <tt>BoolEval</tt>,
|
||||||
|
* <tt>BlankEval</tt> or <tt>ErrorEval</tt>. Never <code>null</code>.
|
||||||
|
*/
|
||||||
|
private static ValueEval dereferenceValue(ValueEval evaluationResult, int srcRowNum, short srcColNum) {
|
||||||
|
if (evaluationResult instanceof RefEval) {
|
||||||
|
RefEval rv = (RefEval) evaluationResult;
|
||||||
|
return rv.getInnerValueEval();
|
||||||
|
}
|
||||||
|
if (evaluationResult instanceof AreaEval) {
|
||||||
|
AreaEval ae = (AreaEval) evaluationResult;
|
||||||
|
if (ae.isRow()) {
|
||||||
|
if(ae.isColumn()) {
|
||||||
|
return ae.getValues()[0];
|
||||||
|
}
|
||||||
|
return ae.getValueAt(ae.getFirstRow(), srcColNum);
|
||||||
|
}
|
||||||
|
if (ae.isColumn()) {
|
||||||
|
return ae.getValueAt(srcRowNum, ae.getFirstColumn());
|
||||||
|
}
|
||||||
|
return ErrorEval.VALUE_INVALID;
|
||||||
|
}
|
||||||
|
return evaluationResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Eval invokeOperation(OperationEval operation, Eval[] ops, int srcRowNum, short srcColNum,
|
||||||
|
Workbook workbook, Sheet sheet) {
|
||||||
|
|
||||||
|
if(operation instanceof FunctionEval) {
|
||||||
|
FunctionEval fe = (FunctionEval) operation;
|
||||||
|
if(fe.isFreeRefFunction()) {
|
||||||
|
return fe.getFreeRefFunction().evaluate(ops, srcRowNum, srcColNum, workbook, sheet);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return operation.evaluate(ops, srcRowNum, srcColNum);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static AreaEval evaluateAreaPtg(Sheet sheet, Workbook workbook, AreaPtg ap) {
|
||||||
|
int row0 = ap.getFirstRow();
|
||||||
|
int col0 = ap.getFirstColumn();
|
||||||
|
int row1 = ap.getLastRow();
|
||||||
|
int col1 = ap.getLastColumn();
|
||||||
|
|
||||||
|
// If the last row is -1, then the
|
||||||
|
// reference is for the rest of the column
|
||||||
|
// (eg C:C)
|
||||||
|
// TODO: Handle whole column ranges properly
|
||||||
|
if(row1 == -1 && row0 >= 0) {
|
||||||
|
row1 = (short)sheet.getLastRowNum();
|
||||||
|
}
|
||||||
|
ValueEval[] values = evalArea(workbook, sheet, row0, col0, row1, col1);
|
||||||
|
return new Area2DEval(ap, values);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static AreaEval evaluateArea3dPtg(Workbook workbook, Area3DPtg a3dp) {
|
||||||
|
int row0 = a3dp.getFirstRow();
|
||||||
|
int col0 = a3dp.getFirstColumn();
|
||||||
|
int row1 = a3dp.getLastRow();
|
||||||
|
int col1 = a3dp.getLastColumn();
|
||||||
|
Sheet xsheet = workbook.getSheetAt(
|
||||||
|
workbook.getSheetIndexFromExternSheetIndex(a3dp.getExternSheetIndex())
|
||||||
|
);
|
||||||
|
|
||||||
|
// If the last row is -1, then the
|
||||||
|
// reference is for the rest of the column
|
||||||
|
// (eg C:C)
|
||||||
|
// TODO: Handle whole column ranges properly
|
||||||
|
if(row1 == -1 && row0 >= 0) {
|
||||||
|
row1 = (short)xsheet.getLastRowNum();
|
||||||
|
}
|
||||||
|
|
||||||
|
ValueEval[] values = evalArea(workbook, xsheet, row0, col0, row1, col1);
|
||||||
|
return new Area3DEval(a3dp, values);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ValueEval[] evalArea(Workbook workbook, Sheet sheet,
|
||||||
|
int row0, int col0, int row1, int col1) {
|
||||||
|
ValueEval[] values = new ValueEval[(row1 - row0 + 1) * (col1 - col0 + 1)];
|
||||||
|
for (int x = row0; sheet != null && x < row1 + 1; x++) {
|
||||||
|
Row row = sheet.getRow(x);
|
||||||
|
for (int y = col0; y < col1 + 1; y++) {
|
||||||
|
ValueEval cellEval;
|
||||||
|
if(row == null) {
|
||||||
|
cellEval = BlankEval.INSTANCE;
|
||||||
|
} else {
|
||||||
|
cellEval = getEvalForCell(row.getCell(y), row, sheet, workbook);
|
||||||
|
}
|
||||||
|
values[(x - row0) * (col1 - col0 + 1) + (y - col0)] = cellEval;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* returns an appropriate Eval impl instance for the Ptg. The Ptg must be
|
||||||
|
* one of: Area3DPtg, AreaPtg, ReferencePtg, Ref3DPtg, IntPtg, NumberPtg,
|
||||||
|
* StringPtg, BoolPtg <br/>special Note: OperationPtg subtypes cannot be
|
||||||
|
* passed here!
|
||||||
|
*
|
||||||
|
* @param ptg
|
||||||
|
*/
|
||||||
|
protected static Eval getEvalForPtg(Ptg ptg) {
|
||||||
|
Eval retval = null;
|
||||||
|
|
||||||
|
Class clazz = (Class) VALUE_EVALS_MAP.get(ptg.getClass());
|
||||||
|
try {
|
||||||
|
if (ptg instanceof Area3DPtg) {
|
||||||
|
Constructor constructor = clazz.getConstructor(AREA3D_CONSTRUCTOR_CLASS_ARRAY);
|
||||||
|
retval = (OperationEval) constructor.newInstance(new Ptg[] { ptg });
|
||||||
|
}
|
||||||
|
else if (ptg instanceof AreaPtg) {
|
||||||
|
Constructor constructor = clazz.getConstructor(AREA3D_CONSTRUCTOR_CLASS_ARRAY);
|
||||||
|
retval = (OperationEval) constructor.newInstance(new Ptg[] { ptg });
|
||||||
|
}
|
||||||
|
else if (ptg instanceof ReferencePtg) {
|
||||||
|
Constructor constructor = clazz.getConstructor(REFERENCE_CONSTRUCTOR_CLASS_ARRAY);
|
||||||
|
retval = (OperationEval) constructor.newInstance(new Ptg[] { ptg });
|
||||||
|
}
|
||||||
|
else if (ptg instanceof Ref3DPtg) {
|
||||||
|
Constructor constructor = clazz.getConstructor(REF3D_CONSTRUCTOR_CLASS_ARRAY);
|
||||||
|
retval = (OperationEval) constructor.newInstance(new Ptg[] { ptg });
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (ptg instanceof IntPtg || ptg instanceof NumberPtg || ptg instanceof StringPtg
|
||||||
|
|| ptg instanceof BoolPtg) {
|
||||||
|
Constructor constructor = clazz.getConstructor(VALUE_CONTRUCTOR_CLASS_ARRAY);
|
||||||
|
retval = (ValueEval) constructor.newInstance(new Ptg[] { ptg });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e) {
|
||||||
|
throw new RuntimeException("Fatal Error: ", e);
|
||||||
|
}
|
||||||
|
return retval;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given a cell, find its type and from that create an appropriate ValueEval
|
||||||
|
* impl instance and return that. Since the cell could be an external
|
||||||
|
* reference, we need the sheet that this belongs to.
|
||||||
|
* Non existent cells are treated as empty.
|
||||||
|
* @param cell
|
||||||
|
* @param sheet
|
||||||
|
* @param workbook
|
||||||
|
*/
|
||||||
|
protected static ValueEval getEvalForCell(Cell cell, Row row, Sheet sheet, Workbook workbook) {
|
||||||
|
|
||||||
|
if (cell == null) {
|
||||||
|
return BlankEval.INSTANCE;
|
||||||
|
}
|
||||||
|
switch (cell.getCellType()) {
|
||||||
|
case Cell.CELL_TYPE_NUMERIC:
|
||||||
|
return new NumberEval(cell.getNumericCellValue());
|
||||||
|
case Cell.CELL_TYPE_STRING:
|
||||||
|
return new StringEval(cell.getRichStringCellValue().getString());
|
||||||
|
case Cell.CELL_TYPE_FORMULA:
|
||||||
|
return internalEvaluate(cell, row, sheet, workbook);
|
||||||
|
case Cell.CELL_TYPE_BOOLEAN:
|
||||||
|
return BoolEval.valueOf(cell.getBooleanCellValue());
|
||||||
|
case Cell.CELL_TYPE_BLANK:
|
||||||
|
return BlankEval.INSTANCE;
|
||||||
|
case Cell.CELL_TYPE_ERROR:
|
||||||
|
return ErrorEval.valueOf(cell.getErrorCellValue());
|
||||||
|
}
|
||||||
|
throw new RuntimeException("Unexpected cell type (" + cell.getCellType() + ")");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a Ref2DEval for ReferencePtg.
|
||||||
|
* Non existent cells are treated as RefEvals containing BlankEval.
|
||||||
|
*/
|
||||||
|
private static Ref2DEval createRef2DEval(ReferencePtg ptg, Cell cell,
|
||||||
|
Row row, Sheet sheet, Workbook workbook) {
|
||||||
|
if (cell == null) {
|
||||||
|
return new Ref2DEval(ptg, BlankEval.INSTANCE);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (cell.getCellType()) {
|
||||||
|
case Cell.CELL_TYPE_NUMERIC:
|
||||||
|
return new Ref2DEval(ptg, new NumberEval(cell.getNumericCellValue()));
|
||||||
|
case Cell.CELL_TYPE_STRING:
|
||||||
|
return new Ref2DEval(ptg, new StringEval(cell.getRichStringCellValue().getString()));
|
||||||
|
case Cell.CELL_TYPE_FORMULA:
|
||||||
|
return new Ref2DEval(ptg, internalEvaluate(cell, row, sheet, workbook));
|
||||||
|
case Cell.CELL_TYPE_BOOLEAN:
|
||||||
|
return new Ref2DEval(ptg, BoolEval.valueOf(cell.getBooleanCellValue()));
|
||||||
|
case Cell.CELL_TYPE_BLANK:
|
||||||
|
return new Ref2DEval(ptg, BlankEval.INSTANCE);
|
||||||
|
case Cell.CELL_TYPE_ERROR:
|
||||||
|
return new Ref2DEval(ptg, ErrorEval.valueOf(cell.getErrorCellValue()));
|
||||||
|
}
|
||||||
|
throw new RuntimeException("Unexpected cell type (" + cell.getCellType() + ")");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* create a Ref3DEval for Ref3DPtg.
|
||||||
|
*/
|
||||||
|
private static Ref3DEval createRef3DEval(Ref3DPtg ptg, Cell cell,
|
||||||
|
Row row, Sheet sheet, Workbook workbook) {
|
||||||
|
if (cell == null) {
|
||||||
|
return new Ref3DEval(ptg, BlankEval.INSTANCE);
|
||||||
|
}
|
||||||
|
switch (cell.getCellType()) {
|
||||||
|
case Cell.CELL_TYPE_NUMERIC:
|
||||||
|
return new Ref3DEval(ptg, new NumberEval(cell.getNumericCellValue()));
|
||||||
|
case Cell.CELL_TYPE_STRING:
|
||||||
|
return new Ref3DEval(ptg, new StringEval(cell.getRichStringCellValue().getString()));
|
||||||
|
case Cell.CELL_TYPE_FORMULA:
|
||||||
|
return new Ref3DEval(ptg, internalEvaluate(cell, row, sheet, workbook));
|
||||||
|
case Cell.CELL_TYPE_BOOLEAN:
|
||||||
|
return new Ref3DEval(ptg, BoolEval.valueOf(cell.getBooleanCellValue()));
|
||||||
|
case Cell.CELL_TYPE_BLANK:
|
||||||
|
return new Ref3DEval(ptg, BlankEval.INSTANCE);
|
||||||
|
case Cell.CELL_TYPE_ERROR:
|
||||||
|
return new Ref3DEval(ptg, ErrorEval.valueOf(cell.getErrorCellValue()));
|
||||||
|
}
|
||||||
|
throw new RuntimeException("Unexpected cell type (" + cell.getCellType() + ")");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mimics the 'data view' of a cell. This allows formula evaluator
|
||||||
|
* to return a CellValue instead of precasting the value to String
|
||||||
|
* or Number or boolean type.
|
||||||
|
* @author Amol S. Deshmukh < amolweb at ya hoo dot com >
|
||||||
|
*/
|
||||||
|
public static class CellValue {
|
||||||
|
private CreationHelper creationHelper;
|
||||||
|
private int cellType;
|
||||||
|
private RichTextString richTextStringValue;
|
||||||
|
private double numberValue;
|
||||||
|
private boolean booleanValue;
|
||||||
|
private byte errorValue;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CellType should be one of the types defined in HSSFCell
|
||||||
|
* @param cellType
|
||||||
|
*/
|
||||||
|
public CellValue(int cellType, CreationHelper creationHelper) {
|
||||||
|
super();
|
||||||
|
this.creationHelper = creationHelper;
|
||||||
|
this.cellType = cellType;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @return Returns the booleanValue.
|
||||||
|
*/
|
||||||
|
public boolean getBooleanValue() {
|
||||||
|
return booleanValue;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @param booleanValue The booleanValue to set.
|
||||||
|
*/
|
||||||
|
public void setBooleanValue(boolean booleanValue) {
|
||||||
|
this.booleanValue = booleanValue;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @return Returns the numberValue.
|
||||||
|
*/
|
||||||
|
public double getNumberValue() {
|
||||||
|
return numberValue;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @param numberValue The numberValue to set.
|
||||||
|
*/
|
||||||
|
public void setNumberValue(double numberValue) {
|
||||||
|
this.numberValue = numberValue;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @return Returns the stringValue. This method is deprecated, use
|
||||||
|
* getRichTextStringValue instead
|
||||||
|
* @deprecated
|
||||||
|
*/
|
||||||
|
public String getStringValue() {
|
||||||
|
return richTextStringValue.getString();
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @param stringValue The stringValue to set. This method is deprecated, use
|
||||||
|
* getRichTextStringValue instead.
|
||||||
|
* @deprecated
|
||||||
|
*/
|
||||||
|
public void setStringValue(String stringValue) {
|
||||||
|
this.richTextStringValue =
|
||||||
|
creationHelper.createRichTextString(stringValue);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @return Returns the cellType.
|
||||||
|
*/
|
||||||
|
public int getCellType() {
|
||||||
|
return cellType;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @return Returns the errorValue.
|
||||||
|
*/
|
||||||
|
public byte getErrorValue() {
|
||||||
|
return errorValue;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @param errorValue The errorValue to set.
|
||||||
|
*/
|
||||||
|
public void setErrorValue(byte errorValue) {
|
||||||
|
this.errorValue = errorValue;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @return Returns the richTextStringValue.
|
||||||
|
*/
|
||||||
|
public RichTextString getRichTextStringValue() {
|
||||||
|
return richTextStringValue;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @param richTextStringValue The richTextStringValue to set.
|
||||||
|
*/
|
||||||
|
public void setRichTextStringValue(RichTextString richTextStringValue) {
|
||||||
|
this.richTextStringValue = richTextStringValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -15,7 +15,7 @@
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
==================================================================== */
|
==================================================================== */
|
||||||
|
|
||||||
package org.apache.poi.hssf.usermodel;
|
package org.apache.poi.ss.usermodel;
|
||||||
|
|
||||||
import java.lang.reflect.Constructor;
|
import java.lang.reflect.Constructor;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
@ -174,6 +174,14 @@ public interface Workbook {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
int getNumberOfSheets();
|
int getNumberOfSheets();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds the sheet index for a particular external sheet number.
|
||||||
|
* @param externSheetNumber The external sheet number to convert
|
||||||
|
* @return The index to the sheet found.
|
||||||
|
*/
|
||||||
|
int getSheetIndexFromExternSheetIndex(int externSheetNumber);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the HSSFSheet object at the given index.
|
* Get the HSSFSheet object at the given index.
|
||||||
|
@ -472,8 +472,17 @@ public class XSSFWorkbook extends POIXMLDocument implements Workbook {
|
|||||||
}
|
}
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Doesn't do anything - returns the same index
|
||||||
|
* TODO - figure out if this is a ole2 specific thing, or
|
||||||
|
* if we need to do something proper here too!
|
||||||
|
*/
|
||||||
|
public int getSheetIndexFromExternSheetIndex(int externSheetNumber) {
|
||||||
|
return externSheetNumber;
|
||||||
|
}
|
||||||
|
|
||||||
public Sheet getSheet(String name) {
|
public Sheet getSheet(String name) {
|
||||||
CTSheet[] sheets = this.workbook.getSheets().getSheetArray();
|
CTSheet[] sheets = this.workbook.getSheets().getSheetArray();
|
||||||
for (int i = 0 ; i < sheets.length ; ++i) {
|
for (int i = 0 ; i < sheets.length ; ++i) {
|
||||||
if (name.equals(sheets[i].getName())) {
|
if (name.equals(sheets[i].getName())) {
|
||||||
|
@ -29,7 +29,7 @@ import org.apache.poi.hssf.usermodel.HSSFName;
|
|||||||
import org.apache.poi.hssf.usermodel.HSSFRow;
|
import org.apache.poi.hssf.usermodel.HSSFRow;
|
||||||
import org.apache.poi.hssf.usermodel.HSSFSheet;
|
import org.apache.poi.hssf.usermodel.HSSFSheet;
|
||||||
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
|
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
|
||||||
import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator.CellValue;
|
import org.apache.poi.ss.usermodel.FormulaEvaluator.CellValue;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test the low level formula parser functionality,
|
* Test the low level formula parser functionality,
|
||||||
|
@ -25,7 +25,7 @@ import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator;
|
|||||||
import org.apache.poi.hssf.usermodel.HSSFRow;
|
import org.apache.poi.hssf.usermodel.HSSFRow;
|
||||||
import org.apache.poi.hssf.usermodel.HSSFSheet;
|
import org.apache.poi.hssf.usermodel.HSSFSheet;
|
||||||
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
|
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
|
||||||
import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator.CellValue;
|
import org.apache.poi.ss.usermodel.FormulaEvaluator.CellValue;
|
||||||
/**
|
/**
|
||||||
* Tests HSSFFormulaEvaluator for its handling of cell formula circular references.
|
* Tests HSSFFormulaEvaluator for its handling of cell formula circular references.
|
||||||
*
|
*
|
||||||
|
@ -25,7 +25,7 @@ import org.apache.poi.hssf.usermodel.HSSFName;
|
|||||||
import org.apache.poi.hssf.usermodel.HSSFRow;
|
import org.apache.poi.hssf.usermodel.HSSFRow;
|
||||||
import org.apache.poi.hssf.usermodel.HSSFSheet;
|
import org.apache.poi.hssf.usermodel.HSSFSheet;
|
||||||
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
|
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
|
||||||
import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator.CellValue;
|
import org.apache.poi.ss.usermodel.FormulaEvaluator.CellValue;
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @author Josh Micich
|
* @author Josh Micich
|
||||||
|
@ -32,7 +32,7 @@ import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator;
|
|||||||
import org.apache.poi.hssf.usermodel.HSSFRow;
|
import org.apache.poi.hssf.usermodel.HSSFRow;
|
||||||
import org.apache.poi.hssf.usermodel.HSSFSheet;
|
import org.apache.poi.hssf.usermodel.HSSFSheet;
|
||||||
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
|
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
|
||||||
import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator.CellValue;
|
import org.apache.poi.ss.usermodel.FormulaEvaluator.CellValue;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Miscellaneous tests for bugzilla entries.<p/> The test name contains the
|
* Miscellaneous tests for bugzilla entries.<p/> The test name contains the
|
||||||
|
@ -30,6 +30,8 @@ import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator;
|
|||||||
import org.apache.poi.hssf.usermodel.HSSFRow;
|
import org.apache.poi.hssf.usermodel.HSSFRow;
|
||||||
import org.apache.poi.hssf.usermodel.HSSFSheet;
|
import org.apache.poi.hssf.usermodel.HSSFSheet;
|
||||||
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
|
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
|
||||||
|
import org.apache.poi.ss.usermodel.Cell;
|
||||||
|
import org.apache.poi.ss.usermodel.FormulaEvaluator;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests formulas and operators as loaded from a test data spreadsheet.<p/>
|
* Tests formulas and operators as loaded from a test data spreadsheet.<p/>
|
||||||
@ -104,7 +106,7 @@ public final class TestFormulasFromSpreadsheet extends TestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private static void confirmExpectedResult(String msg, HSSFCell expected, HSSFFormulaEvaluator.CellValue actual) {
|
private static void confirmExpectedResult(String msg, Cell expected, FormulaEvaluator.CellValue actual) {
|
||||||
if (expected == null) {
|
if (expected == null) {
|
||||||
throw new AssertionFailedError(msg + " - Bad setup data expected value is null");
|
throw new AssertionFailedError(msg + " - Bad setup data expected value is null");
|
||||||
}
|
}
|
||||||
@ -249,7 +251,7 @@ public final class TestFormulasFromSpreadsheet extends TestCase {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
HSSFFormulaEvaluator.CellValue actualValue = evaluator.evaluate(c);
|
FormulaEvaluator.CellValue actualValue = evaluator.evaluate(c);
|
||||||
|
|
||||||
HSSFCell expectedValueCell = getExpectedValueCell(expectedValuesRow, colnum);
|
HSSFCell expectedValueCell = getExpectedValueCell(expectedValuesRow, colnum);
|
||||||
try {
|
try {
|
||||||
|
@ -27,7 +27,7 @@ import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator;
|
|||||||
import org.apache.poi.hssf.usermodel.HSSFRow;
|
import org.apache.poi.hssf.usermodel.HSSFRow;
|
||||||
import org.apache.poi.hssf.usermodel.HSSFSheet;
|
import org.apache.poi.hssf.usermodel.HSSFSheet;
|
||||||
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
|
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
|
||||||
import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator.CellValue;
|
import org.apache.poi.ss.usermodel.FormulaEvaluator.CellValue;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test for percent operator evaluator.
|
* Test for percent operator evaluator.
|
||||||
|
@ -24,7 +24,7 @@ import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator;
|
|||||||
import org.apache.poi.hssf.usermodel.HSSFRow;
|
import org.apache.poi.hssf.usermodel.HSSFRow;
|
||||||
import org.apache.poi.hssf.usermodel.HSSFSheet;
|
import org.apache.poi.hssf.usermodel.HSSFSheet;
|
||||||
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
|
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
|
||||||
import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator.CellValue;
|
import org.apache.poi.ss.usermodel.FormulaEvaluator.CellValue;
|
||||||
/**
|
/**
|
||||||
* Tests for Excel function ISBLANK()
|
* Tests for Excel function ISBLANK()
|
||||||
*
|
*
|
||||||
|
@ -32,7 +32,7 @@ import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator;
|
|||||||
import org.apache.poi.hssf.usermodel.HSSFRow;
|
import org.apache.poi.hssf.usermodel.HSSFRow;
|
||||||
import org.apache.poi.hssf.usermodel.HSSFSheet;
|
import org.apache.poi.hssf.usermodel.HSSFSheet;
|
||||||
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
|
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
|
||||||
import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator.CellValue;
|
import org.apache.poi.ss.usermodel.FormulaEvaluator.CellValue;
|
||||||
import org.apache.poi.hssf.util.CellReference;
|
import org.apache.poi.hssf.util.CellReference;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -90,7 +90,7 @@ public final class TestLookupFunctionsFromSpreadsheet extends TestCase {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
private static void confirmExpectedResult(String msg, HSSFCell expected, HSSFFormulaEvaluator.CellValue actual) {
|
private static void confirmExpectedResult(String msg, HSSFCell expected, CellValue actual) {
|
||||||
if (expected == null) {
|
if (expected == null) {
|
||||||
throw new AssertionFailedError(msg + " - Bad setup data expected value is null");
|
throw new AssertionFailedError(msg + " - Bad setup data expected value is null");
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,7 @@ import junit.framework.TestCase;
|
|||||||
|
|
||||||
import org.apache.poi.hssf.record.FormulaRecord;
|
import org.apache.poi.hssf.record.FormulaRecord;
|
||||||
import org.apache.poi.hssf.record.aggregates.FormulaRecordAggregate;
|
import org.apache.poi.hssf.record.aggregates.FormulaRecordAggregate;
|
||||||
import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator.CellValue;
|
import org.apache.poi.ss.usermodel.FormulaEvaluator.CellValue;
|
||||||
import org.apache.poi.hssf.util.CellReference;
|
import org.apache.poi.hssf.util.CellReference;
|
||||||
|
|
||||||
public final class TestBug42464 extends TestCase {
|
public final class TestBug42464 extends TestCase {
|
||||||
|
Loading…
Reference in New Issue
Block a user