]> source.dussan.org Git - poi.git/commitdiff
[bug-65467] support IFNA() function. Thanks to Ross Patterson
authorPJ Fanning <fanningpj@apache.org>
Thu, 29 Jul 2021 16:57:03 +0000 (16:57 +0000)
committerPJ Fanning <fanningpj@apache.org>
Thu, 29 Jul 2021 16:57:03 +0000 (16:57 +0000)
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1891876 13f79535-47bb-0310-9956-ffa450edef68

poi/src/main/java/org/apache/poi/ss/formula/atp/AnalysisToolPak.java
poi/src/main/java/org/apache/poi/ss/formula/atp/IfNa.java [new file with mode: 0644]
poi/src/test/java/org/apache/poi/ss/formula/TestFunctionRegistry.java
poi/src/test/java/org/apache/poi/ss/formula/atp/TestIfna.java [new file with mode: 0644]
poi/src/test/java/org/apache/poi/ss/formula/functions/TestIfnaFromSpreadsheet.java [new file with mode: 0644]
test-data/spreadsheet/IfNaTestCaseData.xls [new file with mode: 0644]

index 51667cbd9e8b5597834311a8e7c075194d44761e..f37ac9ecfa13a1417795a30e4e28a72e69e14b11 100644 (file)
@@ -80,7 +80,7 @@ public final class AnalysisToolPak implements UDFFinder {
     }
 
     private Map<String, FreeRefFunction> createFunctionsMap() {
-        Map<String, FreeRefFunction> m = new HashMap<>(108);
+        Map<String, FreeRefFunction> 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 (file)
index 0000000..a388560
--- /dev/null
@@ -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()
+ *
+ * <b>Syntax</b>:<br>
+ * <b>IFNA</b>(<b>test_value</b>,<b>default_value</b>)<p>
+ *
+ * <b>test_value</b>  The value to be tested<br>
+ * <b>default_value</b>  The value to be tested<br>
+ * <br>
+ * 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();
+        }
+    }
+}
index 1889f8b82dd2803879f61c8345d299b5c8b29d8f..1316215623e8604e03d380092ba7a5352cfb9c3b 100644 (file)
@@ -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 (file)
index 0000000..a57f197
--- /dev/null
@@ -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 (file)
index 0000000..d17f9dc
--- /dev/null
@@ -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.<p>
+ */
+class TestIfnaFromSpreadsheet extends BaseTestFunctionsFromSpreadsheet {
+
+    public static Stream<Arguments> 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 (file)
index 0000000..03b6d09
Binary files /dev/null and b/test-data/spreadsheet/IfNaTestCaseData.xls differ