You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@kie.apache.org by tz...@apache.org on 2024/08/23 07:09:59 UTC

(incubator-kie-drools) branch main updated: [incubator-kie-issues#1409]-Fixed matches function behavior (#6055)

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

tzimanyi 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 edc77d536e [incubator-kie-issues#1409]-Fixed matches function behavior (#6055)
edc77d536e is described below

commit edc77d536e07a9c3cf879f51351bea28e7889897
Author: bncriju <bi...@ibm.com>
AuthorDate: Fri Aug 23 12:39:55 2024 +0530

    [incubator-kie-issues#1409]-Fixed matches function behavior (#6055)
---
 .../feel/runtime/functions/MatchesFunction.java    | 79 +++++++++++-------
 .../runtime/functions/MatchesFunctionTest.java     | 94 ++++++++++++++++++----
 2 files changed, 129 insertions(+), 44 deletions(-)

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 f6b1806ca2..79ff2a0136 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,16 +18,21 @@
  */
 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.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 public class MatchesFunction
         extends BaseFEELFunction {
-
+    private static final Logger log = LoggerFactory.getLogger(MatchesFunction.class);
     public static final MatchesFunction INSTANCE = new MatchesFunction();
 
     private MatchesFunction() {
@@ -39,40 +44,58 @@ public class MatchesFunction
     }
 
     public FEELFnResult<Boolean> invoke(@ParameterName("input") String input, @ParameterName("pattern") String pattern, @ParameterName("flags") String flags) {
-        if ( input == null ) {
-            return FEELFnResult.ofError( new InvalidParametersEvent( Severity.ERROR, "input", "cannot be null" ) );
-        }
-        if ( pattern == null ) {
-            return FEELFnResult.ofError( new InvalidParametersEvent( Severity.ERROR, "pattern", "cannot be null" ) );
-        }
         try {
-            int f = processFlags( flags );
-            Pattern p = Pattern.compile( pattern, f );
-            Matcher m = p.matcher( input );
-            return FEELFnResult.ofResult( m.find() );
-        } catch ( PatternSyntaxException e ) {
-            return FEELFnResult.ofError( new InvalidParametersEvent( Severity.ERROR, "pattern", "is invalid and can not be compiled", e ) );
-        } catch ( IllegalArgumentException t ) {
+            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) {
+        } catch (Throwable t) {
             return FEELFnResult.ofError( new InvalidParametersEvent( Severity.ERROR, "pattern", "is invalid and can not be compiled", t ) );
         }
     }
 
-    private int processFlags(String flags) {
-        int f = 0;
-        if( flags != null ) {
-            if( flags.contains( "s" ) ) {
-                f |= Pattern.DOTALL;
-            }
-            if( flags.contains( "m" ) ) {
-                f |= Pattern.MULTILINE;
-            }
-            if( flags.contains( "i" ) ) {
-                f |= Pattern.CASE_INSENSITIVE;
-            }
+    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");
+        }
+        if ( pattern == null ) {
+            throw new InvalidParameterException("pattern");
         }
-        return f;
+            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);
+         }
+    }
 }
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 eeb86f511a..eea47b17de 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,54 +19,116 @@
 package org.kie.dmn.feel.runtime.functions;
 
 import org.junit.jupiter.api.Test;
-import org.kie.dmn.feel.runtime.events.InvalidParametersEvent;
+import org.mockito.MockedStatic;
 
-class MatchesFunctionTest {
+import java.security.InvalidParameterException;
+import java.util.regex.PatternSyntaxException;
+
+import static org.junit.jupiter.api.Assertions.*;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.mockito.Mockito.*;
 
-    private final static MatchesFunction matchesFunction = MatchesFunction.INSTANCE;
+class MatchesFunctionTest {
 
     @Test
     void invokeNull() {
-        FunctionTestUtil.assertResultError(matchesFunction.invoke((String) null, null), InvalidParametersEvent.class);
-        FunctionTestUtil.assertResultError(matchesFunction.invoke(null, "test"), InvalidParametersEvent.class);
-        FunctionTestUtil.assertResultError(matchesFunction.invoke("test", null), InvalidParametersEvent.class);
+        assertThrows(InvalidParameterException.class, () -> MatchesFunction.matchFunctionWithFlags(null, null, null));
+        assertThrows(InvalidParameterException.class, () -> MatchesFunction.matchFunctionWithFlags(null, "test",null));
+        assertThrows(InvalidParameterException.class, () -> MatchesFunction.matchFunctionWithFlags("test", null,null));
     }
 
     @Test
     void invokeUnsupportedFlags() {
-        FunctionTestUtil.assertResult(matchesFunction.invoke("foobar", "fo.bar", "g"), true);
+        assertThrows(IllegalArgumentException.class, () -> MatchesFunction.matchFunctionWithFlags("foobar", "fo.bar", "g"));
+        assertThrows(IllegalArgumentException.class, () -> MatchesFunction.matchFunctionWithFlags("abracadabra", "bra", "p"));
+        assertThrows(IllegalArgumentException.class, () -> MatchesFunction.matchFunctionWithFlags("abracadabra", "bra", "X"));
+        assertThrows(IllegalArgumentException.class, () -> MatchesFunction.matchFunctionWithFlags("abracadabra", "bra", " "));
+        assertThrows(IllegalArgumentException.class, () -> MatchesFunction.matchFunctionWithFlags("abracadabra", "bra", "iU"));
     }
 
     @Test
     void invokeWithoutFlagsMatch() {
-        FunctionTestUtil.assertResult(matchesFunction.invoke("test", "test"), true);
-        FunctionTestUtil.assertResult(matchesFunction.invoke("foobar", "^fo*b"), true);
+        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);
     }
 
     @Test
     void invokeWithoutFlagsNotMatch() {
-        FunctionTestUtil.assertResult(matchesFunction.invoke("test", "testt"), false);
-        FunctionTestUtil.assertResult(matchesFunction.invoke("foobar", "^fo*bb"), false);
-        FunctionTestUtil.assertResult(matchesFunction.invoke("fo\nbar", "fo.bar"), false);
+        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);
     }
 
     @Test
     void invokeWithFlagDotAll() {
-        FunctionTestUtil.assertResult(matchesFunction.invoke("fo\nbar", "fo.bar", "s"), true);
+        FunctionTestUtil.assertResult(MatchesFunction.matchFunctionWithFlags("fo\nbar", "fo.bar", "s"), true);
     }
 
     @Test
     void invokeWithFlagMultiline() {
-        FunctionTestUtil.assertResult(matchesFunction.invoke("fo\nbar", "^bar", "m"), true);
+        FunctionTestUtil.assertResult(MatchesFunction.matchFunctionWithFlags("fo\nbar", "^bar", "m"), true);
     }
 
     @Test
     void invokeWithFlagCaseInsensitive() {
-        FunctionTestUtil.assertResult(matchesFunction.invoke("foobar", "^Fo*bar", "i"), true);
+        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);
+    }
+
+    @Test
+    void invokeWithFlagComments() {
+        FunctionTestUtil.assertResult(MatchesFunction.matchFunctionWithFlags("hello world", "hello"+"\"+ sworld", "x"), false);
     }
 
     @Test
     void invokeWithAllFlags() {
-        FunctionTestUtil.assertResult(matchesFunction.invoke("fo\nbar", "Fo.^bar", "smi"), true);
+        FunctionTestUtil.assertResult(MatchesFunction.matchFunctionWithFlags("fo\nbar", "Fo.^bar", "smi"), true);
+    }
+
+    @Test
+    void checkForPatternTest() {
+        assertThrows(PatternSyntaxException.class, () -> MatchesFunction.matchFunctionWithFlags("foobar",  "(abc|def(ghi", "i"));
+    }
+
+    @Test
+    void checkFlagsTest() {
+        assertDoesNotThrow(() -> MatchesFunction.checkFlags("s"));
+        assertDoesNotThrow(() -> MatchesFunction.checkFlags("i"));
+        assertDoesNotThrow(() -> MatchesFunction.checkFlags("sx"));
+        assertDoesNotThrow(() -> MatchesFunction.checkFlags("six"));
+        assertDoesNotThrow(() -> MatchesFunction.checkFlags("sixm"));
+        assertThrows(IllegalArgumentException.class, () -> MatchesFunction.checkFlags("a"));
+        assertThrows(IllegalArgumentException.class, () -> MatchesFunction.checkFlags("sa"));
+        assertThrows(IllegalArgumentException.class, () -> MatchesFunction.checkFlags("siU@"));
+        assertThrows(IllegalArgumentException.class, () -> MatchesFunction.checkFlags("siUxU"));
+        assertThrows(IllegalArgumentException.class, () -> MatchesFunction.checkFlags("ss"));
+        assertThrows(IllegalArgumentException.class, () -> MatchesFunction.checkFlags("siiU"));
+        assertThrows(IllegalArgumentException.class, () -> MatchesFunction.checkFlags("si U"));
+        assertThrows(IllegalArgumentException.class, () -> MatchesFunction.checkFlags("U"));
     }
+
+    @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"));
+        }
+    }
+
 }
\ No newline at end of file


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@kie.apache.org
For additional commands, e-mail: commits-help@kie.apache.org