From f4fc7453fd086e2ae8e31b4727823ca8e1d259c0 Mon Sep 17 00:00:00 2001 From: Tiago Bagni Date: Mon, 9 Oct 2023 21:23:52 -0500 Subject: [PATCH] Make non-regex filters faster --- .../com/tibagni/logviewer/filter/Filter.java | 109 ++++++++++-------- .../tibagni/logviewer/util/StringUtils.java | 11 ++ .../logviewer/util/StringUtilsTests.kt | 23 ++++ 3 files changed, 94 insertions(+), 49 deletions(-) diff --git a/src/main/java/com/tibagni/logviewer/filter/Filter.java b/src/main/java/com/tibagni/logviewer/filter/Filter.java index 6c47e23..e6ec1d3 100644 --- a/src/main/java/com/tibagni/logviewer/filter/Filter.java +++ b/src/main/java/com/tibagni/logviewer/filter/Filter.java @@ -17,12 +17,12 @@ public class Filter { private String name; private Color color; private LogLevel verbosity = LogLevel.VERBOSE; - private Pattern pattern; private int flags = Pattern.CASE_INSENSITIVE; private ContextInfo temporaryInfo; + private boolean isSimpleFilter; public boolean wasLoadedFromLegacyFile = false; private Filter() { } @@ -34,6 +34,7 @@ public Filter(Filter from) throws FilterException { applied = from.isApplied(); pattern = getPattern(from.pattern.pattern()); verbosity = from.verbosity; + isSimpleFilter = from.isSimpleFilter; if (temporaryInfo != null) { temporaryInfo = new ContextInfo(from.temporaryInfo); } @@ -69,6 +70,38 @@ public void updateFilter(String name, String pattern, Color color, LogLevel verb this.color = color; this.pattern = getPattern(pattern); this.verbosity = verbosity; + this.isSimpleFilter = !StringUtils.isPotentialRegex(pattern); + } + + public static Filter createFromString(String filterString) throws FilterException { + // See format in 'serializeFilter' + try { + String[] params = filterString.split(","); + if (params.length < 4) { + throw new IllegalArgumentException(); + } + + String[] rgb = params[3].split(":"); + if (rgb.length != 3) { + throw new IllegalArgumentException("Wrong color format"); + } + + boolean isLegacy = params.length == 4; + + String name = params[0]; + String pattern = StringUtils.decodeBase64(params[1]); + Color color = new Color(Integer.parseInt(rgb[0]), Integer.parseInt(rgb[1]), Integer.parseInt(rgb[2])); + LogLevel verbosity = isLegacy ? LogLevel.VERBOSE : LogLevel.valueOf(params[4]); + int flags = Integer.parseInt(params[2]); + boolean isCaseSensitive = (flags & Pattern.CASE_INSENSITIVE) == 0; + + Filter filter = new Filter(); + filter.updateFilter(name, pattern, color, verbosity, isCaseSensitive); + filter.wasLoadedFromLegacyFile = isLegacy; + return filter; + } catch (Exception e) { + throw new FilterException("Wrong filter format: " + filterString, e); + } } public boolean isApplied() { @@ -120,12 +153,23 @@ public boolean isCaseSensitive() { */ public boolean appliesTo(LogEntry entry) { String inputLine = entry.getLogText(); - boolean foundPattern = pattern.matcher(inputLine).find(); + boolean foundPattern = isSimpleFilter ? simpleMatch(inputLine) : regexMatch(inputLine); boolean isVerbosityAllowed = verbosity.ordinal() <= entry.logLevel.ordinal(); return foundPattern && isVerbosityAllowed; } + private boolean simpleMatch(String inputLine) { + if (isCaseSensitive()) { + return inputLine.contains(getPatternString()); + } + return inputLine.toLowerCase().contains(getPatternString().toLowerCase()); + } + + private boolean regexMatch(String inputLine) { + return pattern.matcher(inputLine).find(); + } + private Pattern getPattern(String pattern) throws FilterException { try { return Pattern.compile(pattern, flags); @@ -151,37 +195,21 @@ public String serializeFilter() { verbosity); } - public static Filter createFromString(String filterString) throws FilterException { - // See format in 'serializeFilter' - try { - String[] params = filterString.split(","); - if (params.length < 4) { - throw new IllegalArgumentException(); - } - - Filter filter = new Filter(); - filter.name = params[0]; - filter.flags = Integer.parseInt(params[2]); - filter.pattern = filter.getPattern(StringUtils.decodeBase64(params[1])); - - String[] rgb = params[3].split(":"); - if (rgb.length != 3) { - throw new IllegalArgumentException("Wrong color format"); - } - - filter.color = new Color(Integer.parseInt(rgb[0]), Integer.parseInt(rgb[1]), Integer.parseInt(rgb[2])); - - // Check if the filter has information about verbosity level - if (params.length > 4) { - filter.verbosity = LogLevel.valueOf(params[4]); - } else { - filter.wasLoadedFromLegacyFile = true; - } + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Filter filter = (Filter) o; + return flags == filter.flags && + Objects.equals(name, filter.name) && + Objects.equals(color, filter.color) && + Objects.equals(getPatternString(), filter.getPatternString()) && + Objects.equals(temporaryInfo, filter.temporaryInfo); + } - return filter; - } catch (Exception e) { - throw new FilterException("Wrong filter format: " + filterString, e); - } + @Override + public int hashCode() { + return Objects.hash(name, color, pattern, flags, temporaryInfo); } public static class ContextInfo { @@ -238,21 +266,4 @@ public int hashCode() { return Objects.hash(linesFound, allowedStreams); } } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - Filter filter = (Filter) o; - return flags == filter.flags && - Objects.equals(name, filter.name) && - Objects.equals(color, filter.color) && - Objects.equals(getPatternString(), filter.getPatternString()) && - Objects.equals(temporaryInfo, filter.temporaryInfo); - } - - @Override - public int hashCode() { - return Objects.hash(name, color, pattern, flags, temporaryInfo); - } } \ No newline at end of file diff --git a/src/main/java/com/tibagni/logviewer/util/StringUtils.java b/src/main/java/com/tibagni/logviewer/util/StringUtils.java index 8f244d1..7b485a9 100644 --- a/src/main/java/com/tibagni/logviewer/util/StringUtils.java +++ b/src/main/java/com/tibagni/logviewer/util/StringUtils.java @@ -79,4 +79,15 @@ private static String escape(String text) { public static String wrapHtml(String text) { return "" + text + ""; } + + public static boolean isPotentialRegex(String input) { + final String regexSpecialCharacters = ".*+?|()[]{}\\^$"; + for (char c : input.toCharArray()) { + if (regexSpecialCharacters.contains(String.valueOf(c))) { + return true; + } + } + + return false; + } } diff --git a/src/test/java/com/tibagni/logviewer/util/StringUtilsTests.kt b/src/test/java/com/tibagni/logviewer/util/StringUtilsTests.kt index d4a169e..a3a0360 100644 --- a/src/test/java/com/tibagni/logviewer/util/StringUtilsTests.kt +++ b/src/test/java/com/tibagni/logviewer/util/StringUtilsTests.kt @@ -54,4 +54,27 @@ class StringUtilsTests { fun testHtmlEscape() { assertEquals("<>&"", StringUtils.htmlEscape("<>&\"")) } + + @Test + fun testPotentialRegexForValidRegex() { + assertTrue(StringUtils.isPotentialRegex(".*")) + assertTrue(StringUtils.isPotentialRegex(".")) + assertTrue(StringUtils.isPotentialRegex("\\d{3}-\\d{2}-\\d{4}")) + assertTrue(StringUtils.isPotentialRegex("[A-Za-z]+")) + assertTrue(StringUtils.isPotentialRegex("\\d{1,3}")) + assertTrue(StringUtils.isPotentialRegex("(red|blue)")) + assertTrue(StringUtils.isPotentialRegex("a{5}")) + assertTrue(StringUtils.isPotentialRegex("a{3,5}")) + assertTrue(StringUtils.isPotentialRegex("^[A-Za-z]+$")) + assertTrue(StringUtils.isPotentialRegex("[0-9]{2,3}|[A-Za-z]{3,4}")) + assertTrue(StringUtils.isPotentialRegex("(\\d{3}-\\d{2}-\\d{4})|(\\d{4}-\\d{2}-\\d{2})")) + assertTrue(StringUtils.isPotentialRegex("word.word")) + } + + @Test + fun testPotentialRegexForNonRegex() { + assertFalse(StringUtils.isPotentialRegex("Not a regex")) + assertFalse(StringUtils.isPotentialRegex("This is a normal string")) + assertFalse(StringUtils.isPotentialRegex("12345")) + } } \ No newline at end of file