diff --git a/docs/references/functions.md b/docs/references/functions.md index 8289e7d3..00f87729 100644 --- a/docs/references/functions.md +++ b/docs/references/functions.md @@ -15,6 +15,7 @@ Available through the _ExpressionConfiguration.StandardFunctionsDictionary_ cons |--------------------------------------------|-------------------------------------------------------------------------------------------------------------------------| | ABS(value) | Absolute (non-negative) value | | CEILING(value) | Rounds the given value an integer using the rounding mode CEILING | +| COALESCE(value, ...) | Returns the first non-null parameter, or NULL if all parameters are null | | FACT(base) | Calculates the factorial of a base value | | FLOOR(value) | Rounds the given value an integer using the rounding mode FLOOR | | IF(condition, resultIfTrue, resultIfFalse) | Conditional evaluation function. If _condition_ is true, the _resultIfTrue_ is returned, else the _resultIfFalse_ value | diff --git a/src/main/java/com/ezylang/evalex/config/ExpressionConfiguration.java b/src/main/java/com/ezylang/evalex/config/ExpressionConfiguration.java index 53b5549c..f2d97e9d 100644 --- a/src/main/java/com/ezylang/evalex/config/ExpressionConfiguration.java +++ b/src/main/java/com/ezylang/evalex/config/ExpressionConfiguration.java @@ -113,6 +113,7 @@ public class ExpressionConfiguration { // basic functions Map.entry("ABS", new AbsFunction()), Map.entry("CEILING", new CeilingFunction()), + Map.entry("COALESCE", new CoalesceFunction()), Map.entry("FACT", new FactFunction()), Map.entry("FLOOR", new FloorFunction()), Map.entry("IF", new IfFunction()), diff --git a/src/main/java/com/ezylang/evalex/functions/basic/CoalesceFunction.java b/src/main/java/com/ezylang/evalex/functions/basic/CoalesceFunction.java index 8b13d4fa..86ee2d08 100644 --- a/src/main/java/com/ezylang/evalex/functions/basic/CoalesceFunction.java +++ b/src/main/java/com/ezylang/evalex/functions/basic/CoalesceFunction.java @@ -20,20 +20,21 @@ import com.ezylang.evalex.functions.AbstractFunction; import com.ezylang.evalex.functions.FunctionParameter; import com.ezylang.evalex.parser.Token; -import java.math.BigDecimal; -/** Returns the minimum value of all parameters. */ +/** + * Returns the first non-null parameter, or {@link EvaluationValue#nullValue()} if all parameters + * are null. + */ @FunctionParameter(name = "value", isVarArg = true) -public class MinFunction extends AbstractFunction { +public class CoalesceFunction extends AbstractFunction { @Override public EvaluationValue evaluate( Expression expression, Token functionToken, EvaluationValue... parameterValues) { - BigDecimal min = null; for (EvaluationValue parameter : parameterValues) { - if (min == null || parameter.getNumberValue().compareTo(min) < 0) { - min = parameter.getNumberValue(); + if (!parameter.isNullValue()) { + return parameter; } } - return expression.convertValue(min); + return EvaluationValue.nullValue(); } } diff --git a/src/test/java/com/ezylang/evalex/functions/basic/BasicFunctionsTest.java b/src/test/java/com/ezylang/evalex/functions/basic/BasicFunctionsTest.java index 683a132e..f9ae420a 100644 --- a/src/test/java/com/ezylang/evalex/functions/basic/BasicFunctionsTest.java +++ b/src/test/java/com/ezylang/evalex/functions/basic/BasicFunctionsTest.java @@ -22,6 +22,7 @@ import com.ezylang.evalex.EvaluationException; import com.ezylang.evalex.Expression; import com.ezylang.evalex.config.ExpressionConfiguration; +import com.ezylang.evalex.config.TestConfigurationProvider; import com.ezylang.evalex.data.EvaluationValue; import com.ezylang.evalex.parser.ParseException; import com.ezylang.evalex.parser.Token; @@ -320,4 +321,31 @@ void testLog10Zero() { .isInstanceOf(EvaluationException.class) .hasMessage("Parameter must not be zero"); } + + @ParameterizedTest + @CsvSource( + delimiter = ':', + value = { + "COALESCE(null,1) : 1", + "COALESCE(null,\"abc\",1,null,2,3) : abc", + "COALESCE(1,null) : 1", + "COALESCE(null,null,null,null,1.1) : 1.1", + "COALESCE(null,null,null) :", + "COALESCE(null) :" + }) + void testCoalesce(String expression, String expectedResult) + throws EvaluationException, ParseException { + EvaluationValue evaluationValue = + new Expression( + expression, + TestConfigurationProvider.StandardConfigurationWithAdditionalTestOperators) + .evaluate(); + + if (expectedResult == null) { + assertThat(evaluationValue.isNullValue()).isTrue(); + } else { + assertThat(evaluationValue.isNullValue()).isFalse(); + assertThat(evaluationValue.getStringValue()).isEqualTo(expectedResult); + } + } }