From b7e60fd0432d6dc4acbdc04ddec3fa23c62fb27a Mon Sep 17 00:00:00 2001 From: Gilles Querret Date: Mon, 4 Sep 2023 08:40:33 +0200 Subject: [PATCH 1/2] Prepare 2.22.3 --- coverage-report/pom.xml | 16 ++++++++-------- database-parser/pom.xml | 2 +- listing-parser/pom.xml | 2 +- openedge-checks/pom.xml | 6 +++--- openedge-plugin/pom.xml | 8 ++++---- pom.xml | 2 +- profiler-parser/pom.xml | 2 +- proparse/pom.xml | 4 ++-- rcode-reader/pom.xml | 2 +- 9 files changed, 22 insertions(+), 22 deletions(-) diff --git a/coverage-report/pom.xml b/coverage-report/pom.xml index 3f629d8cb..df361527f 100644 --- a/coverage-report/pom.xml +++ b/coverage-report/pom.xml @@ -4,7 +4,7 @@ eu.rssw.openedge.parsers coverage-report - 2.22.2 + 2.22.3 JaCoCo report Fake module for code coverage report in SonarQube @@ -20,37 +20,37 @@ eu.rssw.openedge.rcode rcode-reader - 2.22.2 + 2.22.3 eu.rssw.sonar.openedge sonar-openedge-plugin - 2.22.2 + 2.22.3 eu.rssw.openedge.parsers proparse - 2.22.2 + 2.22.3 eu.rssw.openedge.parsers profiler-parser - 2.22.2 + 2.22.3 eu.rssw.openedge.parsers database-parser - 2.22.2 + 2.22.3 eu.rssw.openedge.parsers listing-parser - 2.22.2 + 2.22.3 eu.rssw.openedge.checks openedge-checks - 2.22.2 + 2.22.3 diff --git a/database-parser/pom.xml b/database-parser/pom.xml index 5ac877c1a..9ecffd606 100644 --- a/database-parser/pom.xml +++ b/database-parser/pom.xml @@ -4,7 +4,7 @@ eu.rssw.openedge.parsers database-parser - 2.22.2 + 2.22.3 OpenEdge database definition lexer and parser OpenEdge dump files parser diff --git a/listing-parser/pom.xml b/listing-parser/pom.xml index 891db0bdb..e6f2ca1f8 100644 --- a/listing-parser/pom.xml +++ b/listing-parser/pom.xml @@ -4,7 +4,7 @@ eu.rssw.openedge.parsers listing-parser - 2.22.2 + 2.22.3 OpenEdge listing so-called parser OpenEdge listing files parser diff --git a/openedge-checks/pom.xml b/openedge-checks/pom.xml index 8ecc98fe2..d567e7acd 100644 --- a/openedge-checks/pom.xml +++ b/openedge-checks/pom.xml @@ -4,7 +4,7 @@ eu.rssw.openedge.checks openedge-checks - 2.22.2 + 2.22.3 OpenEdge checks OpenEdge checks @@ -55,12 +55,12 @@ eu.rssw.openedge.parsers database-parser - 2.22.2 + 2.22.3 eu.rssw.openedge.parsers proparse - 2.22.2 + 2.22.3 org.testng diff --git a/openedge-plugin/pom.xml b/openedge-plugin/pom.xml index 351c37db2..fd60be5df 100644 --- a/openedge-plugin/pom.xml +++ b/openedge-plugin/pom.xml @@ -4,7 +4,7 @@ eu.rssw.sonar.openedge sonar-openedge-plugin - 2.22.2 + 2.22.3 sonar-plugin OpenEdge plugin for SonarQube @@ -60,17 +60,17 @@ eu.rssw.openedge.checks openedge-checks - 2.22.2 + 2.22.3 eu.rssw.openedge.parsers listing-parser - 2.22.2 + 2.22.3 eu.rssw.openedge.parsers profiler-parser - 2.22.2 + 2.22.3 commons-io diff --git a/pom.xml b/pom.xml index 6b6a08bae..afa8ab7b5 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ eu.rssw sonar-openedge - 2.22.2 + 2.22.3 pom OpenEdge plugin for SonarQube diff --git a/profiler-parser/pom.xml b/profiler-parser/pom.xml index 18605e87e..533308ec3 100644 --- a/profiler-parser/pom.xml +++ b/profiler-parser/pom.xml @@ -4,7 +4,7 @@ eu.rssw.openedge.parsers profiler-parser - 2.22.2 + 2.22.3 OpenEdge profiler output lexer and parser OpenEdge profiler files parser diff --git a/proparse/pom.xml b/proparse/pom.xml index 38c58ec32..a26303f4c 100644 --- a/proparse/pom.xml +++ b/proparse/pom.xml @@ -4,7 +4,7 @@ eu.rssw.openedge.parsers proparse - 2.22.2 + 2.22.3 Proparse ABL code parser @@ -55,7 +55,7 @@ eu.rssw.openedge.rcode rcode-reader - 2.22.2 + 2.22.3 org.antlr diff --git a/rcode-reader/pom.xml b/rcode-reader/pom.xml index 97b136c46..227873795 100644 --- a/rcode-reader/pom.xml +++ b/rcode-reader/pom.xml @@ -4,7 +4,7 @@ eu.rssw.openedge.rcode rcode-reader - 2.22.2 + 2.22.3 rcode-reader rcode reader From 4b1fc1c0cd708fc6d248d98ab09f424cd691f7f1 Mon Sep 17 00:00:00 2001 From: Gilles Querret Date: Mon, 4 Sep 2023 09:42:04 +0200 Subject: [PATCH 2/2] Better implementation of PreproEval.matches function Don't recursively call matches for every single character in source string --- .../org/prorefactor/proparse/PreproEval.java | 100 ++++++++++++++---- .../core/PreprocessorParserTest.java | 16 +++ 2 files changed, 94 insertions(+), 22 deletions(-) diff --git a/proparse/src/main/java/org/prorefactor/proparse/PreproEval.java b/proparse/src/main/java/org/prorefactor/proparse/PreproEval.java index 0a892a35f..318b5b771 100644 --- a/proparse/src/main/java/org/prorefactor/proparse/PreproEval.java +++ b/proparse/src/main/java/org/prorefactor/proparse/PreproEval.java @@ -14,6 +14,7 @@ ********************************************************************************/ package org.prorefactor.proparse; +import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.regex.Pattern; @@ -68,6 +69,8 @@ public class PreproEval extends PreprocessorParserBaseVisitor { private static final Logger LOGGER = LoggerFactory.getLogger(PreproEval.class); + private static final String TILDE_STAR = "\u0005"; + private static final String TILDE_DOT = "\u0006"; private final IProparseSettings settings; @@ -117,7 +120,7 @@ public Object visitStringOp(StringOpContext ctx) { Object o2 = visit(ctx.expr(1)); if (ctx.op.getType() == PreprocessorParser.MATCHES) { - return matches(o1, o2); + return matches(getString(o1), getString(o2)); } else { return getString(o1).toLowerCase().startsWith(getString(o2).toLowerCase()); } @@ -673,34 +676,87 @@ static Integer lookup(Object x, Object y, Object z) { return 0; } - static Boolean matches(Object y, Object z) { - String a = getString(y).toLowerCase(); - String b = getString(z).toLowerCase(); - // Completion conditions - if (b.length() == 1 && b.charAt(0) == '*') - return true; - if (a.length() == 0) { - return b.length() == 0; - } - if (b.length() == 0) + public static Boolean matches(String a, String b) { + if ((a == null) || (b == null)) return false; - // Match any single char - if (b.charAt(0) == '.') + // Sanitize input. Escaped stars and dots converted to non-printable character. + a = a.toLowerCase(); + b = b.toLowerCase().replace("~~", "~").replace("~*", TILDE_STAR).replace("~.", TILDE_DOT); + + // Empty pattern ? True if source is empty + if (b.isEmpty()) + return a.isEmpty(); + + // First character is a dot ? Consume one character + if (b.charAt(0) == '.') { + if (a.isEmpty()) + return false; return matches(a.substring(1), b.substring(1)); + } - // Match any number of chars + // First character a star ? if (b.charAt(0) == '*') { - return matches(a, b.substring(1)) || matches(a.substring(1), b); - } + // Short-circuit if pattern is just a star + if( b.length() == 1) + return true; - // Match an escaped char - if (b.charAt(0) == '~') { - return a.charAt(0) == b.charAt(1) && matches(a.substring(1), b.substring(2)); - } + // Consume the star(s) and dot(s), and text coming after that. Then recursive call with the remaining of the string + int offset1 = 1; // Offset of first non-star, non-dot character + int offsetSrc = 0; // Start offset in source string (depends on the number of dots) + while ((offset1 < b.length()) && ((b.charAt(offset1) == '*') || (b.charAt(offset1) == '.'))) { + if (b.charAt(offset1) == '.') + offsetSrc++; + offset1++; + } + int offset2 = offset1 + 1; // Offset of end of alphanumeric string + while ((offset2 < b.length()) && ((b.charAt(offset2) != '*') && (b.charAt(offset2) != '.'))) { + offset2++; + } - // Match a single specific char - return a.charAt(0) == b.charAt(0) && matches(a.substring(1), b.substring(1)); + // String to be consumed after the star + String str = ""; + if (offset2 > b.length()) { + if (offset1 <= b.length()) + str = b.substring(offset1); + } else + str = b.substring(offset1, offset2); + + // Empty string ? Then we just need to find at least enough chars for dots + if (str.isEmpty()) { + return a.length() > offsetSrc; + } + // All possible occurences of string to be consumed. We keep the remaining of the string in the list + List occurences = new ArrayList<>(); + while (offsetSrc < a.length()) { + int xx = a.indexOf(str, offsetSrc); + if (xx == -1) { + offsetSrc = a.length() + 1; + } else { + occurences.add(a.substring(xx + str.length())); + offsetSrc = xx + 1; + } + } + + // If remaining of pattern is valid, then return true + for (String s : occurences) { + if (matches(s, b.substring(offset2)).booleanValue()) + return true; + } + // No valid possibility + return false; + } else { + // First character is alphanumeric. Consume until next dot / star, then return matches of remaining string + int starPos = b.indexOf('*'); + int dotPos = b.indexOf('.'); + if ((starPos == -1) && (dotPos == -1)) { + return a.equals(b.replace(TILDE_STAR, "*").replace(TILDE_DOT, ".")); + } else { + int endPos = (starPos != -1) && (dotPos != -1) ? Math.min(starPos, dotPos) : Math.max(starPos, dotPos); + String substr = b.substring(0, endPos).replace(TILDE_STAR, "*").replace(TILDE_DOT, "."); + return a.startsWith(substr) && matches(a.substring(substr.length()), b.substring(substr.length())); + } + } } static Integer numentries(Object a, Object b) { diff --git a/proparse/src/test/java/org/prorefactor/core/PreprocessorParserTest.java b/proparse/src/test/java/org/prorefactor/core/PreprocessorParserTest.java index 2258c23fa..449261798 100644 --- a/proparse/src/test/java/org/prorefactor/core/PreprocessorParserTest.java +++ b/proparse/src/test/java/org/prorefactor/core/PreprocessorParserTest.java @@ -15,10 +15,12 @@ package org.prorefactor.core; import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertTrue; import java.io.File; import org.prorefactor.core.util.UnitTestModule; +import org.prorefactor.proparse.PreproEval; import org.prorefactor.refactor.RefactorSession; import org.prorefactor.treeparser.ParseUnit; import org.testng.Assert; @@ -410,4 +412,18 @@ public void testQStrings() { testVariable(unit.getTopNode(), "var81"); } + @Test + public void testMatchesFunction() { + assertTrue(PreproEval.matches("test1,test2,test3", "*test2*")); + assertTrue(PreproEval.matches("test1,test2,test3", "*test2*..")); + assertTrue(PreproEval.matches("test1,test2,test3", "*test2*.....")); + assertFalse(PreproEval.matches("test1,test2,test3", "*test2*.......")); + assertFalse(PreproEval.matches("test1,test2,test3", "*test2*.......*")); + assertFalse(PreproEval.matches("test1,test2,test3", "*test2.......**")); + assertFalse(PreproEval.matches("test1,test2,test3", "*test2**.......")); + + assertTrue(PreproEval.matches("xxxfoobarxxx", "*foo*bar*")); + assertFalse(PreproEval.matches("xxxfoobarxxx", "*foo*.*bar*")); + assertTrue(PreproEval.matches("xxxfooxbarxxx", "*foo*.*bar*")); + } }