From 6812d3f43bce26436e9b4e5fd4e012c10a14e30b Mon Sep 17 00:00:00 2001 From: peter Date: Fri, 6 Jul 2012 17:48:22 +0200 Subject: [PATCH] file structure wants uppercase after asterisk to be really upper --- .../com/intellij/psi/util/NameUtilTest.java | 922 +++++++++--------- .../psi/codeStyle/MinusculeMatcher.java | 648 ++++++------ 2 files changed, 785 insertions(+), 785 deletions(-) diff --git a/platform/platform-tests/testSrc/com/intellij/psi/util/NameUtilTest.java b/platform/platform-tests/testSrc/com/intellij/psi/util/NameUtilTest.java index 1b0535d3e279..70b4197bf47f 100644 --- a/platform/platform-tests/testSrc/com/intellij/psi/util/NameUtilTest.java +++ b/platform/platform-tests/testSrc/com/intellij/psi/util/NameUtilTest.java @@ -1,461 +1,461 @@ -/* - * Copyright 2000-2012 JetBrains s.r.o. - * - * Licensed 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. - */ - -/* - * @author max - */ -package com.intellij.psi.util; - -import com.intellij.openapi.util.TextRange; -import com.intellij.psi.codeStyle.MinusculeMatcher; -import com.intellij.psi.codeStyle.NameUtil; -import com.intellij.testFramework.PlatformTestUtil; -import com.intellij.testFramework.UsefulTestCase; -import com.intellij.util.ThrowableRunnable; -import com.intellij.util.containers.CollectionFactory; -import com.intellij.util.text.Matcher; -import org.jetbrains.annotations.NonNls; - -import java.util.ArrayList; -import java.util.List; - -/** - * @author peter - * @author Konstantin Bulenkov - */ -public class NameUtilTest extends UsefulTestCase { - - public void testSimpleCases() throws Exception { - assertMatches("N", "NameUtilTest"); - assertMatches("NU", "NameUtilTest"); - assertMatches("NUT", "NameUtilTest"); - assertMatches("NaUT", "NameUtilTest"); - assertDoesntMatch("NeUT", "NameUtilTest"); - assertDoesntMatch("NaUTa", "NameUtilTest"); - assertMatches("NaUtT", "NameUtilTest"); - assertMatches("NaUtT", "NameUtilTest"); - assertMatches("NaUtTe", "NameUtilTest"); - assertMatches("AACl", "AAClass"); - assertMatches("ZZZ", "ZZZZZZZZZZ"); - } - - public void testSimpleCasesWithFirstLowercased() throws Exception { - assertMatches("N", "nameUtilTest"); - assertDoesntMatch("N", "anameUtilTest"); - assertMatches("NU", "nameUtilTest"); - assertDoesntMatch("NU", "anameUtilTest"); - assertMatches("NUT", "nameUtilTest"); - assertMatches("NaUT", "nameUtilTest"); - assertDoesntMatch("NeUT", "nameUtilTest"); - assertDoesntMatch("NaUTa", "nameUtilTest"); - assertMatches("NaUtT", "nameUtilTest"); - assertMatches("NaUtT", "nameUtilTest"); - assertMatches("NaUtTe", "nameUtilTest"); - } - - public void testSpaceDelimiters() throws Exception { - assertMatches("Na Ut Te", "name util test"); - assertMatches("Na Ut Te", "name Util Test"); - assertDoesntMatch("Na Ut Ta", "name Util Test"); - assertMatches("na ut te", "name util test"); - - assertMatches("na ut", "name_util_test"); - assertMatches("na te", "name_util_test"); - assertDoesntMatch("na ti", "name_util_test"); - } - - public void testXMLCompletion() throws Exception { - assertDoesntMatch("N_T", "NameUtilTest"); - assertDoesntMatch("ORGS_ACC", "ORGS_POSITION_ACCOUNTABILITY"); - assertDoesntMatch("ORGS-ACC", "ORGS-POSITION_ACCOUNTABILITY"); - assertDoesntMatch("ORGS.ACC", "ORGS.POSITION_ACCOUNTABILITY"); - } - - public void testStarFalsePositive() throws Exception { - assertDoesntMatch("ar*l*p", "AbstractResponseHandler"); - } - - public void testUnderscoreStyle() throws Exception { - assertMatches("N_U_T", "NAME_UTIL_TEST"); - assertMatches("NUT", "NAME_UTIL_TEST"); - assertDoesntMatch("NUT", "NameutilTest"); - } - - public void testAllUppercase() { - assertMatches("NOS", "NetOutputStream"); - } - - public void testCommonFileNameConventions() throws Exception { - // See IDEADEV-12310 - - assertMatches("BLWN", "base_layout_without_navigation.xhtml"); - assertMatches("BLWN", "base-layout-without-navigation.xhtml"); - assertMatches("FC", "faces-config.xml"); - assertMatches("ARS", "activity_report_summary.jsp"); - assertMatches("AD", "arrow_down.gif"); - assertMatches("VL", "vehicle-listings.css"); - - assertMatches("ARS.j", "activity_report_summary.jsp"); - assertDoesntMatch("ARS.j", "activity_report_summary.xml"); - assertDoesntMatch("ARS.j", "activity_report_summary_justsometingwrong.xml"); - - assertDoesntMatch("foo.goo", "foo.bar.goo"); - } - - public void testSpaceForAnyWordsInBetween() { - assertMatches("fo bar", "fooBar"); - assertMatches("foo bar", "fooBar"); - assertMatches("foo bar", "fooGooBar"); - assertMatches("foo bar", "fooGoo bar"); - assertDoesntMatch(" b", "fbi"); - assertDoesntMatch(" for", "performAction"); - assertTrue(caseInsensitiveMatcher(" us").matches("getUsage")); - assertTrue(caseInsensitiveMatcher(" us").matches("getMyUsage")); - } - - private static Matcher caseInsensitiveMatcher(String pattern) { - return NameUtil.buildMatcher(pattern, NameUtil.MatchingCaseSensitivity.NONE); - } - - public void testStartDot() { - assertMatches("A*.html", "A.html"); - assertMatches("A*.html", "Abc.html"); - assertMatches("A*.html", "after.html"); - assertDoesntMatch("A*.html", "10_after.html"); - } - - public void testIDEADEV15503() throws Exception { - assertMatches("AR.jsp", "add_relationship.jsp"); - assertMatches("AR.jsp", "advanced_rule.jsp"); - assertMatches("AR.jsp", "alarm_reduction.jsp"); - assertMatches("AR.jsp", "audiot_report.jsp"); - assertMatches("AR.jsp", "audiot_r.jsp"); - - assertMatches("AR.jsp", "alarm_rule_action.jsp"); - assertMatches("AR.jsp", "alarm_rule_admin.jsp"); - assertMatches("AR.jsp", "alarm_rule_administration.jsp"); - assertMatches("AR.jsp", "alarm_rule_controller.jsp"); - assertMatches("AR.jsp", "alarm_rule_frame.jsp"); - assertMatches("AR.jsp", "alarm_rule_severity.jsp"); - - assertMatches("AR.jsp", "AddRelationship.jsp"); - assertMatches("AR.jsp", "AdvancedRule.jsp"); - assertMatches("AR.jsp", "AlarmReduction.jsp"); - assertMatches("AR.jsp", "AudiotReport.jsp"); - assertMatches("AR.jsp", "AudiotR.jsp"); - - assertMatches("AR.jsp", "AlarmRuleAction.jsp"); - assertMatches("AR.jsp", "AlarmRuleAdmin.jsp"); - assertMatches("AR.jsp", "AlarmRuleAdministration.jsp"); - assertMatches("AR.jsp", "AlarmRuleController.jsp"); - assertMatches("AR.jsp", "AlarmRuleFrame.jsp"); - assertMatches("AR.jsp", "AlarmRuleSeverity.jsp"); - } - - public void testSkipDot() { - assertMatches("ja", "jquery.autocomplete.js"); - assertMatches("ja.js", "jquery.autocomplete.js"); - assertDoesntMatch("jajs", "jquery.autocomplete.js"); - assertDoesntMatch("j.ajs", "jquery.autocomplete.js"); - } - - public void testNoExtension() { - assertMatches("#.p", "#.php"); - assertMatches("#", "#.php"); - assertMatches("a", "a.php"); - } - - public void testIgnoreCaseWhenCompleteMatch() { - assertMatches("comboBox", "combobox"); - assertMatches("combobox", "comboBox"); - } - - public void testStartsWithDot() throws Exception { - assertMatches(".foo", ".foo"); - } - - public void testProperDotEscaping() throws Exception { - assertMatches("*inspection*.pro", "InspectionsBundle.properties"); - assertDoesntMatch("*inspection*.pro", "InspectionsInProgress.png"); - } - - public void testIgnoreLeadingUnderscore() throws Exception { - assertMatches("form", "_form.html.erb"); - assertMatches("_form", "_form.html.erb"); - assertMatches("_form", "__form"); - assertFalse(NameUtil.buildMatcher("_form", 1, true, true, false).matches("__form")); - } - - public void testLowerCaseWords() throws Exception { - assertTrue(matches("uct", "unit_controller_test")); - assertTrue(matches("unictest", "unit_controller_test")); - assertTrue(matches("uc", "unit_controller_test")); - assertFalse(matches("nc", "unit_controller_test")); - assertFalse(matches("utc", "unit_controller_test")); - } - - public void testObjectiveCCases() throws Exception { - assertMatches("text:sh", "textField:shouldChangeCharactersInRange:replacementString:"); - assertMatches("abc", "aaa:bbb:ccc"); - assertMatches("textField:sh", "textField:shouldChangeCharactersInRange:replacementString:"); - assertMatches("text*:sh", "textField:shouldChangeCharactersInRange:replacementString:"); - } - - public void testMiddleMatching() { - assertTrue(caseInsensitiveMatcher("*OLD").matches("folder")); - assertTrue(caseInsensitiveMatcher("*old").matches("folder")); - assertMatches("SWU*H*7", "SWUpgradeHdlrFSPR7Test"); - assertMatches("SWU*H*R", "SWUpgradeHdlrFSPR7Test"); - assertMatches("SWU*H*R", "SWUPGRADEHDLRFSPR7TEST"); - assertMatches("*git", "GitBlaBla"); - assertMatches("*Git", "GitBlaBla"); - assertMatches("*git", "BlaGitBla"); - assertMatches("*Git", "BlaGitBla"); - assertFalse(firstLetterMatcher("*Git").matches("BlagitBla")); - assertMatches("*git", "BlagitBla"); - assertMatches("*Git*", "AtpGenerationItem"); - assertMatches("Collec*Util*", "CollectionUtils"); - assertMatches("Collec*Util*", "CollectionUtilsTest"); - assertTrue(caseInsensitiveMatcher("*us").matches("usage")); - assertTrue(caseInsensitiveMatcher(" us").matches("usage")); - assertTrue(caseInsensitiveMatcher(" fo. ba").matches("getFoo.getBar")); - assertMatches(" File. sepa", "File.separator"); - assertMatches(" File. sepa", "File._separator"); - assertMatches(" File. _sepa", "File._separator"); - assertMatches(" _fo", "_foo"); - } - - public void testMiddleMatchingFirstLetterSensitive() { - assertTrue(firstLetterMatcher(" cl").matches("getClass")); - assertTrue(firstLetterMatcher(" EUC-").matches("x-EUC-TW")); - assertTrue(firstLetterMatcher(" a").matches("aaa")); - assertFalse(firstLetterMatcher(" a").matches("Aaa")); - assertFalse(firstLetterMatcher(" a").matches("Aaa")); - assertFalse(firstLetterMatcher(" _bl").matches("_top")); - } - - private static Matcher firstLetterMatcher(String pattern) { - return NameUtil.buildMatcher(pattern, NameUtil.MatchingCaseSensitivity.FIRST_LETTER); - } - - public void testSpaceInCompletionPrefix() throws Exception { - assertTrue(caseInsensitiveMatcher("create ").matches("create module")); - } - - public void testLong() throws Exception { - assertTrue(matches("Product.findByDateAndNameGreaterThanEqualsAndQualityGreaterThanEqual", "Product.findByDateAndNameGreaterThanEqualsAndQualityGreaterThanEqualsIntellijIdeaRulezzz")); - } - - private static void assertMatches(@NonNls String pattern, @NonNls String name) { - assertTrue(matches(pattern, name)); - } - private static void assertDoesntMatch(@NonNls String pattern, @NonNls String name) { - assertFalse(matches(pattern, name)); - } - - private static boolean matches(@NonNls final String pattern, @NonNls final String name) { - return caseInsensitiveMatcher(pattern).matches(name); - } - - public void testLowerCaseHumps() { - assertMatches("foo", "foo"); - assertDoesntMatch("foo", "fxoo"); - assertMatches("foo", "fOo"); - assertMatches("foo", "fxOo"); - assertDoesntMatch("foo", "fXOo"); - assertMatches("fOo", "foo"); - assertMatches("fOo", "FaOaOaXXXX"); - assertMatches("ncdfoe", "NoClassDefFoundException"); - assertMatches("fob", "FOO_BAR"); - assertMatches("fo_b", "FOO_BAR"); - assertMatches("fob", "FOO BAR"); - assertMatches("fo b", "FOO BAR"); - assertMatches("AACl", "AAClass"); - assertMatches("ZZZ", "ZZZZZZZZZZ"); - assertMatches("em", "emptyList"); - assertMatches("bui", "BuildConfig.groovy"); - assertMatches("buico", "BuildConfig.groovy"); - assertMatches("buico.gr", "BuildConfig.groovy"); - assertMatches("bui.gr", "BuildConfig.groovy"); - assertMatches("*fz", "azzzfzzz"); - - assertMatches("WebLogic", "Weblogic"); - assertMatches("WebLOgic", "WebLogic"); - assertMatches("WEbLogic", "WebLogic"); - assertDoesntMatch("WebLogic", "Webologic"); - } - - public void testFinalSpace() { - assertMatches("GrDebT ", "GroovyDebuggerTest"); - assertMatches("grdebT ", "GroovyDebuggerTest"); - assertDoesntMatch("grdebt ", "GroovyDebuggerTest"); - assertMatches("Foo ", "Foo"); - assertDoesntMatch("Foo ", "FooBar"); - assertDoesntMatch("Foo ", "Foox"); - assertDoesntMatch("Collections ", "CollectionSplitter"); - assertMatches("CollectionS ", "CollectionSplitter"); - } - - public void testDigits() { - assertMatches("foba4", "FooBar4"); - assertMatches("foba", "Foo4Bar"); - assertMatches("*TEST-* ", "TEST-001"); - assertMatches("*TEST-0* ", "TEST-001"); - } - - public void testSpecialSymbols() { - assertMatches("a@b", "a@bc"); - - assertMatches("a/text", "a/Text"); - assertDoesntMatch("a/text", "a/bbbText"); - } - - public void testMinusculeFirstLetter() { - assertTrue(new MinusculeMatcher("WebLogic", NameUtil.MatchingCaseSensitivity.FIRST_LETTER).matches("WebLogic")); - assertFalse(new MinusculeMatcher("webLogic", NameUtil.MatchingCaseSensitivity.FIRST_LETTER).matches("WebLogic")); - assertTrue(new MinusculeMatcher("cL", NameUtil.MatchingCaseSensitivity.FIRST_LETTER).matches("class")); - assertTrue(new MinusculeMatcher("CL", NameUtil.MatchingCaseSensitivity.FIRST_LETTER).matches("Class")); - assertFalse(new MinusculeMatcher("abc", NameUtil.MatchingCaseSensitivity.FIRST_LETTER).matches("_abc")); - } - - public void testMinusculeAllImportant() { - assertTrue(new MinusculeMatcher("WebLogic", NameUtil.MatchingCaseSensitivity.ALL).matches("WebLogic")); - assertFalse(new MinusculeMatcher("webLogic", NameUtil.MatchingCaseSensitivity.ALL).matches("weblogic")); - assertFalse(new MinusculeMatcher("FOO", NameUtil.MatchingCaseSensitivity.ALL).matches("foo")); - assertFalse(new MinusculeMatcher("foo", NameUtil.MatchingCaseSensitivity.ALL).matches("fOO")); - assertFalse(new MinusculeMatcher("Wl", NameUtil.MatchingCaseSensitivity.ALL).matches("WebLogic")); - assertTrue(new MinusculeMatcher("WL", NameUtil.MatchingCaseSensitivity.ALL).matches("WebLogic")); - assertFalse(new MinusculeMatcher("WL", NameUtil.MatchingCaseSensitivity.ALL).matches("Weblogic")); - assertFalse(new MinusculeMatcher("WL", NameUtil.MatchingCaseSensitivity.ALL).matches("weblogic")); - assertFalse(new MinusculeMatcher("webLogic", NameUtil.MatchingCaseSensitivity.ALL).matches("WebLogic")); - assertFalse(new MinusculeMatcher("Str", NameUtil.MatchingCaseSensitivity.ALL).matches("SomeThingRidiculous")); - } - - public void testMatchingFragments() { - @NonNls String sample = "NoClassDefFoundException"; - // 0 2 7 10 15 21 - assertOrderedEquals(new MinusculeMatcher("ncldfou*ion", NameUtil.MatchingCaseSensitivity.NONE).matchingFragments(sample), - TextRange.from(0, 1), TextRange.from(2, 2), TextRange.from(7, 1), TextRange.from(10, 3), TextRange.from(21, 3)); - - sample = "doGet(HttpServletRequest, HttpServletResponse):void"; - // 0 22 - assertOrderedEquals(new MinusculeMatcher("d*st", NameUtil.MatchingCaseSensitivity.NONE).matchingFragments(sample), - TextRange.from(0, 1), TextRange.from(22, 2)); - assertOrderedEquals(new MinusculeMatcher("doge*st", NameUtil.MatchingCaseSensitivity.NONE).matchingFragments(sample), - TextRange.from(0, 4), TextRange.from(22, 2)); - - sample = "_test"; - assertOrderedEquals(new MinusculeMatcher("_", NameUtil.MatchingCaseSensitivity.NONE).matchingFragments(sample), - TextRange.from(0, 1)); - assertOrderedEquals(new MinusculeMatcher("_t", NameUtil.MatchingCaseSensitivity.NONE).matchingFragments(sample), - TextRange.from(0, 2)); - } - - public void testMatchingFragmentsSorted() { - @NonNls String sample = "SWUPGRADEHDLRFSPR7TEST"; - // 0 9 12 - assertOrderedEquals(new MinusculeMatcher("SWU*H*R", NameUtil.MatchingCaseSensitivity.NONE).matchingFragments(sample), - TextRange.from(0, 3), TextRange.from(9, 1), TextRange.from(12, 1)); - } - - public void testPreferCapsMatching() { - String sample = "getCurrentUser"; - // 0 4 10 - assertOrderedEquals(new MinusculeMatcher("getCU", NameUtil.MatchingCaseSensitivity.NONE).matchingFragments(sample), - TextRange.from(0, 4), TextRange.from(10, 1)); - } - - public void testMatchingDegree() { - assertPreference("jscote", "JsfCompletionTest", "JSCompletionTest", NameUtil.MatchingCaseSensitivity.NONE); - assertPreference("OCO", "OneCoolObject", "OCObject"); - assertPreference("MUp", "MavenUmlProvider", "MarkUp"); - assertPreference("MUP", "MarkUp", "MavenUmlProvider"); - assertPreference("CertificateExce", "CertificateEncodingException", "CertificateException"); - assertPreference("boo", "Boolean", "boolean", NameUtil.MatchingCaseSensitivity.NONE); - assertPreference("Boo", "boolean", "Boolean", NameUtil.MatchingCaseSensitivity.NONE); - assertPreference("getCU", "getCurrentSomething", "getCurrentUser"); - assertPreference("cL", "class", "coreLoader"); - assertPreference("cL", "class", "classLoader"); - } - - public void testPreferStartMatchToMiddleMatch() { - assertPreference(" fb", "FooBar", "_fooBar", NameUtil.MatchingCaseSensitivity.NONE); - assertPreference("*foo", "barFoo", "foobar"); - assertPreference("*f", "barfoo", "barFoo"); - assertPreference("*f", "barfoo", "foo"); - assertPreference("*f", "asdf", "Foo", NameUtil.MatchingCaseSensitivity.NONE); - assertPreference(" sto", "ArrayStoreException", "StackOverflowError", NameUtil.MatchingCaseSensitivity.NONE); - assertPreference(" EUC-", "x-EUC-TW", "EUC-JP"); - assertPreference(" boo", "Boolean", "boolean", NameUtil.MatchingCaseSensitivity.NONE); - assertPreference(" Boo", "boolean", "Boolean", NameUtil.MatchingCaseSensitivity.NONE); - } - - public void testPreferWordBoundaryMatch() { - assertPreference("*les", "configureByFiles", "getLookupElementStrings", NameUtil.MatchingCaseSensitivity.FIRST_LETTER); - assertPreference("*les", "configureByFiles", "getLookupElementStrings", NameUtil.MatchingCaseSensitivity.NONE); - } - - public void testPreferEarlyMatching() { - assertPreference(" path", "getAbsolutePath", "findPath"); - } - - public void testMeaningfulMatchingDegree() { - assertTrue(new MinusculeMatcher(" EUC-", NameUtil.MatchingCaseSensitivity.FIRST_LETTER).matchingDegree("x-EUC-TW") > Integer.MIN_VALUE); - } - - private static void assertPreference(@NonNls String pattern, - @NonNls String less, - @NonNls String more) { - assertPreference(pattern, less, more, NameUtil.MatchingCaseSensitivity.FIRST_LETTER); - } - - private static void assertPreference(@NonNls String pattern, - @NonNls String less, - @NonNls String more, - NameUtil.MatchingCaseSensitivity sensitivity) { - MinusculeMatcher matcher = new MinusculeMatcher(pattern, sensitivity); - int iLess = matcher.matchingDegree(less); - int iMore = matcher.matchingDegree(more); - assertTrue(iLess + ">=" + iMore + "; " + less + ">=" + more, iLess < iMore); - } - - public void testPerformance() { - @NonNls final String longName = "ThisIsAQuiteLongNameWithParentheses().Dots.-Minuses-_UNDERSCORES_digits239:colons:/slashes\\AndOfCourseManyLetters"; - final List matching = new ArrayList(); - final List nonMatching = new ArrayList(); - - for (String s : CollectionFactory.ar("*", "*i", "*a", "*u", "T", "ti", longName, longName.substring(0, 20))) { - matching.add(new MinusculeMatcher(s, NameUtil.MatchingCaseSensitivity.NONE)); - } - for (String s : CollectionFactory.ar("A", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "ta")) { - nonMatching.add(new MinusculeMatcher(s, NameUtil.MatchingCaseSensitivity.NONE)); - } - - PlatformTestUtil.startPerformanceTest("Matcher is slow", 1700, new ThrowableRunnable() { - @Override - public void run() { - for (int i = 0; i < 100000; i++) { - for (MinusculeMatcher matcher : matching) { - assertTrue(matcher.matches(longName)); - } - for (MinusculeMatcher matcher : nonMatching) { - assertFalse(matcher.matches(longName)); - } - } - } - }).cpuBound().assertTiming(); - } -} +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed 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. + */ + +/* + * @author max + */ +package com.intellij.psi.util; + +import com.intellij.openapi.util.TextRange; +import com.intellij.psi.codeStyle.MinusculeMatcher; +import com.intellij.psi.codeStyle.NameUtil; +import com.intellij.testFramework.PlatformTestUtil; +import com.intellij.testFramework.UsefulTestCase; +import com.intellij.util.ThrowableRunnable; +import com.intellij.util.containers.CollectionFactory; +import com.intellij.util.text.Matcher; +import org.jetbrains.annotations.NonNls; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author peter + * @author Konstantin Bulenkov + */ +public class NameUtilTest extends UsefulTestCase { + + public void testSimpleCases() throws Exception { + assertMatches("N", "NameUtilTest"); + assertMatches("NU", "NameUtilTest"); + assertMatches("NUT", "NameUtilTest"); + assertMatches("NaUT", "NameUtilTest"); + assertDoesntMatch("NeUT", "NameUtilTest"); + assertDoesntMatch("NaUTa", "NameUtilTest"); + assertMatches("NaUtT", "NameUtilTest"); + assertMatches("NaUtT", "NameUtilTest"); + assertMatches("NaUtTe", "NameUtilTest"); + assertMatches("AACl", "AAClass"); + assertMatches("ZZZ", "ZZZZZZZZZZ"); + } + + public void testSimpleCasesWithFirstLowercased() throws Exception { + assertMatches("N", "nameUtilTest"); + assertDoesntMatch("N", "anameUtilTest"); + assertMatches("NU", "nameUtilTest"); + assertDoesntMatch("NU", "anameUtilTest"); + assertMatches("NUT", "nameUtilTest"); + assertMatches("NaUT", "nameUtilTest"); + assertDoesntMatch("NeUT", "nameUtilTest"); + assertDoesntMatch("NaUTa", "nameUtilTest"); + assertMatches("NaUtT", "nameUtilTest"); + assertMatches("NaUtT", "nameUtilTest"); + assertMatches("NaUtTe", "nameUtilTest"); + } + + public void testSpaceDelimiters() throws Exception { + assertMatches("Na Ut Te", "name util test"); + assertMatches("Na Ut Te", "name Util Test"); + assertDoesntMatch("Na Ut Ta", "name Util Test"); + assertMatches("na ut te", "name util test"); + + assertMatches("na ut", "name_util_test"); + assertMatches("na te", "name_util_test"); + assertDoesntMatch("na ti", "name_util_test"); + } + + public void testXMLCompletion() throws Exception { + assertDoesntMatch("N_T", "NameUtilTest"); + assertDoesntMatch("ORGS_ACC", "ORGS_POSITION_ACCOUNTABILITY"); + assertDoesntMatch("ORGS-ACC", "ORGS-POSITION_ACCOUNTABILITY"); + assertDoesntMatch("ORGS.ACC", "ORGS.POSITION_ACCOUNTABILITY"); + } + + public void testStarFalsePositive() throws Exception { + assertDoesntMatch("ar*l*p", "AbstractResponseHandler"); + } + + public void testUnderscoreStyle() throws Exception { + assertMatches("N_U_T", "NAME_UTIL_TEST"); + assertMatches("NUT", "NAME_UTIL_TEST"); + assertDoesntMatch("NUT", "NameutilTest"); + } + + public void testAllUppercase() { + assertMatches("NOS", "NetOutputStream"); + } + + public void testCommonFileNameConventions() throws Exception { + // See IDEADEV-12310 + + assertMatches("BLWN", "base_layout_without_navigation.xhtml"); + assertMatches("BLWN", "base-layout-without-navigation.xhtml"); + assertMatches("FC", "faces-config.xml"); + assertMatches("ARS", "activity_report_summary.jsp"); + assertMatches("AD", "arrow_down.gif"); + assertMatches("VL", "vehicle-listings.css"); + + assertMatches("ARS.j", "activity_report_summary.jsp"); + assertDoesntMatch("ARS.j", "activity_report_summary.xml"); + assertDoesntMatch("ARS.j", "activity_report_summary_justsometingwrong.xml"); + + assertDoesntMatch("foo.goo", "foo.bar.goo"); + } + + public void testSpaceForAnyWordsInBetween() { + assertMatches("fo bar", "fooBar"); + assertMatches("foo bar", "fooBar"); + assertMatches("foo bar", "fooGooBar"); + assertMatches("foo bar", "fooGoo bar"); + assertDoesntMatch(" b", "fbi"); + assertDoesntMatch(" for", "performAction"); + assertTrue(caseInsensitiveMatcher(" us").matches("getUsage")); + assertTrue(caseInsensitiveMatcher(" us").matches("getMyUsage")); + } + + private static Matcher caseInsensitiveMatcher(String pattern) { + return NameUtil.buildMatcher(pattern, NameUtil.MatchingCaseSensitivity.NONE); + } + + public void testStartDot() { + assertMatches("A*.html", "A.html"); + assertMatches("A*.html", "Abc.html"); + assertMatches("A*.html", "after.html"); + assertDoesntMatch("A*.html", "10_after.html"); + } + + public void testIDEADEV15503() throws Exception { + assertMatches("AR.jsp", "add_relationship.jsp"); + assertMatches("AR.jsp", "advanced_rule.jsp"); + assertMatches("AR.jsp", "alarm_reduction.jsp"); + assertMatches("AR.jsp", "audiot_report.jsp"); + assertMatches("AR.jsp", "audiot_r.jsp"); + + assertMatches("AR.jsp", "alarm_rule_action.jsp"); + assertMatches("AR.jsp", "alarm_rule_admin.jsp"); + assertMatches("AR.jsp", "alarm_rule_administration.jsp"); + assertMatches("AR.jsp", "alarm_rule_controller.jsp"); + assertMatches("AR.jsp", "alarm_rule_frame.jsp"); + assertMatches("AR.jsp", "alarm_rule_severity.jsp"); + + assertMatches("AR.jsp", "AddRelationship.jsp"); + assertMatches("AR.jsp", "AdvancedRule.jsp"); + assertMatches("AR.jsp", "AlarmReduction.jsp"); + assertMatches("AR.jsp", "AudiotReport.jsp"); + assertMatches("AR.jsp", "AudiotR.jsp"); + + assertMatches("AR.jsp", "AlarmRuleAction.jsp"); + assertMatches("AR.jsp", "AlarmRuleAdmin.jsp"); + assertMatches("AR.jsp", "AlarmRuleAdministration.jsp"); + assertMatches("AR.jsp", "AlarmRuleController.jsp"); + assertMatches("AR.jsp", "AlarmRuleFrame.jsp"); + assertMatches("AR.jsp", "AlarmRuleSeverity.jsp"); + } + + public void testSkipDot() { + assertMatches("ja", "jquery.autocomplete.js"); + assertMatches("ja.js", "jquery.autocomplete.js"); + assertDoesntMatch("jajs", "jquery.autocomplete.js"); + assertDoesntMatch("j.ajs", "jquery.autocomplete.js"); + } + + public void testNoExtension() { + assertMatches("#.p", "#.php"); + assertMatches("#", "#.php"); + assertMatches("a", "a.php"); + } + + public void testIgnoreCaseWhenCompleteMatch() { + assertMatches("comboBox", "combobox"); + assertMatches("combobox", "comboBox"); + } + + public void testStartsWithDot() throws Exception { + assertMatches(".foo", ".foo"); + } + + public void testProperDotEscaping() throws Exception { + assertMatches("*inspection*.pro", "InspectionsBundle.properties"); + assertDoesntMatch("*inspection*.pro", "InspectionsInProgress.png"); + } + + public void testIgnoreLeadingUnderscore() throws Exception { + assertMatches("form", "_form.html.erb"); + assertMatches("_form", "_form.html.erb"); + assertMatches("_form", "__form"); + assertFalse(NameUtil.buildMatcher("_form", 1, true, true, false).matches("__form")); + } + + public void testLowerCaseWords() throws Exception { + assertTrue(matches("uct", "unit_controller_test")); + assertTrue(matches("unictest", "unit_controller_test")); + assertTrue(matches("uc", "unit_controller_test")); + assertFalse(matches("nc", "unit_controller_test")); + assertFalse(matches("utc", "unit_controller_test")); + } + + public void testObjectiveCCases() throws Exception { + assertMatches("text:sh", "textField:shouldChangeCharactersInRange:replacementString:"); + assertMatches("abc", "aaa:bbb:ccc"); + assertMatches("textField:sh", "textField:shouldChangeCharactersInRange:replacementString:"); + assertMatches("text*:sh", "textField:shouldChangeCharactersInRange:replacementString:"); + } + + public void testMiddleMatching() { + assertTrue(caseInsensitiveMatcher("*old").matches("folder")); + assertMatches("SWU*H*7", "SWUpgradeHdlrFSPR7Test"); + assertMatches("SWU*H*R", "SWUpgradeHdlrFSPR7Test"); + assertMatches("SWU*H*R", "SWUPGRADEHDLRFSPR7TEST"); + assertMatches("*git", "GitBlaBla"); + assertMatches("*Git", "GitBlaBla"); + assertDoesntMatch("*get*A", "getClass"); + assertMatches("*git", "BlaGitBla"); + assertMatches("*Git", "BlaGitBla"); + assertFalse(firstLetterMatcher("*Git").matches("BlagitBla")); + assertMatches("*git", "BlagitBla"); + assertMatches("*Git*", "AtpGenerationItem"); + assertMatches("Collec*Util*", "CollectionUtils"); + assertMatches("Collec*Util*", "CollectionUtilsTest"); + assertTrue(caseInsensitiveMatcher("*us").matches("usage")); + assertTrue(caseInsensitiveMatcher(" us").matches("usage")); + assertTrue(caseInsensitiveMatcher(" fo. ba").matches("getFoo.getBar")); + assertMatches(" File. sepa", "File.separator"); + assertMatches(" File. sepa", "File._separator"); + assertMatches(" File. _sepa", "File._separator"); + assertMatches(" _fo", "_foo"); + } + + public void testMiddleMatchingFirstLetterSensitive() { + assertTrue(firstLetterMatcher(" cl").matches("getClass")); + assertTrue(firstLetterMatcher(" EUC-").matches("x-EUC-TW")); + assertTrue(firstLetterMatcher(" a").matches("aaa")); + assertFalse(firstLetterMatcher(" a").matches("Aaa")); + assertFalse(firstLetterMatcher(" a").matches("Aaa")); + assertFalse(firstLetterMatcher(" _bl").matches("_top")); + } + + private static Matcher firstLetterMatcher(String pattern) { + return NameUtil.buildMatcher(pattern, NameUtil.MatchingCaseSensitivity.FIRST_LETTER); + } + + public void testSpaceInCompletionPrefix() throws Exception { + assertTrue(caseInsensitiveMatcher("create ").matches("create module")); + } + + public void testLong() throws Exception { + assertTrue(matches("Product.findByDateAndNameGreaterThanEqualsAndQualityGreaterThanEqual", "Product.findByDateAndNameGreaterThanEqualsAndQualityGreaterThanEqualsIntellijIdeaRulezzz")); + } + + private static void assertMatches(@NonNls String pattern, @NonNls String name) { + assertTrue(matches(pattern, name)); + } + private static void assertDoesntMatch(@NonNls String pattern, @NonNls String name) { + assertFalse(matches(pattern, name)); + } + + private static boolean matches(@NonNls final String pattern, @NonNls final String name) { + return caseInsensitiveMatcher(pattern).matches(name); + } + + public void testLowerCaseHumps() { + assertMatches("foo", "foo"); + assertDoesntMatch("foo", "fxoo"); + assertMatches("foo", "fOo"); + assertMatches("foo", "fxOo"); + assertDoesntMatch("foo", "fXOo"); + assertMatches("fOo", "foo"); + assertMatches("fOo", "FaOaOaXXXX"); + assertMatches("ncdfoe", "NoClassDefFoundException"); + assertMatches("fob", "FOO_BAR"); + assertMatches("fo_b", "FOO_BAR"); + assertMatches("fob", "FOO BAR"); + assertMatches("fo b", "FOO BAR"); + assertMatches("AACl", "AAClass"); + assertMatches("ZZZ", "ZZZZZZZZZZ"); + assertMatches("em", "emptyList"); + assertMatches("bui", "BuildConfig.groovy"); + assertMatches("buico", "BuildConfig.groovy"); + assertMatches("buico.gr", "BuildConfig.groovy"); + assertMatches("bui.gr", "BuildConfig.groovy"); + assertMatches("*fz", "azzzfzzz"); + + assertMatches("WebLogic", "Weblogic"); + assertMatches("WebLOgic", "WebLogic"); + assertMatches("WEbLogic", "WebLogic"); + assertDoesntMatch("WebLogic", "Webologic"); + } + + public void testFinalSpace() { + assertMatches("GrDebT ", "GroovyDebuggerTest"); + assertMatches("grdebT ", "GroovyDebuggerTest"); + assertDoesntMatch("grdebt ", "GroovyDebuggerTest"); + assertMatches("Foo ", "Foo"); + assertDoesntMatch("Foo ", "FooBar"); + assertDoesntMatch("Foo ", "Foox"); + assertDoesntMatch("Collections ", "CollectionSplitter"); + assertMatches("CollectionS ", "CollectionSplitter"); + } + + public void testDigits() { + assertMatches("foba4", "FooBar4"); + assertMatches("foba", "Foo4Bar"); + assertMatches("*TEST-* ", "TEST-001"); + assertMatches("*TEST-0* ", "TEST-001"); + } + + public void testSpecialSymbols() { + assertMatches("a@b", "a@bc"); + + assertMatches("a/text", "a/Text"); + assertDoesntMatch("a/text", "a/bbbText"); + } + + public void testMinusculeFirstLetter() { + assertTrue(new MinusculeMatcher("WebLogic", NameUtil.MatchingCaseSensitivity.FIRST_LETTER).matches("WebLogic")); + assertFalse(new MinusculeMatcher("webLogic", NameUtil.MatchingCaseSensitivity.FIRST_LETTER).matches("WebLogic")); + assertTrue(new MinusculeMatcher("cL", NameUtil.MatchingCaseSensitivity.FIRST_LETTER).matches("class")); + assertTrue(new MinusculeMatcher("CL", NameUtil.MatchingCaseSensitivity.FIRST_LETTER).matches("Class")); + assertFalse(new MinusculeMatcher("abc", NameUtil.MatchingCaseSensitivity.FIRST_LETTER).matches("_abc")); + } + + public void testMinusculeAllImportant() { + assertTrue(new MinusculeMatcher("WebLogic", NameUtil.MatchingCaseSensitivity.ALL).matches("WebLogic")); + assertFalse(new MinusculeMatcher("webLogic", NameUtil.MatchingCaseSensitivity.ALL).matches("weblogic")); + assertFalse(new MinusculeMatcher("FOO", NameUtil.MatchingCaseSensitivity.ALL).matches("foo")); + assertFalse(new MinusculeMatcher("foo", NameUtil.MatchingCaseSensitivity.ALL).matches("fOO")); + assertFalse(new MinusculeMatcher("Wl", NameUtil.MatchingCaseSensitivity.ALL).matches("WebLogic")); + assertTrue(new MinusculeMatcher("WL", NameUtil.MatchingCaseSensitivity.ALL).matches("WebLogic")); + assertFalse(new MinusculeMatcher("WL", NameUtil.MatchingCaseSensitivity.ALL).matches("Weblogic")); + assertFalse(new MinusculeMatcher("WL", NameUtil.MatchingCaseSensitivity.ALL).matches("weblogic")); + assertFalse(new MinusculeMatcher("webLogic", NameUtil.MatchingCaseSensitivity.ALL).matches("WebLogic")); + assertFalse(new MinusculeMatcher("Str", NameUtil.MatchingCaseSensitivity.ALL).matches("SomeThingRidiculous")); + } + + public void testMatchingFragments() { + @NonNls String sample = "NoClassDefFoundException"; + // 0 2 7 10 15 21 + assertOrderedEquals(new MinusculeMatcher("ncldfou*ion", NameUtil.MatchingCaseSensitivity.NONE).matchingFragments(sample), + TextRange.from(0, 1), TextRange.from(2, 2), TextRange.from(7, 1), TextRange.from(10, 3), TextRange.from(21, 3)); + + sample = "doGet(HttpServletRequest, HttpServletResponse):void"; + // 0 22 + assertOrderedEquals(new MinusculeMatcher("d*st", NameUtil.MatchingCaseSensitivity.NONE).matchingFragments(sample), + TextRange.from(0, 1), TextRange.from(22, 2)); + assertOrderedEquals(new MinusculeMatcher("doge*st", NameUtil.MatchingCaseSensitivity.NONE).matchingFragments(sample), + TextRange.from(0, 4), TextRange.from(22, 2)); + + sample = "_test"; + assertOrderedEquals(new MinusculeMatcher("_", NameUtil.MatchingCaseSensitivity.NONE).matchingFragments(sample), + TextRange.from(0, 1)); + assertOrderedEquals(new MinusculeMatcher("_t", NameUtil.MatchingCaseSensitivity.NONE).matchingFragments(sample), + TextRange.from(0, 2)); + } + + public void testMatchingFragmentsSorted() { + @NonNls String sample = "SWUPGRADEHDLRFSPR7TEST"; + // 0 9 12 + assertOrderedEquals(new MinusculeMatcher("SWU*H*R", NameUtil.MatchingCaseSensitivity.NONE).matchingFragments(sample), + TextRange.from(0, 3), TextRange.from(9, 1), TextRange.from(12, 1)); + } + + public void testPreferCapsMatching() { + String sample = "getCurrentUser"; + // 0 4 10 + assertOrderedEquals(new MinusculeMatcher("getCU", NameUtil.MatchingCaseSensitivity.NONE).matchingFragments(sample), + TextRange.from(0, 4), TextRange.from(10, 1)); + } + + public void testMatchingDegree() { + assertPreference("jscote", "JsfCompletionTest", "JSCompletionTest", NameUtil.MatchingCaseSensitivity.NONE); + assertPreference("OCO", "OneCoolObject", "OCObject"); + assertPreference("MUp", "MavenUmlProvider", "MarkUp"); + assertPreference("MUP", "MarkUp", "MavenUmlProvider"); + assertPreference("CertificateExce", "CertificateEncodingException", "CertificateException"); + assertPreference("boo", "Boolean", "boolean", NameUtil.MatchingCaseSensitivity.NONE); + assertPreference("Boo", "boolean", "Boolean", NameUtil.MatchingCaseSensitivity.NONE); + assertPreference("getCU", "getCurrentSomething", "getCurrentUser"); + assertPreference("cL", "class", "coreLoader"); + assertPreference("cL", "class", "classLoader"); + } + + public void testPreferStartMatchToMiddleMatch() { + assertPreference(" fb", "FooBar", "_fooBar", NameUtil.MatchingCaseSensitivity.NONE); + assertPreference("*foo", "barFoo", "foobar"); + assertPreference("*f", "barfoo", "barFoo"); + assertPreference("*f", "barfoo", "foo"); + assertPreference("*f", "asdf", "Foo", NameUtil.MatchingCaseSensitivity.NONE); + assertPreference(" sto", "ArrayStoreException", "StackOverflowError", NameUtil.MatchingCaseSensitivity.NONE); + assertPreference(" EUC-", "x-EUC-TW", "EUC-JP"); + assertPreference(" boo", "Boolean", "boolean", NameUtil.MatchingCaseSensitivity.NONE); + assertPreference(" Boo", "boolean", "Boolean", NameUtil.MatchingCaseSensitivity.NONE); + } + + public void testPreferWordBoundaryMatch() { + assertPreference("*les", "configureByFiles", "getLookupElementStrings", NameUtil.MatchingCaseSensitivity.FIRST_LETTER); + assertPreference("*les", "configureByFiles", "getLookupElementStrings", NameUtil.MatchingCaseSensitivity.NONE); + } + + public void testPreferEarlyMatching() { + assertPreference(" path", "getAbsolutePath", "findPath"); + } + + public void testMeaningfulMatchingDegree() { + assertTrue(new MinusculeMatcher(" EUC-", NameUtil.MatchingCaseSensitivity.FIRST_LETTER).matchingDegree("x-EUC-TW") > Integer.MIN_VALUE); + } + + private static void assertPreference(@NonNls String pattern, + @NonNls String less, + @NonNls String more) { + assertPreference(pattern, less, more, NameUtil.MatchingCaseSensitivity.FIRST_LETTER); + } + + private static void assertPreference(@NonNls String pattern, + @NonNls String less, + @NonNls String more, + NameUtil.MatchingCaseSensitivity sensitivity) { + MinusculeMatcher matcher = new MinusculeMatcher(pattern, sensitivity); + int iLess = matcher.matchingDegree(less); + int iMore = matcher.matchingDegree(more); + assertTrue(iLess + ">=" + iMore + "; " + less + ">=" + more, iLess < iMore); + } + + public void testPerformance() { + @NonNls final String longName = "ThisIsAQuiteLongNameWithParentheses().Dots.-Minuses-_UNDERSCORES_digits239:colons:/slashes\\AndOfCourseManyLetters"; + final List matching = new ArrayList(); + final List nonMatching = new ArrayList(); + + for (String s : CollectionFactory.ar("*", "*i", "*a", "*u", "T", "ti", longName, longName.substring(0, 20))) { + matching.add(new MinusculeMatcher(s, NameUtil.MatchingCaseSensitivity.NONE)); + } + for (String s : CollectionFactory.ar("A", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "ta")) { + nonMatching.add(new MinusculeMatcher(s, NameUtil.MatchingCaseSensitivity.NONE)); + } + + PlatformTestUtil.startPerformanceTest("Matcher is slow", 1700, new ThrowableRunnable() { + @Override + public void run() { + for (int i = 0; i < 100000; i++) { + for (MinusculeMatcher matcher : matching) { + assertTrue(matcher.matches(longName)); + } + for (MinusculeMatcher matcher : nonMatching) { + assertFalse(matcher.matches(longName)); + } + } + } + }).cpuBound().assertTiming(); + } +} diff --git a/platform/util/src/com/intellij/psi/codeStyle/MinusculeMatcher.java b/platform/util/src/com/intellij/psi/codeStyle/MinusculeMatcher.java index d48eb69989c7..4eb0ca422af5 100644 --- a/platform/util/src/com/intellij/psi/codeStyle/MinusculeMatcher.java +++ b/platform/util/src/com/intellij/psi/codeStyle/MinusculeMatcher.java @@ -1,324 +1,324 @@ -/* - * Copyright 2000-2012 JetBrains s.r.o. - * - * Licensed 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 com.intellij.psi.codeStyle; - -import com.intellij.openapi.util.TextRange; -import com.intellij.openapi.util.text.StringUtil; -import com.intellij.util.containers.FList; -import com.intellij.util.text.CharArrayCharSequence; -import com.intellij.util.text.Matcher; -import org.jetbrains.annotations.Nullable; - -import java.util.Collections; -import java.util.Iterator; - -/** -* @author peter -*/ -public class MinusculeMatcher implements Matcher { - private final char[] myPattern; - private final NameUtil.MatchingCaseSensitivity myOptions; - - public MinusculeMatcher(String pattern, NameUtil.MatchingCaseSensitivity options) { - myOptions = options; - myPattern = StringUtil.trimEnd(pattern, "* ").replaceAll(":", "\\*:").toCharArray(); - } - - @Nullable - private FList matchName(String name, int patternIndex, int nameIndex) { - if (patternIndex == myPattern.length) { - return FList.emptyList(); - } - if ('*' == myPattern[patternIndex]) { - return skipChars(name, patternIndex, nameIndex, true); - } - if (nameIndex == name.length()) { - return null; - } - - if ('.' == myPattern[patternIndex] && name.charAt(nameIndex) != '.') { - return skipChars(name, patternIndex, nameIndex, false); - } - - if (' ' == myPattern[patternIndex] && patternIndex != myPattern.length - 1) { - return skipWords(name, patternIndex, nameIndex); - } - - if ((patternIndex == 0 || patternIndex == 1 && myPattern[0] == ' ' && nameIndex == 0) && - myOptions != NameUtil.MatchingCaseSensitivity.NONE && name.charAt(nameIndex) != myPattern[patternIndex]) { - return null; - } - - if (NameUtil.isWordSeparator(name.charAt(nameIndex))) { - return skipSeparators(name, patternIndex, nameIndex); - } - - if (StringUtil.toLowerCase(name.charAt(nameIndex)) != StringUtil.toLowerCase(myPattern[patternIndex])) { - if (Character.isDigit(name.charAt(nameIndex)) && nameIndex > 0 || (name.charAt(nameIndex) == '.' && name.indexOf('.', nameIndex + 1) > 0)) { - return matchName(name, patternIndex, nameIndex + 1); - } - return null; - } - - if (myOptions == NameUtil.MatchingCaseSensitivity.ALL && name.charAt(nameIndex) != myPattern[patternIndex]) { - return null; - } - - int nextStart = NameUtil.nextWord(name, nameIndex); - - int lastUpper = NameUtil.isWordStart(myPattern[patternIndex]) ? 0 : -1; - - int i = 1; - while (true) { - if (patternIndex + i == myPattern.length || i + nameIndex == nextStart) { - break; - } - char p = myPattern[patternIndex + i]; - char w = name.charAt(i + nameIndex); - if (lastUpper == i - 1 && NameUtil.isWordStart(p) && myOptions != NameUtil.MatchingCaseSensitivity.ALL) { - if (p == w) { - lastUpper = i; - } - p = StringUtil.toLowerCase(p); - } - - if (myOptions != NameUtil.MatchingCaseSensitivity.ALL) { - w = StringUtil.toLowerCase(w); - } - if (w != p) { - break; - } - i++; - } - - if (isFinalSpaceMatch(name, patternIndex, nameIndex, nextStart, i)) { - return FList.emptyList().prepend(TextRange.from(nameIndex, i)); - } - - return matchAfterFragment(name, patternIndex, nameIndex, nextStart, lastUpper, i); - } - - private boolean isFinalSpaceMatch(String name, int patternIndex, int nameIndex, int nextStart, int i) { - return nextStart == name.length() && - patternIndex + i == myPattern.length - 1 && - ' ' == myPattern[patternIndex + i] && - (i == 1 && NameUtil.isWordStart(myPattern[patternIndex]) || i + nameIndex == name.length()); - } - - @Nullable - private FList matchAfterFragment(String name, int patternIndex, int nameIndex, int nextStart, int lastUpper, int matchLen) { - boolean star = patternIndex + matchLen < myPattern.length && myPattern[patternIndex + matchLen] == '*'; - if (lastUpper >= 0) { - FList ranges = matchName(name, patternIndex + lastUpper + 1, star && matchLen == lastUpper ? nameIndex + lastUpper : nextStart); - if (ranges != null) { - return prependRange(ranges, nameIndex, lastUpper + 1); - } - } - - int trial = matchLen; - while (trial > 0) { - FList ranges = matchName(name, patternIndex + trial, nextStart); - if (ranges != null) { - return prependRange(ranges, nameIndex, trial); - } - trial--; - } - - FList ranges = matchName(name, patternIndex + matchLen, nameIndex + matchLen); - if (ranges != null) { - return prependRange(ranges, nameIndex, matchLen); - } - return null; - } - - private static FList prependRange(FList ranges, int from, int length) { - TextRange head = ranges.getHead(); - if (head != null && head.getStartOffset() == from + length) { - return ranges.getTail().prepend(new TextRange(from, head.getEndOffset())); - } - return ranges.prepend(TextRange.from(from, length)); - } - - @Nullable - private FList skipSeparators(String name, int patternIndex, int nameIndex) { - int nextStart = NameUtil.nextWord(name, nameIndex); - assert nextStart - nameIndex == 1 : "'" + name + "'" + nameIndex + " " + nextStart; - char p = myPattern[patternIndex]; - if (NameUtil.isWordSeparator(p)) { - if (myOptions != NameUtil.MatchingCaseSensitivity.NONE && - nameIndex == 0 && name.length() > 1 && patternIndex + 1 < myPattern.length && - NameUtil.isWordSeparator(name.charAt(1)) && !NameUtil.isWordSeparator(myPattern[patternIndex + 1])) { - return null; - } - - final FList ranges = matchName(name, patternIndex + 1, nextStart); - if (ranges != null) { - return prependRange(ranges, nameIndex, 1); - } - - return null; - } - - return matchName(name, patternIndex, nextStart); - } - - @Nullable - private FList skipChars(String name, int patternIndex, int nameIndex, boolean maySkipNextChar) { - while ('*' == myPattern[patternIndex]) { - patternIndex++; - if (patternIndex == myPattern.length) { - return FList.emptyList(); - } - } - - char nextChar = myPattern[patternIndex]; - boolean upper = Character.isUpperCase(myPattern[patternIndex]); - - int fromIndex = nameIndex; - while (true) { - int next = StringUtil.indexOfIgnoreCase(name, nextChar, fromIndex); - if (next < 0) { - break; - } - if (upper && next > 0 && !Character.isUpperCase(name.charAt(next)) && myOptions != NameUtil.MatchingCaseSensitivity.NONE) { - fromIndex = next + 1; - continue; - } - - FList ranges = matchName(name, patternIndex, next); - if (ranges != null) { - return ranges; - } - if (!maySkipNextChar) { - return null; - } - fromIndex = next + 1; - } - return null; - } - - @Nullable - private FList skipWords(String name, int patternIndex, int nameIndex) { - while (' ' == myPattern[patternIndex]) { - patternIndex++; - if (patternIndex == myPattern.length) { - return null; - } - } - - if (nameIndex == 0 || NameUtil.isWordStart(name, nameIndex)) { - FList ranges = matchName(name, patternIndex, nameIndex); - if (ranges != null) { - return ranges; - } - } - - boolean separatorInPattern = NameUtil.isWordSeparator(myPattern[patternIndex]); - int fromIndex = nameIndex; - while (fromIndex < name.length()) { - int next = separatorInPattern ? name.indexOf(myPattern[patternIndex], fromIndex) : NameUtil.nextWord(name, fromIndex); - if (next < 0) { - break; - } - - FList ranges = matchName(name, patternIndex, next); - if (ranges != null) { - return ranges; - } - fromIndex = next; - if (separatorInPattern) { - fromIndex++; - } - } - return null; - } - - public int matchingDegree(String name) { - Iterable iterable = matchingFragments(name); - if (iterable == null) return Integer.MIN_VALUE; - - int fragmentCount = 0; - int matchingCase = 0; - CharArrayCharSequence seq = new CharArrayCharSequence(myPattern); - int p = -1; - TextRange first = null; - for (TextRange range : iterable) { - if (first == null) { - first = range; - } - for (int i = range.getStartOffset(); i < range.getEndOffset(); i++) { - char c = name.charAt(i); - p = StringUtil.indexOf(seq, c, p + 1, myPattern.length, false); - if (p < 0) { - break; - } - if (Character.isUpperCase(myPattern[p]) || i == range.getStartOffset()) { - matchingCase += c == myPattern[p] ? 1 : 0; - } - } - fragmentCount++; - } - - if (first == null) { - return 0; - } - - int commonStart = 0; - while (commonStart < name.length() && commonStart < myPattern.length && name.charAt(commonStart) == myPattern[commonStart]) { - commonStart++; - } - - int startIndex = first.getStartOffset(); - boolean prefixMatching = isStartMatch(name, startIndex); - boolean middleWordStart = !prefixMatching && NameUtil.isWordStart(name, first.getStartOffset()); - - return -fragmentCount + matchingCase * 10 + commonStart - startIndex + (prefixMatching ? 2 : middleWordStart ? 1 : 0) * 100; - } - - public boolean isStartMatch(String name) { - Iterable fragments = matchingFragments(name); - if (fragments != null) { - Iterator iterator = fragments.iterator(); - if (!iterator.hasNext() || isStartMatch(name, iterator.next().getStartOffset())) { - return true; - } - } - return false; - } - - private static boolean isStartMatch(String name, int startIndex) { - for (int i = 0; i < startIndex; i++) { - if (!NameUtil.isWordSeparator(name.charAt(i))) { - return false; - } - } - return true; - } - - @Override - public boolean matches(String name) { - return matchingFragments(name) != null; - } - - @Nullable - public Iterable matchingFragments(String name) { - if (name.isEmpty()) { - return myPattern.length == 0 ? Collections.emptyList() : null; - } - - return matchName(name, 0, 0); - } -} +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed 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 com.intellij.psi.codeStyle; + +import com.intellij.openapi.util.TextRange; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.util.containers.FList; +import com.intellij.util.text.CharArrayCharSequence; +import com.intellij.util.text.Matcher; +import org.jetbrains.annotations.Nullable; + +import java.util.Collections; +import java.util.Iterator; + +/** +* @author peter +*/ +public class MinusculeMatcher implements Matcher { + private final char[] myPattern; + private final NameUtil.MatchingCaseSensitivity myOptions; + + public MinusculeMatcher(String pattern, NameUtil.MatchingCaseSensitivity options) { + myOptions = options; + myPattern = StringUtil.trimEnd(pattern, "* ").replaceAll(":", "\\*:").toCharArray(); + } + + @Nullable + private FList matchName(String name, int patternIndex, int nameIndex) { + if (patternIndex == myPattern.length) { + return FList.emptyList(); + } + if ('*' == myPattern[patternIndex]) { + return skipChars(name, patternIndex, nameIndex, true); + } + if (nameIndex == name.length()) { + return null; + } + + if ('.' == myPattern[patternIndex] && name.charAt(nameIndex) != '.') { + return skipChars(name, patternIndex, nameIndex, false); + } + + if (' ' == myPattern[patternIndex] && patternIndex != myPattern.length - 1) { + return skipWords(name, patternIndex, nameIndex); + } + + if ((patternIndex == 0 || patternIndex == 1 && myPattern[0] == ' ' && nameIndex == 0) && + myOptions != NameUtil.MatchingCaseSensitivity.NONE && name.charAt(nameIndex) != myPattern[patternIndex]) { + return null; + } + + if (NameUtil.isWordSeparator(name.charAt(nameIndex))) { + return skipSeparators(name, patternIndex, nameIndex); + } + + if (StringUtil.toLowerCase(name.charAt(nameIndex)) != StringUtil.toLowerCase(myPattern[patternIndex])) { + if (Character.isDigit(name.charAt(nameIndex)) && nameIndex > 0 || (name.charAt(nameIndex) == '.' && name.indexOf('.', nameIndex + 1) > 0)) { + return matchName(name, patternIndex, nameIndex + 1); + } + return null; + } + + if (myOptions == NameUtil.MatchingCaseSensitivity.ALL && name.charAt(nameIndex) != myPattern[patternIndex]) { + return null; + } + + int nextStart = NameUtil.nextWord(name, nameIndex); + + int lastUpper = NameUtil.isWordStart(myPattern[patternIndex]) ? 0 : -1; + + int i = 1; + while (true) { + if (patternIndex + i == myPattern.length || i + nameIndex == nextStart) { + break; + } + char p = myPattern[patternIndex + i]; + char w = name.charAt(i + nameIndex); + if (lastUpper == i - 1 && NameUtil.isWordStart(p) && myOptions != NameUtil.MatchingCaseSensitivity.ALL) { + if (p == w) { + lastUpper = i; + } + p = StringUtil.toLowerCase(p); + } + + if (myOptions != NameUtil.MatchingCaseSensitivity.ALL) { + w = StringUtil.toLowerCase(w); + } + if (w != p) { + break; + } + i++; + } + + if (isFinalSpaceMatch(name, patternIndex, nameIndex, nextStart, i)) { + return FList.emptyList().prepend(TextRange.from(nameIndex, i)); + } + + return matchAfterFragment(name, patternIndex, nameIndex, nextStart, lastUpper, i); + } + + private boolean isFinalSpaceMatch(String name, int patternIndex, int nameIndex, int nextStart, int i) { + return nextStart == name.length() && + patternIndex + i == myPattern.length - 1 && + ' ' == myPattern[patternIndex + i] && + (i == 1 && NameUtil.isWordStart(myPattern[patternIndex]) || i + nameIndex == name.length()); + } + + @Nullable + private FList matchAfterFragment(String name, int patternIndex, int nameIndex, int nextStart, int lastUpper, int matchLen) { + boolean star = patternIndex + matchLen < myPattern.length && myPattern[patternIndex + matchLen] == '*'; + if (lastUpper >= 0) { + FList ranges = matchName(name, patternIndex + lastUpper + 1, star && matchLen == lastUpper ? nameIndex + lastUpper : nextStart); + if (ranges != null) { + return prependRange(ranges, nameIndex, lastUpper + 1); + } + } + + int trial = matchLen; + while (trial > 0) { + FList ranges = matchName(name, patternIndex + trial, nextStart); + if (ranges != null) { + return prependRange(ranges, nameIndex, trial); + } + trial--; + } + + FList ranges = matchName(name, patternIndex + matchLen, nameIndex + matchLen); + if (ranges != null) { + return prependRange(ranges, nameIndex, matchLen); + } + return null; + } + + private static FList prependRange(FList ranges, int from, int length) { + TextRange head = ranges.getHead(); + if (head != null && head.getStartOffset() == from + length) { + return ranges.getTail().prepend(new TextRange(from, head.getEndOffset())); + } + return ranges.prepend(TextRange.from(from, length)); + } + + @Nullable + private FList skipSeparators(String name, int patternIndex, int nameIndex) { + int nextStart = NameUtil.nextWord(name, nameIndex); + assert nextStart - nameIndex == 1 : "'" + name + "'" + nameIndex + " " + nextStart; + char p = myPattern[patternIndex]; + if (NameUtil.isWordSeparator(p)) { + if (myOptions != NameUtil.MatchingCaseSensitivity.NONE && + nameIndex == 0 && name.length() > 1 && patternIndex + 1 < myPattern.length && + NameUtil.isWordSeparator(name.charAt(1)) && !NameUtil.isWordSeparator(myPattern[patternIndex + 1])) { + return null; + } + + final FList ranges = matchName(name, patternIndex + 1, nextStart); + if (ranges != null) { + return prependRange(ranges, nameIndex, 1); + } + + return null; + } + + return matchName(name, patternIndex, nextStart); + } + + @Nullable + private FList skipChars(String name, int patternIndex, int nameIndex, boolean maySkipNextChar) { + while ('*' == myPattern[patternIndex]) { + patternIndex++; + if (patternIndex == myPattern.length) { + return FList.emptyList(); + } + } + + char nextChar = myPattern[patternIndex]; + boolean upper = Character.isUpperCase(myPattern[patternIndex]); + + int fromIndex = nameIndex; + while (true) { + int next = StringUtil.indexOfIgnoreCase(name, nextChar, fromIndex); + if (next < 0) { + break; + } + if (upper && next > 0 && !Character.isUpperCase(name.charAt(next))) { + fromIndex = next + 1; + continue; + } + + FList ranges = matchName(name, patternIndex, next); + if (ranges != null) { + return ranges; + } + if (!maySkipNextChar) { + return null; + } + fromIndex = next + 1; + } + return null; + } + + @Nullable + private FList skipWords(String name, int patternIndex, int nameIndex) { + while (' ' == myPattern[patternIndex]) { + patternIndex++; + if (patternIndex == myPattern.length) { + return null; + } + } + + if (nameIndex == 0 || NameUtil.isWordStart(name, nameIndex)) { + FList ranges = matchName(name, patternIndex, nameIndex); + if (ranges != null) { + return ranges; + } + } + + boolean separatorInPattern = NameUtil.isWordSeparator(myPattern[patternIndex]); + int fromIndex = nameIndex; + while (fromIndex < name.length()) { + int next = separatorInPattern ? name.indexOf(myPattern[patternIndex], fromIndex) : NameUtil.nextWord(name, fromIndex); + if (next < 0) { + break; + } + + FList ranges = matchName(name, patternIndex, next); + if (ranges != null) { + return ranges; + } + fromIndex = next; + if (separatorInPattern) { + fromIndex++; + } + } + return null; + } + + public int matchingDegree(String name) { + Iterable iterable = matchingFragments(name); + if (iterable == null) return Integer.MIN_VALUE; + + int fragmentCount = 0; + int matchingCase = 0; + CharArrayCharSequence seq = new CharArrayCharSequence(myPattern); + int p = -1; + TextRange first = null; + for (TextRange range : iterable) { + if (first == null) { + first = range; + } + for (int i = range.getStartOffset(); i < range.getEndOffset(); i++) { + char c = name.charAt(i); + p = StringUtil.indexOf(seq, c, p + 1, myPattern.length, false); + if (p < 0) { + break; + } + if (Character.isUpperCase(myPattern[p]) || i == range.getStartOffset()) { + matchingCase += c == myPattern[p] ? 1 : 0; + } + } + fragmentCount++; + } + + if (first == null) { + return 0; + } + + int commonStart = 0; + while (commonStart < name.length() && commonStart < myPattern.length && name.charAt(commonStart) == myPattern[commonStart]) { + commonStart++; + } + + int startIndex = first.getStartOffset(); + boolean prefixMatching = isStartMatch(name, startIndex); + boolean middleWordStart = !prefixMatching && NameUtil.isWordStart(name, first.getStartOffset()); + + return -fragmentCount + matchingCase * 10 + commonStart - startIndex + (prefixMatching ? 2 : middleWordStart ? 1 : 0) * 100; + } + + public boolean isStartMatch(String name) { + Iterable fragments = matchingFragments(name); + if (fragments != null) { + Iterator iterator = fragments.iterator(); + if (!iterator.hasNext() || isStartMatch(name, iterator.next().getStartOffset())) { + return true; + } + } + return false; + } + + private static boolean isStartMatch(String name, int startIndex) { + for (int i = 0; i < startIndex; i++) { + if (!NameUtil.isWordSeparator(name.charAt(i))) { + return false; + } + } + return true; + } + + @Override + public boolean matches(String name) { + return matchingFragments(name) != null; + } + + @Nullable + public Iterable matchingFragments(String name) { + if (name.isEmpty()) { + return myPattern.length == 0 ? Collections.emptyList() : null; + } + + return matchName(name, 0, 0); + } +}