Fix for 43354 - made the formula evaluator capable of handling missing function arguments

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@702231 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Josh Micich 2008-10-06 19:13:41 +00:00
parent f9adf67cff
commit 3a2feafaa6
7 changed files with 125 additions and 4 deletions

View File

@ -37,6 +37,7 @@
<!-- Don't forget to update status.xml too! --> <!-- Don't forget to update status.xml too! -->
<release version="3.2-alpha1" date="2008-??-??"> <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">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="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> <action dev="POI-DEVELOPERS" type="add">Initial support for creating hyperlinks in HSLF</action>

View File

@ -34,6 +34,7 @@
<!-- Don't forget to update changes.xml too! --> <!-- Don't forget to update changes.xml too! -->
<changes> <changes>
<release version="3.2-alpha1" date="2008-??-??"> <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">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="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> <action dev="POI-DEVELOPERS" type="add">Initial support for creating hyperlinks in HSLF</action>

View File

@ -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() {
}
}

View 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.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.MissingArgEval;
import org.apache.poi.hssf.record.formula.eval.NumberEval; import org.apache.poi.hssf.record.formula.eval.NumberEval;
import org.apache.poi.hssf.record.formula.functions.CountUtils.I_MatchPredicate; 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 // only numbers are counted
return true; return true;
} }
if(valueEval == MissingArgEval.instance) {
// oh yeah, and missing arguments
return true;
}
// error values and string values not counted // error values and string values not counted
return false; return false;

View 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.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.FunctionEval; 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.NameEval;
import org.apache.poi.hssf.record.formula.eval.NameXEval; import org.apache.poi.hssf.record.formula.eval.NameXEval;
import org.apache.poi.hssf.record.formula.eval.NumberEval; import org.apache.poi.hssf.record.formula.eval.NumberEval;
@ -284,10 +285,7 @@ public final class WorkbookEvaluator {
continue; continue;
} }
if (ptg instanceof MemErrPtg) { continue; } if (ptg instanceof MemErrPtg) { continue; }
if (ptg instanceof MissingArgPtg) {
// TODO - might need to push BlankEval or MissingArgEval
continue;
}
Eval opResult; Eval opResult;
if (ptg instanceof OperationPtg) { if (ptg instanceof OperationPtg) {
OperationPtg optg = (OperationPtg) ptg; OperationPtg optg = (OperationPtg) ptg;
@ -306,6 +304,9 @@ public final class WorkbookEvaluator {
} }
// logDebug("invoke " + operation + " (nAgs=" + numops + ")"); // logDebug("invoke " + operation + " (nAgs=" + numops + ")");
opResult = invokeOperation(operation, ops, _workbook, sheetIndex, srcRowNum, srcColNum); opResult = invokeOperation(operation, ops, _workbook, sheetIndex, srcRowNum, srcColNum);
if (opResult == MissingArgEval.instance) {
opResult = BlankEval.INSTANCE;
}
} else { } else {
opResult = getEvalForPtg(ptg, sheetIndex, tracker); opResult = getEvalForPtg(ptg, sheetIndex, tracker);
} }
@ -424,6 +425,9 @@ public final class WorkbookEvaluator {
if (ptg instanceof ErrPtg) { if (ptg instanceof ErrPtg) {
return ErrorEval.valueOf(((ErrPtg) ptg).getErrorCode()); return ErrorEval.valueOf(((ErrPtg) ptg).getErrorCode());
} }
if (ptg instanceof MissingArgPtg) {
return MissingArgEval.instance;
}
if (ptg instanceof AreaErrPtg ||ptg instanceof RefErrorPtg if (ptg instanceof AreaErrPtg ||ptg instanceof RefErrorPtg
|| ptg instanceof DeletedArea3DPtg || ptg instanceof DeletedRef3DPtg) { || ptg instanceof DeletedArea3DPtg || ptg instanceof DeletedRef3DPtg) {
return ErrorEval.REF_INVALID; return ErrorEval.REF_INVALID;

View File

@ -36,6 +36,7 @@ public class AllFormulaEvalTests {
result.addTestSuite(TestExternalFunction.class); result.addTestSuite(TestExternalFunction.class);
result.addTestSuite(TestFormulaBugs.class); result.addTestSuite(TestFormulaBugs.class);
result.addTestSuite(TestFormulasFromSpreadsheet.class); result.addTestSuite(TestFormulasFromSpreadsheet.class);
result.addTestSuite(TestMissingArgEval.class);
result.addTestSuite(TestPercentEval.class); result.addTestSuite(TestPercentEval.class);
result.addTestSuite(TestRangeEval.class); result.addTestSuite(TestRangeEval.class);
result.addTestSuite(TestUnaryPlusEval.class); result.addTestSuite(TestUnaryPlusEval.class);

View File

@ -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);
}
}