]> source.dussan.org Git - poi.git/commitdiff
Fix for 43354 - made the formula evaluator capable of handling missing function arguments
authorJosh Micich <josh@apache.org>
Mon, 6 Oct 2008 19:13:41 +0000 (19:13 +0000)
committerJosh Micich <josh@apache.org>
Mon, 6 Oct 2008 19:13:41 +0000 (19:13 +0000)
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@702231 13f79535-47bb-0310-9956-ffa450edef68

src/documentation/content/xdocs/changes.xml
src/documentation/content/xdocs/status.xml
src/java/org/apache/poi/hssf/record/formula/eval/MissingArgEval.java [new file with mode: 0644]
src/java/org/apache/poi/hssf/record/formula/functions/Count.java
src/java/org/apache/poi/ss/formula/WorkbookEvaluator.java
src/testcases/org/apache/poi/hssf/record/formula/eval/AllFormulaEvalTests.java
src/testcases/org/apache/poi/hssf/record/formula/eval/TestMissingArgEval.java [new file with mode: 0644]

index a30727d236c31ef7bce6fcd54df23933fdaedc7c..19943dc4aff224a7e464bbf96cc8613c636dc466 100644 (file)
@@ -37,6 +37,7 @@
 
                <!-- Don't forget to update status.xml too! -->
         <release version="3.2-alpha1" date="2008-??-??">
+           <action dev="POI-DEVELOPERS" type="fix">43354 - support for evalating formulas with missing args</action>
            <action dev="POI-DEVELOPERS" type="fix">45912 - fixed ArrayIndexOutOfBoundsException in EmbeddedObjectRefSubRecord</action>
            <action dev="POI-DEVELOPERS" type="fix">45889 - fixed ArrayIndexOutOfBoundsException when constructing HSLF Table with a single row </action>
            <action dev="POI-DEVELOPERS" type="add">Initial support for creating hyperlinks in HSLF</action>
index 5083faa385ccf7dce90cb4663864fd41a18a048c..94d1e2caee6a21152e67ca08f03d4d13f64bf5aa 100644 (file)
@@ -34,6 +34,7 @@
        <!-- Don't forget to update changes.xml too! -->
     <changes>
         <release version="3.2-alpha1" date="2008-??-??">
+           <action dev="POI-DEVELOPERS" type="fix">43354 - support for evalating formulas with missing args</action>
            <action dev="POI-DEVELOPERS" type="fix">45912 - fixed ArrayIndexOutOfBoundsException in EmbeddedObjectRefSubRecord</action>
            <action dev="POI-DEVELOPERS" type="fix">45889 - fixed ArrayIndexOutOfBoundsException when constructing HSLF Table with a single row </action>
            <action dev="POI-DEVELOPERS" type="add">Initial support for creating hyperlinks in HSLF</action>
diff --git a/src/java/org/apache/poi/hssf/record/formula/eval/MissingArgEval.java b/src/java/org/apache/poi/hssf/record/formula/eval/MissingArgEval.java
new file mode 100644 (file)
index 0000000..708329b
--- /dev/null
@@ -0,0 +1,35 @@
+/* ====================================================================
+   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.hssf.record.formula.eval;
+
+/**
+ * Represents the (intermediate) evaluated result of a missing function argument.  In most cases
+ * this can be translated into {@link BlankEval} but there are some notable exceptions.  Functions
+ * COUNT and COUNTA <em>do</em> count their missing args.  Note - the differences between 
+ * {@link MissingArgEval} and {@link BlankEval} have not been investigated fully, so the POI
+ * evaluator may need to be updated to account for these as they are found.
+ *
+ * @author Josh Micich
+ */
+public final class MissingArgEval implements ValueEval {
+
+    public static MissingArgEval instance = new MissingArgEval();
+
+    private MissingArgEval() {
+    }
+}
index fd5944e8584ad81e7abdc804427ec03b9b4b1582..62f4b6137af05b5cf96a8d2dcb00b609b0484357 100644 (file)
@@ -19,6 +19,7 @@ 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.Eval;
+import org.apache.poi.hssf.record.formula.eval.MissingArgEval;
 import org.apache.poi.hssf.record.formula.eval.NumberEval;
 import org.apache.poi.hssf.record.formula.functions.CountUtils.I_MatchPredicate;
 
@@ -64,6 +65,10 @@ public final class Count implements Function {
                                // only numbers are counted
                                return true;
                        }
+                       if(valueEval == MissingArgEval.instance) {
+                               // oh yeah, and missing arguments
+                               return true;
+                       }
 
                        // error values and string values not counted
                        return false;
index cf334454bdd15415aacea20f4d82993db5e6bc7f..e80de22fc03f63df24774723b4e61a7630623dd5 100644 (file)
@@ -52,6 +52,7 @@ 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.MissingArgEval;
 import org.apache.poi.hssf.record.formula.eval.NameEval;
 import org.apache.poi.hssf.record.formula.eval.NameXEval;
 import org.apache.poi.hssf.record.formula.eval.NumberEval;
@@ -284,10 +285,7 @@ public final class WorkbookEvaluator {
                                continue;
                        }
                        if (ptg instanceof MemErrPtg) { continue; }
-                       if (ptg instanceof MissingArgPtg) {
-                               // TODO - might need to push BlankEval or MissingArgEval
-                               continue;
-                       }
+
                        Eval opResult;
                        if (ptg instanceof OperationPtg) {
                                OperationPtg optg = (OperationPtg) ptg;
@@ -306,6 +304,9 @@ public final class WorkbookEvaluator {
                                }
 //                             logDebug("invoke " + operation + " (nAgs=" + numops + ")");
                                opResult = invokeOperation(operation, ops, _workbook, sheetIndex, srcRowNum, srcColNum);
+                               if (opResult == MissingArgEval.instance) {
+                                       opResult = BlankEval.INSTANCE;
+                               }
                        } else {
                                opResult = getEvalForPtg(ptg, sheetIndex, tracker);
                        }
@@ -424,6 +425,9 @@ public final class WorkbookEvaluator {
                if (ptg instanceof ErrPtg) {
                        return ErrorEval.valueOf(((ErrPtg) ptg).getErrorCode());
                }
+               if (ptg instanceof MissingArgPtg) {
+                       return MissingArgEval.instance;
+               }
                if (ptg instanceof AreaErrPtg ||ptg instanceof RefErrorPtg 
                                || ptg instanceof DeletedArea3DPtg || ptg instanceof DeletedRef3DPtg) {
                                return ErrorEval.REF_INVALID;
index fe37a3c84b139f86b9f9c659d003882ae045769e..a26539cdd2b393558428112556816372a3ab8952 100755 (executable)
@@ -36,6 +36,7 @@ public class AllFormulaEvalTests {
                result.addTestSuite(TestExternalFunction.class);
                result.addTestSuite(TestFormulaBugs.class);
                result.addTestSuite(TestFormulasFromSpreadsheet.class);
+               result.addTestSuite(TestMissingArgEval.class);
                result.addTestSuite(TestPercentEval.class);
                result.addTestSuite(TestRangeEval.class);
                result.addTestSuite(TestUnaryPlusEval.class);
diff --git a/src/testcases/org/apache/poi/hssf/record/formula/eval/TestMissingArgEval.java b/src/testcases/org/apache/poi/hssf/record/formula/eval/TestMissingArgEval.java
new file mode 100644 (file)
index 0000000..1605b12
--- /dev/null
@@ -0,0 +1,74 @@
+/* ====================================================================
+   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.hssf.record.formula.eval;
+
+import java.util.EmptyStackException;
+
+import junit.framework.AssertionFailedError;
+import junit.framework.TestCase;
+
+import org.apache.poi.hssf.usermodel.HSSFCell;
+import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator;
+import org.apache.poi.hssf.usermodel.HSSFSheet;
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator.CellValue;
+
+/**
+ * Tests for {@link MissingArgEval}
+ *
+ * @author Josh Micich
+ */
+public final class TestMissingArgEval extends TestCase {
+       
+       public void testEvaluateMissingArgs() {
+               HSSFWorkbook wb = new HSSFWorkbook();
+               HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(wb);
+               HSSFSheet sheet = wb.createSheet("Sheet1");
+               HSSFCell cell = sheet.createRow(0).createCell(0);
+               
+               cell.setCellFormula("if(true,)"); 
+               fe.clearAllCachedResultValues();
+               CellValue cv;
+               try {
+                       cv = fe.evaluate(cell);
+               } catch (EmptyStackException e) {
+                       throw new AssertionFailedError("Missing args evaluation not implemented (bug 43354");
+               }
+               // MissingArg -> BlankEval -> zero (as formula result)
+               assertEquals(0.0, cv.getNumberValue(), 0.0);
+               
+               // MissingArg -> BlankEval -> empty string (in concatenation)
+               cell.setCellFormula("\"abc\"&if(true,)"); 
+               fe.clearAllCachedResultValues();
+               assertEquals("abc", fe.evaluate(cell).getStringValue());
+       }
+       
+       public void testCountFuncs() {
+               HSSFWorkbook wb = new HSSFWorkbook();
+               HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(wb);
+               HSSFSheet sheet = wb.createSheet("Sheet1");
+               HSSFCell cell = sheet.createRow(0).createCell(0);
+               
+               cell.setCellFormula("COUNT(C5,,,,)"); // 4 missing args, C5 is blank 
+               assertEquals(4.0, fe.evaluate(cell).getNumberValue(), 0.0);
+
+               cell.setCellFormula("COUNTA(C5,,)"); // 2 missing args, C5 is blank 
+               fe.clearAllCachedResultValues();
+               assertEquals(2.0, fe.evaluate(cell).getNumberValue(), 0.0);
+       }
+}