From 698d8eb0066ff7fa3fa0addcc042ea260e5756e4 Mon Sep 17 00:00:00 2001 From: Yegor Kozlov Date: Mon, 14 Jan 2019 14:48:21 +0000 Subject: [PATCH] follow-up to Bug 62904. More tests and improved evaluation of IF in array mode git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1851263 13f79535-47bb-0310-9956-ffa450edef68 --- .../poi/ss/formula/WorkbookEvaluator.java | 64 +++++---- .../poi/ss/formula/eval/UnaryMinusEval.java | 5 +- .../poi/ss/formula/eval/UnaryPlusEval.java | 5 +- .../ss/formula/functions/ArrayFunction.java | 22 +++- .../ss/formula/functions/BooleanFunction.java | 28 +++- .../poi/ss/formula/functions/IfFunc.java | 121 ++++++++++++++++-- .../ss/formula/functions/LogicalFunction.java | 5 +- .../TestBooleanFunctionsFromSpreadsheet.java | 32 +++++ .../BooleanFunctionsTestCaseData.xls | Bin 0 -> 28160 bytes .../spreadsheet/IfFunctionTestCaseData.xls | Bin 32768 -> 34816 bytes .../spreadsheet/RowFunctionTestCaseData.xls | Bin 0 -> 28160 bytes 11 files changed, 229 insertions(+), 53 deletions(-) create mode 100644 src/testcases/org/apache/poi/ss/formula/functions/TestBooleanFunctionsFromSpreadsheet.java create mode 100644 test-data/spreadsheet/BooleanFunctionsTestCaseData.xls create mode 100644 test-data/spreadsheet/RowFunctionTestCaseData.xls diff --git a/src/java/org/apache/poi/ss/formula/WorkbookEvaluator.java b/src/java/org/apache/poi/ss/formula/WorkbookEvaluator.java index e91c0e1095..bab3e4e66e 100644 --- a/src/java/org/apache/poi/ss/formula/WorkbookEvaluator.java +++ b/src/java/org/apache/poi/ss/formula/WorkbookEvaluator.java @@ -398,6 +398,9 @@ public final class WorkbookEvaluator { dbgEvaluationOutputIndent++; } + EvaluationSheet evalSheet = ec.getWorkbook().getSheet(ec.getSheetIndex()); + EvaluationCell evalCell = evalSheet.getCell(ec.getRowIndex(), ec.getColumnIndex()); + Stack stack = new Stack<>(); for (int i = 0, iSize = ptgs.length; i < iSize; i++) { // since we don't know how to handle these yet :( @@ -436,46 +439,41 @@ public final class WorkbookEvaluator { continue; } if (attrPtg.isOptimizedIf()) { - ValueEval arg0 = stack.pop(); - boolean evaluatedPredicate; - try { - evaluatedPredicate = IfFunc.evaluateFirstArg(arg0, ec.getRowIndex(), ec.getColumnIndex()); - } catch (EvaluationException e) { - stack.push(e.getErrorEval()); - int dist = attrPtg.getData(); - i+= countTokensToBeSkipped(ptgs, i, dist); - attrPtg = (AttrPtg) ptgs[i]; - dist = attrPtg.getData()+1; - i+= countTokensToBeSkipped(ptgs, i, dist); - continue; - } - if (evaluatedPredicate) { - // 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(!evalCell.isPartOfArrayFormulaGroup()) { + ValueEval arg0 = stack.pop(); + boolean evaluatedPredicate; - 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(arg0); - if(currPtg instanceof AreaPtg){ - // IF in array mode. See Bug 62904 - ValueEval currEval = getEvalForPtg(currPtg, ec); - stack.push(currEval); - } else { + try { + evaluatedPredicate = IfFunc.evaluateFirstArg(arg0, ec.getRowIndex(), ec.getColumnIndex()); + } catch (EvaluationException e) { + stack.push(e.getErrorEval()); + int dist = attrPtg.getData(); + i += countTokensToBeSkipped(ptgs, i, dist); + attrPtg = (AttrPtg) ptgs[i]; + dist = attrPtg.getData() + 1; + i += countTokensToBeSkipped(ptgs, i, dist); + continue; + } + if (evaluatedPredicate) { + // nothing to skip - true param follows + } else { + int dist = attrPtg.getData(); + 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 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(arg0); stack.push(BoolEval.FALSE); } } } continue; } - if (attrPtg.isSkip()) { + if (attrPtg.isSkip() && !evalCell.isPartOfArrayFormulaGroup()) { int dist = attrPtg.getData()+1; i+= countTokensToBeSkipped(ptgs, i, dist); if (stack.peek() == MissingArgEval.instance) { 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 ac1ee34118..8107c48544 100644 --- a/src/java/org/apache/poi/ss/formula/eval/UnaryMinusEval.java +++ b/src/java/org/apache/poi/ss/formula/eval/UnaryMinusEval.java @@ -48,7 +48,10 @@ public final class UnaryMinusEval extends Fixed1ArgFunction implements ArrayFun @Override public ValueEval evaluateArray(ValueEval[] args, int srcRowIndex, int srcColumnIndex){ - return evaluateOneArrayArg(args, srcRowIndex, srcColumnIndex, (valA) -> + if (args.length != 1) { + return ErrorEval.VALUE_INVALID; + } + return evaluateOneArrayArg(args[0], 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 d5cb586a85..81b36aad40 100644 --- a/src/java/org/apache/poi/ss/formula/eval/UnaryPlusEval.java +++ b/src/java/org/apache/poi/ss/formula/eval/UnaryPlusEval.java @@ -52,7 +52,10 @@ public final class UnaryPlusEval extends Fixed1ArgFunction implements ArrayFunc @Override public ValueEval evaluateArray(ValueEval[] args, int srcRowIndex, int srcColumnIndex){ - return evaluateOneArrayArg(args, srcRowIndex, srcColumnIndex, (valA) -> + if (args.length != 1) { + return ErrorEval.VALUE_INVALID; + } + return evaluateOneArrayArg(args[0], 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 088d7b34fd..7f8a359d3a 100644 --- a/src/java/org/apache/poi/ss/formula/functions/ArrayFunction.java +++ b/src/java/org/apache/poi/ss/formula/functions/ArrayFunction.java @@ -111,6 +111,12 @@ public interface ArrayFunction { vA = ErrorEval.NAME_INVALID; } catch (EvaluationException e) { vA = e.getErrorEval(); + } catch (RuntimeException e) { + if(e.getMessage().startsWith("Don't now how to evaluate name")){ + vA = ErrorEval.NAME_INVALID; + } else { + throw e; + } } ValueEval vB; try { @@ -119,6 +125,12 @@ public interface ArrayFunction { vB = ErrorEval.NAME_INVALID; } catch (EvaluationException e) { vB = e.getErrorEval(); + } catch (RuntimeException e) { + if(e.getMessage().startsWith("Don't now how to evaluate name")){ + vB = ErrorEval.NAME_INVALID; + } else { + throw e; + } } if(vA instanceof ErrorEval){ vals[idx++] = vA; @@ -138,10 +150,8 @@ public interface ArrayFunction { return new CacheAreaEval(srcRowIndex, srcColumnIndex, srcRowIndex + height - 1, srcColumnIndex + width - 1, vals); } - default ValueEval evaluateOneArrayArg(ValueEval[] args, int srcRowIndex, int srcColumnIndex, + default ValueEval evaluateOneArrayArg(ValueEval arg0, 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) { @@ -178,6 +188,12 @@ public interface ArrayFunction { vA = ErrorEval.NAME_INVALID; } catch (EvaluationException e) { vA = e.getErrorEval(); + } catch (RuntimeException e) { + if(e.getMessage().startsWith("Don't now how to evaluate name")){ + vA = ErrorEval.NAME_INVALID; + } else { + throw e; + } } vals[idx++] = evalFunc.apply(vA); } diff --git a/src/java/org/apache/poi/ss/formula/functions/BooleanFunction.java b/src/java/org/apache/poi/ss/formula/functions/BooleanFunction.java index 5d011e3b20..5b5b539fef 100644 --- a/src/java/org/apache/poi/ss/formula/functions/BooleanFunction.java +++ b/src/java/org/apache/poi/ss/formula/functions/BooleanFunction.java @@ -37,7 +37,7 @@ import org.apache.poi.ss.formula.eval.ValueEval; * * @author Amol S. Deshmukh < amolweb at ya hoo dot com > */ -public abstract class BooleanFunction implements Function { +public abstract class BooleanFunction implements Function,ArrayFunction { public final ValueEval evaluate(ValueEval[] args, int srcRow, int srcCol) { if (args.length < 1) { @@ -142,7 +142,20 @@ public abstract class BooleanFunction implements Function { return BoolEval.TRUE; } }; - public static final Function NOT = new Fixed1ArgFunction() { + + abstract static class Boolean1ArgFunction extends Fixed1ArgFunction implements ArrayFunction { + @Override + public ValueEval evaluateArray(ValueEval[] args, int srcRowIndex, int srcColumnIndex) { + if (args.length != 1) { + return ErrorEval.VALUE_INVALID; + } + return evaluateOneArrayArg(args[0], srcRowIndex, srcColumnIndex, + vA -> evaluate(srcRowIndex, srcColumnIndex, vA)); + } + + } + + public static final Function NOT = new Boolean1ArgFunction() { public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0) { boolean boolArgVal; try { @@ -156,4 +169,13 @@ public abstract class BooleanFunction implements Function { return BoolEval.valueOf(!boolArgVal); } }; -} + + @Override + public ValueEval evaluateArray(ValueEval[] args, int srcRowIndex, int srcColumnIndex) { + if (args.length != 1) { + return ErrorEval.VALUE_INVALID; + } + return evaluateOneArrayArg(args[0], srcRowIndex, srcColumnIndex, + vA -> evaluate(new ValueEval[]{vA}, srcRowIndex, srcColumnIndex)); + } +} \ No newline at end of file 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 effff27815..7f92881353 100644 --- a/src/java/org/apache/poi/ss/formula/functions/IfFunc.java +++ b/src/java/org/apache/poi/ss/formula/functions/IfFunc.java @@ -17,10 +17,14 @@ package org.apache.poi.ss.formula.functions; +import org.apache.poi.ss.formula.CacheAreaEval; +import org.apache.poi.ss.formula.FormulaParseException; import org.apache.poi.ss.formula.eval.*; import org.apache.poi.ss.formula.ptg.Ptg; import org.apache.poi.ss.formula.ptg.RefPtg; +import java.util.function.BiFunction; + /** * Implementation for the Excel function IF *

@@ -84,25 +88,120 @@ public final class IfFunc extends Var2or3ArgFunction implements ArrayFunction { @Override public ValueEval evaluateArray(ValueEval[] args, int srcRowIndex, int srcColumnIndex) { + if (args.length < 2 || args.length > 3) { + return ErrorEval.VALUE_INVALID; + } + ValueEval arg0 = args[0]; ValueEval arg1 = args[1]; - return evaluateTwoArrayArgs(arg0, arg1, srcRowIndex, srcColumnIndex, - (vA, vB) -> { + ValueEval arg2 = args.length == 2 ? BoolEval.FALSE : args[2]; + return evaluateArrayArgs(arg0, arg1, arg2, srcRowIndex, srcColumnIndex); + } + + ValueEval evaluateArrayArgs(ValueEval arg0, ValueEval arg1, ValueEval arg2, int srcRowIndex, int srcColumnIndex) { + 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 a3FirstCol = 0, a3FirstRow = 0; + if (arg2 instanceof AreaEval) { + AreaEval ae = (AreaEval)arg2; + a3FirstCol = ae.getFirstColumn(); + a3FirstRow = ae.getFirstRow(); + } else if (arg2 instanceof RefEval){ + RefEval ref = (RefEval)arg2; + a3FirstCol = ref.getColumn(); + a3FirstRow = ref.getRow(); + } + + 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(); + } + + ValueEval vC; + try { + vC = OperandResolver.getSingleValue(arg2, a3FirstRow + i, a3FirstCol + j); + } catch (FormulaParseException e) { + vC = ErrorEval.NAME_INVALID; + } catch (EvaluationException e) { + vC = e.getErrorEval(); + } + + if(vA instanceof ErrorEval){ + vals[idx++] = vA; + } else if (vB instanceof ErrorEval) { + vals[idx++] = vB; + } else { Boolean b; try { b = OperandResolver.coerceValueToBoolean(vA, false); + vals[idx++] = b != null && b ? vB : vC; } catch (EvaluationException e) { - return e.getErrorEval(); + vals[idx++] = e.getErrorEval(); } - if (b != null && b) { - if (vB == MissingArgEval.instance) { - return BlankEval.instance; - } - return vB; - } - return BoolEval.FALSE; } - ); + + } + } + + 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/LogicalFunction.java b/src/java/org/apache/poi/ss/formula/functions/LogicalFunction.java index 5cb7d25505..9df2803c3c 100644 --- a/src/java/org/apache/poi/ss/formula/functions/LogicalFunction.java +++ b/src/java/org/apache/poi/ss/formula/functions/LogicalFunction.java @@ -44,7 +44,10 @@ public abstract class LogicalFunction extends Fixed1ArgFunction implements Array @Override public ValueEval evaluateArray(ValueEval[] args, int srcRowIndex, int srcColumnIndex){ - return evaluateOneArrayArg(args, srcRowIndex, srcColumnIndex, (valA) -> + if (args.length != 1) { + return ErrorEval.VALUE_INVALID; + } + return evaluateOneArrayArg(args[0], srcRowIndex, srcColumnIndex, (valA) -> BoolEval.valueOf(evaluate(valA)) ); } diff --git a/src/testcases/org/apache/poi/ss/formula/functions/TestBooleanFunctionsFromSpreadsheet.java b/src/testcases/org/apache/poi/ss/formula/functions/TestBooleanFunctionsFromSpreadsheet.java new file mode 100644 index 0000000000..ef69a9c65b --- /dev/null +++ b/src/testcases/org/apache/poi/ss/formula/functions/TestBooleanFunctionsFromSpreadsheet.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 boolean functions as loaded from a test data spreadsheet.

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