From cd7b6b71972d00613da4a7f2672916c1df70f531 Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Thu, 29 Jul 2021 16:57:03 +0000 Subject: [PATCH] [bug-65467] support IFNA() function. Thanks to Ross Patterson git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1891876 13f79535-47bb-0310-9956-ffa450edef68 --- .../poi/ss/formula/atp/AnalysisToolPak.java | 5 +- .../org/apache/poi/ss/formula/atp/IfNa.java | 65 ++++++++++++ .../poi/ss/formula/TestFunctionRegistry.java | 2 +- .../apache/poi/ss/formula/atp/TestIfna.java | 100 ++++++++++++++++++ .../functions/TestIfnaFromSpreadsheet.java | 32 ++++++ test-data/spreadsheet/IfNaTestCaseData.xls | Bin 0 -> 28160 bytes 6 files changed, 201 insertions(+), 3 deletions(-) create mode 100644 poi/src/main/java/org/apache/poi/ss/formula/atp/IfNa.java create mode 100644 poi/src/test/java/org/apache/poi/ss/formula/atp/TestIfna.java create mode 100644 poi/src/test/java/org/apache/poi/ss/formula/functions/TestIfnaFromSpreadsheet.java create mode 100644 test-data/spreadsheet/IfNaTestCaseData.xls diff --git a/poi/src/main/java/org/apache/poi/ss/formula/atp/AnalysisToolPak.java b/poi/src/main/java/org/apache/poi/ss/formula/atp/AnalysisToolPak.java index 51667cbd9e..f37ac9ecfa 100644 --- a/poi/src/main/java/org/apache/poi/ss/formula/atp/AnalysisToolPak.java +++ b/poi/src/main/java/org/apache/poi/ss/formula/atp/AnalysisToolPak.java @@ -80,7 +80,7 @@ public final class AnalysisToolPak implements UDFFinder { } private Map createFunctionsMap() { - Map m = new HashMap<>(108); + Map m = new HashMap<>(127); r(m, "ACCRINT", null); r(m, "ACCRINTM", null); @@ -136,6 +136,7 @@ public final class AnalysisToolPak implements UDFFinder { r(m, "HEX2DEC", Hex2Dec.instance); r(m, "HEX2OCT", null); r(m, "IFERROR", IfError.instance); + r(m, "IFNA", IfNa.instance); r(m, "IFS", Ifs.instance); r(m, "IMABS", null); r(m, "IMAGINARY", Imaginary.instance); @@ -260,7 +261,7 @@ public final class AnalysisToolPak implements UDFFinder { FunctionMetadata metaData = FunctionMetadataRegistry.getFunctionByName(name); if(metaData != null) { throw new IllegalArgumentException(name + " is a built-in Excel function. " + - "Use FunctoinEval.registerFunction(String name, Function func) instead."); + "Use FunctionEval.registerFunction(String name, Function func) instead."); } throw new IllegalArgumentException(name + " is not a function from the Excel Analysis Toolpack."); diff --git a/poi/src/main/java/org/apache/poi/ss/formula/atp/IfNa.java b/poi/src/main/java/org/apache/poi/ss/formula/atp/IfNa.java new file mode 100644 index 0000000000..a3885601f4 --- /dev/null +++ b/poi/src/main/java/org/apache/poi/ss/formula/atp/IfNa.java @@ -0,0 +1,65 @@ +/* ==================================================================== + 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.formula.atp; + +import org.apache.poi.ss.formula.OperationEvaluationContext; +import org.apache.poi.ss.formula.eval.ErrorEval; +import org.apache.poi.ss.formula.eval.EvaluationException; +import org.apache.poi.ss.formula.eval.OperandResolver; +import org.apache.poi.ss.formula.eval.ValueEval; +import org.apache.poi.ss.formula.functions.FreeRefFunction; + +/** + * Implementation of 'Analysis Toolpak' the Excel function IFNA() + * + * Syntax:
+ * IFNA(test_value,default_value)

+ * + * test_value The value to be tested
+ * default_value The value to be tested
+ *
+ * Returns {@code default_value} if {@code test_value} is '#N/A', {@code test_value} otherwise. + */ +public final class IfNa implements FreeRefFunction { + + public static final FreeRefFunction instance = new IfNa(); + + private IfNa() { + // Enforce singleton + } + + public ValueEval evaluate(ValueEval[] args, OperationEvaluationContext ec) { + if (args.length != 2) { + return ErrorEval.VALUE_INVALID; + } + + try { + return OperandResolver.getSingleValue(args[0], ec.getRowIndex(), ec.getColumnIndex()); + } catch (EvaluationException e) { + ValueEval error = e.getErrorEval(); + if (error != ErrorEval.NA) { + return error; + } + } + try { + return OperandResolver.getSingleValue(args[1], ec.getRowIndex(), ec.getColumnIndex()); + } catch (EvaluationException e) { + return e.getErrorEval(); + } + } +} diff --git a/poi/src/test/java/org/apache/poi/ss/formula/TestFunctionRegistry.java b/poi/src/test/java/org/apache/poi/ss/formula/TestFunctionRegistry.java index 1889f8b82d..1316215623 100644 --- a/poi/src/test/java/org/apache/poi/ss/formula/TestFunctionRegistry.java +++ b/poi/src/test/java/org/apache/poi/ss/formula/TestFunctionRegistry.java @@ -163,7 +163,7 @@ class TestFunctionRegistry { () -> AnalysisToolPak.registerFunction("SUM", TestFunctionRegistry::atpFunc) ); assertEquals("SUM is a built-in Excel function. " + - "Use FunctoinEval.registerFunction(String name, Function func) instead.", + "Use FunctionEval.registerFunction(String name, Function func) instead.", ex.getMessage()); } } diff --git a/poi/src/test/java/org/apache/poi/ss/formula/atp/TestIfna.java b/poi/src/test/java/org/apache/poi/ss/formula/atp/TestIfna.java new file mode 100644 index 0000000000..a57f197b37 --- /dev/null +++ b/poi/src/test/java/org/apache/poi/ss/formula/atp/TestIfna.java @@ -0,0 +1,100 @@ +/* ==================================================================== + 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.formula.atp; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.apache.poi.hssf.usermodel.HSSFCell; +import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator; +import org.apache.poi.hssf.usermodel.HSSFWorkbook; +import org.apache.poi.ss.formula.eval.ErrorEval; +import org.apache.poi.ss.usermodel.CellType; +import org.apache.poi.ss.usermodel.CellValue; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.BeforeEach; + + +/** + * IfNa unit tests. + */ +class TestIfna { + + HSSFWorkbook wb; + HSSFCell cell; + HSSFFormulaEvaluator fe; + + @BeforeEach + void setup() { + wb = new HSSFWorkbook(); + cell = wb.createSheet().createRow(0).createCell(0); + fe = new HSSFFormulaEvaluator(wb); + } + + @Test + void testNumbericArgsWorkCorrectly() { + confirmResult(fe, cell, "IFNA(-1,42)", new CellValue(-1.0)); + confirmResult(fe, cell, "IFNA(NA(),42)", new CellValue(42.0)); + } + + @Test + void testStringArgsWorkCorrectly() { + confirmResult(fe, cell, "IFNA(\"a1\",\"a2\")", new CellValue("a1")); + confirmResult(fe, cell, "IFNA(NA(),\"a2\")", new CellValue("a2")); + } + + @Test + void testUsageErrorsThrowErrors() { + confirmError(fe, cell, "IFNA(1)", ErrorEval.VALUE_INVALID); + confirmError(fe, cell, "IFNA(1,2,3)", ErrorEval.VALUE_INVALID); + } + + @Test + void testErrorInArgSelectsNAResult() { + confirmError(fe, cell, "IFNA(1/0,42)", ErrorEval.DIV_ZERO); + } + + @Test + void testErrorFromNAArgPassesThrough() { + confirmError(fe, cell, "IFNA(NA(),1/0)", ErrorEval.DIV_ZERO); + } + + @Test + void testNaArgNotEvaledIfUnneeded() { + confirmResult(fe, cell, "IFNA(42,1/0)", new CellValue(42.0)); + } + + private static void confirmResult(HSSFFormulaEvaluator fe, HSSFCell cell, String formulaText, + CellValue expectedResult) { + fe.setDebugEvaluationOutputForNextEval(true); + cell.setCellFormula(formulaText); + fe.notifyUpdateCell(cell); + CellValue result = fe.evaluate(cell); + assertEquals(expectedResult.getCellType(), result.getCellType(), "Testing result type for: " + formulaText); + assertEquals(expectedResult.formatAsString(), result.formatAsString(), "Testing result for: " + formulaText); + } + + private static void confirmError(HSSFFormulaEvaluator fe, HSSFCell cell, String formulaText, + ErrorEval expectedError) { + fe.setDebugEvaluationOutputForNextEval(true); + cell.setCellFormula(formulaText); + fe.notifyUpdateCell(cell); + CellValue result = fe.evaluate(cell); + assertEquals(CellType.ERROR, result.getCellType(), "Testing result type for: " + formulaText); + assertEquals(expectedError.getErrorString(), result.formatAsString(), "Testing error type for: " + formulaText); + } +} diff --git a/poi/src/test/java/org/apache/poi/ss/formula/functions/TestIfnaFromSpreadsheet.java b/poi/src/test/java/org/apache/poi/ss/formula/functions/TestIfnaFromSpreadsheet.java new file mode 100644 index 0000000000..d17f9dc8cb --- /dev/null +++ b/poi/src/test/java/org/apache/poi/ss/formula/functions/TestIfnaFromSpreadsheet.java @@ -0,0 +1,32 @@ +/* ==================================================================== + 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.formula.functions; + +import java.util.stream.Stream; + +import org.junit.jupiter.params.provider.Arguments; + +/** + * Tests for IFNA function as loaded from a test data spreadsheet.

+ */ +class TestIfnaFromSpreadsheet extends BaseTestFunctionsFromSpreadsheet { + + public static Stream data() throws Exception { + return data(TestIfnaFromSpreadsheet.class, "IfNaTestCaseData.xls"); + } +} diff --git a/test-data/spreadsheet/IfNaTestCaseData.xls b/test-data/spreadsheet/IfNaTestCaseData.xls new file mode 100644 index 0000000000000000000000000000000000000000..03b6d09da58f3a52e272358a7b67ef98b8c78add GIT binary patch literal 28160 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 literal 0 HcmV?d00001 -- 2.39.5