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 0000000000..03b6d09da5 Binary files /dev/null and b/test-data/spreadsheet/IfNaTestCaseData.xls differ