From a5aa573c1104f084251c0ef63f4158bf17039799 Mon Sep 17 00:00:00 2001 From: hsgamer Date: Tue, 10 Sep 2024 00:20:37 +0700 Subject: [PATCH 1/6] Add STR_LENGTH and STR_MATCHES --- .../config/ExpressionConfiguration.java | 2 + .../string/StringLengthFunction.java | 38 +++++++++++++++++ .../string/StringMatchesFunction.java | 41 +++++++++++++++++++ .../functions/string/StringFunctionsTest.java | 33 +++++++++++++++ 4 files changed, 114 insertions(+) create mode 100644 src/main/java/com/ezylang/evalex/functions/string/StringLengthFunction.java create mode 100644 src/main/java/com/ezylang/evalex/functions/string/StringMatchesFunction.java diff --git a/src/main/java/com/ezylang/evalex/config/ExpressionConfiguration.java b/src/main/java/com/ezylang/evalex/config/ExpressionConfiguration.java index d8ca3da3..08ddccb1 100644 --- a/src/main/java/com/ezylang/evalex/config/ExpressionConfiguration.java +++ b/src/main/java/com/ezylang/evalex/config/ExpressionConfiguration.java @@ -187,6 +187,8 @@ public class ExpressionConfiguration { Map.entry("STR_STARTS_WITH", new StringStartsWithFunction()), Map.entry("STR_TRIM", new StringTrimFunction()), Map.entry("STR_UPPER", new StringUpperFunction()), + Map.entry("STR_LENGTH", new StringLengthFunction()), + Map.entry("STR_MATCHES", new StringMatchesFunction()), // date time functions Map.entry("DT_DATE_NEW", new DateTimeNewFunction()), Map.entry("DT_DATE_PARSE", new DateTimeParseFunction()), diff --git a/src/main/java/com/ezylang/evalex/functions/string/StringLengthFunction.java b/src/main/java/com/ezylang/evalex/functions/string/StringLengthFunction.java new file mode 100644 index 00000000..6fafdca1 --- /dev/null +++ b/src/main/java/com/ezylang/evalex/functions/string/StringLengthFunction.java @@ -0,0 +1,38 @@ +/* + 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.string; + +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; + +/** + * Returns the length of the string. + * + * @author HSGamer + */ +@FunctionParameter(name = "string") +public class StringLengthFunction extends AbstractFunction { + @Override + public EvaluationValue evaluate( + Expression expression, Token functionToken, EvaluationValue... parameterValues) + throws EvaluationException { + return expression.convertValue(parameterValues[0].getStringValue().length()); + } +} diff --git a/src/main/java/com/ezylang/evalex/functions/string/StringMatchesFunction.java b/src/main/java/com/ezylang/evalex/functions/string/StringMatchesFunction.java new file mode 100644 index 00000000..eacacc07 --- /dev/null +++ b/src/main/java/com/ezylang/evalex/functions/string/StringMatchesFunction.java @@ -0,0 +1,41 @@ +/* + 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.string; + +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; + +/** + * Returns true if the string matches the pattern. + * + * @author HSGamer + */ +@FunctionParameter(name = "string") +@FunctionParameter(name = "pattern") +public class StringMatchesFunction extends AbstractFunction { + @Override + public EvaluationValue evaluate( + Expression expression, Token functionToken, EvaluationValue... parameterValues) + throws EvaluationException { + String string = parameterValues[0].getStringValue(); + String pattern = parameterValues[1].getStringValue(); + return expression.convertValue(string.matches(pattern)); + } +} diff --git a/src/test/java/com/ezylang/evalex/functions/string/StringFunctionsTest.java b/src/test/java/com/ezylang/evalex/functions/string/StringFunctionsTest.java index 7da46dd0..cdb2a424 100644 --- a/src/test/java/com/ezylang/evalex/functions/string/StringFunctionsTest.java +++ b/src/test/java/com/ezylang/evalex/functions/string/StringFunctionsTest.java @@ -165,4 +165,37 @@ void testTrimString(String expression, String expectedResult) throws EvaluationException, ParseException { assertExpressionHasExpectedResult(expression, expectedResult); } + + @ParameterizedTest + @CsvSource( + delimiter = ':', + value = { + "STR_LENGTH(\"\") : 0", + "STR_LENGTH(\"a\") : 1", + "STR_LENGTH(\"AbCdEf\") : 6", + "STR_LENGTH(\"A1b3C4/?\") : 8", + "STR_LENGTH(\"äöüß\") : 4" + }) + void testLength(String expression, String expectedResult) + throws EvaluationException, ParseException { + assertExpressionHasExpectedResult(expression, expectedResult); + } + + @ParameterizedTest + @CsvSource( + delimiter = ':', + value = { + "STR_MATCHES(\"\", \"\") : true", + "STR_MATCHES(\"a\", \"a\") : true", + "STR_MATCHES(\"Hello World\", \"Hello\") : false", + "STR_MATCHES(\"Hello World\", \"hello\") : false", + "STR_MATCHES(\"Hello world\", \"text\") : false", + "STR_MATCHES(\"\", \"text\") : false", + "STR_MATCHES(\"Hello World\", \".*World\") : true", + "STR_MATCHES(\"Hello World\", \".*world\") : false", + }) + void testMatches(String expression, String expectedResult) + throws EvaluationException, ParseException { + assertExpressionHasExpectedResult(expression, expectedResult); + } } From 298718b43d4afe472124f2f7f8c1fe95de2e5dfa Mon Sep 17 00:00:00 2001 From: hsgamer Date: Tue, 10 Sep 2024 00:23:19 +0700 Subject: [PATCH 2/6] Add docs for STR_LENGTH and STR_MATCHES --- docs/references/functions.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/references/functions.md b/docs/references/functions.md index d79f8bc5..71a470cc 100644 --- a/docs/references/functions.md +++ b/docs/references/functions.md @@ -42,6 +42,8 @@ Available through the _ExpressionConfiguration.StandardFunctionsDictionary_ cons | STR_STARTS_WITH(string, substring) | Returns true if the string starts with the substring (case-sensitive) | | STR_TRIM(string) | Returns the given string with all leading and trailing space removed. | | STR_UPPER(value) | Converts the given value to upper case | +| STR_LENGTH(string) | Returns the length of the string | +| STR_MATCHES(string, pattern) | Returns true if the string matches the RegEx pattern | ### Trigonometric Functions From e719fb65d2b800b3244d24a8421387dbf8695893 Mon Sep 17 00:00:00 2001 From: hsgamer Date: Tue, 10 Sep 2024 00:44:27 +0700 Subject: [PATCH 3/6] add STR_SUBSTRING --- docs/references/functions.md | 23 +++---- .../config/ExpressionConfiguration.java | 1 + .../string/StringSubstringFunction.java | 64 +++++++++++++++++++ .../functions/string/StringFunctionsTest.java | 28 ++++++++ 4 files changed, 105 insertions(+), 11 deletions(-) create mode 100644 src/main/java/com/ezylang/evalex/functions/string/StringSubstringFunction.java diff --git a/docs/references/functions.md b/docs/references/functions.md index 71a470cc..7513accf 100644 --- a/docs/references/functions.md +++ b/docs/references/functions.md @@ -33,17 +33,18 @@ Available through the _ExpressionConfiguration.StandardFunctionsDictionary_ cons ### String Functions -| Name | Description | -|-------------------------------------|---------------------------------------------------------------------------------------------------------| -| STR_CONTAINS(string, substring) | Returns true if the string contains the substring (case-insensitive) | -| STR_ENDS_WITH(string, substring) | Returns true if the string ends with the substring (case-sensitive) | -| STR_FORMAT(format [,argument, ...]) | Returns a formatted string using the specified format string and arguments, using the configured locale | -| STR_LOWER(value) | Converts the given value to lower case | -| STR_STARTS_WITH(string, substring) | Returns true if the string starts with the substring (case-sensitive) | -| STR_TRIM(string) | Returns the given string with all leading and trailing space removed. | -| STR_UPPER(value) | Converts the given value to upper case | -| STR_LENGTH(string) | Returns the length of the string | -| STR_MATCHES(string, pattern) | Returns true if the string matches the RegEx pattern | +| Name | Description | +|-------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------| +| STR_CONTAINS(string, substring) | Returns true if the string contains the substring (case-insensitive) | +| STR_ENDS_WITH(string, substring) | Returns true if the string ends with the substring (case-sensitive) | +| STR_FORMAT(format [,argument, ...]) | Returns a formatted string using the specified format string and arguments, using the configured locale | +| STR_LOWER(value) | Converts the given value to lower case | +| STR_STARTS_WITH(string, substring) | Returns true if the string starts with the substring (case-sensitive) | +| STR_TRIM(string) | Returns the given string with all leading and trailing space removed. | +| STR_UPPER(value) | Converts the given value to upper case | +| STR_LENGTH(string) | Returns the length of the string | +| STR_MATCHES(string, pattern) | Returns true if the string matches the RegEx pattern | +| STR_SUBSTRING(string, start[, end]) | Returns a substring of the given string, starting at the _start_ index and ending at the _end_ index (the end of the string if not specified) | ### Trigonometric Functions diff --git a/src/main/java/com/ezylang/evalex/config/ExpressionConfiguration.java b/src/main/java/com/ezylang/evalex/config/ExpressionConfiguration.java index 08ddccb1..a2ce2efc 100644 --- a/src/main/java/com/ezylang/evalex/config/ExpressionConfiguration.java +++ b/src/main/java/com/ezylang/evalex/config/ExpressionConfiguration.java @@ -189,6 +189,7 @@ public class ExpressionConfiguration { Map.entry("STR_UPPER", new StringUpperFunction()), Map.entry("STR_LENGTH", new StringLengthFunction()), Map.entry("STR_MATCHES", new StringMatchesFunction()), + Map.entry("STR_SUBSTRING", new StringSubstringFunction()), // date time functions Map.entry("DT_DATE_NEW", new DateTimeNewFunction()), Map.entry("DT_DATE_PARSE", new DateTimeParseFunction()), diff --git a/src/main/java/com/ezylang/evalex/functions/string/StringSubstringFunction.java b/src/main/java/com/ezylang/evalex/functions/string/StringSubstringFunction.java new file mode 100644 index 00000000..313acf02 --- /dev/null +++ b/src/main/java/com/ezylang/evalex/functions/string/StringSubstringFunction.java @@ -0,0 +1,64 @@ +/* + 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.string; + +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; + +/** + * Returns a substring of a string. + * + * @author HSGamer + */ +@FunctionParameter(name = "string") +@FunctionParameter(name = "start", nonNegative = true) +@FunctionParameter(name = "end", isVarArg = true, nonNegative = true) +public class StringSubstringFunction extends AbstractFunction { + @Override + public void validatePreEvaluation(Token token, EvaluationValue... parameterValues) + throws EvaluationException { + super.validatePreEvaluation(token, parameterValues); + if (parameterValues.length > 2) { + if (parameterValues[2].getNumberValue().intValue() + < parameterValues[1].getNumberValue().intValue()) { + throw new EvaluationException( + token, "End index must be greater than or equal to start index"); + } + } + } + + @Override + public EvaluationValue evaluate( + Expression expression, Token functionToken, EvaluationValue... parameterValues) + throws EvaluationException { + String string = parameterValues[0].getStringValue(); + int start = parameterValues[1].getNumberValue().intValue(); + String result; + if (parameterValues.length > 2) { + int end = parameterValues[2].getNumberValue().intValue(); + int length = string.length(); + int finalEnd = Math.min(end, length); + result = string.substring(start, finalEnd); + } else { + result = string.substring(start); + } + return expression.convertValue(result); + } +} diff --git a/src/test/java/com/ezylang/evalex/functions/string/StringFunctionsTest.java b/src/test/java/com/ezylang/evalex/functions/string/StringFunctionsTest.java index cdb2a424..65d94888 100644 --- a/src/test/java/com/ezylang/evalex/functions/string/StringFunctionsTest.java +++ b/src/test/java/com/ezylang/evalex/functions/string/StringFunctionsTest.java @@ -15,10 +15,13 @@ */ package com.ezylang.evalex.functions.string; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + import com.ezylang.evalex.BaseEvaluationTest; import com.ezylang.evalex.EvaluationException; import com.ezylang.evalex.config.TestConfigurationProvider; import com.ezylang.evalex.parser.ParseException; +import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; @@ -198,4 +201,29 @@ void testMatches(String expression, String expectedResult) throws EvaluationException, ParseException { assertExpressionHasExpectedResult(expression, expectedResult); } + + @ParameterizedTest + @CsvSource( + delimiter = ':', + value = { + "STR_SUBSTRING(\"\", 0, 0) : ''", + "STR_SUBSTRING(\"Hello World\", 0) : Hello World", + "STR_SUBSTRING(\"Hello World\", 6) : World", + "STR_SUBSTRING(\"Hello World\", 0, 5) : Hello", + "STR_SUBSTRING(\"Hello World\", 6, 11) : World", + "STR_SUBSTRING(\"Hello World\", 6, 6) : ''", + "STR_SUBSTRING(\"Hello World\", 0, 11) : Hello World", + "STR_SUBSTRING(\"Hello World\", 0, 12) : Hello World", + }) + void testSubstring(String expression, String expectedResult) + throws EvaluationException, ParseException { + assertExpressionHasExpectedResult(expression, expectedResult); + } + + @Test + void testSubstringEndLessThanStart() { + assertThatThrownBy(() -> evaluate("STR_SUBSTRING(\"Hello World\", 6, 5)")) + .isInstanceOf(EvaluationException.class) + .hasMessage("End index must be greater than or equal to start index"); + } } From 51e12cc5cbb824126cba1a47867f907bfd187bba Mon Sep 17 00:00:00 2001 From: hsgamer Date: Mon, 16 Sep 2024 22:01:02 +0700 Subject: [PATCH 4/6] alphabet order in docs --- docs/references/functions.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/references/functions.md b/docs/references/functions.md index 7513accf..95d93ec8 100644 --- a/docs/references/functions.md +++ b/docs/references/functions.md @@ -38,13 +38,13 @@ Available through the _ExpressionConfiguration.StandardFunctionsDictionary_ cons | STR_CONTAINS(string, substring) | Returns true if the string contains the substring (case-insensitive) | | STR_ENDS_WITH(string, substring) | Returns true if the string ends with the substring (case-sensitive) | | STR_FORMAT(format [,argument, ...]) | Returns a formatted string using the specified format string and arguments, using the configured locale | +| STR_LENGTH(string) | Returns the length of the string | | STR_LOWER(value) | Converts the given value to lower case | +| STR_MATCHES(string, pattern) | Returns true if the string matches the RegEx pattern | | STR_STARTS_WITH(string, substring) | Returns true if the string starts with the substring (case-sensitive) | +| STR_SUBSTRING(string, start[, end]) | Returns a substring of the given string, starting at the _start_ index and ending at the _end_ index (the end of the string if not specified) | | STR_TRIM(string) | Returns the given string with all leading and trailing space removed. | | STR_UPPER(value) | Converts the given value to upper case | -| STR_LENGTH(string) | Returns the length of the string | -| STR_MATCHES(string, pattern) | Returns true if the string matches the RegEx pattern | -| STR_SUBSTRING(string, start[, end]) | Returns a substring of the given string, starting at the _start_ index and ending at the _end_ index (the end of the string if not specified) | ### Trigonometric Functions From 78e99239973f8890b714ecd4568025c2118b2e31 Mon Sep 17 00:00:00 2001 From: hsgamer Date: Mon, 16 Sep 2024 22:02:38 +0700 Subject: [PATCH 5/6] alphabet order in config --- .../com/ezylang/evalex/config/ExpressionConfiguration.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/ezylang/evalex/config/ExpressionConfiguration.java b/src/main/java/com/ezylang/evalex/config/ExpressionConfiguration.java index a2ce2efc..898f2341 100644 --- a/src/main/java/com/ezylang/evalex/config/ExpressionConfiguration.java +++ b/src/main/java/com/ezylang/evalex/config/ExpressionConfiguration.java @@ -183,13 +183,13 @@ public class ExpressionConfiguration { Map.entry("STR_CONTAINS", new StringContains()), Map.entry("STR_ENDS_WITH", new StringEndsWithFunction()), Map.entry("STR_FORMAT", new StringFormatFunction()), + Map.entry("STR_LENGTH", new StringLengthFunction()), Map.entry("STR_LOWER", new StringLowerFunction()), + Map.entry("STR_MATCHES", new StringMatchesFunction()), Map.entry("STR_STARTS_WITH", new StringStartsWithFunction()), + Map.entry("STR_SUBSTRING", new StringSubstringFunction()), Map.entry("STR_TRIM", new StringTrimFunction()), Map.entry("STR_UPPER", new StringUpperFunction()), - Map.entry("STR_LENGTH", new StringLengthFunction()), - Map.entry("STR_MATCHES", new StringMatchesFunction()), - Map.entry("STR_SUBSTRING", new StringSubstringFunction()), // date time functions Map.entry("DT_DATE_NEW", new DateTimeNewFunction()), Map.entry("DT_DATE_PARSE", new DateTimeParseFunction()), From 5fe1ee61291f4c1ac185b94e20697ad345c61a71 Mon Sep 17 00:00:00 2001 From: hsgamer Date: Mon, 16 Sep 2024 22:03:39 +0700 Subject: [PATCH 6/6] merge if statement in StringSubstringFunction --- .../functions/string/StringSubstringFunction.java | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/ezylang/evalex/functions/string/StringSubstringFunction.java b/src/main/java/com/ezylang/evalex/functions/string/StringSubstringFunction.java index 313acf02..33fccf8c 100644 --- a/src/main/java/com/ezylang/evalex/functions/string/StringSubstringFunction.java +++ b/src/main/java/com/ezylang/evalex/functions/string/StringSubstringFunction.java @@ -35,12 +35,11 @@ public class StringSubstringFunction extends AbstractFunction { public void validatePreEvaluation(Token token, EvaluationValue... parameterValues) throws EvaluationException { super.validatePreEvaluation(token, parameterValues); - if (parameterValues.length > 2) { - if (parameterValues[2].getNumberValue().intValue() - < parameterValues[1].getNumberValue().intValue()) { - throw new EvaluationException( - token, "End index must be greater than or equal to start index"); - } + if (parameterValues.length > 2 + && parameterValues[2].getNumberValue().intValue() + < parameterValues[1].getNumberValue().intValue()) { + throw new EvaluationException( + token, "End index must be greater than or equal to start index"); } }