From 81033fbad0765ca81e47ac88e055bf5bd622fe72 Mon Sep 17 00:00:00 2001 From: Yegor Kozlov Date: Mon, 7 Jan 2019 14:34:19 +0000 Subject: [PATCH] Bug 62904: Support array arguments in IF and logical IS*** functions git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1850646 13f79535-47bb-0310-9956-ffa450edef68 --- .../ss/formula/OperationEvaluatorFactory.java | 13 +- .../poi/ss/formula/WorkbookEvaluator.java | 19 ++- .../formula/eval/RelationalOperationEval.java | 81 +-------- .../eval/TwoOperandNumericOperation.java | 15 +- .../poi/ss/formula/eval/UnaryMinusEval.java | 11 +- .../poi/ss/formula/eval/UnaryPlusEval.java | 11 +- .../ss/formula/functions/ArrayFunction.java | 158 +++++++++++++++++- .../poi/ss/formula/functions/IfFunc.java | 36 +++- .../ss/formula/functions/LogicalFunction.java | 10 +- .../TestIFFunctionFromSpreadsheet.java | 32 ++++ .../TestLogicalFunctionsFromSpreadsheet.java | 32 ++++ .../spreadsheet/IfFunctionTestCaseData.xls | Bin 0 -> 32768 bytes .../LogicalFunctionsTestCaseData.xls | Bin 0 -> 30720 bytes 13 files changed, 321 insertions(+), 97 deletions(-) create mode 100644 src/testcases/org/apache/poi/ss/formula/functions/TestIFFunctionFromSpreadsheet.java create mode 100644 src/testcases/org/apache/poi/ss/formula/functions/TestLogicalFunctionsFromSpreadsheet.java create mode 100644 test-data/spreadsheet/IfFunctionTestCaseData.xls create mode 100644 test-data/spreadsheet/LogicalFunctionsTestCaseData.xls diff --git a/src/java/org/apache/poi/ss/formula/OperationEvaluatorFactory.java b/src/java/org/apache/poi/ss/formula/OperationEvaluatorFactory.java index 60befcaab3..65676a61fd 100644 --- a/src/java/org/apache/poi/ss/formula/OperationEvaluatorFactory.java +++ b/src/java/org/apache/poi/ss/formula/OperationEvaluatorFactory.java @@ -56,6 +56,7 @@ import org.apache.poi.ss.formula.function.FunctionMetadataRegistry; import org.apache.poi.ss.formula.functions.ArrayFunction; import org.apache.poi.ss.formula.functions.Function; import org.apache.poi.ss.formula.functions.Indirect; +import org.apache.poi.ss.util.CellRangeAddress; /** * This class creates OperationEval instances to help evaluate OperationPtg @@ -138,8 +139,16 @@ final class OperationEvaluatorFactory { EvaluationSheet evalSheet = ec.getWorkbook().getSheet(ec.getSheetIndex()); EvaluationCell evalCell = evalSheet.getCell(ec.getRowIndex(), ec.getColumnIndex()); - if (evalCell != null && (evalCell.isPartOfArrayFormulaGroup() || ec.isArraymode()) && result instanceof ArrayFunction) - return ((ArrayFunction) result).evaluateArray(args, ec.getRowIndex(), ec.getColumnIndex()); + if (evalCell != null && result instanceof ArrayFunction) { + ArrayFunction func = (ArrayFunction) result; + if(evalCell.isPartOfArrayFormulaGroup()){ + // array arguments must be evaluated relative to the function defining range + CellRangeAddress ca = evalCell.getArrayFormulaRange(); + return func.evaluateArray(args, ca.getFirstRow(), ca.getFirstColumn()); + } else if (ec.isArraymode()){ + return func.evaluateArray(args, ec.getRowIndex(), ec.getColumnIndex()); + } + } return result.evaluate(args, ec.getRowIndex(), ec.getColumnIndex()); } else if (udfFunc != null){ diff --git a/src/java/org/apache/poi/ss/formula/WorkbookEvaluator.java b/src/java/org/apache/poi/ss/formula/WorkbookEvaluator.java index 594dfb4541..e91c0e1095 100644 --- a/src/java/org/apache/poi/ss/formula/WorkbookEvaluator.java +++ b/src/java/org/apache/poi/ss/formula/WorkbookEvaluator.java @@ -453,15 +453,24 @@ public final class WorkbookEvaluator { // nothing to skip - true param follows } else { int dist = attrPtg.getData(); + Ptg currPtg = ptgs[i+1]; i+= countTokensToBeSkipped(ptgs, i, dist); Ptg nextPtg = ptgs[i+1]; - if (ptgs[i] instanceof AttrPtg && nextPtg instanceof FuncVarPtg && - // in order to verify that there is no third param, we need to check + + if (ptgs[i] instanceof AttrPtg && nextPtg instanceof FuncVarPtg && + // in order to verify that there is no third param, we need to check // if we really have the IF next or some other FuncVarPtg as third param, e.g. ROW()/COLUMN()! ((FuncVarPtg)nextPtg).getFunctionIndex() == FunctionMetadataRegistry.FUNCTION_INDEX_IF) { // this is an if statement without a false param (as opposed to MissingArgPtg as the false param) - i++; - stack.push(BoolEval.FALSE); + //i++; + stack.push(arg0); + if(currPtg instanceof AreaPtg){ + // IF in array mode. See Bug 62904 + ValueEval currEval = getEvalForPtg(currPtg, ec); + stack.push(currEval); + } else { + stack.push(BoolEval.FALSE); + } } } continue; @@ -759,7 +768,7 @@ public final class WorkbookEvaluator { return evaluateNameFormula(nameRecord.getNameDefinition(), ec); } - throw new RuntimeException("Don't now how to evalate name '" + nameRecord.getNameText() + "'"); + throw new RuntimeException("Don't now how to evaluate name '" + nameRecord.getNameText() + "'"); } /** diff --git a/src/java/org/apache/poi/ss/formula/eval/RelationalOperationEval.java b/src/java/org/apache/poi/ss/formula/eval/RelationalOperationEval.java index 0b0cc87c8f..f83a54de47 100644 --- a/src/java/org/apache/poi/ss/formula/eval/RelationalOperationEval.java +++ b/src/java/org/apache/poi/ss/formula/eval/RelationalOperationEval.java @@ -17,7 +17,6 @@ package org.apache.poi.ss.formula.eval; -import org.apache.poi.ss.formula.CacheAreaEval; import org.apache.poi.ss.formula.functions.ArrayFunction; import org.apache.poi.ss.formula.functions.Fixed2ArgFunction; import org.apache.poi.ss.formula.functions.Function; @@ -74,84 +73,16 @@ public abstract class RelationalOperationEval extends Fixed2ArgFunction implemen return BoolEval.valueOf(result); } + @Override public ValueEval evaluateArray(ValueEval[] args, int srcRowIndex, int srcColumnIndex) { ValueEval arg0 = args[0]; ValueEval arg1 = args[1]; + return evaluateTwoArrayArgs(arg0, arg1, srcRowIndex, srcColumnIndex, (vA, vB) -> { + int cmpResult = doCompare(vA, vB); + boolean result = convertComparisonResult(cmpResult); + return BoolEval.valueOf(result); + }); - int w1, w2, h1, h2; - int a1FirstCol = 0, a1FirstRow = 0; - if (arg0 instanceof AreaEval) { - AreaEval ae = (AreaEval)arg0; - w1 = ae.getWidth(); - h1 = ae.getHeight(); - a1FirstCol = ae.getFirstColumn(); - a1FirstRow = ae.getFirstRow(); - } else if (arg0 instanceof RefEval){ - RefEval ref = (RefEval)arg0; - w1 = 1; - h1 = 1; - a1FirstCol = ref.getColumn(); - a1FirstRow = ref.getRow(); - } else { - w1 = 1; - h1 = 1; - } - int a2FirstCol = 0, a2FirstRow = 0; - if (arg1 instanceof AreaEval) { - AreaEval ae = (AreaEval)arg1; - w2 = ae.getWidth(); - h2 = ae.getHeight(); - a2FirstCol = ae.getFirstColumn(); - a2FirstRow = ae.getFirstRow(); - } else if (arg1 instanceof RefEval){ - RefEval ref = (RefEval)arg1; - w2 = 1; - h2 = 1; - a2FirstCol = ref.getColumn(); - a2FirstRow = ref.getRow(); - } else { - w2 = 1; - h2 = 1; - } - - int width = Math.max(w1, w2); - int height = Math.max(h1, h2); - - ValueEval[] vals = new ValueEval[height * width]; - - int idx = 0; - for(int i = 0; i < height; i++){ - for(int j = 0; j < width; j++){ - ValueEval vA; - try { - vA = OperandResolver.getSingleValue(arg0, a1FirstRow + i, a1FirstCol + j); - } catch (EvaluationException e) { - vA = e.getErrorEval(); - } - ValueEval vB; - try { - vB = OperandResolver.getSingleValue(arg1, a2FirstRow + i, a2FirstCol + j); - } catch (EvaluationException e) { - vB = e.getErrorEval(); - } - if(vA instanceof ErrorEval){ - vals[idx++] = vA; - } else if (vB instanceof ErrorEval) { - vals[idx++] = vB; - } else { - int cmpResult = doCompare(vA, vB); - boolean result = convertComparisonResult(cmpResult); - vals[idx++] = BoolEval.valueOf(result); - } - - } - } - - if (vals.length == 1) { - return vals[0]; - } - - return new CacheAreaEval(srcRowIndex, srcColumnIndex, srcRowIndex + height - 1, srcColumnIndex + width - 1, vals); } private static int doCompare(ValueEval va, ValueEval vb) { diff --git a/src/java/org/apache/poi/ss/formula/eval/TwoOperandNumericOperation.java b/src/java/org/apache/poi/ss/formula/eval/TwoOperandNumericOperation.java index 7bec3599b6..8d15ebea93 100644 --- a/src/java/org/apache/poi/ss/formula/eval/TwoOperandNumericOperation.java +++ b/src/java/org/apache/poi/ss/formula/eval/TwoOperandNumericOperation.java @@ -37,7 +37,20 @@ public abstract class TwoOperandNumericOperation extends Fixed2ArgFunction imple if (args.length != 2) { return ErrorEval.VALUE_INVALID; } - return new ArrayEval().evaluate(srcRowIndex, srcColumnIndex, args[0], args[1]); + //return new ArrayEval().evaluate(srcRowIndex, srcColumnIndex, args[0], args[1]); + + return evaluateTwoArrayArgs(args[0], args[1], srcRowIndex, srcColumnIndex, + (vA, vB) -> { + try { + double d0 = OperandResolver.coerceValueToDouble(vA); + double d1 = OperandResolver.coerceValueToDouble(vB); + double result = evaluate(d0, d1); + return new NumberEval(result); + } catch (EvaluationException e){ + return e.getErrorEval(); + } + }); + } public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1) { diff --git a/src/java/org/apache/poi/ss/formula/eval/UnaryMinusEval.java b/src/java/org/apache/poi/ss/formula/eval/UnaryMinusEval.java index 02d6d5e12c..ac1ee34118 100644 --- a/src/java/org/apache/poi/ss/formula/eval/UnaryMinusEval.java +++ b/src/java/org/apache/poi/ss/formula/eval/UnaryMinusEval.java @@ -17,13 +17,14 @@ package org.apache.poi.ss.formula.eval; +import org.apache.poi.ss.formula.functions.ArrayFunction; import org.apache.poi.ss.formula.functions.Fixed1ArgFunction; import org.apache.poi.ss.formula.functions.Function; /** * @author Amol S. Deshmukh < amolweb at ya hoo dot com > */ -public final class UnaryMinusEval extends Fixed1ArgFunction { +public final class UnaryMinusEval extends Fixed1ArgFunction implements ArrayFunction { public static final Function instance = new UnaryMinusEval(); @@ -44,4 +45,12 @@ public final class UnaryMinusEval extends Fixed1ArgFunction { } return new NumberEval(-d); } + + @Override + public ValueEval evaluateArray(ValueEval[] args, int srcRowIndex, int srcColumnIndex){ + return evaluateOneArrayArg(args, srcRowIndex, srcColumnIndex, (valA) -> + evaluate(srcRowIndex, srcColumnIndex, valA) + ); + } + } diff --git a/src/java/org/apache/poi/ss/formula/eval/UnaryPlusEval.java b/src/java/org/apache/poi/ss/formula/eval/UnaryPlusEval.java index 9b10f2b10c..d5cb586a85 100644 --- a/src/java/org/apache/poi/ss/formula/eval/UnaryPlusEval.java +++ b/src/java/org/apache/poi/ss/formula/eval/UnaryPlusEval.java @@ -17,6 +17,7 @@ package org.apache.poi.ss.formula.eval; +import org.apache.poi.ss.formula.functions.ArrayFunction; import org.apache.poi.ss.formula.functions.Fixed1ArgFunction; import org.apache.poi.ss.formula.functions.Function; @@ -24,7 +25,7 @@ import org.apache.poi.ss.formula.functions.Function; /** * @author Amol S. Deshmukh < amolweb at ya hoo dot com > */ -public final class UnaryPlusEval extends Fixed1ArgFunction { +public final class UnaryPlusEval extends Fixed1ArgFunction implements ArrayFunction { public static final Function instance = new UnaryPlusEval(); @@ -48,4 +49,12 @@ public final class UnaryPlusEval extends Fixed1ArgFunction { } return new NumberEval(+d); } + + @Override + public ValueEval evaluateArray(ValueEval[] args, int srcRowIndex, int srcColumnIndex){ + return evaluateOneArrayArg(args, srcRowIndex, srcColumnIndex, (valA) -> + evaluate(srcRowIndex, srcColumnIndex, valA) + ); + } + } diff --git a/src/java/org/apache/poi/ss/formula/functions/ArrayFunction.java b/src/java/org/apache/poi/ss/formula/functions/ArrayFunction.java index 3e864e5b9b..088d7b34fd 100644 --- a/src/java/org/apache/poi/ss/formula/functions/ArrayFunction.java +++ b/src/java/org/apache/poi/ss/formula/functions/ArrayFunction.java @@ -17,10 +17,11 @@ package org.apache.poi.ss.formula.functions; -import org.apache.poi.ss.formula.eval.BlankEval; -import org.apache.poi.ss.formula.eval.ErrorEval; -import org.apache.poi.ss.formula.eval.MissingArgEval; -import org.apache.poi.ss.formula.eval.ValueEval; +import org.apache.poi.ss.formula.CacheAreaEval; +import org.apache.poi.ss.formula.FormulaParseException; +import org.apache.poi.ss.formula.eval.*; + +import java.util.function.BiFunction; /** * @author Robert Hulbert @@ -41,4 +42,153 @@ public interface ArrayFunction { */ ValueEval evaluateArray(ValueEval[] args, int srcRowIndex, int srcColumnIndex); + + /** + * Evaluate an array function with two arguments. + * + * @param arg0 the first function argument. Empty values are represented with + * {@link BlankEval} or {@link MissingArgEval}, never null + * @param arg1 the first function argument. Empty values are represented with + * @link BlankEval} or {@link MissingArgEval}, never null + * + * @param srcRowIndex row index of the cell containing the formula under evaluation + * @param srcColumnIndex column index of the cell containing the formula under evaluation + * @return The evaluated result, possibly an {@link ErrorEval}, never null. + * Note - Excel uses the error code #NUM! instead of IEEE NaN, so when + * numeric functions evaluate to {@link Double#NaN} be sure to translate the result to {@link + * ErrorEval#NUM_ERROR}. + */ + default ValueEval evaluateTwoArrayArgs(ValueEval arg0, ValueEval arg1, int srcRowIndex, int srcColumnIndex, + BiFunction evalFunc) { + int w1, w2, h1, h2; + int a1FirstCol = 0, a1FirstRow = 0; + if (arg0 instanceof AreaEval) { + AreaEval ae = (AreaEval)arg0; + w1 = ae.getWidth(); + h1 = ae.getHeight(); + a1FirstCol = ae.getFirstColumn(); + a1FirstRow = ae.getFirstRow(); + } else if (arg0 instanceof RefEval){ + RefEval ref = (RefEval)arg0; + w1 = 1; + h1 = 1; + a1FirstCol = ref.getColumn(); + a1FirstRow = ref.getRow(); + } else { + w1 = 1; + h1 = 1; + } + int a2FirstCol = 0, a2FirstRow = 0; + if (arg1 instanceof AreaEval) { + AreaEval ae = (AreaEval)arg1; + w2 = ae.getWidth(); + h2 = ae.getHeight(); + a2FirstCol = ae.getFirstColumn(); + a2FirstRow = ae.getFirstRow(); + } else if (arg1 instanceof RefEval){ + RefEval ref = (RefEval)arg1; + w2 = 1; + h2 = 1; + a2FirstCol = ref.getColumn(); + a2FirstRow = ref.getRow(); + } else { + w2 = 1; + h2 = 1; + } + + int width = Math.max(w1, w2); + int height = Math.max(h1, h2); + + ValueEval[] vals = new ValueEval[height * width]; + + int idx = 0; + for(int i = 0; i < height; i++){ + for(int j = 0; j < width; j++){ + ValueEval vA; + try { + vA = OperandResolver.getSingleValue(arg0, a1FirstRow + i, a1FirstCol + j); + } catch (FormulaParseException e) { + vA = ErrorEval.NAME_INVALID; + } catch (EvaluationException e) { + vA = e.getErrorEval(); + } + ValueEval vB; + try { + vB = OperandResolver.getSingleValue(arg1, a2FirstRow + i, a2FirstCol + j); + } catch (FormulaParseException e) { + vB = ErrorEval.NAME_INVALID; + } catch (EvaluationException e) { + vB = e.getErrorEval(); + } + if(vA instanceof ErrorEval){ + vals[idx++] = vA; + } else if (vB instanceof ErrorEval) { + vals[idx++] = vB; + } else { + vals[idx++] = evalFunc.apply(vA, vB); + } + + } + } + + if (vals.length == 1) { + return vals[0]; + } + + return new CacheAreaEval(srcRowIndex, srcColumnIndex, srcRowIndex + height - 1, srcColumnIndex + width - 1, vals); + } + + default ValueEval evaluateOneArrayArg(ValueEval[] args, int srcRowIndex, int srcColumnIndex, + java.util.function.Function evalFunc){ + ValueEval arg0 = args[0]; + + int w1, w2, h1, h2; + int a1FirstCol = 0, a1FirstRow = 0; + if (arg0 instanceof AreaEval) { + AreaEval ae = (AreaEval)arg0; + w1 = ae.getWidth(); + h1 = ae.getHeight(); + a1FirstCol = ae.getFirstColumn(); + a1FirstRow = ae.getFirstRow(); + } else if (arg0 instanceof RefEval){ + RefEval ref = (RefEval)arg0; + w1 = 1; + h1 = 1; + a1FirstCol = ref.getColumn(); + a1FirstRow = ref.getRow(); + } else { + w1 = 1; + h1 = 1; + } + w2 = 1; + h2 = 1; + + int width = Math.max(w1, w2); + int height = Math.max(h1, h2); + + ValueEval[] vals = new ValueEval[height * width]; + + int idx = 0; + for(int i = 0; i < height; i++){ + for(int j = 0; j < width; j++){ + ValueEval vA; + try { + vA = OperandResolver.getSingleValue(arg0, a1FirstRow + i, a1FirstCol + j); + } catch (FormulaParseException e) { + vA = ErrorEval.NAME_INVALID; + } catch (EvaluationException e) { + vA = e.getErrorEval(); + } + vals[idx++] = evalFunc.apply(vA); + } + } + + if (vals.length == 1) { + return vals[0]; + } + + return new CacheAreaEval(srcRowIndex, srcColumnIndex, srcRowIndex + height - 1, srcColumnIndex + width - 1, vals); + + } + } diff --git a/src/java/org/apache/poi/ss/formula/functions/IfFunc.java b/src/java/org/apache/poi/ss/formula/functions/IfFunc.java index d410350b7c..effff27815 100644 --- a/src/java/org/apache/poi/ss/formula/functions/IfFunc.java +++ b/src/java/org/apache/poi/ss/formula/functions/IfFunc.java @@ -17,12 +17,7 @@ package org.apache.poi.ss.formula.functions; -import org.apache.poi.ss.formula.eval.BlankEval; -import org.apache.poi.ss.formula.eval.BoolEval; -import org.apache.poi.ss.formula.eval.EvaluationException; -import org.apache.poi.ss.formula.eval.MissingArgEval; -import org.apache.poi.ss.formula.eval.OperandResolver; -import org.apache.poi.ss.formula.eval.ValueEval; +import org.apache.poi.ss.formula.eval.*; import org.apache.poi.ss.formula.ptg.Ptg; import org.apache.poi.ss.formula.ptg.RefPtg; @@ -36,8 +31,9 @@ import org.apache.poi.ss.formula.ptg.RefPtg; * See bug numbers #55324 and #55747 for the full details on this. * TODO Fix this... */ -public final class IfFunc extends Var2or3ArgFunction { +public final class IfFunc extends Var2or3ArgFunction implements ArrayFunction { + @Override public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1) { boolean b; try { @@ -54,6 +50,7 @@ public final class IfFunc extends Var2or3ArgFunction { return BoolEval.FALSE; } + @Override public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1, ValueEval arg2) { boolean b; @@ -83,4 +80,29 @@ public final class IfFunc extends Var2or3ArgFunction { } return b.booleanValue(); } + + + @Override + public ValueEval evaluateArray(ValueEval[] args, int srcRowIndex, int srcColumnIndex) { + ValueEval arg0 = args[0]; + ValueEval arg1 = args[1]; + return evaluateTwoArrayArgs(arg0, arg1, srcRowIndex, srcColumnIndex, + (vA, vB) -> { + Boolean b; + try { + b = OperandResolver.coerceValueToBoolean(vA, false); + } catch (EvaluationException e) { + return e.getErrorEval(); + } + if (b != null && b) { + if (vB == MissingArgEval.instance) { + return BlankEval.instance; + } + return vB; + } + return BoolEval.FALSE; + } + ); + } + } diff --git a/src/java/org/apache/poi/ss/formula/functions/LogicalFunction.java b/src/java/org/apache/poi/ss/formula/functions/LogicalFunction.java index a3fe304746..5cb7d25505 100644 --- a/src/java/org/apache/poi/ss/formula/functions/LogicalFunction.java +++ b/src/java/org/apache/poi/ss/formula/functions/LogicalFunction.java @@ -23,7 +23,7 @@ import org.apache.poi.ss.formula.eval.*; * Implementation of the various ISxxx Logical Functions, which * take a single expression argument, and return True or False. */ -public abstract class LogicalFunction extends Fixed1ArgFunction { +public abstract class LogicalFunction extends Fixed1ArgFunction implements ArrayFunction{ @SuppressWarnings("unused") public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0) { @@ -41,6 +41,14 @@ public abstract class LogicalFunction extends Fixed1ArgFunction { return BoolEval.valueOf(evaluate(ve)); } + + @Override + public ValueEval evaluateArray(ValueEval[] args, int srcRowIndex, int srcColumnIndex){ + return evaluateOneArrayArg(args, srcRowIndex, srcColumnIndex, (valA) -> + BoolEval.valueOf(evaluate(valA)) + ); + } + /** * @param arg any {@link ValueEval}, potentially {@link BlankEval} or {@link ErrorEval}. */ diff --git a/src/testcases/org/apache/poi/ss/formula/functions/TestIFFunctionFromSpreadsheet.java b/src/testcases/org/apache/poi/ss/formula/functions/TestIFFunctionFromSpreadsheet.java new file mode 100644 index 0000000000..cbdbfa5043 --- /dev/null +++ b/src/testcases/org/apache/poi/ss/formula/functions/TestIFFunctionFromSpreadsheet.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 org.junit.runners.Parameterized.Parameters; + +import java.util.Collection; + +/** + * Tests IF() as loaded from a test data spreadsheet.

+ */ +public class TestIFFunctionFromSpreadsheet extends BaseTestFunctionsFromSpreadsheet { + @Parameters(name="{0}") + public static Collection data() throws Exception { + return data(TestIFFunctionFromSpreadsheet.class, "IfFunctionTestCaseData.xls"); + } +} diff --git a/src/testcases/org/apache/poi/ss/formula/functions/TestLogicalFunctionsFromSpreadsheet.java b/src/testcases/org/apache/poi/ss/formula/functions/TestLogicalFunctionsFromSpreadsheet.java new file mode 100644 index 0000000000..6432eb2f42 --- /dev/null +++ b/src/testcases/org/apache/poi/ss/formula/functions/TestLogicalFunctionsFromSpreadsheet.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 org.junit.runners.Parameterized.Parameters; + +import java.util.Collection; + +/** + * Tests for logical ISxxx functions as loaded from a test data spreadsheet.

+ */ +public class TestLogicalFunctionsFromSpreadsheet extends BaseTestFunctionsFromSpreadsheet { + @Parameters(name="{0}") + public static Collection data() throws Exception { + return data(TestLogicalFunctionsFromSpreadsheet.class, "LogicalFunctionsTestCaseData.xls"); + } +} diff --git a/test-data/spreadsheet/IfFunctionTestCaseData.xls b/test-data/spreadsheet/IfFunctionTestCaseData.xls new file mode 100644 index 0000000000000000000000000000000000000000..1d289d792ca951c88466b6c1be5b34e2cebb5e4c GIT binary patch literal 32768 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 literal 0 HcmV?d00001 diff --git a/test-data/spreadsheet/LogicalFunctionsTestCaseData.xls b/test-data/spreadsheet/LogicalFunctionsTestCaseData.xls new file mode 100644 index 0000000000000000000000000000000000000000..df62cdeb06b422c8f5973b427e9687d3c4a95552 GIT binary patch literal 30720 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