From 3a5a9572440e9768debac2f9b060ced725a80b24 Mon Sep 17 00:00:00 2001 From: Oswaldo Baptista Vicente Junior <45291656+oswaldobapvicjr@users.noreply.github.com> Date: Sun, 5 May 2024 05:07:05 -0300 Subject: [PATCH] Adds new function SWITCH (#469) --- docs/references/functions.md | 37 +++---- .../config/ExpressionConfiguration.java | 3 +- .../functions/basic/SwitchFunction.java | 101 ++++++++++++++++++ .../functions/basic/BasicFunctionsTest.java | 25 +++++ 4 files changed, 147 insertions(+), 19 deletions(-) create mode 100644 src/main/java/com/ezylang/evalex/functions/basic/SwitchFunction.java diff --git a/docs/references/functions.md b/docs/references/functions.md index f39f2d7..8cee7e6 100644 --- a/docs/references/functions.md +++ b/docs/references/functions.md @@ -11,24 +11,25 @@ Available through the _ExpressionConfiguration.StandardFunctionsDictionary_ cons ### Basic Functions -| Name | Description | -|--------------------------------------------|----------------------------------------------------------------------------------------------------------------------------| -| ABS(value) | Absolute (non-negative) value | -| AVERAGE(value, ...) | Returns the average (arithmetic mean) of all parameters. | -| 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 | -| LOG(value) | The natural logarithm (base e) of a value | -| LOG10(value) | The base 10 logarithm of a value | -| MAX(value, ...) | Returns the maximum value of all parameters. If a parameter is of type _ARRAY_, the maximum of all elements is calculated. | -| MIN(value, ...) | Returns the minimum value of all parameters. If a parameter is of type _ARRAY_, the minimum of all elements is calculated. | -| NOT(value) | Boolean negation, implemented as a function (for compatibility) | -| RANDOM() | Produces a random value between 0 and 1 | -| ROUND(value, scale) | Rounds the given value to the specified scale, using the current rounding mode | -| SQRT(value) | Square root function | -| SUM(value, ...) | Returns the sum of all parameters. If a parameter is of type _ARRAY_, the sum of all elements is calculated. | +| Name | Description | +|---------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------| +| ABS(value) | Absolute (non-negative) value | +| AVERAGE(value, ...) | Returns the average (arithmetic mean) of all parameters. | +| 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 | +| LOG(value) | The natural logarithm (base e) of a value | +| LOG10(value) | The base 10 logarithm of a value | +| MAX(value, ...) | Returns the maximum value of all parameters. If a parameter is of type _ARRAY_, the maximum of all elements is calculated. | +| MIN(value, ...) | Returns the minimum value of all parameters. If a parameter is of type _ARRAY_, the minimum of all elements is calculated. | +| NOT(value) | Boolean negation, implemented as a function (for compatibility) | +| RANDOM() | Produces a random value between 0 and 1 | +| ROUND(value, scale) | Rounds the given value to the specified scale, using the current rounding mode | +| SQRT(value) | Square root function | +| SUM(value, ...) | Returns the sum of all parameters. If a parameter is of type _ARRAY_, the sum of all elements is calculated. | +| SWITCH(expression, value1, result1, [value2-N, result2-N ...], [default]) | Returns the _result_ correponding to the first matching _value_ in the specified _expression_ or an optional _default_ value if no match found. | ### String Functions diff --git a/src/main/java/com/ezylang/evalex/config/ExpressionConfiguration.java b/src/main/java/com/ezylang/evalex/config/ExpressionConfiguration.java index d19522e..1f3008e 100644 --- a/src/main/java/com/ezylang/evalex/config/ExpressionConfiguration.java +++ b/src/main/java/com/ezylang/evalex/config/ExpressionConfiguration.java @@ -142,8 +142,9 @@ public class ExpressionConfiguration { Map.entry("NOT", new NotFunction()), Map.entry("RANDOM", new RandomFunction()), Map.entry("ROUND", new RoundFunction()), - Map.entry("SUM", new SumFunction()), Map.entry("SQRT", new SqrtFunction()), + Map.entry("SUM", new SumFunction()), + Map.entry("SWITCH", new SwitchFunction()), // trigonometric Map.entry("ACOS", new AcosFunction()), Map.entry("ACOSH", new AcosHFunction()), diff --git a/src/main/java/com/ezylang/evalex/functions/basic/SwitchFunction.java b/src/main/java/com/ezylang/evalex/functions/basic/SwitchFunction.java new file mode 100644 index 0000000..686f157 --- /dev/null +++ b/src/main/java/com/ezylang/evalex/functions/basic/SwitchFunction.java @@ -0,0 +1,101 @@ +/* + Copyright 2012-2024 Udo Klimaschewski + + Licensed 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 com.ezylang.evalex.functions.basic; + +import com.ezylang.evalex.EvaluationException; +import com.ezylang.evalex.Expression; +import com.ezylang.evalex.data.EvaluationValue; +import com.ezylang.evalex.functions.AbstractFunction; +import com.ezylang.evalex.functions.FunctionParameter; +import com.ezylang.evalex.parser.Token; + +/** + * A function that evaluates one value (or expression) against a list of values, and returns the + * result corresponding to the first matching value. If there is no match, an optional default value + * may be returned. + * + *
Syntax: + * + *
+ * + * {@code SWITCH(expression, value1, result1, [value, result, ...], [default])} + * + *+ * + *
Examples: + * + *
1. The following function will return either "Sunday", "Monday", or "Tuesday", depending on + * the result of the variable {@code weekday}. Since no default value was specified, the function + * will return a null value if there is no match: + * + *
+ * + * {@code SWITCH(weekday, 1, "Sunday", 2, "Monday", 3, "Tuesday")} + * + *+ * + *
2. The following function will return either "Sunday", "Monday", "Tuesday", or "No match", + * depending on the result of the variable {@code weekday}: + * + *
+ * + * {@code SWITCH(weekday, 1, "Sunday", 2, "Monday", 3, "Tuesday", "No match")} + * + *+ * + * @author oswaldo.bapvic.jr + */ +@FunctionParameter(name = "expression") +@FunctionParameter(name = "value1") +@FunctionParameter(name = "result1", isLazy = true) +@FunctionParameter(name = "additionalValues", isLazy = true, isVarArg = true) +public class SwitchFunction extends AbstractFunction { + @Override + public EvaluationValue evaluate( + Expression expression, Token functionToken, EvaluationValue... parameterValues) + throws EvaluationException { + + EvaluationValue result = EvaluationValue.nullValue(); + + // First get the first parameter + EvaluationValue value = parameterValues[0]; + + // Iterate through the parameters to parse the pairs of value-result and the default result if + // present. + int index = 1; + while (index < parameterValues.length) { + int next = index + 1; + if (next < parameterValues.length) { + if (value.equals(evaluateParameter(expression, parameterValues[index]))) { + result = parameterValues[next]; + break; + } + index += 2; + } else { + // The default result + result = parameterValues[index++]; + } + } + return evaluateParameter(expression, result); + } + + private EvaluationValue evaluateParameter(Expression expression, EvaluationValue parameter) + throws EvaluationException { + return parameter.isExpressionNode() + ? expression.evaluateSubtree(parameter.getExpressionNode()) + : parameter; + } +} 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 9a55cc4..63f4c7b 100644 --- a/src/test/java/com/ezylang/evalex/functions/basic/BasicFunctionsTest.java +++ b/src/test/java/com/ezylang/evalex/functions/basic/BasicFunctionsTest.java @@ -65,6 +65,31 @@ class BasicFunctionsTest extends BaseEvaluationTest { assertExpressionHasExpectedResult(expression, expectedResult); } + @ParameterizedTest + @CsvSource( + delimiter = ':', + value = { + "SWITCH(1, 1, \"one\", 2, \"two\") : one", + "SWITCH(1+1, 1, \"one\", 2, \"two\") : two", + "SWITCH(9 , 1, \"one\", 2, \"two\") : ", + "SWITCH(9-8, 1, \"one\", 2, \"two\", \"n/a\") : one", + "SWITCH(2, 1, \"one\", 2, \"two\", \"n/a\") : two", + "SWITCH(\"a\", 1, \"one\", 2, \"two\", \"n/a\") : n/a", + "SWITCH(true, true, \"Y\", \"N\") : Y", + "SWITCH(false, true, \"Y\", \"N\") : N", + "SWITCH(0, true, \"Y\", \"N\") : N", + "SWITCH(null, 5, 50, 90) : 90", + "SWITCH(null, null, 50, 90) : 50", + // The following divisions by zero are not supposed to occur due to lazy parameters + "SWITCH(\"BR\", \"BR\", \"result\"+123, \"DE\", 3/0, 2/0) : result123", + "SWITCH(\"DE\", \"BR\", 3/0, \"DE\", \"result\"+\"ABC\", 2/0) : resultABC", + "SWITCH(1000+900+10, 1909+1, \"OK\", 10/0, \"impossible\") : OK" + }) + void testSwitch(String expression, String expectedResult) + throws EvaluationException, ParseException { + assertExpressionHasExpectedResult(expression, expectedResult); + } + @ParameterizedTest @CsvSource( delimiter = ':', -- 2.51.2