diff --git a/src/documentation/content/xdocs/hssf/eval-devguide.xml b/src/documentation/content/xdocs/hssf/eval-devguide.xml index 29c33b8a20..558620bfa4 100644 --- a/src/documentation/content/xdocs/hssf/eval-devguide.xml +++ b/src/documentation/content/xdocs/hssf/eval-devguide.xml @@ -81,84 +81,45 @@
Walkthrough of an "evaluate()" implementation.

So here is the fun part - lets walk through the implementation of the excel - function... SQRT()

+ function... NOT()

The Code -public class Sqrt extends NumericFunction { - - private static final ValueEvalToNumericXlator NUM_XLATOR = - new ValueEvalToNumericXlator((short) - ( ValueEvalToNumericXlator.BOOL_IS_PARSED - | ValueEvalToNumericXlator.EVALUATED_REF_BOOL_IS_PARSED - | ValueEvalToNumericXlator.EVALUATED_REF_STRING_IS_PARSED - | ValueEvalToNumericXlator.REF_BOOL_IS_PARSED - | ValueEvalToNumericXlator.STRING_IS_PARSED - )); - - protected ValueEvalToNumericXlator getXlator() { - return NUM_XLATOR; - } - - public Eval evaluate(Eval[] operands, int srcRow, short srcCol) { - double d = 0; - ValueEval retval = null; - - switch (operands.length) { - default: - retval = ErrorEval.VALUE_INVALID; - break; - case 1: - ValueEval ve = singleOperandEvaluate(operands[0], srcRow, srcCol); - if (ve instanceof NumericValueEval) { - NumericValueEval ne = (NumericValueEval) ve; - d = ne.getNumberValue(); - } - else if (ve instanceof BlankEval) { - // do nothing - } - else { - retval = ErrorEval.NUM_ERROR; - } - } - - if (retval == null) { - d = Math.sqrt(d); - retval = (Double.isNaN(d)) ? (ValueEval) ErrorEval.VALUE_INVALID : new NumberEval(d); - } - return retval; - } +public final class Not implements Function { + public Eval evaluate(Eval[] args, int srcCellRow, short srcCellCol) { + if (args.length != 1) { + return ErrorEval.VALUE_INVALID; + } + boolean boolArgVal; + try { + ValueEval ve = OperandResolver.getSingleValue(args[0], srcCellRow, srcCellCol); + Boolean b = OperandResolver.coerceValueToBoolean(ve, false); + boolArgVal = b == null ? false : b.booleanValue(); + } catch (EvaluationException e) { + return e.getErrorEval(); + } + + return BoolEval.valueOf(!boolArgVal); + } } +
Implementation Details
Modelling Excel Semantics @@ -170,8 +131,8 @@ public class Sqrt extends NumericFunction { Because when you use TRUE in referenced cells with arithmetic functions, it evaluates to blank - meaning it is not evaluated - as if it was string or a blank cell. eg. "=SUM(1,A1)" when A1 is TRUE evaluates to 1. This behaviour changes depending on which function you are using. eg. SQRT(..) that was - described earlier treats a TRUE as 1 in all cases. This is why the configurable ValueEvalToNumericXlator - class had to be written. + described earlier treats a TRUE as 1 in all cases. The various conversion logic has been refactored into common places like the following classes: + OperandResolver, TextFunction, NumericFunction, MultiOperandNumericFunction and FinanceFunction.

Note that when you are extending from an abstract function class like NumericFunction (rather than implementing the interface o.a.p.hssf.record.formula.eval.Function directly) @@ -186,18 +147,16 @@ public class Sqrt extends NumericFunction {

Testing Framework

Automated testing of the implemented Function is easy. - The source code for this is in the file: o.a.p.h.record.formula.GenericFormulaTestCase.java - This class has a reference to the test xls file (not /a/ test xls, /the/ test xls :) - which may need to be changed for your environment. Once you do that, in the test xls, + The source code for this is in the file: o.a.p.h.record.formula.TestFormulasFromSpreadsheet.java + This class has a reference to the Excel test sample file 'FormulaEvalTestData.xls'. In this file, locate the entry for the function that you have implemented and enter different tests in a cell in the FORMULA row. Then copy the "value of" the formula that you entered in the cell just below it (this is easily done in excel as: [copy the formula cell] > [go to cell below] > Edit > Paste Special > Values > "ok"). You can enter multiple such formulas and paste their values in the cell below and the test framework will automatically test if the formula evaluation matches the expected - value (Again, hard to put in words, so if you will, please take time to quickly look - at the code and the currently entered tests in the patch attachment "FormulaEvalTestData.xls" - file). + value (Please take time to quickly look at the code and the currently entered tests in the + file "FormulaEvalTestData.xls").

diff --git a/src/documentation/content/xdocs/hssf/eval.xml b/src/documentation/content/xdocs/hssf/eval.xml index 32279cc4a3..61cb663747 100644 --- a/src/documentation/content/xdocs/hssf/eval.xml +++ b/src/documentation/content/xdocs/hssf/eval.xml @@ -20,13 +20,13 @@ -
- Formula Evaluation - +
+ Formula Evaluation + -
- +
+
+
Introduction

The POI formula evaluation code enables you to calculate the result of formulas in Excels sheets read-in, or created in POI. This document explains @@ -66,35 +66,34 @@ FileInputStream fis = new FileInputStream("c:/temp/test.xls"); HSSFWorkbook wb = new HSSFWorkbook(fis); HSSFSheet sheet = wb.getSheetAt(0); -HSSFFormulaEvaluator evaluator = new HSSFFormulaEvaluator(sheet, wb); +HSSFFormulaEvaluator evaluator = new HSSFFormulaEvaluator(wb); // suppose your formula is in B3 -CellReference cellReference = new CellReference("B3"); +CellReference cellReference = new CellReference("B3"); HSSFRow row = sheet.getRow(cellReference.getRow()); -HSSFCell cell = row.getCell(cellReference.getCol()); +HSSFCell cell = row.getCell((int)cellReference.getCol()); -evaluator.setCurrentRow(row); HSSFFormulaEvaluator.CellValue cellValue = evaluator.evaluate(cell); switch (cellValue.getCellType()) { case HSSFCell.CELL_TYPE_BOOLEAN: - System.out.println(cellValue.getBooleanValue()); - break; + System.out.println(cellValue.getBooleanValue()); + break; case HSSFCell.CELL_TYPE_NUMERIC: - System.out.println(cellValue.getNumberValue()); - break; + System.out.println(cellValue.getNumberValue()); + break; case HSSFCell.CELL_TYPE_STRING: - System.out.println(cellValue.getStringValue()); - break; + System.out.println(cellValue.getStringValue()); + break; case HSSFCell.CELL_TYPE_BLANK: - break; + break; case HSSFCell.CELL_TYPE_ERROR: - break; + break; // CELL_TYPE_FORMULA will never happen - case HSSFCell.CELL_TYPE_FORMULA: - break; -} + case HSSFCell.CELL_TYPE_FORMULA: + break; +}

Thus using the retrieved value (of type HSSFFormulaEvaluator.CellValue - a nested class) returned @@ -119,36 +118,42 @@ switch (cellValue.getCellType()) { FileInputStream fis = new FileInputStream("/somepath/test.xls"); HSSFWorkbook wb = new HSSFWorkbook(fis); HSSFSheet sheet = wb.getSheetAt(0); -HSSFFormulaEvaluator evaluator = new HSSFFormulaEvaluator(sheet, wb); +HSSFFormulaEvaluator evaluator = new HSSFFormulaEvaluator(wb); // suppose your formula is in B3 -CellReference cellReference = new CellReference("B3"); +CellReference cellReference = new CellReference("B3"); HSSFRow row = sheet.getRow(cellReference.getRow()); -HSSFCell cell = row.getCell(cellReference.getCol()); -evaluator.setCurrentRow(row); +HSSFCell cell = row.getCell((int)cellReference.getCol()); if (cell!=null) { - switch (evaluator.evaluateFormulaCell(cell)) { + int valueType = evaluator.evaluateFormulaCell(cell); + if (valueType == -1) { + // Cell was not a formula cell + // but we can read the plain value from the cell just the same as a formula result + valueType = cell.getCellType(); + } + switch (valueType) { case HSSFCell.CELL_TYPE_BOOLEAN: - System.out.println(cell.getBooleanCellValue()); - break; + System.out.println(cell.getBooleanCellValue()); + break; case HSSFCell.CELL_TYPE_NUMERIC: - System.out.println(cell.getNumberCellValue()); - break; + System.out.println(cell.getNumericCellValue()); + break; case HSSFCell.CELL_TYPE_STRING: - System.out.println(cell.getStringCellValue()); - break; + System.out.println(cell.getRichStringCellValue().getString()); + break; case HSSFCell.CELL_TYPE_BLANK: - break; + break; case HSSFCell.CELL_TYPE_ERROR: - System.out.println(cell.getErrorCellValue()); - break; - - // CELL_TYPE_FORMULA will never occur - case HSSFCell.CELL_TYPE_FORMULA: - break; + System.out.println(HSSFErrorConstants.getText(cell.getErrorCellValue())); + break; + + case HSSFCell.CELL_TYPE_FORMULA: + throw new IllegalStateException("Result-type 'formula' cannot occur"); } } + +

@@ -163,36 +168,36 @@ if (cell!=null) { FileInputStream fis = new FileInputStream("/somepath/test.xls"); HSSFWorkbook wb = new HSSFWorkbook(fis); HSSFSheet sheet = wb.getSheetAt(0); -HSSFFormulaEvaluator evaluator = new HSSFFormulaEvaluator(sheet, wb); +HSSFFormulaEvaluator evaluator = new HSSFFormulaEvaluator(wb); // suppose your formula is in B3 -CellReference cellReference = new CellReference("B3"); +CellReference cellReference = new CellReference("B3"); HSSFRow row = sheet.getRow(cellReference.getRow()); -HSSFCell cell = row.getCell(cellReference.getCol()); -evaluator.setCurrentRow(row); +HSSFCell cell = row.getCell((int)cellReference.getCol()); if (cell!=null) { - switch (evaluator.evaluateInCell(cell).getCellType()) { + switch (evaluator.evaluateInCell(cell).getCellType()) { case HSSFCell.CELL_TYPE_BOOLEAN: - System.out.println(cell.getBooleanCellValue()); - break; + System.out.println(cell.getBooleanCellValue()); + break; case HSSFCell.CELL_TYPE_NUMERIC: - System.out.println(cell.getNumberCellValue()); - break; + System.out.println(cell.getNumericCellValue()); + break; case HSSFCell.CELL_TYPE_STRING: - System.out.println(cell.getStringCellValue()); - break; + System.out.println(cell.getRichStringCellValue().getString()); + break; case HSSFCell.CELL_TYPE_BLANK: - break; + break; case HSSFCell.CELL_TYPE_ERROR: - System.out.println(cell.getErrorCellValue()); - break; - + System.out.println(cell.getErrorCellValue()); + break; + // CELL_TYPE_FORMULA will never occur - case HSSFCell.CELL_TYPE_FORMULA: - break; + case HSSFCell.CELL_TYPE_FORMULA: + break; } } +
@@ -201,13 +206,12 @@ if (cell!=null) { FileInputStream fis = new FileInputStream("/somepath/test.xls"); HSSFWorkbook wb = new HSSFWorkbook(fis); -for(int sheetNum = 0; sheetNum < wb.getNumberOfSheets(); sheetNum++) { +HSSFFormulaEvaluator evaluator = new HSSFFormulaEvaluator(wb); +for(int sheetNum = 0; sheetNum < wb.getNumberOfSheets(); sheetNum++) { HSSFSheet sheet = wb.getSheetAt(sheetNum); - 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(); @@ -218,6 +222,7 @@ for(int sheetNum = 0; sheetNum < wb.getNumberOfSheets(); sheetNum++) { } } wb.write(new FileOutputStream("/somepath/changed.xls")); + @@ -226,12 +231,12 @@ wb.write(new FileOutputStream("/somepath/changed.xls"));
Performance Notes