replace over engineered inner classes with lambdas/method references

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1888743 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Andreas Beeker 2021-04-13 19:44:38 +00:00
parent 3c03974499
commit 240b02daec
16 changed files with 437 additions and 605 deletions

View File

@ -73,9 +73,9 @@ public final class FunctionEval {
retval[5] = AggregateFunction.AVERAGE;
retval[6] = AggregateFunction.MIN;
retval[7] = AggregateFunction.MAX;
retval[8] = new RowFunc(); // ROW
retval[9] = new Column();
retval[10] = new Na();
retval[8] = RowFunc::evaluate; // ROW
retval[9] = Column::evaluate;
retval[10] = Na::evaluate;
retval[11] = new Npv();
retval[12] = AggregateFunction.STDEV;
retval[13] = NumericFunction.DOLLAR;
@ -137,7 +137,7 @@ public final class FunctionEval {
retval[71] = CalendarFieldFunction.HOUR;
retval[72] = CalendarFieldFunction.MINUTE;
retval[73] = CalendarFieldFunction.SECOND;
retval[74] = new Now();
retval[74] = Now::evaluate;
retval[75] = new Areas();
retval[76] = new Rows();
retval[77] = new Columns();
@ -226,7 +226,7 @@ public final class FunctionEval {
retval[216] = new Rank();
retval[219] = new Address();
retval[220] = new Days360();
retval[221] = new Today();
retval[221] = Today::evaluate;
//222: VBD
retval[227] = AggregateFunction.MEDIAN;
@ -259,7 +259,7 @@ public final class FunctionEval {
retval[276] = NumericFunction.COMBIN;
// 277: CONFIDENCE
// 278:CRITBINOM
retval[279] = new Even();
retval[279] = NumericFunction.EVEN;
// 280: EXPONDIST
// 281: FDIST
// 282: FINV
@ -278,7 +278,7 @@ public final class FunctionEval {
// 295: NORMINV
// 296: NORMSINV
// 297: STANDARDIZE
retval[298] = new Odd();
retval[298] = NumericFunction.ODD;
// 299: PERMUT
retval[300] = NumericFunction.POISSON;
// 301: TDIST

View File

@ -32,10 +32,10 @@ public interface ArrayFunction {
/**
* @param args the evaluated function arguments. Empty values are represented with
* {@link BlankEval} or {@link MissingArgEval}, never <code>null</code>.
* {@link BlankEval} or {@link MissingArgEval}, never {@code 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 <code>null</code>.
* @return The evaluated result, possibly an {@link ErrorEval}, never {@code null}.
* <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}.
@ -47,19 +47,29 @@ public interface ArrayFunction {
* 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>
* {@link BlankEval} or {@link MissingArgEval}, never {@code null}
* @param arg1 the first function argument. Empty values are represented with
* @link BlankEval} or {@link MissingArgEval}, never <code>null</code>
* @link BlankEval} or {@link MissingArgEval}, never {@code 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 <code>null</code>.
* @return The evaluated result, possibly an {@link ErrorEval}, never {@code null}.
* <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) {
return _evaluateTwoArrayArgs(arg0, arg1, srcRowIndex, srcColumnIndex, evalFunc);
}
default ValueEval evaluateOneArrayArg(ValueEval arg0, int srcRowIndex, int srcColumnIndex,
java.util.function.Function<ValueEval, ValueEval> evalFunc) {
return _evaluateOneArrayArg(arg0, srcRowIndex, srcColumnIndex, evalFunc);
}
static 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) {
@ -150,7 +160,8 @@ public interface ArrayFunction {
return new CacheAreaEval(srcRowIndex, srcColumnIndex, srcRowIndex + height - 1, srcColumnIndex + width - 1, vals);
}
default ValueEval evaluateOneArrayArg(ValueEval arg0, int srcRowIndex, int srcColumnIndex,
static ValueEval _evaluateOneArrayArg(ValueEval arg0, int srcRowIndex, int srcColumnIndex,
java.util.function.Function<ValueEval, ValueEval> evalFunc){
int w1, w2, h1, h2;
int a1FirstCol = 0, a1FirstRow = 0;
@ -204,7 +215,6 @@ public interface ArrayFunction {
}
return new CacheAreaEval(srcRowIndex, srcColumnIndex, srcRowIndex + height - 1, srcColumnIndex + width - 1, vals);
}
}

View File

@ -34,8 +34,6 @@ import org.apache.poi.ss.formula.eval.ValueEval;
* <li> Numbers: 0 is false. Any other number is TRUE </li>
* <li> Areas: *all* cells in area are evaluated according to the above rules</li>
* </ol>
*
* @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
*/
public abstract class BooleanFunction implements Function,ArrayFunction {
@ -132,43 +130,12 @@ public abstract class BooleanFunction implements Function,ArrayFunction {
return cumulativeResult || currentValue;
}
};
public static final Function FALSE = new Fixed0ArgFunction() {
public ValueEval evaluate(int srcRowIndex, int srcColumnIndex) {
return BoolEval.FALSE;
}
};
public static final Function TRUE = new Fixed0ArgFunction() {
public ValueEval evaluate(int srcRowIndex, int srcColumnIndex) {
return BoolEval.TRUE;
}
};
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 FALSE = BooleanFunction::evaluateFalse;
}
public static final Function TRUE = BooleanFunction::evaluateTrue;
public static final Function NOT = new Boolean1ArgFunction() {
public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0) {
boolean boolArgVal;
try {
ValueEval ve = OperandResolver.getSingleValue(arg0, srcRowIndex, srcColumnIndex);
Boolean b = OperandResolver.coerceValueToBoolean(ve, false);
boolArgVal = b == null ? false : b;
} catch (EvaluationException e) {
return e.getErrorEval();
}
return BoolEval.valueOf(!boolArgVal);
}
};
public static final Function NOT = BooleanFunction::evaluateNot;
@Override
public ValueEval evaluateArray(ValueEval[] args, int srcRowIndex, int srcColumnIndex) {
@ -178,4 +145,30 @@ public abstract class BooleanFunction implements Function,ArrayFunction {
return evaluateOneArrayArg(args[0], srcRowIndex, srcColumnIndex,
vA -> evaluate(new ValueEval[]{vA}, srcRowIndex, srcColumnIndex));
}
private static ValueEval evaluateFalse(ValueEval[] args, int srcRowIndex, int srcColumnIndex) {
return args.length != 0 ? ErrorEval.VALUE_INVALID : BoolEval.FALSE;
}
private static ValueEval evaluateTrue(ValueEval[] args, int srcRowIndex, int srcColumnIndex) {
return args.length != 0 ? ErrorEval.VALUE_INVALID : BoolEval.TRUE;
}
private static ValueEval evaluateNot(ValueEval[] args, int srcRowIndex, int srcColumnIndex) {
if (args.length != 1) {
return ErrorEval.VALUE_INVALID;
}
java.util.function.Function<ValueEval, ValueEval> notInner = (va) -> {
try {
ValueEval ve = OperandResolver.getSingleValue(va, srcRowIndex, srcColumnIndex);
Boolean b = OperandResolver.coerceValueToBoolean(ve, false);
boolean boolArgVal = b != null && b;
return BoolEval.valueOf(!boolArgVal);
} catch (EvaluationException e) {
return e.getErrorEval();
}
};
return ArrayFunction._evaluateOneArrayArg(args[0], srcRowIndex, srcColumnIndex, notInner);
}
}

View File

@ -23,18 +23,19 @@ import org.apache.poi.ss.formula.eval.NumberEval;
import org.apache.poi.ss.formula.eval.RefEval;
import org.apache.poi.ss.formula.eval.ValueEval;
public final class Column implements Function0Arg, Function1Arg {
public ValueEval evaluate(int srcRowIndex, int srcColumnIndex) {
return new NumberEval(srcColumnIndex+1.);
public final class Column {
public static ValueEval evaluate(ValueEval[] args, int srcRowIndex, int srcColumnIndex) {
if (args.length > 1) {
return ErrorEval.VALUE_INVALID;
}
public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0) {
int rnum;
if (arg0 instanceof AreaEval) {
rnum = ((AreaEval) arg0).getFirstColumn();
} else if (arg0 instanceof RefEval) {
rnum = ((RefEval) arg0).getColumn();
int rnum;
if (args.length == 0) {
rnum = srcColumnIndex;
} else if (args[0] instanceof AreaEval) {
rnum = ((AreaEval) args[0]).getFirstColumn();
} else if (args[0] instanceof RefEval) {
rnum = ((RefEval) args[0]).getColumn();
} else {
// anything else is not valid argument
return ErrorEval.VALUE_INVALID;
@ -42,13 +43,4 @@ public final class Column implements Function0Arg, Function1Arg {
return new NumberEval(rnum + 1.);
}
public ValueEval evaluate(ValueEval[] args, int srcRowIndex, int srcColumnIndex) {
switch (args.length) {
case 1:
return evaluate(srcRowIndex, srcColumnIndex, args[0]);
case 0:
return new NumberEval(srcColumnIndex+1.);
}
return ErrorEval.VALUE_INVALID;
}
}

View File

@ -1,49 +0,0 @@
/* ====================================================================
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;
/**
* @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
*
*/
public final class Even extends NumericFunction.OneArg {
private static final long PARITY_MASK = 0xFFFFFFFFFFFFFFFEL;
protected double evaluate(double d) {
if (d==0) {
return 0;
}
long result;
if (d>0) {
result = calcEven(d);
} else {
result = -calcEven(-d);
}
return result;
}
private static long calcEven(double d) {
long x = ((long) d) & PARITY_MASK;
if (x == d) {
return x;
}
return x + 2;
}
}

View File

@ -19,12 +19,15 @@ package org.apache.poi.ss.formula.functions;
import org.apache.poi.ss.formula.eval.ErrorEval;
import org.apache.poi.ss.formula.eval.ValueEval;
import org.apache.poi.util.Removal;
/**
* Convenience base class for functions that only take zero arguments.
*
* @author Josh Micich
* @deprecated replaced by lambda expressions in 5.0.1
*/
@Deprecated
@Removal(version = "6.0.0")
public abstract class Fixed0ArgFunction implements Function0Arg {
public final ValueEval evaluate(ValueEval[] args, int srcRowIndex, int srcColumnIndex) {
if (args.length != 0) {

View File

@ -0,0 +1,50 @@
/* ====================================================================
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.apache.poi.ss.formula.eval.ErrorEval;
import org.apache.poi.ss.formula.eval.EvaluationException;
import org.apache.poi.ss.formula.eval.NumberEval;
import org.apache.poi.ss.formula.eval.ValueEval;
public class Log {
private static final double TEN = 10.0;
private static final double LOG_10_TO_BASE_e = Math.log(TEN);
public static ValueEval evaluate(ValueEval[] args, int srcRowIndex, int srcColumnIndex) {
if (args.length != 1 && args.length != 2) {
return ErrorEval.VALUE_INVALID;
}
try {
double d0 = NumericFunction.singleOperandEvaluate(args[0], srcRowIndex, srcColumnIndex);
double result;
if (args.length == 1) {
result = Math.log(d0) / LOG_10_TO_BASE_e;
} else {
double d1 = NumericFunction.singleOperandEvaluate(args[1], srcRowIndex, srcColumnIndex);
double logE = Math.log(d0);
result = (Double.compare(d1, Math.E) == 0) ? logE : (logE / Math.log(d1));
}
NumericFunction.checkValue(result);
return new NumberEval(result);
} catch (EvaluationException e) {
return e.getErrorEval();
}
}
}

View File

@ -51,6 +51,12 @@ final class MathX {
return round(n, p, java.math.RoundingMode.HALF_UP);
}
public static double round(double n, double p) {
return round(n, (int)p);
}
/**
* Returns a value rounded-up to p digits after decimal.
* If p is negative, then the number is rounded to
@ -70,6 +76,11 @@ final class MathX {
return round(n, p, java.math.RoundingMode.UP);
}
public static double roundUp(double n, double p) {
return roundUp(n, (int)p);
}
/**
* Returns a value rounded to p digits after decimal.
* If p is negative, then the number is rounded to
@ -89,6 +100,10 @@ final class MathX {
return round(n, p, java.math.RoundingMode.DOWN);
}
public static double roundDown(double n, double p) {
return roundDown(n, (int)p);
}
private static double round(double n, int p, java.math.RoundingMode rounding) {
if (Double.isNaN(n) || Double.isInfinite(n)) {
return Double.NaN;
@ -273,6 +288,10 @@ final class MathX {
return d;
}
public static double factorial(double d) {
return factorial((int)d);
}
/**
* returns the remainder resulting from operation:

View File

@ -22,12 +22,9 @@ import org.apache.poi.ss.formula.eval.ValueEval;
/**
* Implementation of Excel function NA()
*
* @author Josh Micich
*/
public final class Na extends Fixed0ArgFunction {
public ValueEval evaluate(int srcCellRow, int srcCellCol) {
return ErrorEval.NA;
public final class Na {
public static ValueEval evaluate(ValueEval[] args, int srcCellRow, int srcCellCol) {
return args.length != 0 ? ErrorEval.VALUE_INVALID : ErrorEval.NA;
}
}

View File

@ -19,18 +19,19 @@ package org.apache.poi.ss.formula.functions;
import java.util.Date;
import org.apache.poi.ss.formula.eval.ErrorEval;
import org.apache.poi.ss.formula.eval.NumberEval;
import org.apache.poi.ss.formula.eval.ValueEval;
import org.apache.poi.ss.usermodel.DateUtil;
/**
* Implementation of Excel NOW() Function
*
* @author Frank Taffelt
*/
public final class Now extends Fixed0ArgFunction {
public ValueEval evaluate(int srcRowIndex, int srcColumnIndex) {
public final class Now {
public static ValueEval evaluate(ValueEval[] args, int srcRowIndex, int srcColumnIndex) {
if (args.length != 0) {
return ErrorEval.VALUE_INVALID;
}
Date now = new Date(System.currentTimeMillis());
return new NumberEval(DateUtil.getExcelDate(now));
}

View File

@ -17,18 +17,17 @@
package org.apache.poi.ss.formula.functions;
import static org.apache.poi.ss.formula.eval.ErrorEval.VALUE_INVALID;
import org.apache.poi.ss.formula.eval.*;
/**
* @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
* @author Josh Micich
* @author Stephen Wolke (smwolke at geistig.com)
*/
public abstract class NumericFunction implements Function {
static final double ZERO = 0.0;
static final double TEN = 10.0;
static final double LOG_10_TO_BASE_e = Math.log(TEN);
private static final double ZERO = 0.0;
private static final double TEN = 10.0;
private static final double LOG_10_TO_BASE_e = Math.log(TEN);
private static final long PARITY_MASK = 0xFFFFFFFFFFFFFFFEL;
protected static double singleOperandEvaluate(ValueEval arg, int srcRowIndex, int srcColumnIndex) throws EvaluationException {
if (arg == null) {
@ -41,7 +40,7 @@ public abstract class NumericFunction implements Function {
}
/**
* @throws EvaluationException (#NUM!) if <tt>result</tt> is <tt>NaN</tt> or <tt>Infinity</tt>
* @throws EvaluationException (#NUM!) if <tt>result</tt> is {@code NaN} or {@code Infinity}
*/
public static void checkValue(double result) throws EvaluationException {
if (Double.isNaN(result) || Double.isInfinite(result)) {
@ -62,432 +61,179 @@ public abstract class NumericFunction implements Function {
protected abstract double eval(ValueEval[] args, int srcCellRow, int srcCellCol) throws EvaluationException;
/* -------------------------------------------------------------------------- */
// intermediate sub-classes (one-arg, two-arg and multi-arg)
public static final Function ABS = oneDouble(Math::abs);
public static final Function ACOS = oneDouble(Math::acos);
public static final Function ACOSH = oneDouble(MathX::acosh);
public static final Function ASIN = oneDouble(Math::asin);
public static final Function ASINH = oneDouble(MathX::asinh);
public static final Function ATAN = oneDouble(Math::atan);
public static final Function ATANH = oneDouble(MathX::atanh);
public static final Function COS = oneDouble(Math::cos);
public static final Function COSH = oneDouble(MathX::cosh);
public static final Function DEGREES = oneDouble(Math::toDegrees);
public static final Function DOLLAR = NumericFunction::evaluateDollar;
public static abstract class OneArg extends Fixed1ArgFunction {
protected OneArg() {
// no fields to initialise
private static ValueEval evaluateDollar(ValueEval[] args, int srcRowIndex, int srcColumnIndex) {
if (args.length != 1 && args.length != 2) {
return ErrorEval.VALUE_INVALID;
}
public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0) {
double result;
try {
double d = singleOperandEvaluate(arg0, srcRowIndex, srcColumnIndex);
result = evaluate(d);
checkValue(result);
} catch (EvaluationException e) {
return e.getErrorEval();
}
return new NumberEval(result);
}
protected final double eval(ValueEval[] args, int srcCellRow, int srcCellCol) throws EvaluationException {
if (args.length != 1) {
throw new EvaluationException(ErrorEval.VALUE_INVALID);
}
double d = singleOperandEvaluate(args[0], srcCellRow, srcCellCol);
return evaluate(d);
}
protected abstract double evaluate(double d) throws EvaluationException;
}
double val = singleOperandEvaluate(args[0], srcRowIndex, srcColumnIndex);
double d1 = args.length == 1 ? 2.0 : singleOperandEvaluate(args[1], srcRowIndex, srcColumnIndex);
public static abstract class TwoArg extends Fixed2ArgFunction {
protected TwoArg() {
// no fields to initialise
}
public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1) {
double result;
try {
double d0 = singleOperandEvaluate(arg0, srcRowIndex, srcColumnIndex);
double d1 = singleOperandEvaluate(arg1, srcRowIndex, srcColumnIndex);
result = evaluate(d0, d1);
checkValue(result);
} catch (EvaluationException e) {
return e.getErrorEval();
}
return new NumberEval(result);
}
protected abstract double evaluate(double d0, double d1) throws EvaluationException;
}
/* -------------------------------------------------------------------------- */
public static final Function ABS = new OneArg() {
protected double evaluate(double d) {
return Math.abs(d);
}
};
public static final Function ACOS = new OneArg() {
protected double evaluate(double d) {
return Math.acos(d);
}
};
public static final Function ACOSH = new OneArg() {
protected double evaluate(double d) {
return MathX.acosh(d);
}
};
public static final Function ASIN = new OneArg() {
protected double evaluate(double d) {
return Math.asin(d);
}
};
public static final Function ASINH = new OneArg() {
protected double evaluate(double d) {
return MathX.asinh(d);
}
};
public static final Function ATAN = new OneArg() {
protected double evaluate(double d) {
return Math.atan(d);
}
};
public static final Function ATANH = new OneArg() {
protected double evaluate(double d) {
return MathX.atanh(d);
}
};
public static final Function COS = new OneArg() {
protected double evaluate(double d) {
return Math.cos(d);
}
};
public static final Function COSH = new OneArg() {
protected double evaluate(double d) {
return MathX.cosh(d);
}
};
public static final Function DEGREES = new OneArg() {
protected double evaluate(double d) {
return Math.toDegrees(d);
}
};
static final NumberEval DOLLAR_ARG2_DEFAULT = new NumberEval(2.0);
public static final Function DOLLAR = new Var1or2ArgFunction() {
public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0) {
return evaluate(srcRowIndex, srcColumnIndex, arg0, DOLLAR_ARG2_DEFAULT);
}
public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0,
ValueEval arg1) {
double val;
double d1;
try {
val = singleOperandEvaluate(arg0, srcRowIndex, srcColumnIndex);
d1 = singleOperandEvaluate(arg1, srcRowIndex, srcColumnIndex);
} catch (EvaluationException e) {
return e.getErrorEval();
}
// second arg converts to int by truncating toward zero
int nPlaces = (int)d1;
if (nPlaces > 127) {
return ErrorEval.VALUE_INVALID;
return VALUE_INVALID;
}
// TODO - DOLLAR() function impl is NQR
// result should be StringEval, with leading '$' and thousands separators
// current junits are asserting incorrect behaviour
return new NumberEval(val);
}catch (EvaluationException e) {
return e.getErrorEval();
}
};
public static final Function EXP = new OneArg() {
protected double evaluate(double d) {
return Math.pow(Math.E, d);
}
};
public static final Function FACT = new OneArg() {
protected double evaluate(double d) {
return MathX.factorial((int)d);
}
};
public static final Function INT = new OneArg() {
protected double evaluate(double d) {
return Math.round(d-0.5);
}
};
public static final Function LN = new OneArg() {
protected double evaluate(double d) {
return Math.log(d);
}
};
public static final Function LOG10 = new OneArg() {
protected double evaluate(double d) {
return Math.log(d) / LOG_10_TO_BASE_e;
}
};
public static final Function RADIANS = new OneArg() {
protected double evaluate(double d) {
return Math.toRadians(d);
}
};
public static final Function SIGN = new OneArg() {
protected double evaluate(double d) {
return MathX.sign(d);
}
};
public static final Function SIN = new OneArg() {
protected double evaluate(double d) {
return Math.sin(d);
}
};
public static final Function SINH = new OneArg() {
protected double evaluate(double d) {
return MathX.sinh(d);
}
};
public static final Function SQRT = new OneArg() {
protected double evaluate(double d) {
return Math.sqrt(d);
}
};
public static final Function TAN = new OneArg() {
protected double evaluate(double d) {
return Math.tan(d);
}
};
public static final Function TANH = new OneArg() {
protected double evaluate(double d) {
return MathX.tanh(d);
}
};
public static final Function EXP = oneDouble(d -> Math.pow(Math.E, d));
public static final Function FACT = oneDouble(MathX::factorial);
public static final Function INT = oneDouble(d -> Math.round(d-0.5));
public static final Function LN = oneDouble(Math::log);
public static final Function LOG10 = oneDouble(d -> Math.log(d) / LOG_10_TO_BASE_e);
public static final Function RADIANS = oneDouble(Math::toRadians);
public static final Function SIGN = oneDouble(MathX::sign);
public static final Function SIN = oneDouble(Math::sin);
public static final Function SINH = oneDouble(MathX::sinh);
public static final Function SQRT = oneDouble(Math::sqrt);
public static final Function TAN = oneDouble(Math::tan);
public static final Function TANH = oneDouble(MathX::tanh);
/* -------------------------------------------------------------------------- */
public static final Function ATAN2 = new TwoArg() {
protected double evaluate(double d0, double d1) throws EvaluationException {
if (d0 == ZERO && d1 == ZERO) {
throw new EvaluationException(ErrorEval.DIV_ZERO);
}
return Math.atan2(d1, d0);
}
};
public static final Function CEILING = new TwoArg() {
protected double evaluate(double d0, double d1) {
return MathX.ceiling(d0, d1);
}
};
public static final Function COMBIN = new TwoArg() {
protected double evaluate(double d0, double d1) throws EvaluationException {
if (d0 > Integer.MAX_VALUE || d1 > Integer.MAX_VALUE) {
throw new EvaluationException(ErrorEval.NUM_ERROR);
}
return MathX.nChooseK((int) d0, (int) d1);
}
};
public static final Function FLOOR = new TwoArg() {
protected double evaluate(double d0, double d1) throws EvaluationException {
if (d1 == ZERO) {
if (d0 == ZERO) {
return ZERO;
}
throw new EvaluationException(ErrorEval.DIV_ZERO);
}
return MathX.floor(d0, d1);
}
};
public static final Function MOD = new TwoArg() {
protected double evaluate(double d0, double d1) throws EvaluationException {
if (d1 == ZERO) {
throw new EvaluationException(ErrorEval.DIV_ZERO);
}
return MathX.mod(d0, d1);
}
};
public static final Function POWER = new TwoArg() {
protected double evaluate(double d0, double d1) {
return Math.pow(d0, d1);
}
};
public static final Function ROUND = new TwoArg() {
protected double evaluate(double d0, double d1) {
return MathX.round(d0, (int)d1);
}
};
public static final Function ROUNDDOWN = new TwoArg() {
protected double evaluate(double d0, double d1) {
return MathX.roundDown(d0, (int)d1);
}
};
public static final Function ROUNDUP = new TwoArg() {
protected double evaluate(double d0, double d1) {
return MathX.roundUp(d0, (int)d1);
}
};
static final NumberEval TRUNC_ARG2_DEFAULT = new NumberEval(0);
public static final Function TRUNC = new Var1or2ArgFunction() {
public static final Function ATAN2 = twoDouble((d0, d1) ->
(d0 == ZERO && d1 == ZERO) ? ErrorEval.DIV_ZERO : Math.atan2(d1, d0)
);
public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0) {
return evaluate(srcRowIndex, srcColumnIndex, arg0, TRUNC_ARG2_DEFAULT);
}
public static final Function CEILING = twoDouble(MathX::ceiling);
public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1) {
double result;
public static final Function COMBIN = twoDouble((d0, d1) ->
(d0 > Integer.MAX_VALUE || d1 > Integer.MAX_VALUE) ? ErrorEval.NUM_ERROR : MathX.nChooseK((int) d0, (int) d1));
public static final Function FLOOR = twoDouble((d0, d1) ->
(d1 == ZERO) ? (d0 == ZERO ? ZERO : ErrorEval.DIV_ZERO) : MathX.floor(d0, d1));
public static final Function MOD = twoDouble((d0, d1) ->
(d1 == ZERO) ? ErrorEval.DIV_ZERO : MathX.mod(d0, d1));
public static final Function POWER = twoDouble(Math::pow);
public static final Function ROUND = twoDouble(MathX::round);
public static final Function ROUNDDOWN = twoDouble(MathX::roundDown);
public static final Function ROUNDUP = twoDouble(MathX::roundUp);
public static final Function TRUNC = NumericFunction::evaluateTrunc;
private static ValueEval evaluateTrunc(ValueEval[] args, int srcRowIndex, int srcColumnIndex) {
if (args.length != 1 && args.length != 2) {
return ErrorEval.VALUE_INVALID;
}
try {
double d0 = singleOperandEvaluate(arg0, srcRowIndex, srcColumnIndex);
double d1 = singleOperandEvaluate(arg1, srcRowIndex, srcColumnIndex);
result = MathX.roundDown(d0, (int)d1);
double d0 = singleOperandEvaluate(args[0], srcRowIndex, srcColumnIndex);
double d1 = args.length == 1 ? 0 : singleOperandEvaluate(args[1], srcRowIndex, srcColumnIndex);
double result = MathX.roundDown(d0, d1);
checkValue(result);
return new NumberEval(result);
}catch (EvaluationException e) {
return e.getErrorEval();
}
return new NumberEval(result);
}
};
/* -------------------------------------------------------------------------- */
private static final class Log extends Var1or2ArgFunction {
public Log() {
// no instance fields
}
public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0) {
double result;
try {
double d0 = NumericFunction.singleOperandEvaluate(arg0, srcRowIndex, srcColumnIndex);
result = Math.log(d0) / LOG_10_TO_BASE_e;
NumericFunction.checkValue(result);
} catch (EvaluationException e) {
return e.getErrorEval();
}
return new NumberEval(result);
}
public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0,
ValueEval arg1) {
double result;
try {
double d0 = NumericFunction.singleOperandEvaluate(arg0, srcRowIndex, srcColumnIndex);
double d1 = NumericFunction.singleOperandEvaluate(arg1, srcRowIndex, srcColumnIndex);
double logE = Math.log(d0);
if (Double.compare(d1, Math.E) == 0) {
result = logE;
} else {
result = logE / Math.log(d1);
}
NumericFunction.checkValue(result);
} catch (EvaluationException e) {
return e.getErrorEval();
}
return new NumberEval(result);
}
}
public static final Function LOG = new Log();
public static final Function LOG = Log::evaluate;
static final NumberEval PI_EVAL = new NumberEval(Math.PI);
public static final Function PI = new Fixed0ArgFunction() {
public ValueEval evaluate(int srcRowIndex, int srcColumnIndex) {
return PI_EVAL;
}
};
public static final Function RAND = new Fixed0ArgFunction() {
public ValueEval evaluate(int srcRowIndex, int srcColumnIndex) {
return new NumberEval(Math.random());
}
};
public static final Function POISSON = new Fixed3ArgFunction() {
public static final Function PI = NumericFunction::evaluatePI;
private static final double DEFAULT_RETURN_RESULT =1;
/**
* This checks is x = 0 and the mean = 0.
* Excel currently returns the value 1 where as the
* maths common implementation will error.
* @param x The number.
* @param mean The mean.
* @return If a default value should be returned.
*/
private boolean isDefaultResult(double x, double mean) {
if ( x == 0 && mean == 0 ) {
return true;
}
return false;
private static ValueEval evaluatePI(ValueEval[] args, int srcRowIndex, int srcColumnIndex) {
return (args.length != 0) ? ErrorEval.VALUE_INVALID : PI_EVAL;
}
private boolean checkArgument(double aDouble) throws EvaluationException {
public static final Function RAND = NumericFunction::evaluateRand;
NumericFunction.checkValue(aDouble);
// make sure that the number is positive
if (aDouble < 0) {
throw new EvaluationException(ErrorEval.NUM_ERROR);
private static ValueEval evaluateRand(ValueEval[] args, int srcRowIndex, int srcColumnIndex) {
return (args.length != 0) ? ErrorEval.VALUE_INVALID : new NumberEval(Math.random());
}
return true;
public static final Function POISSON = Poisson::evaluate;
public static final Function ODD = oneDouble(NumericFunction::evaluateOdd);
private static double evaluateOdd(double d) {
if (d==0) {
return 1;
}
double dpm = Math.abs(d)+1;
long x = ((long) dpm) & PARITY_MASK;
return MathX.sign(d) * ((Double.compare(x, dpm) == 0) ? x-1 : x+1);
}
private double probability(int k, double lambda) {
return Math.pow(lambda, k) * Math.exp(-lambda) / factorial(k);
public static final Function EVEN = oneDouble(NumericFunction::evaluateEven);
private static double evaluateEven(double d) {
if (d==0) {
return 0;
}
private double cumulativeProbability(int x, double lambda) {
double result = 0;
for(int k = 0; k <= x; k++){
result += probability(k, lambda);
}
return result;
double dpm = Math.abs(d);
long x = ((long) dpm) & PARITY_MASK;
return MathX.sign(d) * ((Double.compare(x, dpm) == 0) ? x : (x + 2));
}
/** All long-representable factorials */
private final long[] FACTORIALS = new long[] {
1l, 1l, 2l,
6l, 24l, 120l,
720l, 5040l, 40320l,
362880l, 3628800l, 39916800l,
479001600l, 6227020800l, 87178291200l,
1307674368000l, 20922789888000l, 355687428096000l,
6402373705728000l, 121645100408832000l, 2432902008176640000l };
public long factorial(final int n) {
if (n < 0 || n > 20) {
throw new IllegalArgumentException("Valid argument should be in the range [0..20]");
}
return FACTORIALS[n];
private interface OneDoubleIf {
double apply(double d);
}
public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1, ValueEval arg2) {
// arguments/result for this function
double mean=0;
double x=0;
boolean cumulative = ((BoolEval)arg2).getBooleanValue();
double result=0;
private static Function oneDouble(OneDoubleIf doubleFun) {
return (args, srcCellRow, srcCellCol) -> {
if (args.length != 1) {
return VALUE_INVALID;
}
try {
x = NumericFunction.singleOperandEvaluate(arg0, srcRowIndex, srcColumnIndex);
mean = NumericFunction.singleOperandEvaluate(arg1, srcRowIndex, srcColumnIndex);
// check for default result : excel implementation for 0,0
// is different to Math Common.
if (isDefaultResult(x,mean)) {
return new NumberEval(DEFAULT_RETURN_RESULT);
}
// check the arguments : as per excel function def
checkArgument(x);
checkArgument(mean);
// truncate x : as per excel function def
if ( cumulative ) {
result = cumulativeProbability((int)x, mean);
} else {
result = probability((int)x, mean);
}
// check the result
NumericFunction.checkValue(result);
double d = singleOperandEvaluate(args[0], srcCellRow, srcCellCol);
double res = doubleFun.apply(d);
return (Double.isNaN(res) || Double.isInfinite(res)) ? ErrorEval.NUM_ERROR : new NumberEval(res);
} catch (EvaluationException e) {
return e.getErrorEval();
}
};
}
return new NumberEval(result);
private interface TwoDoubleIf {
Object apply(double d1, double d2);
}
private static Function twoDouble(TwoDoubleIf doubleFun) {
return (args, srcCellRow, srcCellCol) -> {
if (args.length != 2) {
return VALUE_INVALID;
}
try {
double d1 = singleOperandEvaluate(args[0], srcCellRow, srcCellCol);
double d2 = singleOperandEvaluate(args[1], srcCellRow, srcCellCol);
Object res = doubleFun.apply(d1, d2);
if (res instanceof ErrorEval) {
return (ErrorEval)res;
}
assert(res instanceof Double);
double d = (Double)res;
return (Double.isNaN(d) || Double.isInfinite(d)) ? ErrorEval.NUM_ERROR : new NumberEval(d);
} catch (EvaluationException e) {
return e.getErrorEval();
}
};
}
}

View File

@ -1,39 +0,0 @@
/* ====================================================================
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;
/**
* @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
*
*/
public final class Odd extends NumericFunction.OneArg {
private static final long PARITY_MASK = 0xFFFFFFFFFFFFFFFEL;
protected double evaluate(double d) {
if (d==0) {
return 1;
}
return (d>0) ? calcOdd(d) : -calcOdd(-d);
}
private static long calcOdd(double d) {
double dpm1 = d+1;
long x = ((long) dpm1) & PARITY_MASK;
return ( Double.compare(x, dpm1) == 0 ) ? x-1 : x+1;
}
}

View File

@ -0,0 +1,115 @@
/* ====================================================================
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.apache.poi.ss.formula.eval.BoolEval;
import org.apache.poi.ss.formula.eval.ErrorEval;
import org.apache.poi.ss.formula.eval.EvaluationException;
import org.apache.poi.ss.formula.eval.NumberEval;
import org.apache.poi.ss.formula.eval.ValueEval;
public class Poisson {
private static final double DEFAULT_RETURN_RESULT =1;
/** All long-representable factorials */
private static final long[] FACTORIALS = {
1L, 1L, 2L,
6L, 24L, 120L,
720L, 5040L, 40320L,
362880L, 3628800L, 39916800L,
479001600L, 6227020800L, 87178291200L,
1307674368000L, 20922789888000L, 355687428096000L,
6402373705728000L, 121645100408832000L, 2432902008176640000L };
/**
* This checks is x = 0 and the mean = 0.
* Excel currently returns the value 1 where as the
* maths common implementation will error.
* @param x The number.
* @param mean The mean.
* @return If a default value should be returned.
*/
private static boolean isDefaultResult(double x, double mean) {
return x == 0 && mean == 0;
}
private static void checkArgument(double aDouble) throws EvaluationException {
NumericFunction.checkValue(aDouble);
// make sure that the number is positive
if (aDouble < 0) {
throw new EvaluationException(ErrorEval.NUM_ERROR);
}
}
private static double probability(int k, double lambda) {
return Math.pow(lambda, k) * Math.exp(-lambda) / factorial(k);
}
private static double cumulativeProbability(int x, double lambda) {
double result = 0;
for(int k = 0; k <= x; k++){
result += probability(k, lambda);
}
return result;
}
private static long factorial(final int n) {
if (n < 0 || n > 20) {
throw new IllegalArgumentException("Valid argument should be in the range [0..20]");
}
return FACTORIALS[n];
}
public static ValueEval evaluate(ValueEval[] args, int srcRowIndex, int srcColumnIndex) {
if (args.length != 3) {
return ErrorEval.VALUE_INVALID;
}
ValueEval arg0 = args[0];
ValueEval arg1 = args[1];
ValueEval arg2 = args[2];
try {
// arguments/result for this function
double x = NumericFunction.singleOperandEvaluate(arg0, srcRowIndex, srcColumnIndex);
double mean = NumericFunction.singleOperandEvaluate(arg1, srcRowIndex, srcColumnIndex);
// check for default result : excel implementation for 0,0
// is different to Math Common.
if (isDefaultResult(x,mean)) {
return new NumberEval(DEFAULT_RETURN_RESULT);
}
// check the arguments : as per excel function def
checkArgument(x);
checkArgument(mean);
// truncate x : as per excel function def
boolean cumulative = ((BoolEval)arg2).getBooleanValue();
double result = cumulative ? cumulativeProbability((int) x, mean) : probability((int) x, mean);
// check the result
NumericFunction.checkValue(result);
return new NumberEval(result);
} catch (EvaluationException e) {
return e.getErrorEval();
}
}
}

View File

@ -26,18 +26,19 @@ import org.apache.poi.ss.formula.eval.ValueEval;
/**
* Implementation for the Excel function ROW
*/
public final class RowFunc implements Function0Arg, Function1Arg {
public ValueEval evaluate(int srcRowIndex, int srcColumnIndex) {
return new NumberEval(srcRowIndex+1.);
public final class RowFunc {
public static ValueEval evaluate(ValueEval[] args, int srcRowIndex, int srcColumnIndex) {
if (args.length > 1) {
return ErrorEval.VALUE_INVALID;
}
public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0) {
int rnum;
if (arg0 instanceof AreaEval) {
rnum = ((AreaEval) arg0).getFirstRow();
} else if (arg0 instanceof RefEval) {
rnum = ((RefEval) arg0).getRow();
int rnum;
if (args.length == 0) {
rnum = srcRowIndex;
} else if (args[0] instanceof AreaEval) {
rnum = ((AreaEval) args[0]).getFirstRow();
} else if (args[0] instanceof RefEval) {
rnum = ((RefEval) args[0]).getRow();
} else {
// anything else is not valid argument
return ErrorEval.VALUE_INVALID;
@ -45,13 +46,4 @@ public final class RowFunc implements Function0Arg, Function1Arg {
return new NumberEval(rnum + 1.);
}
public ValueEval evaluate(ValueEval[] args, int srcRowIndex, int srcColumnIndex) {
switch (args.length) {
case 1:
return evaluate(srcRowIndex, srcColumnIndex, args[0]);
case 0:
return new NumberEval(srcRowIndex+1.);
}
return ErrorEval.VALUE_INVALID;
}
}

View File

@ -19,6 +19,7 @@ package org.apache.poi.ss.formula.functions;
import java.util.Calendar;
import org.apache.poi.ss.formula.eval.ErrorEval;
import org.apache.poi.ss.formula.eval.NumberEval;
import org.apache.poi.ss.formula.eval.ValueEval;
import org.apache.poi.ss.usermodel.DateUtil;
@ -27,8 +28,11 @@ import org.apache.poi.util.LocaleUtil;
/**
* Implementation of Excel TODAY() Function<br>
*/
public final class Today extends Fixed0ArgFunction {
public ValueEval evaluate(int srcRowIndex, int srcColumnIndex) {
public final class Today {
public static ValueEval evaluate(ValueEval[] args, int srcRowIndex, int srcColumnIndex) {
if (args.length != 0) {
return ErrorEval.VALUE_INVALID;
}
Calendar now = LocaleUtil.getLocaleCalendar();
now.clear(Calendar.HOUR);
now.set(Calendar.HOUR_OF_DAY,0);

View File

@ -24,14 +24,12 @@ import org.junit.jupiter.api.Test;
/**
* Tests for ROW(), ROWS(), COLUMN(), COLUMNS()
*
* @author Josh Micich
*/
final class TestRowCol {
@Test
void testCol() {
Function target = new Column();
Function target = Column::evaluate;
{
ValueEval[] args = { EvalFactory.createRefEval("C5"), };
double actual = NumericFunctionInvoker.invoke(target, args);
@ -46,7 +44,7 @@ final class TestRowCol {
@Test
void testRow() {
Function target = new RowFunc();
Function target = RowFunc::evaluate;
{
ValueEval[] args = { EvalFactory.createRefEval("C5"), };
double actual = NumericFunctionInvoker.invoke(target, args);