From d683ddaa806ff78ccde77be1236c762b44172fad Mon Sep 17 00:00:00 2001
From: Josh Micich
Date: Mon, 29 Sep 2008 23:12:53 +0000
Subject: [PATCH] Updated formula evaluator documentation due to bugzilla 45768
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@700304 13f79535-47bb-0310-9956-ffa450edef68
---
.../content/xdocs/hssf/eval-devguide.xml | 109 +++++----------
src/documentation/content/xdocs/hssf/eval.xml | 129 +++++++++---------
.../hssf/usermodel/HSSFFormulaEvaluator.java | 21 ++-
3 files changed, 110 insertions(+), 149 deletions(-)
diff --git a/src/documentation/content/xdocs/hssf/eval-devguide.xml b/src/documentation/content/xdocs/hssf/eval-devguide.xml
index 29c33b8a2..558620bfa 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 CodeImplementation Details
The first thing to realise is that classes already exist, even for functions that are not yet implemented.
- Just that they extend from DefaultFunctionImpl whose behaviour is to return an ErrorEval.FUNCTION_NOT_IMPLEMENTED value.
-
In order to implement SQRT(..), we need to: a. Extend from the correct Abstract super class; b. implement the evaluate(..) method
-
Hence we extend SQRT(..) from the predefined class NumericFunction
-
Since SQRT(..) takes a single argument, we verify the length of the operands array else set the return value to ErrorEval.VALUE_INVALID
+ Just that they extend from NotImplementedFunction whose behaviour is to return an ErrorEval.FUNCTION_NOT_IMPLEMENTED value.
+
In order to implement NOT(..), we need to implement the interface 'Function' which has a method 'evaluate(..)'
+
Since NOT(..) takes a single argument, we verify the length of the operands array else set the return value to ErrorEval.VALUE_INVALID
Next we normalize each operand to a limited set of ValueEval subtypes, specifically, we call the function
- singleOperandEvaluate(..) to do conversions of different value eval types to one of: NumericValueEval,
- BlankEval and ErrorEval. The conversion logic is configured by a ValueEvalToNumericXlator instance which
- is returned by the Factory method: getXlator(..) The flags used to create the ValueEvalToNumericXlator
- instance are briefly explained as follows:
- BOOL_IS_PARSED means whether this function treats Boolean values as 1,
- REF_BOOL_IS_PARSED means whether Boolean values in cell references are parsed or not.
- So also, EVALUATED_REF_BOOL_IS_PARSED means if the operand was a RefEval that was assigned a
- Boolean value as a result of evaluation of the formula that it contained.
- eg. SQRT(TRUE) returns 1: This means BOOL_IS_PARSED should be set.
- SQRT(A1) returns 1 when A1 has TRUE: This means REF_BOOL_IS_PARSED should be set.
- SQRT(A1) returns 1 when A1 has a formula that evaluates to TRUE: This means EVALUATED_REF_BOOL_IS_PARSED should be set.
- If the flag is not set for a particular case, that case is ignored (treated as if the cell is blank) _unless_
- there is a flag like: STRING_IS_INVALID_VALUE (which means that Strings should be treated as resulting in VALUE_INVALID ErrorEval)
+ OperandResolver.getSingleValue to do conversions of different value eval types to one of: NumericValueEval,
+ BlankEval and ErrorEval. The conversion logic is performed by OperandResolver.coerceValueToBoolean()
-
Next perform the appropriate Math function on the double value (if an error didnt occur already).
-
Finally before returning the NumberEval wrapping the double value that
- you computed, do one final check to see if the double is a NaN, (or if it is "Infinite")
- If it is return the appropriate ErrorEval instance. Note: The OpenOffice.org error codes
- should NOT be preferred. Instead use the excel specific error codes like VALUE_INVALID, NUM_ERROR, DIV_ZERO etc.
- (Thanks to Avik for bringing this issue up early!) The Oo.o ErrorCodes will be removed (if they havent already been :)
+
Next perform the appropriate java operation (if an error didnt occur already).
+
Finally return the BoolEval wrapping primitive boolean result. Note - in the case of numeric results
+ you should check for NaN and Infinity, because exel likes these translated into specific error codes like
+ VALUE_INVALID, NUM_ERROR, DIV_ZERO etc.
+ (Thanks to Avik for bringing this issue up early!)
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").