This is an automated email from the ASF dual-hosted git repository.

gitgabrio pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/incubator-kie-drools.git


The following commit(s) were added to refs/heads/main by this push:
     new 8dac313955 kie-issues#1448: `matches()` function wrongly behaves 
(#6085)
8dac313955 is described below

commit 8dac313955aa6693bc024cdbb644c58968db7ec0
Author: Yeser Amer <[email protected]>
AuthorDate: Wed Sep 18 09:02:56 2024 +0200

    kie-issues#1448: `matches()` function wrongly behaves (#6085)
    
    * matches and replace using saxon
    
    * saxon usage simplified and modified tests
    
    * clean up code as per standards
    
    * changes as per suggestions
    
    * changes as per suggestions and restored the earlier tests
    
    * changes as per suggestions and restored the earlier tests
    
    * changes as per suggestions
    
    * exceptions handled and changes to util class
    
    * wip
    
    * wip
    
    * wip
    
    * wip
    
    * wip
    
    * CR
    
    * CR
    
    * CR
    
    * CR
    
    * Notice updated
    
    * CR
    
    ---------
    
    Co-authored-by: bncriju <[email protected]>
---
 NOTICE                                             |   4 +
 kie-dmn/kie-dmn-feel/pom.xml                       |  15 ++
 .../feel/runtime/functions/MatchesFunction.java    |  67 ++------
 .../feel/runtime/functions/ReplaceFunction.java    |  21 ++-
 .../java/org/kie/dmn/feel/util/XQueryImplUtil.java |  62 +++++++
 .../kie/dmn/feel/runtime/FEELFunctionsTest.java    |   9 +-
 .../runtime/functions/MatchesFunctionTest.java     | 188 ++++++++++++---------
 .../runtime/functions/ReplaceFunctionTest.java     | 112 +++++++-----
 .../org/kie/dmn/feel/util/XQueryImplUtilTest.java  | 117 +++++++++++++
 9 files changed, 409 insertions(+), 186 deletions(-)

diff --git a/NOTICE b/NOTICE
index 2fe21eeead..43c4b92efb 100644
--- a/NOTICE
+++ b/NOTICE
@@ -14,6 +14,10 @@ This product also includes the following third-party 
components:
   Downloaded from: https://lunrjs.com/
   License: MIT
 
+* Saxon-HE
+  Downloaded from: https://www.saxonica.com/
+  License: Mozilla Public License 2.0
+
 * search-ui
   Downloaded from: https://gitlab.com/antora/antora-lunr-extension
   License: Mozilla Public License 2.0
diff --git a/kie-dmn/kie-dmn-feel/pom.xml b/kie-dmn/kie-dmn-feel/pom.xml
index 51fbef420b..545d181c62 100644
--- a/kie-dmn/kie-dmn-feel/pom.xml
+++ b/kie-dmn/kie-dmn-feel/pom.xml
@@ -38,8 +38,19 @@
     <java.module.name>org.kie.dmn.feel</java.module.name>
     <surefire.forkCount>2</surefire.forkCount>
     <enforcer.skip>true</enforcer.skip>
+    <version.net.sf.saxon.Saxon-HE>12.5</version.net.sf.saxon.Saxon-HE>
   </properties>
 
+  <dependencyManagement>
+    <dependencies>
+      <dependency>
+        <groupId>net.sf.saxon</groupId>
+        <artifactId>Saxon-HE</artifactId>
+        <version>${version.net.sf.saxon.Saxon-HE}</version>
+      </dependency>
+    </dependencies>
+  </dependencyManagement>
+
   <dependencies>
     <!-- Internal dependencies -->
     <dependency>
@@ -52,6 +63,10 @@
     </dependency>
 
     <!-- External dependencies -->
+    <dependency>
+      <groupId>net.sf.saxon</groupId>
+      <artifactId>Saxon-HE</artifactId>
+    </dependency>
     <dependency>
       <groupId>org.antlr</groupId>
       <artifactId>antlr4-runtime</artifactId>
diff --git 
a/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/runtime/functions/MatchesFunction.java
 
b/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/runtime/functions/MatchesFunction.java
index 79ff2a0136..2acb38ad26 100644
--- 
a/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/runtime/functions/MatchesFunction.java
+++ 
b/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/runtime/functions/MatchesFunction.java
@@ -18,15 +18,9 @@
  */
 package org.kie.dmn.feel.runtime.functions;
 
-import java.security.InvalidParameterException;
-import java.util.Set;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-import java.util.regex.PatternSyntaxException;
-import java.util.stream.Collectors;
-
 import org.kie.dmn.api.feel.runtime.events.FEELEvent.Severity;
 import org.kie.dmn.feel.runtime.events.InvalidParametersEvent;
+import org.kie.dmn.feel.util.XQueryImplUtil;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -44,58 +38,23 @@ public class MatchesFunction
     }
 
     public FEELFnResult<Boolean> invoke(@ParameterName("input") String input, 
@ParameterName("pattern") String pattern, @ParameterName("flags") String flags) 
{
-        try {
-            return matchFunctionWithFlags(input,pattern,flags);
-        } catch ( PatternSyntaxException t ) {
-            return FEELFnResult.ofError( new InvalidParametersEvent( 
Severity.ERROR, "pattern", "is invalid and can not be compiled", t ) );
-        } catch (InvalidParameterException t ) {
-            return FEELFnResult.ofError( new InvalidParametersEvent( 
Severity.ERROR, t.getMessage(), "cannot be null", t ) );
-        } catch (IllegalArgumentException t ) {
-            return FEELFnResult.ofError( new InvalidParametersEvent( 
Severity.ERROR, "flags", "contains unknown flags", t ) );
-        } catch (Throwable t) {
-            return FEELFnResult.ofError( new InvalidParametersEvent( 
Severity.ERROR, "pattern", "is invalid and can not be compiled", t ) );
-        }
-    }
-
-    static FEELFnResult<Boolean> matchFunctionWithFlags(String input, String 
pattern, String flags) {
         log.debug("Input:  {} , Pattern: {}, Flags: {}", input, pattern, 
flags);
+
         if ( input == null ) {
-            throw new InvalidParameterException("input");
+            return FEELFnResult.ofError( new InvalidParametersEvent( 
Severity.ERROR, "input", "cannot be null" ) );
         }
         if ( pattern == null ) {
-            throw new InvalidParameterException("pattern");
+            return FEELFnResult.ofError( new InvalidParametersEvent( 
Severity.ERROR, "pattern", "cannot be null" ) );
         }
-            final String flagsString;
-            if (flags != null && !flags.isEmpty()) {
-                checkFlags(flags);
-                if(!flags.contains("U")){
-                    flags += "U";
-                }
-                flagsString = String.format("(?%s)", flags);
-            } else {
-                flagsString = "";
-            }
-            log.debug("flagsString: {}", flagsString);
-            String stringToBeMatched = flagsString + pattern;
-            log.debug("stringToBeMatched: {}", stringToBeMatched);
-            Pattern p=Pattern.compile(stringToBeMatched);
-            Matcher m = p.matcher( input );
-            boolean matchFound=m.find();
-            log.debug("matchFound: {}", matchFound);
-            return FEELFnResult.ofResult(matchFound);
-    }
 
-   static void checkFlags(String flags) {
-        Set<Character> allowedChars = Set.of('s','i','x','m');
-         boolean isValidFlag= flags.chars()
-                .mapToObj(c -> (char) c)
-                .allMatch(allowedChars::contains)
-                && flags.chars()
-                .mapToObj(c -> (char) c)
-                .collect(Collectors.toSet())
-                .size() == flags.length();
-         if(!isValidFlag){
-             throw new IllegalArgumentException("Not a valid flag parameter " 
+flags);
-         }
+        try {
+            return 
FEELFnResult.ofResult(XQueryImplUtil.executeMatchesFunction(input, pattern, 
flags));
+        } catch (Exception e) {
+            String errorMessage = String.format("Provided parameters lead to 
an error. Input: '%s', Pattern: '%s', Flags: '%s'. ", input, pattern, flags);
+            if (e.getMessage() != null && !e.getMessage().isEmpty()) {
+                errorMessage += e.getMessage();
+            }
+            return FEELFnResult.ofError(new 
InvalidParametersEvent(Severity.ERROR, errorMessage, e));
+        }
     }
 }
diff --git 
a/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/runtime/functions/ReplaceFunction.java
 
b/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/runtime/functions/ReplaceFunction.java
index 723efcbdd3..a7ae497870 100644
--- 
a/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/runtime/functions/ReplaceFunction.java
+++ 
b/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/runtime/functions/ReplaceFunction.java
@@ -20,6 +20,7 @@ package org.kie.dmn.feel.runtime.functions;
 
 import org.kie.dmn.api.feel.runtime.events.FEELEvent.Severity;
 import org.kie.dmn.feel.runtime.events.InvalidParametersEvent;
+import org.kie.dmn.feel.util.XQueryImplUtil;
 
 public class ReplaceFunction
         extends BaseFEELFunction {
@@ -30,12 +31,12 @@ public class ReplaceFunction
         super( "replace" );
     }
 
-    public FEELFnResult<Object> invoke(@ParameterName("input") String input, 
@ParameterName("pattern") String pattern,
+    public FEELFnResult<String> invoke(@ParameterName("input") String input, 
@ParameterName("pattern") String pattern,
                                        @ParameterName( "replacement" ) String 
replacement ) {
         return invoke(input, pattern, replacement, null);
     }
 
-    public FEELFnResult<Object> invoke(@ParameterName("input") String input, 
@ParameterName("pattern") String pattern,
+    public FEELFnResult<String> invoke(@ParameterName("input") String input, 
@ParameterName("pattern") String pattern,
                                        @ParameterName( "replacement" ) String 
replacement, @ParameterName("flags") String flags) {
         if ( input == null ) {
             return FEELFnResult.ofError( new InvalidParametersEvent( 
Severity.ERROR, "input", "cannot be null" ) );
@@ -47,14 +48,16 @@ public class ReplaceFunction
             return FEELFnResult.ofError( new InvalidParametersEvent( 
Severity.ERROR, "replacement", "cannot be null" ) );
         }
 
-        final String flagsString;
-        if (flags != null && !flags.isEmpty()) {
-            flagsString = "(?" + flags + ")";
-        } else {
-            flagsString = "";
+        try {
+            return 
FEELFnResult.ofResult(XQueryImplUtil.executeReplaceFunction(input, pattern, 
replacement, flags));
+        } catch (Exception e) {
+            String errorMessage = String.format("Provided parameters lead to 
an error. Input: '%s', Pattern: '%s', Replacement: '%s', Flags: '%s'. ",
+                    input, pattern, replacement, flags);
+            if (e.getMessage() != null && !e.getMessage().isEmpty()) {
+                errorMessage += e.getMessage();
+            }
+            return FEELFnResult.ofError(new 
InvalidParametersEvent(Severity.ERROR, errorMessage, e));
         }
-
-        return FEELFnResult.ofResult( input.replaceAll( flagsString + pattern, 
replacement ) );
     }
 
 }
diff --git 
a/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/util/XQueryImplUtil.java 
b/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/util/XQueryImplUtil.java
new file mode 100644
index 0000000000..a3ae1c6e53
--- /dev/null
+++ 
b/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/util/XQueryImplUtil.java
@@ -0,0 +1,62 @@
+/**
+ * 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.kie.dmn.feel.util;
+
+import net.sf.saxon.s9api.Processor;
+import net.sf.saxon.s9api.XdmAtomicValue;
+import net.sf.saxon.s9api.XdmItem;
+import net.sf.saxon.s9api.XQueryCompiler;
+import net.sf.saxon.s9api.XQueryEvaluator;
+import net.sf.saxon.s9api.XQueryExecutable;
+import net.sf.saxon.s9api.SaxonApiException;
+
+public class XQueryImplUtil {
+
+    public static Boolean executeMatchesFunction(String input, String pattern, 
String flags) {
+        flags = flags == null ? "" : flags;
+        String xQueryExpression = String.format("matches('%s', '%s', '%s')", 
input, pattern, flags);
+        return evaluateXQueryExpression(xQueryExpression, Boolean.class);
+    }
+
+    public static String executeReplaceFunction(String input, String pattern, 
String replacement, String flags) {
+        flags = flags == null ? "" : flags;
+        String xQueryExpression = String.format("replace('%s', '%s', '%s', 
'%s')", input, pattern, replacement, flags);
+        return evaluateXQueryExpression(xQueryExpression, String.class);
+    }
+
+     static <T> T evaluateXQueryExpression(String expression, Class<T> 
expectedTypeResult) {
+         try {
+             Processor processor = new Processor(false);
+             XQueryCompiler compiler = processor.newXQueryCompiler();
+             XQueryExecutable executable = compiler.compile(expression);
+             XQueryEvaluator queryEvaluator = executable.load();
+             XdmItem resultItem = queryEvaluator.evaluateSingle();
+
+             Object value = switch (expectedTypeResult.getSimpleName()) {
+                 case "Boolean" -> ((XdmAtomicValue) 
resultItem).getBooleanValue();
+                 case "String" -> resultItem.getStringValue();
+                 default -> throw new UnsupportedOperationException("Type " + 
expectedTypeResult.getSimpleName() + " is not managed.");
+             };
+
+             return expectedTypeResult.cast(value);
+         } catch (SaxonApiException e) {
+             throw new IllegalArgumentException(e);
+         }
+    }
+}
diff --git 
a/kie-dmn/kie-dmn-feel/src/test/java/org/kie/dmn/feel/runtime/FEELFunctionsTest.java
 
b/kie-dmn/kie-dmn-feel/src/test/java/org/kie/dmn/feel/runtime/FEELFunctionsTest.java
index 3707929e3b..a199e8f1f6 100644
--- 
a/kie-dmn/kie-dmn-feel/src/test/java/org/kie/dmn/feel/runtime/FEELFunctionsTest.java
+++ 
b/kie-dmn/kie-dmn-feel/src/test/java/org/kie/dmn/feel/runtime/FEELFunctionsTest.java
@@ -43,7 +43,6 @@ public class FEELFunctionsTest extends BaseFEELTest {
         final Object[][] cases = new Object[][] {
                 // constants
                 { "string(1.1)", "1.1" , null},
-                { "replace( \"  foo   bar zed  \", 
\"^(\\s)+|(\\s)+$|\\s+(?=\\s)\", \"\" )", "foo bar zed", null },
                 { "string(null)", null, null},
                 { "string(date(\"2016-08-14\"))", "2016-08-14" , null},
                 { "string(\"Happy %.0fth birthday, Mr %s!\", 38, \"Doe\")", 
"Happy 38th birthday, Mr Doe!", null},
@@ -85,6 +84,14 @@ public class FEELFunctionsTest extends BaseFEELTest {
                 { "matches(\"one\\ntwo\\nthree\", \"^two$\", \"m\")", 
Boolean.TRUE , null}, // MULTILINE flag set by "m"
                 { "matches(\"FoO\", \"foo\")", Boolean.FALSE , null},
                 { "matches(\"FoO\", \"foo\", \"i\")", Boolean.TRUE , null}, // 
CASE_INSENSITIVE flag set by "i"
+                { "matches(\"\\u212A\", \"k\", \"i\")", Boolean.TRUE , null},
+                { "matches(\"O\", \"[A-Z-[OI]]\", \"i\")", Boolean.FALSE , 
null},
+                { "matches(\"i\", \"[A-Z-[OI]]\", \"i\")", Boolean.FALSE , 
null},
+                { "matches(\"hello world\", \"hello\\ sworld\", \"x\")", 
Boolean.TRUE , null},
+                { "matches(\"abracadabra\", \"bra\", \"p\")", null , 
FEELEvent.Severity.ERROR},
+                { "matches(\"input\", \"pattern\", \" \")", null , 
FEELEvent.Severity.ERROR},
+                { "matches(\"input\", \"pattern\", \"X\")", null , 
FEELEvent.Severity.ERROR},
+                { "matches(\"h\", \"(.)\\2\")", null , 
FEELEvent.Severity.ERROR},
                 { "replace(\"banana\",\"a\",\"o\")", "bonono" , null},
                 { "replace(\"banana\",\"(an)+\", \"**\")", "b**a" , null},
                 { "replace(\"banana\",\"[aeiouy]\",\"[$0]\")", "b[a]n[a]n[a]" 
, null},
diff --git 
a/kie-dmn/kie-dmn-feel/src/test/java/org/kie/dmn/feel/runtime/functions/MatchesFunctionTest.java
 
b/kie-dmn/kie-dmn-feel/src/test/java/org/kie/dmn/feel/runtime/functions/MatchesFunctionTest.java
index b12612bb04..589f37e3d2 100644
--- 
a/kie-dmn/kie-dmn-feel/src/test/java/org/kie/dmn/feel/runtime/functions/MatchesFunctionTest.java
+++ 
b/kie-dmn/kie-dmn-feel/src/test/java/org/kie/dmn/feel/runtime/functions/MatchesFunctionTest.java
@@ -19,116 +19,146 @@
 package org.kie.dmn.feel.runtime.functions;
 
 import org.junit.jupiter.api.Test;
-import org.mockito.MockedStatic;
 
-import java.security.InvalidParameterException;
-import java.util.regex.PatternSyntaxException;
-
-import static 
org.assertj.core.api.AssertionsForClassTypes.assertThatExceptionOfType;
-import static 
org.assertj.core.api.AssertionsForClassTypes.assertThatNoException;
-import static org.mockito.Mockito.*;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.MethodSource;
+import org.kie.dmn.feel.runtime.events.InvalidParametersEvent;
 
 class MatchesFunctionTest {
 
-    @Test
-    void invokeNull() {
-        
assertThatExceptionOfType(InvalidParameterException.class).isThrownBy(() -> 
MatchesFunction.matchFunctionWithFlags(null, null, null));
-        
assertThatExceptionOfType(InvalidParameterException.class).isThrownBy(() -> 
MatchesFunction.matchFunctionWithFlags(null, "test", null));
-        
assertThatExceptionOfType(InvalidParameterException.class).isThrownBy(() -> 
MatchesFunction.matchFunctionWithFlags("test", null, null));
+    private static final MatchesFunction matchesFunction = 
MatchesFunction.INSTANCE;
+
+    @ParameterizedTest
+    @MethodSource("invokeNullTestData")
+    void invokeNullTest(String input, String pattern) {
+        FunctionTestUtil.assertResultError(matchesFunction.invoke(input, 
pattern), InvalidParametersEvent.class);
     }
 
-    @Test
-    void invokeUnsupportedFlags() {
-        
assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> 
MatchesFunction.matchFunctionWithFlags("foobar", "fo.bar", "g"));
-        
assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> 
MatchesFunction.matchFunctionWithFlags("abracadabra", "bra", "p"));
-        
assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> 
MatchesFunction.matchFunctionWithFlags("abracadabra", "bra", "X"));
-        
assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> 
MatchesFunction.matchFunctionWithFlags("abracadabra", "bra", " "));
-        
assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> 
MatchesFunction.matchFunctionWithFlags("abracadabra", "bra", "iU"));
+    private static Object[][] invokeNullTestData() {
+        return new Object[][] {
+                { null, null },
+                { null, "test" },
+                { "test", null },
+        };
     }
 
-    @Test
-    void invokeWithoutFlagsMatch() {
-        
FunctionTestUtil.assertResult(MatchesFunction.matchFunctionWithFlags("test", 
"test",null), true);
-        
FunctionTestUtil.assertResult(MatchesFunction.matchFunctionWithFlags("foobar", 
"^fo*b",null), true);
-        
FunctionTestUtil.assertResult(MatchesFunction.matchFunctionWithFlags("abracadabra",
 "bra", ""), true);
-        
FunctionTestUtil.assertResult(MatchesFunction.matchFunctionWithFlags("abracadabra",
 "bra",null), true);
-        
FunctionTestUtil.assertResult(MatchesFunction.matchFunctionWithFlags("(?xi)[hello
 world()]", "hello",null), true);
+    @ParameterizedTest
+    @MethodSource("invokeUnsupportedFlagsTestData")
+    void invokeUnsupportedFlagsTest(String input, String pattern, String 
flags) {
+        FunctionTestUtil.assertResultError(matchesFunction.invoke(input, 
pattern, flags), InvalidParametersEvent.class);
+   }
+
+    private static Object[][] invokeUnsupportedFlagsTestData() {
+        return new Object[][] {
+                { "foobar", "fo.bar", "g" },
+                { "abracadabra", "bra", "p" },
+                { "abracadabra", "bra", "X" },
+                { "abracadabra", "bra", "iU" },
+                { "abracadabra", "bra", "iU asd" },
+        };
     }
 
-    @Test
-    void invokeWithoutFlagsNotMatch() {
-        
FunctionTestUtil.assertResult(MatchesFunction.matchFunctionWithFlags("test", 
"testt",null), false);
-        
FunctionTestUtil.assertResult(MatchesFunction.matchFunctionWithFlags("foobar", 
"^fo*bb",null), false);
-        
FunctionTestUtil.assertResult(MatchesFunction.matchFunctionWithFlags("fo\nbar", 
"fo.bar",null), false);
-        
FunctionTestUtil.assertResult(MatchesFunction.matchFunctionWithFlags("h", 
"(.)\3",null), false);
-        
FunctionTestUtil.assertResult(MatchesFunction.matchFunctionWithFlags("h", 
"(.)\2",null), false);
-        
FunctionTestUtil.assertResult(MatchesFunction.matchFunctionWithFlags("input", 
"\3",null), false);
-        
FunctionTestUtil.assertResult(MatchesFunction.matchFunctionWithFlags("fo\nbar", 
"(?iU)(?iU)(ab)[|cd]",null), false);
-        
FunctionTestUtil.assertResult(MatchesFunction.matchFunctionWithFlags("fo\nbar", 
"(?x)(?i)hello world","i"), false);
-        
FunctionTestUtil.assertResult(MatchesFunction.matchFunctionWithFlags("fo\nbar", 
"(?xi)hello world",null), false);
+    @ParameterizedTest
+    @MethodSource("invokeWithoutFlagsMatchTestData")
+    void invokeWithoutFlagsMatchTest(String input, String pattern, Boolean 
expectedResult) {
+        FunctionTestUtil.assertResult(matchesFunction.invoke(input, pattern), 
expectedResult);
     }
 
-    @Test
-    void invokeWithFlagDotAll() {
-        
FunctionTestUtil.assertResult(MatchesFunction.matchFunctionWithFlags("fo\nbar", 
"fo.bar", "s"), true);
+    private static Object[][] invokeWithoutFlagsMatchTestData() {
+        return new Object[][] {
+                { "test", "test", true },
+                { "foobar", "^fo*b", true },
+                { "abracadabra", "bra", true },
+                { "abracadabra", "bra", true },
+                { "(?xi)[hello world()]", "hello", true }
+        };
     }
 
-    @Test
-    void invokeWithFlagMultiline() {
-        
FunctionTestUtil.assertResult(MatchesFunction.matchFunctionWithFlags("fo\nbar", 
"^bar", "m"), true);
+    @ParameterizedTest
+    @MethodSource("invokeNullOrEmptyFlagsMatchTestData")
+    void invokeNullOrEmptyFlagsMatchTest(String input, String pattern, String 
flags, Boolean expectedResult) {
+        FunctionTestUtil.assertResult(matchesFunction.invoke(input, pattern, 
flags), expectedResult);
     }
 
-    @Test
-    void invokeWithFlagCaseInsensitive() {
-        
FunctionTestUtil.assertResult(MatchesFunction.matchFunctionWithFlags("foobar", 
"^Fo*bar", "i"), true);
-        
FunctionTestUtil.assertResult(MatchesFunction.matchFunctionWithFlags("foobar", 
"^Fo*bar", "i"), true);
-        
FunctionTestUtil.assertResult(MatchesFunction.matchFunctionWithFlags("\u212A", 
"k","i"), true);
-        
FunctionTestUtil.assertResult(MatchesFunction.matchFunctionWithFlags("\u212A", 
"K","i"), true);
+    private static Object[][] invokeNullOrEmptyFlagsMatchTestData() {
+        return new Object[][] {
+                { "test", "test", null, true },
+                { "foobar", "^fo*b", null, true },
+                { "abracadabra", "bra", null, true },
+                { "abracadabra", "bra", "", true },
+                { "(?xi)[hello world()]", "hello", null, true }
+        };
+    }
+
+    @ParameterizedTest
+    @MethodSource("invokeInvalidRegexTestData")
+    void invokeInvalidRegexTest(String input, String pattern, String flags) {
+        FunctionTestUtil.assertResultError(matchesFunction.invoke(input, 
pattern, flags), InvalidParametersEvent.class);
+    }
+
+    private static Object[][] invokeInvalidRegexTestData() {
+        return new Object[][] {
+                { "testString", "(?=\\\\s)", null },
+                { "fo\nbar", "(?iU)(?iU)(ab)[|cd]", null },
+                { "fo\nbar", "(?x)(?i)hello world", "i" },
+                { "fo\nbar", "(?xi)hello world", null },
+        };
+    }
+
+    @ParameterizedTest
+    @MethodSource("invokeWithoutFlagsNotMatchTestData")
+    void invokeWithoutFlagsNotMatchTest(String input, String pattern, String 
flags, Boolean expectedResult) {
+        FunctionTestUtil.assertResult(matchesFunction.invoke(input, pattern, 
flags), expectedResult);
+    }
+
+    private static Object[][] invokeWithoutFlagsNotMatchTestData() {
+        return new Object[][] {
+                { "test", "testt", null, false },
+                { "foobar", "^fo*bb", null, false },
+                { "h", "(.)\3", null, false },
+                { "h", "(.)\2", null, false },
+                { "input", "\3", null, false }
+        };
+    }
+
+    @ParameterizedTest
+    @MethodSource("invokeWithFlagCaseInsensitiveTestData")
+    void invokeWithoutFlagsPatternTest(String input, String pattern, String 
flags, Boolean expectedResult) {
+        FunctionTestUtil.assertResult(matchesFunction.invoke(input, pattern, 
flags), expectedResult);
+    }
+
+    private static Object[][] invokeWithFlagCaseInsensitiveTestData() {
+        return new Object[][] {
+                { "foobar", "^Fo*bar", "i", true },
+                { "foobar", "^Fo*bar", "i", true },
+                { "\u212A", "k", "i", true },
+                { "\u212A", "K", "i", true }
+        };
     }
 
     @Test
-    void invokeWithFlagComments() {
-        
FunctionTestUtil.assertResult(MatchesFunction.matchFunctionWithFlags("hello 
world", "hello"+"\"+ sworld", "x"), false);
+    void invokeWithFlagDotAll() {
+        FunctionTestUtil.assertResult(matchesFunction.invoke("fo\nbar", 
"fo.bar", "s"), true);
     }
 
     @Test
-    void invokeWithAllFlags() {
-        
FunctionTestUtil.assertResult(MatchesFunction.matchFunctionWithFlags("fo\nbar", 
"Fo.^bar", "smi"), true);
+    void invokeWithFlagMultiline() {
+        FunctionTestUtil.assertResult(matchesFunction.invoke("fo\nbar", 
"^bar", "m"), true);
     }
 
     @Test
-    void checkForPatternTest() {
-        assertThatExceptionOfType(PatternSyntaxException.class).isThrownBy(() 
-> MatchesFunction.matchFunctionWithFlags("foobar", "(abc|def(ghi", "i"));
+    void invokeWithFlagComments() {
+        FunctionTestUtil.assertResult(matchesFunction.invoke("hello world", 
"hello"+"\"+ sworld", "x"), false);
     }
 
     @Test
-    void checkFlagsTest() {
-        assertThatNoException().isThrownBy(() -> 
MatchesFunction.checkFlags("s"));
-        assertThatNoException().isThrownBy(() -> 
MatchesFunction.checkFlags("i"));
-        assertThatNoException().isThrownBy(() -> 
MatchesFunction.checkFlags("sx"));
-        assertThatNoException().isThrownBy(() -> 
MatchesFunction.checkFlags("six"));
-        assertThatNoException().isThrownBy(() -> 
MatchesFunction.checkFlags("sixm"));
-        
assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> 
MatchesFunction.checkFlags("a"));
-        
assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> 
MatchesFunction.checkFlags("sa"));
-        
assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> 
MatchesFunction.checkFlags("siU@"));
-        
assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> 
MatchesFunction.checkFlags("siUxU"));
-        
assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> 
MatchesFunction.checkFlags("ss"));
-        
assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> 
MatchesFunction.checkFlags("siiU"));
-        
assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> 
MatchesFunction.checkFlags("si U"));
-        
assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> 
MatchesFunction.checkFlags("U"));
+    void invokeWithAllFlags() {
+        FunctionTestUtil.assertResult(matchesFunction.invoke("fo\nbar", 
"Fo.^bar", "smi"), true);
     }
 
     @Test
-    void checkMatchFunctionWithFlagsInvocation() {
-        MatchesFunction matchesFunctionSpied = spy(MatchesFunction.INSTANCE);
-        matchesFunctionSpied.invoke("input", "pattern");
-        verify(matchesFunctionSpied, times(1)).invoke("input", "pattern", 
null);
-        try (MockedStatic<MatchesFunction> matchesFunctionMocked = 
mockStatic(MatchesFunction.class)) {
-            matchesFunctionSpied.invoke("input", "pattern");
-            matchesFunctionMocked.verify(() -> 
MatchesFunction.matchFunctionWithFlags("input", "pattern", null));
-            matchesFunctionSpied.invoke("input", "pattern", "flags");
-            matchesFunctionMocked.verify(() -> 
MatchesFunction.matchFunctionWithFlags("input", "pattern", "flags"));
-        }
+    void checkForPatternTest() {
+        FunctionTestUtil.assertResultError(matchesFunction.invoke("foobar", 
"(abc|def(ghi", "i"), InvalidParametersEvent.class);
     }
 
 }
\ No newline at end of file
diff --git 
a/kie-dmn/kie-dmn-feel/src/test/java/org/kie/dmn/feel/runtime/functions/ReplaceFunctionTest.java
 
b/kie-dmn/kie-dmn-feel/src/test/java/org/kie/dmn/feel/runtime/functions/ReplaceFunctionTest.java
index 06e1bc4da0..45d1334f35 100644
--- 
a/kie-dmn/kie-dmn-feel/src/test/java/org/kie/dmn/feel/runtime/functions/ReplaceFunctionTest.java
+++ 
b/kie-dmn/kie-dmn-feel/src/test/java/org/kie/dmn/feel/runtime/functions/ReplaceFunctionTest.java
@@ -19,62 +19,88 @@
 package org.kie.dmn.feel.runtime.functions;
 
 import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.MethodSource;
 import org.kie.dmn.feel.runtime.events.InvalidParametersEvent;
 
 class ReplaceFunctionTest {
 
     private static final ReplaceFunction replaceFunction = 
ReplaceFunction.INSTANCE;
 
-    @Test
-    void invokeNull() {
-        FunctionTestUtil.assertResultError(replaceFunction.invoke(null, null, 
null), InvalidParametersEvent.class);
-        
FunctionTestUtil.assertResultError(replaceFunction.invoke("testString", null, 
null),
-                                           InvalidParametersEvent.class);
-        
FunctionTestUtil.assertResultError(replaceFunction.invoke("testString", "test", 
null),
-                                           InvalidParametersEvent.class);
-        FunctionTestUtil.assertResultError(replaceFunction.invoke(null, 
"test", null), InvalidParametersEvent.class);
-        FunctionTestUtil.assertResultError(replaceFunction.invoke(null, 
"test", "ttt"), InvalidParametersEvent.class);
-        FunctionTestUtil.assertResultError(replaceFunction.invoke(null, null, 
"ttt"), InvalidParametersEvent.class);
+    @ParameterizedTest
+    @MethodSource("invokeNullTestData")
+    void invokeNullTest(String input, String pattern, String replacement) {
+        FunctionTestUtil.assertResultError(replaceFunction.invoke(input, 
pattern, replacement), InvalidParametersEvent.class);
     }
 
-    @Test
-    void invokeNullWithFlags() {
-        FunctionTestUtil.assertResultError(replaceFunction.invoke(null, null, 
null, null),
-                                           InvalidParametersEvent.class);
-        
FunctionTestUtil.assertResultError(replaceFunction.invoke("testString", null, 
null, null),
-                                           InvalidParametersEvent.class);
-        
FunctionTestUtil.assertResultError(replaceFunction.invoke("testString", "test", 
null, null),
-                                           InvalidParametersEvent.class);
-        FunctionTestUtil.assertResultError(replaceFunction.invoke(null, 
"test", null, null),
-                                           InvalidParametersEvent.class);
-        FunctionTestUtil.assertResultError(replaceFunction.invoke(null, 
"test", "ttt", null),
-                                           InvalidParametersEvent.class);
-        FunctionTestUtil.assertResultError(replaceFunction.invoke(null, null, 
"ttt", null),
-                                           InvalidParametersEvent.class);
-
-        FunctionTestUtil.assertResultError(replaceFunction.invoke(null, null, 
null, "s"), InvalidParametersEvent.class);
-        
FunctionTestUtil.assertResultError(replaceFunction.invoke("testString", null, 
null, "s"),
-                                           InvalidParametersEvent.class);
-        
FunctionTestUtil.assertResultError(replaceFunction.invoke("testString", "test", 
null, "s"),
-                                           InvalidParametersEvent.class);
-        FunctionTestUtil.assertResultError(replaceFunction.invoke(null, 
"test", null, "s"),
-                                           InvalidParametersEvent.class);
-        FunctionTestUtil.assertResultError(replaceFunction.invoke(null, 
"test", "ttt", "s"),
-                                           InvalidParametersEvent.class);
-        FunctionTestUtil.assertResultError(replaceFunction.invoke(null, null, 
"ttt", "s"),
-                                           InvalidParametersEvent.class);
+    private static Object[][] invokeNullTestData() {
+        return new Object[][] {
+                { null, null, null },
+                { "testString", null, null },
+                { "testString", "test", null },
+                { null, "test", null },
+                { null, "test", "ttt" },
+                { null, null, "ttt" }
+        };
     }
 
-    @Test
-    void invokeWithoutFlagsPatternMatches() {
-        FunctionTestUtil.assertResult(replaceFunction.invoke("testString", 
"^test", "ttt"), "tttString");
-        FunctionTestUtil.assertResult(replaceFunction.invoke("testStringtest", 
"^test", "ttt"), "tttStringtest");
+    @ParameterizedTest
+    @MethodSource("invokeNullWithFlagsTestData")
+    void invokeNullWithFlagsTest(String input, String pattern, String 
replacement, String flags) {
+        FunctionTestUtil.assertResultError(replaceFunction.invoke(input, 
pattern, replacement, flags), InvalidParametersEvent.class);
+    }
+
+    private static Object[][] invokeNullWithFlagsTestData() {
+        return new Object[][] {
+                { null, null, null, null },
+                { "testString", null, null, null },
+                { "testString", "test", null, null },
+                { null, "test", null, null },
+                { null, "test", "ttt", null },
+                { null, null, "ttt", null },
+                { null, null, null, "s" },
+                { "testString", null, null, "s" },
+                { "testString", "test", null, "s"  },
+                { null, "test", null, "s" },
+                { null, "test", "ttt", "s" },
+                { null, null, "ttt", "s" },
+        };
+    }
+
+    @ParameterizedTest
+    @MethodSource("invokeUnsupportedFlagsTestData")
+    void invokeUnsupportedFlagsTest(String input, String pattern, String 
replacement, String flags) {
+        FunctionTestUtil.assertResultError(replaceFunction.invoke(input, 
pattern, replacement, flags), InvalidParametersEvent.class);
+  }
+
+    private static Object[][] invokeUnsupportedFlagsTestData() {
+        return new Object[][] {
+                { "testString", "^test", "ttt", "g" },
+                { "testString", "^test", "ttt", "p" },
+                { "testString", "^test", "ttt", "X" },
+                { "testString", "^test", "ttt", "iU" },
+                { "testString", "^test", "ttt", "iU asd" },
+        };
+    }
+
+    @ParameterizedTest
+    @MethodSource("invokeWithoutFlagsPatternTestData")
+    void invokeWithoutFlagsPatternTest(String input, String pattern, String 
replacement, String expectedResult) {
+        FunctionTestUtil.assertResult(replaceFunction.invoke(input, pattern, 
replacement), expectedResult);
+    }
+
+    private static Object[][] invokeWithoutFlagsPatternTestData() {
+        return new Object[][] {
+                { "testString", "^test", "ttt", "tttString" },
+                { "testStringtest", "^test", "ttt", "tttStringtest" },
+                { "testString", "ttest", "ttt", "testString" },
+                { "testString", "$test", "ttt", "testString" }
+        };
     }
 
     @Test
-    void invokeWithoutFlagsPatternNotMatches() {
-        FunctionTestUtil.assertResult(replaceFunction.invoke("testString", 
"ttest", "ttt"), "testString");
-        FunctionTestUtil.assertResult(replaceFunction.invoke("testString", 
"$test", "ttt"), "testString");
+    void invokeInvalidRegExPattern() {
+        
FunctionTestUtil.assertResultError(replaceFunction.invoke("testString", 
"(?=\\s)", "ttt"), InvalidParametersEvent.class);
     }
 
     @Test
diff --git 
a/kie-dmn/kie-dmn-feel/src/test/java/org/kie/dmn/feel/util/XQueryImplUtilTest.java
 
b/kie-dmn/kie-dmn-feel/src/test/java/org/kie/dmn/feel/util/XQueryImplUtilTest.java
new file mode 100644
index 0000000000..6cc71d38a6
--- /dev/null
+++ 
b/kie-dmn/kie-dmn-feel/src/test/java/org/kie/dmn/feel/util/XQueryImplUtilTest.java
@@ -0,0 +1,117 @@
+/**
+ * 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.kie.dmn.feel.util;
+
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+class XQueryImplUtilTest {
+
+    @ParameterizedTest
+    @MethodSource("executeMatchesFunctionTestData")
+    void executeMatchesFunctionTest(String input, String pattern, String 
flags, boolean expected) {
+        assertThat(XQueryImplUtil.executeMatchesFunction(input, pattern, 
flags)).isEqualTo(expected);
+    }
+
+    private static Object[][] executeMatchesFunctionTestData() {
+        return new Object[][] {
+                { "test", "^test", "i", true },
+                { "fo\nbar", "o.b", null, false },
+                { "TEST", "test", "i", true },
+        };
+    }
+
+    @ParameterizedTest
+    @MethodSource("executeMatchesFunctionInvokingExceptionTestData")
+    void executeMatchesFunctionInvokingExceptionTest(String input, String 
pattern, String flags,
+                                                     String exceptionMessage) {
+        assertThatThrownBy(() -> XQueryImplUtil.executeMatchesFunction(input, 
pattern, flags))
+                
.isInstanceOf(IllegalArgumentException.class).hasMessageContaining(exceptionMessage);
+    }
+
+    private static Object[][] 
executeMatchesFunctionInvokingExceptionTestData() {
+        return new Object[][] {
+                { "test", "^test", "g", "Unrecognized flag" },
+                { "test", "(?=\\s)", null, "No expression before quantifier" },
+                { "test", "(.)\\2", "i", "invalid backreference \\2" },
+        };
+    }
+
+    @ParameterizedTest
+    @MethodSource("executeReplaceFunctionTestData")
+    void executeReplaceFunctionTest(String input, String pattern, String 
replacement, String flags, String expected) {
+        assertThat(XQueryImplUtil.executeReplaceFunction(input, pattern, 
replacement, flags)).isEqualTo(expected);
+    }
+
+    private static Object[][] executeReplaceFunctionTestData() {
+        return new Object[][] {
+                { "testString", "^test", "ttt", "", "tttString" },
+                { "fo\nbar", "o.b", "ttt", "s", "ftttar" },
+        };
+    }
+
+    @ParameterizedTest
+    @MethodSource("executeReplaceFunctionInvokingExceptionTestData")
+    void executeReplaceFunctionInvokingExceptionTest(String input, String 
pattern, String replacement, String flags,
+                                                     Class<?> 
expectedException, String exceptionMessage) {
+        assertThatThrownBy(() -> XQueryImplUtil.executeReplaceFunction(input, 
pattern, replacement, flags))
+                
.isInstanceOf(expectedException).hasMessageContaining(exceptionMessage);
+    }
+
+    private static Object[][] 
executeReplaceFunctionInvokingExceptionTestData() {
+        return new Object[][] {
+                { "fo\nbar", "o.b", "ttt", "g", 
IllegalArgumentException.class, "Unrecognized flag" },
+                { "test", "(?=\\s)", "ttt", null, 
IllegalArgumentException.class, "No expression before quantifier" },
+                { "test", "(.)\\2", "ttt", null, 
IllegalArgumentException.class, "invalid backreference \\2" },
+        };
+    }
+
+    @ParameterizedTest
+    @MethodSource("evaluateXQueryExpressionValidParametersTestData")
+    void evaluateXQueryExpressionValidParametersTest(String expression, 
Class<?> returnTypeClass, Object expectedResult) {
+        assertThat(XQueryImplUtil.evaluateXQueryExpression(expression, 
returnTypeClass)).isEqualTo(expectedResult);
+
+    }
+
+    private static Object[][] 
evaluateXQueryExpressionValidParametersTestData() {
+        return new Object[][] {
+                { "matches('test', '^test', 'i')", Boolean.class, true },
+                { "matches('fo\\nbar', 'o.b', '')", Boolean.class, false },
+                { "replace('testString', '^test', 'ttt', '')", String.class, 
"tttString" }
+        };
+    }
+
+    @ParameterizedTest
+    @MethodSource("evaluateXQueryExpressionInvalidParametersTestData")
+    void evaluateXQueryExpressionInvalidParametersTest(String expression, 
Class<?> returnTypeClass) {
+        assertThatThrownBy(() -> 
XQueryImplUtil.evaluateXQueryExpression(expression, returnTypeClass))
+                .isInstanceOf(UnsupportedOperationException.class);
+    }
+
+    private static Object[][] 
evaluateXQueryExpressionInvalidParametersTestData() {
+        return new Object[][] {
+                { "matches('test', '^test', 'i')", Integer.class },
+                { "replace('testString', '^test', 'ttt', '')", Double.class },
+        };
+    }
+
+}
\ No newline at end of file


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]


Reply via email to