From db41fb4a1a8edb434df83d273c22fa7bc572ed4d Mon Sep 17 00:00:00 2001 From: Patrick Ziegler Date: Tue, 7 Jan 2025 19:15:55 +0100 Subject: [PATCH] Replace TextMatcher in favor of extended StringMatcher The functionality of the TextMatcher has been integrated in the StringMatcher class from the Equinox bundle, which allows this class to be used without a dependency to the Eclipse UI bundle. Note: To get the same behavior as the TextMatcher, one needs to call matchWords() instead of match(). Furthermore, the pattern needs to be trimmed explicitly, where needed. Contributes to https://github.com/eclipse-platform/eclipse.platform.ui/pull/2567 --- .../org/eclipse/ui/dialogs/FilteredList.java | 8 +- .../org/eclipse/ui/dialogs/PatternFilter.java | 10 +- .../org/eclipse/ui/dialogs/SearchPattern.java | 8 +- .../ui/internal/about/AboutPluginsPage.java | 14 +- .../eclipse/ui/internal/misc/TextMatcher.java | 169 ------------------ .../org/eclipse/ui/tests/UiTestSuite.java | 4 +- .../tests/filteredtree/FilteredTreeTests.java | 6 + .../tests/filteredtree/TextMatcherTest.java | 108 ----------- 8 files changed, 27 insertions(+), 300 deletions(-) delete mode 100644 bundles/org.eclipse.ui.workbench/eclipseui/org/eclipse/ui/internal/misc/TextMatcher.java delete mode 100644 tests/org.eclipse.ui.tests/Eclipse UI Tests/org/eclipse/ui/tests/filteredtree/TextMatcherTest.java diff --git a/bundles/org.eclipse.ui.workbench/eclipseui/org/eclipse/ui/dialogs/FilteredList.java b/bundles/org.eclipse.ui.workbench/eclipseui/org/eclipse/ui/dialogs/FilteredList.java index 56e69c48c5f..1180b1324e6 100644 --- a/bundles/org.eclipse.ui.workbench/eclipseui/org/eclipse/ui/dialogs/FilteredList.java +++ b/bundles/org.eclipse.ui.workbench/eclipseui/org/eclipse/ui/dialogs/FilteredList.java @@ -23,6 +23,7 @@ import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; +import org.eclipse.core.text.StringMatcher; import org.eclipse.jface.viewers.ILabelProvider; import org.eclipse.swt.SWT; import org.eclipse.swt.SWTException; @@ -36,7 +37,6 @@ import org.eclipse.swt.widgets.Table; import org.eclipse.swt.widgets.TableItem; import org.eclipse.ui.internal.WorkbenchMessages; -import org.eclipse.ui.internal.misc.TextMatcher; import org.eclipse.ui.internal.util.Util; import org.eclipse.ui.progress.WorkbenchJob; @@ -73,16 +73,16 @@ public interface FilterMatcher { } private class DefaultFilterMatcher implements FilterMatcher { - private TextMatcher fMatcher; + private StringMatcher fMatcher; @Override public void setFilter(String pattern, boolean ignoreCase, boolean ignoreWildCards) { - fMatcher = new TextMatcher(pattern + '*', ignoreCase, ignoreWildCards); + fMatcher = new StringMatcher(pattern.trim() + '*', ignoreCase, ignoreWildCards); } @Override public boolean match(Object element) { - return fMatcher.match(fLabelProvider.getText(element)); + return fMatcher.matchWords(fLabelProvider.getText(element)); } } diff --git a/bundles/org.eclipse.ui.workbench/eclipseui/org/eclipse/ui/dialogs/PatternFilter.java b/bundles/org.eclipse.ui.workbench/eclipseui/org/eclipse/ui/dialogs/PatternFilter.java index b27c7d89b43..87746c48cb8 100644 --- a/bundles/org.eclipse.ui.workbench/eclipseui/org/eclipse/ui/dialogs/PatternFilter.java +++ b/bundles/org.eclipse.ui.workbench/eclipseui/org/eclipse/ui/dialogs/PatternFilter.java @@ -17,13 +17,13 @@ import java.util.HashMap; import java.util.Map; +import org.eclipse.core.text.StringMatcher; import org.eclipse.jface.viewers.AbstractTreeViewer; import org.eclipse.jface.viewers.ContentViewer; import org.eclipse.jface.viewers.ILabelProvider; import org.eclipse.jface.viewers.ITreeContentProvider; import org.eclipse.jface.viewers.Viewer; import org.eclipse.jface.viewers.ViewerFilter; -import org.eclipse.ui.internal.misc.TextMatcher; /** * A filter used in conjunction with FilteredTree. In order to @@ -57,7 +57,7 @@ public class PatternFilter extends ViewerFilter { /** * The string pattern matcher used for this pattern filter. */ - private TextMatcher matcher; + private StringMatcher matcher; private boolean useEarlyReturnIfMatcherIsNull = true; @@ -173,7 +173,7 @@ public void setPattern(String patternString) { if (includeLeadingWildcard) { pattern = "*" + pattern; //$NON-NLS-1$ } - matcher = new TextMatcher(pattern, true, false); + matcher = new StringMatcher(pattern.trim(), true, false); } } @@ -197,7 +197,7 @@ private boolean match(String string) { if (matcher == null) { return true; } - return matcher.match(string); + return matcher.matchWords(string); } /** @@ -289,7 +289,7 @@ protected boolean wordMatches(String text) { } // Otherwise check if any of the words of the text matches - String[] words = TextMatcher.getWords(text); + String[] words = StringMatcher.getWords(text); for (String word : words) { if (!match(word)) { return false; diff --git a/bundles/org.eclipse.ui.workbench/eclipseui/org/eclipse/ui/dialogs/SearchPattern.java b/bundles/org.eclipse.ui.workbench/eclipseui/org/eclipse/ui/dialogs/SearchPattern.java index 0b219cab6f1..b30e9fdf835 100644 --- a/bundles/org.eclipse.ui.workbench/eclipseui/org/eclipse/ui/dialogs/SearchPattern.java +++ b/bundles/org.eclipse.ui.workbench/eclipseui/org/eclipse/ui/dialogs/SearchPattern.java @@ -13,8 +13,8 @@ *******************************************************************************/ package org.eclipse.ui.dialogs; +import org.eclipse.core.text.StringMatcher; import org.eclipse.jface.util.Util; -import org.eclipse.ui.internal.misc.TextMatcher; /** * A search pattern defines how search results are found. @@ -120,7 +120,7 @@ public class SearchPattern { private String initialPattern; - private TextMatcher stringMatcher; + private StringMatcher stringMatcher; private static final char START_SYMBOL = '>'; @@ -200,7 +200,7 @@ public void setPattern(String stringPattern) { initializePatternAndMatchRule(stringPattern); matchRule = matchRule & this.allowedRules; if (matchRule == RULE_PATTERN_MATCH) { - stringMatcher = new TextMatcher(this.stringPattern, true, false); + stringMatcher = new StringMatcher(this.stringPattern.trim(), true, false); } } @@ -219,7 +219,7 @@ public boolean matches(String text) { case RULE_BLANK_MATCH: return true; case RULE_PATTERN_MATCH: - return stringMatcher.match(text); + return stringMatcher.matchWords(text); case RULE_EXACT_MATCH: return stringPattern.equalsIgnoreCase(text); case RULE_CAMELCASE_MATCH: diff --git a/bundles/org.eclipse.ui.workbench/eclipseui/org/eclipse/ui/internal/about/AboutPluginsPage.java b/bundles/org.eclipse.ui.workbench/eclipseui/org/eclipse/ui/internal/about/AboutPluginsPage.java index f39c445e9b1..d733ee2473a 100644 --- a/bundles/org.eclipse.ui.workbench/eclipseui/org/eclipse/ui/internal/about/AboutPluginsPage.java +++ b/bundles/org.eclipse.ui.workbench/eclipseui/org/eclipse/ui/internal/about/AboutPluginsPage.java @@ -39,6 +39,7 @@ import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.SubMonitor; import org.eclipse.core.runtime.jobs.Job; +import org.eclipse.core.text.StringMatcher; import org.eclipse.jface.dialogs.IDialogConstants; import org.eclipse.jface.layout.GridDataFactory; import org.eclipse.jface.util.ConfigureColumns; @@ -73,7 +74,6 @@ import org.eclipse.ui.internal.WorkbenchMessages; import org.eclipse.ui.internal.WorkbenchPlugin; import org.eclipse.ui.internal.misc.StatusUtil; -import org.eclipse.ui.internal.misc.TextMatcher; import org.eclipse.ui.internal.util.BundleUtility; import org.eclipse.ui.progress.WorkbenchJob; import org.eclipse.ui.statushandlers.StatusManager; @@ -689,14 +689,14 @@ public void setAscending(boolean ascending) { } class BundlePatternFilter extends ViewerFilter { - private TextMatcher matcher; + private StringMatcher matcher; public void setPattern(String searchPattern) { if (searchPattern == null || searchPattern.isEmpty()) { this.matcher = null; } else { String pattern = "*" + searchPattern + "*"; //$NON-NLS-1$//$NON-NLS-2$ - this.matcher = new TextMatcher(pattern, true, false); + this.matcher = new StringMatcher(pattern, true, false); } } @@ -708,12 +708,12 @@ public boolean select(Viewer viewer, Object parentElement, Object element) { if (element instanceof AboutBundleData) { AboutBundleData data = (AboutBundleData) element; - return matcher.match(data.getName()) || matcher.match(data.getProviderName()) - || matcher.match(data.getId()); + return matcher.matchWords(data.getName()) || matcher.matchWords(data.getProviderName()) + || matcher.matchWords(data.getId()); } else if (element instanceof AboutBundleGroupData data) { - return matcher.match(data.getName()) || matcher.match(data.getProviderName()) - || matcher.match(data.getId()); + return matcher.matchWords(data.getName()) || matcher.matchWords(data.getProviderName()) + || matcher.matchWords(data.getId()); } return true; } diff --git a/bundles/org.eclipse.ui.workbench/eclipseui/org/eclipse/ui/internal/misc/TextMatcher.java b/bundles/org.eclipse.ui.workbench/eclipseui/org/eclipse/ui/internal/misc/TextMatcher.java deleted file mode 100644 index 6b07140a2f1..00000000000 --- a/bundles/org.eclipse.ui.workbench/eclipseui/org/eclipse/ui/internal/misc/TextMatcher.java +++ /dev/null @@ -1,169 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2020 Thomas Wolf and others. - * - * This program and the accompanying materials - * are made available under the terms of the Eclipse Public License 2.0 - * which accompanies this distribution, and is available at - * https://www.eclipse.org/legal/epl-2.0/ - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package org.eclipse.ui.internal.misc; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.Objects; -import java.util.regex.Pattern; -import org.eclipse.core.text.StringMatcher; - -/** - * Similar to {@link StringMatcher}, this {@code TextMatcher} matches a pattern - * that may contain the wildcards '?' or '*' against a text. However, the - * matching is not only done on the full text, but also on individual words from - * the text, and if the pattern contains whitespace, the pattern is split into - * sub-patterns and those are matched, too. - *

- * The precise rules are: - *

- * - *

- * An empty pattern matches only the empty text. - *

- */ -public final class TextMatcher { - - private static final Pattern NON_WORD = Pattern.compile("\\W+", Pattern.UNICODE_CHARACTER_CLASS); //$NON-NLS-1$ - - private final StringMatcher full; - - private final List parts; - - /** - * Creates a new {@link TextMatcher}. - * - * @param pattern to match - * @param ignoreCase whether to do case-insensitive matching - * @param ignoreWildCards whether to treat '?' and '*' as normal characters, not - * as wildcards - * @throws IllegalArgumentException if {@code pattern == null} - */ - public TextMatcher(String pattern, boolean ignoreCase, boolean ignoreWildCards) { - full = new StringMatcher(pattern.trim(), ignoreCase, ignoreWildCards); - parts = splitPattern(pattern, ignoreCase, ignoreWildCards); - } - - private List splitPattern(String pattern, - boolean ignoreCase, boolean ignoreWildCards) { - String pat = pattern.trim(); - if (pat.isEmpty()) { - return Collections.emptyList(); - } - String[] subPatterns = pat.split("\\s+"); //$NON-NLS-1$ - if (subPatterns.length <= 1) { - return Collections.emptyList(); - } - List matchers = new ArrayList<>(); - for (String s : subPatterns) { - if (s == null || s.isEmpty()) { - continue; - } - StringMatcher m = new StringMatcher(s, ignoreCase, ignoreWildCards); - m.usePrefixMatch(); - matchers.add(m); - } - return matchers; - } - - /** - * Determines whether the given {@code text} matches the pattern. - * - * @param text String to match; must not be {@code null} - * @return {@code true} if the whole {@code text} matches the pattern; - * {@code false} otherwise - * @throws IllegalArgumentException if {@code text == null} - */ - public boolean match(String text) { - if (text == null) { - throw new IllegalArgumentException(); - } - return match(text, 0, text.length()); - } - - /** - * Determines whether the given sub-string of {@code text} from {@code start} - * (inclusive) to {@code end} (exclusive) matches the pattern. - * - * @param text String to match in; must not be {@code null} - * @param start start index (inclusive) within {@code text} of the sub-string to - * match - * @param end end index (exclusive) within {@code text} of the sub-string to - * match - * @return {@code true} if the given slice of {@code text} matches the pattern; - * {@code false} otherwise - * @throws IllegalArgumentException if {@code text == null} - */ - public boolean match(String text, int start, int end) { - if (text == null) { - throw new IllegalArgumentException(); - } - if (start > end) { - return false; - } - int tlen = text.length(); - start = Math.max(0, start); - end = Math.min(end, tlen); - if (full.match(text, start, end)) { - return true; - } - String[] words = getWords(text.substring(start, end)); - if (match(full, words)) { - return true; - } - if (parts.isEmpty()) { - return false; - } - for (StringMatcher subMatcher : parts) { - if (!subMatcher.match(text, start, end) && !match(subMatcher, words)) { - return false; - } - } - return true; - } - - private boolean match(StringMatcher matcher, String[] words) { - return Arrays.stream(words).filter(Objects::nonNull).anyMatch(matcher::match); - } - - /** - * Splits a given text into words. - * - * @param text to split - * @return the words of the text - */ - public static String[] getWords(String text) { - // Previous implementations (in the removed StringMatcher) used the ICU - // BreakIterator to split the text. That worked well, but in 2020 it was decided - // to drop the dependency to the ICU library due to its size. The JDK - // BreakIterator splits differently, causing e.g. - // https://bugs.eclipse.org/bugs/show_bug.cgi?id=563121 . The NON_WORD regexp - // appears to work well for programming language text, but may give sub-optimal - // results for natural languages. See also - // https://bugs.eclipse.org/bugs/show_bug.cgi?id=90579 . - return NON_WORD.split(text); - } - - @Override - public String toString() { - return '[' + full.toString() + ',' + parts + ']'; - } -} diff --git a/tests/org.eclipse.ui.tests/Eclipse UI Tests/org/eclipse/ui/tests/UiTestSuite.java b/tests/org.eclipse.ui.tests/Eclipse UI Tests/org/eclipse/ui/tests/UiTestSuite.java index 8664529313e..008255bf985 100644 --- a/tests/org.eclipse.ui.tests/Eclipse UI Tests/org/eclipse/ui/tests/UiTestSuite.java +++ b/tests/org.eclipse.ui.tests/Eclipse UI Tests/org/eclipse/ui/tests/UiTestSuite.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2019 IBM Corporation and others. + * Copyright (c) 2000, 2025 IBM Corporation and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 @@ -37,7 +37,6 @@ import org.eclipse.ui.tests.fieldassist.FieldAssistTestSuite; import org.eclipse.ui.tests.filteredtree.FilteredTreeTests; import org.eclipse.ui.tests.filteredtree.PatternFilterTest; -import org.eclipse.ui.tests.filteredtree.TextMatcherTest; import org.eclipse.ui.tests.internal.InternalTestSuite; import org.eclipse.ui.tests.intro.IntroTestSuite; import org.eclipse.ui.tests.keys.KeysTestSuite; @@ -91,7 +90,6 @@ ConcurrencyTestSuite.class, FilteredTreeTests.class, PatternFilterTest.class, - TextMatcherTest.class, StatusHandlingTestSuite.class, MenusTestSuite.class, QuickAccessTestSuite.class, diff --git a/tests/org.eclipse.ui.tests/Eclipse UI Tests/org/eclipse/ui/tests/filteredtree/FilteredTreeTests.java b/tests/org.eclipse.ui.tests/Eclipse UI Tests/org/eclipse/ui/tests/filteredtree/FilteredTreeTests.java index 190d8d6367e..c8975a4d898 100644 --- a/tests/org.eclipse.ui.tests/Eclipse UI Tests/org/eclipse/ui/tests/filteredtree/FilteredTreeTests.java +++ b/tests/org.eclipse.ui.tests/Eclipse UI Tests/org/eclipse/ui/tests/filteredtree/FilteredTreeTests.java @@ -117,6 +117,12 @@ public void testAddAndRemovePattern() { applyPattern("0-0-0-0 name-*"); assertNumberOfTopLevelItems(1); + applyPattern(" 0-0-0-0 name-*"); + assertNumberOfTopLevelItems(1); + + applyPattern("0-0-0-0 name-* "); + assertNumberOfTopLevelItems(1); + applyPattern("0-0-0-0 name unknownWord"); assertNumberOfTopLevelItems(0); diff --git a/tests/org.eclipse.ui.tests/Eclipse UI Tests/org/eclipse/ui/tests/filteredtree/TextMatcherTest.java b/tests/org.eclipse.ui.tests/Eclipse UI Tests/org/eclipse/ui/tests/filteredtree/TextMatcherTest.java deleted file mode 100644 index 7f817e79951..00000000000 --- a/tests/org.eclipse.ui.tests/Eclipse UI Tests/org/eclipse/ui/tests/filteredtree/TextMatcherTest.java +++ /dev/null @@ -1,108 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2020 Thomas Wolf and others. - * - * This program and the accompanying materials - * are made available under the terms of the Eclipse Public License 2.0 - * which accompanies this distribution, and is available at - * https://www.eclipse.org/legal/epl-2.0/ - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package org.eclipse.ui.tests.filteredtree; - -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -import org.eclipse.ui.internal.misc.TextMatcher; -import org.junit.Test; - -/** - * Tests for {@link TextMatcher}. - */ -public class TextMatcherTest { - - @Test - public void testEmpty() { - assertTrue(new TextMatcher("", false, false).match("")); - assertFalse(new TextMatcher("", false, false).match("foo")); - assertFalse(new TextMatcher("", false, false).match("foo bar baz")); - assertTrue(new TextMatcher("", false, true).match("")); - assertFalse(new TextMatcher("", false, true).match("foo")); - assertFalse(new TextMatcher("", false, true).match("foo bar baz")); - } - - @Test - public void testSuffixes() { - assertFalse(new TextMatcher("fo*ar", false, false).match("foobar_123")); - assertFalse(new TextMatcher("fo*ar", false, false).match("foobar_baz")); - } - - @Test - public void testChinese() { - assertTrue(new TextMatcher("喜欢", false, false).match("我 喜欢 吃 苹果。")); - // This test would work only if word-splitting used the ICU BreakIterator. - // "Words" are as shown above. - // assertTrue(new TextMatcher("喜欢", false, false).match("我喜欢吃苹果。")); - } - - @Test - public void testSingleWords() { - assertTrue(new TextMatcher("huhn", false, false).match("hahn henne hühner küken huhn")); - assertTrue(new TextMatcher("h?hner", false, false).match("hahn henne hühner küken huhn")); - assertTrue(new TextMatcher("h*hner", false, false).match("hahn henne hühner küken huhn")); - assertTrue(new TextMatcher("hühner", false, false).match("hahn henne hühner küken huhn")); - // Full pattern must match word fully - assertFalse(new TextMatcher("h?hner", false, false).match("hahn henne hühnerhof küken huhn")); - assertFalse(new TextMatcher("h*hner", false, false).match("hahn henne hühnerhof küken huhn")); - assertFalse(new TextMatcher("hühner", false, false).match("hahn henne hühnerhof küken huhn")); - - assertTrue(new TextMatcher("huhn", false, true).match("hahn henne hühner küken huhn")); - assertFalse(new TextMatcher("h?hner", false, true).match("hahn henne hühner küken huhn")); - assertFalse(new TextMatcher("h*hner", false, true).match("hahn henne hühner küken huhn")); - assertTrue(new TextMatcher("hühner", false, true).match("hahn henne hühner küken huhn")); - // Full pattern must match word fully - assertFalse(new TextMatcher("h?hner", false, true).match("hahn henne hühnerhof küken huhn")); - assertFalse(new TextMatcher("h*hner", false, true).match("hahn henne hühnerhof küken huhn")); - assertFalse(new TextMatcher("hühner", false, true).match("hahn henne hühnerhof küken huhn")); - - // Bug 570390: Pattern starting/ending with whitespace should still match - assertTrue(new TextMatcher("hahn ", false, false).match("hahn henne hühnerhof küken huhn")); - assertTrue(new TextMatcher("huhn ", false, false).match("hahn henne hühnerhof küken huhn")); - assertTrue(new TextMatcher(" hahn", false, false).match("hahn henne hühnerhof küken huhn")); - assertTrue(new TextMatcher(" huhn", false, false).match("hahn henne hühnerhof küken huhn")); - } - - @Test - public void testMultipleWords() { - assertTrue(new TextMatcher("huhn h?hner", false, false).match("hahn henne hühner küken huhn")); - assertTrue(new TextMatcher("huhn h?hner", false, false).match("hahn henne hühnerhof küken huhn")); - assertFalse(new TextMatcher("huhn h?hner", false, true).match("hahn henne hühner küken huhn")); - assertFalse(new TextMatcher("huhn h?hner", false, true).match("hahn henne hühnerhof küken huhn")); - assertTrue(new TextMatcher("huhn h*hner", false, false).match("hahn henne hühner küken huhn")); - assertTrue(new TextMatcher("huhn h*hner", false, false).match("hahn henne hühnerhof küken huhn")); - assertFalse(new TextMatcher("huhn h*hner", false, true).match("hahn henne hühner küken huhn")); - assertFalse(new TextMatcher("huhn h*hner", false, true).match("hahn henne hühnerhof küken huhn")); - assertTrue(new TextMatcher("huhn hühner", false, false).match("hahn henne hühner küken huhn")); - assertTrue(new TextMatcher("huhn hühner", false, false).match("hahn henne hühnerhof küken huhn")); - assertTrue(new TextMatcher("huhn hühner", false, true).match("hahn henne hühner küken huhn")); - assertTrue(new TextMatcher("huhn hühner", false, true).match("hahn henne hühnerhof küken huhn")); - - // Bug 570390: Pattern starting/ending with whitespace should still match - assertTrue(new TextMatcher("huhn hahn ", false, false).match("hahn henne hühnerhof küken huhn")); - assertTrue(new TextMatcher("hahn huhn ", false, false).match("hahn henne hühnerhof küken huhn")); - assertTrue(new TextMatcher(" huhn hahn", false, false).match("hahn henne hühnerhof küken huhn")); - assertTrue(new TextMatcher(" hahn huhn", false, false).match("hahn henne hühnerhof küken huhn")); - } - - @Test - public void testCaseInsensitivity() { - assertTrue(new TextMatcher("Huhn HÜHNER", true, false).match("hahn henne hühner küken huhn")); - assertTrue(new TextMatcher("Huhn HÜHNER", true, false).match("hahn henne hühnerhof küken huhn")); - assertTrue(new TextMatcher("Huhn HÜHNER", true, true).match("hahn henne hühner küken huhn")); - assertTrue(new TextMatcher("Huhn HÜHNER", true, true).match("hahn henne hühnerhof küken huhn")); - assertTrue(new TextMatcher("HüHnEr", true, false).match("hahn henne hühner küken huhn")); - assertFalse(new TextMatcher("HüHnEr", true, false).match("hahn henne hühnerhof küken huhn")); - assertTrue(new TextMatcher("HüHnEr", true, true).match("hahn henne hühner küken huhn")); - assertFalse(new TextMatcher("HüHnEr", true, true).match("hahn henne hühnerhof küken huhn")); - } -}