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 <tt>OperationEval</tt> instances to help evaluate <tt>OperationPtg</tt>
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){
// 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;
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() + "'");
}
/**
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;
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) {
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) {
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();
}
return new NumberEval(-d);
}
+
+ @Override
+ public ValueEval evaluateArray(ValueEval[] args, int srcRowIndex, int srcColumnIndex){
+ return evaluateOneArrayArg(args, srcRowIndex, srcColumnIndex, (valA) ->
+ evaluate(srcRowIndex, srcColumnIndex, valA)
+ );
+ }
+
}
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 UnaryPlusEval extends Fixed1ArgFunction {
+public final class UnaryPlusEval extends Fixed1ArgFunction implements ArrayFunction {
public static final Function instance = new UnaryPlusEval();
}
return new NumberEval(+d);
}
+
+ @Override
+ public ValueEval evaluateArray(ValueEval[] args, int srcRowIndex, int srcColumnIndex){
+ return evaluateOneArrayArg(args, srcRowIndex, srcColumnIndex, (valA) ->
+ evaluate(srcRowIndex, srcColumnIndex, valA)
+ );
+ }
+
}
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
*/
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 <code>null</code>
+ * @param arg1 the first function argument. Empty values are represented with
+ * @link BlankEval} or {@link MissingArgEval}, never <code>null</code>
+ *
+ * @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 <code>null</code>.
+ * <b>Note</b> - Excel uses the error code <i>#NUM!</i> instead of IEEE <i>NaN</i>, 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<ValueEval, ValueEval, ValueEval> 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<ValueEval, ValueEval> 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);
+
+ }
+
}
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;
* 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 {
return BoolEval.FALSE;
}
+ @Override
public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1,
ValueEval arg2) {
boolean b;
}
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;
+ }
+ );
+ }
+
}
* 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) {
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}.
*/
--- /dev/null
+/* ====================================================================
+ 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.<p>
+ */
+public class TestIFFunctionFromSpreadsheet extends BaseTestFunctionsFromSpreadsheet {
+ @Parameters(name="{0}")
+ public static Collection<Object[]> data() throws Exception {
+ return data(TestIFFunctionFromSpreadsheet.class, "IfFunctionTestCaseData.xls");
+ }
+}
--- /dev/null
+/* ====================================================================
+ 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.<p>
+ */
+public class TestLogicalFunctionsFromSpreadsheet extends BaseTestFunctionsFromSpreadsheet {
+ @Parameters(name="{0}")
+ public static Collection<Object[]> data() throws Exception {
+ return data(TestLogicalFunctionsFromSpreadsheet.class, "LogicalFunctionsTestCaseData.xls");
+ }
+}