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 0000000000..1d289d792c Binary files /dev/null and b/test-data/spreadsheet/IfFunctionTestCaseData.xls differ diff --git a/test-data/spreadsheet/LogicalFunctionsTestCaseData.xls b/test-data/spreadsheet/LogicalFunctionsTestCaseData.xls new file mode 100644 index 0000000000..df62cdeb06 Binary files /dev/null and b/test-data/spreadsheet/LogicalFunctionsTestCaseData.xls differ