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

tkobayas 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 ac6b6fecb1 Add optional Regular Expression cache to MatchesOperator 
(#5837)
ac6b6fecb1 is described below

commit ac6b6fecb1bb9fd28ea9819d22fe8362e040dbbd
Author: Jared Davis <[email protected]>
AuthorDate: Tue Apr 16 05:23:21 2024 -0400

    Add optional Regular Expression cache to MatchesOperator (#5837)
    
    * Add optional Regular Expression cache to MatchesOperator
    
    * Improve test cases, variable names and comments.
    
    * Added static to two fields
---
 .../drools/model/operators/MatchesOperator.java    | 60 ++++++++++++++++++-
 .../model/operators/MatchesOperatorTest.java       | 69 ++++++++++++++++++++++
 2 files changed, 126 insertions(+), 3 deletions(-)

diff --git 
a/drools-model/drools-canonical-model/src/main/java/org/drools/model/operators/MatchesOperator.java
 
b/drools-model/drools-canonical-model/src/main/java/org/drools/model/operators/MatchesOperator.java
index 880748db1e..6635f790ca 100644
--- 
a/drools-model/drools-canonical-model/src/main/java/org/drools/model/operators/MatchesOperator.java
+++ 
b/drools-model/drools-canonical-model/src/main/java/org/drools/model/operators/MatchesOperator.java
@@ -7,7 +7,7 @@
  * "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
+ * 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
@@ -20,13 +20,67 @@ package org.drools.model.operators;
 
 import org.drools.model.functions.Operator;
 
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+
 public enum MatchesOperator implements Operator.SingleValue<String, String> {
 
     INSTANCE;
 
+    // not final due to unit tests
+    private static int MAX_SIZE_CACHE = getMaxSizeCache();
+
+    // store Pattern for regular expressions using the regular expression as 
the key up to MAX_SIZE_CACHE entries.
+    private static final Map<String, Pattern> patternMap = 
Collections.synchronizedMap(new LinkedHashMap<>() {
+        @Override
+        protected boolean removeEldestEntry(Map.Entry<String, Pattern> eldest) 
{
+            return size() > (MAX_SIZE_CACHE);
+        }
+    });
+
+    // 0 default disables the Pattern map
+    private static int getMaxSizeCache() {
+        final String CACHE_MATCHES_COMPILED_MAX_PROPERTY = 
"drools.matches.compiled.cache.count";
+        return 
Integer.parseInt(System.getProperty(CACHE_MATCHES_COMPILED_MAX_PROPERTY, "0"));
+    }
+
+    // package-private for unit testing
+    void forceCacheSize(int size) {
+        MAX_SIZE_CACHE = size;
+        patternMap.clear();
+    }
+
+    // package-private for unit testing
+    void reInitialize() {
+        forceCacheSize(getMaxSizeCache());
+    }
+
+    // package-private for unit testing
+    int mapSize() {
+        return patternMap.size();
+    }
+
     @Override
-    public boolean eval( String s1, String s2 ) {
-        return s1 != null && s1.matches( s2 );
+    public boolean eval(String input, String regex) {
+        if (input == null) {
+            return false;
+        } else if (MAX_SIZE_CACHE == 0) {
+            return input.matches(regex);
+        } else {
+            Pattern pattern = patternMap.get(regex);
+            if (pattern == null) {
+                //  Cache miss on regex, compile it, store it.
+                //  Storing in patternMap may remove the oldest entry per 
MAX_SIZE_CACHE.
+                pattern = Pattern.compile(regex);
+                patternMap.put(regex, pattern);
+            }
+            Matcher matcher = pattern.matcher(input);
+            return matcher.matches();
+        }
     }
 
     @Override
diff --git 
a/drools-model/drools-canonical-model/src/test/java/org/drools/model/operators/MatchesOperatorTest.java
 
b/drools-model/drools-canonical-model/src/test/java/org/drools/model/operators/MatchesOperatorTest.java
new file mode 100644
index 0000000000..4520828c4e
--- /dev/null
+++ 
b/drools-model/drools-canonical-model/src/test/java/org/drools/model/operators/MatchesOperatorTest.java
@@ -0,0 +1,69 @@
+/**
+ * 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.drools.model.operators;
+
+import org.junit.After;
+import org.junit.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+
+public class MatchesOperatorTest {
+
+
+    @Test
+    public void testMatchesOperatorCache() {
+        MatchesOperator instance = MatchesOperator.INSTANCE;
+        instance.forceCacheSize(100);
+
+        // input maybe null
+        assertThat(instance.eval(null,"anything")).isFalse();
+        assertThat(instance.mapSize()).isEqualTo(0); // not added to cache 
with null input
+        // cache enabled
+        assertThat(instance.eval("a","a")).isTrue();
+        assertThat(instance.mapSize()).isEqualTo(1);
+        assertThat(instance.eval("a","b")).isFalse();
+        assertThat(instance.mapSize()).isEqualTo(2);
+        assertThat(instance.eval("a","a")).isTrue();  // regular expression 
"a" in map.
+        assertThat(instance.eval("b","b")).isTrue();  // regular expression 
"b" in map.
+        assertThat(instance.eval("c","a")).isFalse(); // regular expression 
"a" in map.
+        assertThat(instance.eval("c","b")).isFalse(); // regular expression 
"b" in map.
+        assertThat(instance.mapSize()).isEqualTo(2);
+    }
+
+    @Test
+    public void testMatchesOperatorNoCache() {
+        MatchesOperator instance = MatchesOperator.INSTANCE;
+        instance.forceCacheSize(0);
+        // input maybe null
+        assertThat(instance.eval(null,"anything")).isFalse();
+        assertThat(instance.eval("a","a")).isTrue();
+        assertThat(instance.eval("a","b")).isFalse();
+        assertThat(instance.eval("b","a")).isFalse();
+        assertThat(instance.eval("b","b")).isTrue();
+        assertThat(instance.mapSize()).isEqualTo(0);
+    }
+
+    @After
+    public void resetCache() {
+        MatchesOperator instance = MatchesOperator.INSTANCE;
+        instance.reInitialize();
+    }
+
+}


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

Reply via email to