diff --git a/idea/tests/org/jetbrains/kotlin/findUsages/AbstractFindUsagesTest.java b/idea/tests/org/jetbrains/kotlin/findUsages/AbstractFindUsagesTest.java deleted file mode 100644 index ac1c586c132..00000000000 --- a/idea/tests/org/jetbrains/kotlin/findUsages/AbstractFindUsagesTest.java +++ /dev/null @@ -1,592 +0,0 @@ -/* - * Copyright 2010-2015 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 org.jetbrains.kotlin.findUsages; - -import com.google.common.base.Function; -import com.google.common.base.Joiner; -import com.google.common.base.Predicates; -import com.google.common.collect.Collections2; -import com.google.common.collect.Ordering; -import com.intellij.codeInsight.JavaTargetElementEvaluator; -import com.intellij.codeInsight.TargetElementUtilBase; -import com.intellij.find.FindManager; -import com.intellij.find.findUsages.*; -import com.intellij.find.impl.FindManagerImpl; -import com.intellij.lang.properties.psi.PropertiesFile; -import com.intellij.lang.properties.psi.Property; -import com.intellij.openapi.editor.markup.TextAttributes; -import com.intellij.openapi.extensions.Extensions; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.io.FileUtil; -import com.intellij.openapi.util.io.FileUtilRt; -import com.intellij.openapi.util.text.StringUtil; -import com.intellij.psi.*; -import com.intellij.psi.search.GlobalSearchScope; -import com.intellij.psi.util.PsiTreeUtil; -import com.intellij.testFramework.LightProjectDescriptor; -import com.intellij.usageView.UsageInfo; -import com.intellij.usages.TextChunk; -import com.intellij.usages.UsageGroup; -import com.intellij.usages.UsageInfo2UsageAdapter; -import com.intellij.usages.UsageViewPresentation; -import com.intellij.usages.impl.rules.UsageType; -import com.intellij.usages.impl.rules.UsageTypeProvider; -import com.intellij.usages.rules.UsageFilteringRule; -import com.intellij.usages.rules.UsageGroupingRule; -import com.intellij.util.ArrayUtil; -import com.intellij.util.CommonProcessors; -import kotlin.collections.CollectionsKt; -import kotlin.jvm.functions.Function1; -import org.gradle.util.CollectionUtils; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.jetbrains.kotlin.idea.findUsages.KotlinClassFindUsagesOptions; -import org.jetbrains.kotlin.idea.findUsages.KotlinFindUsagesHandlerFactory; -import org.jetbrains.kotlin.idea.findUsages.KotlinFunctionFindUsagesOptions; -import org.jetbrains.kotlin.idea.findUsages.KotlinPropertyFindUsagesOptions; -import org.jetbrains.kotlin.idea.search.usagesSearch.ExpressionsOfTypeProcessor; -import org.jetbrains.kotlin.idea.test.KotlinLightCodeInsightFixtureTestCase; -import org.jetbrains.kotlin.idea.test.KotlinWithJdkAndRuntimeLightProjectDescriptor; -import org.jetbrains.kotlin.idea.test.PluginTestCaseBase; -import org.jetbrains.kotlin.idea.test.TestFixtureExtension; -import org.jetbrains.kotlin.idea.util.ProjectRootsUtil; -import org.jetbrains.kotlin.psi.*; -import org.jetbrains.kotlin.test.InTextDirectivesUtils; -import org.jetbrains.kotlin.test.KotlinTestUtils; - -import java.io.File; -import java.io.FilenameFilter; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.List; - -public abstract class AbstractFindUsagesTest extends KotlinLightCodeInsightFixtureTestCase { - - public static final UsageViewPresentation USAGE_VIEW_PRESENTATION = new UsageViewPresentation(); - - protected enum OptionsParser { - CLASS { - @NotNull - @Override - public FindUsagesOptions parse(@NotNull String text, @NotNull Project project) { - KotlinClassFindUsagesOptions options = new KotlinClassFindUsagesOptions(project); - options.isUsages = false; - options.isSearchForTextOccurrences = false; - options.setSearchConstructorUsages(false); - for (String s : InTextDirectivesUtils.findListWithPrefixes(text, "// OPTIONS: ")) { - if (parseCommonOptions(options, s)) continue; - - if (s.equals("constructorUsages")) { - options.setSearchConstructorUsages(true); - } - else if (s.equals("derivedInterfaces")) { - options.isDerivedInterfaces = true; - } - else if (s.equals("derivedClasses")) { - options.isDerivedClasses = true; - } - else if (s.equals("functionUsages")) { - options.isMethodsUsages = true; - } - else if (s.equals("propertyUsages")) { - options.isFieldsUsages = true; - } - else fail("Invalid option: " + s); - } - - return options; - } - }, - FUNCTION { - @NotNull - @Override - public FindUsagesOptions parse(@NotNull String text, @NotNull Project project) { - KotlinFunctionFindUsagesOptions options = new KotlinFunctionFindUsagesOptions(project); - options.isUsages = false; - for (String s : InTextDirectivesUtils.findListWithPrefixes(text, "// OPTIONS: ")) { - if (parseCommonOptions(options, s)) continue; - - if (s.equals("overrides")) { - options.isOverridingMethods = true; - options.isImplementingMethods = true; - } - else if (s.equals("overloadUsages")) { - options.isIncludeOverloadUsages = true; - options.isUsages = true; - } - else fail("Invalid option: " + s); - } - - return options; - } - }, - PROPERTY { - @NotNull - @Override - public FindUsagesOptions parse(@NotNull String text, @NotNull Project project) { - KotlinPropertyFindUsagesOptions options = new KotlinPropertyFindUsagesOptions(project); - options.isUsages = false; - for (String s : InTextDirectivesUtils.findListWithPrefixes(text, "// OPTIONS: ")) { - if (parseCommonOptions(options, s)) continue; - - if (s.equals("overrides")) { - options.setSearchOverrides(true); - } - else if (s.equals("skipRead")) { - options.isReadAccess = false; - } - else if (s.equals("skipWrite")) { - options.isWriteAccess = false; - } - else fail("Invalid option: " + s); - } - - return options; - } - }, - JAVA_CLASS { - @NotNull - @Override - public FindUsagesOptions parse(@NotNull String text, @NotNull Project project) { - KotlinClassFindUsagesOptions options = new KotlinClassFindUsagesOptions(project); - options.isUsages = false; - options.setSearchConstructorUsages(false); - for (String s : InTextDirectivesUtils.findListWithPrefixes(text, "// OPTIONS: ")) { - if (parseCommonOptions(options, s)) continue; - - if (s.equals("derivedInterfaces")) { - options.isDerivedInterfaces = true; - } - else if (s.equals("derivedClasses")) { - options.isDerivedClasses = true; - } - else if (s.equals("implementingClasses")) { - options.isImplementingClasses = true; - } - else if (s.equals("methodUsages")) { - options.isMethodsUsages = true; - } - else if (s.equals("fieldUsages")) { - options.isFieldsUsages = true; - } - else fail("Invalid option: " + s); - } - - return options; - } - }, - JAVA_METHOD { - @NotNull - @Override - public FindUsagesOptions parse(@NotNull String text, @NotNull Project project) { - JavaMethodFindUsagesOptions options = new JavaMethodFindUsagesOptions(project); - options.isUsages = false; - for (String s : InTextDirectivesUtils.findListWithPrefixes(text, "// OPTIONS: ")) { - if (parseCommonOptions(options, s)) continue; - - if (s.equals("overrides")) { - options.isOverridingMethods = true; - options.isImplementingMethods = true; - } - else fail("Invalid option: " + s); - } - - return options; - } - }, - JAVA_FIELD { - @NotNull - @Override - public FindUsagesOptions parse(@NotNull String text, @NotNull Project project) { - return new JavaVariableFindUsagesOptions(project); - } - }, - JAVA_PACKAGE { - @NotNull - @Override - public FindUsagesOptions parse(@NotNull String text, @NotNull Project project) { - return new JavaPackageFindUsagesOptions(project); - } - }, - DEFAULT { - @NotNull - @Override - public FindUsagesOptions parse(@NotNull String text, @NotNull Project project) { - return new FindUsagesOptions(project); - } - }; - - protected static boolean parseCommonOptions(JavaFindUsagesOptions options, String s) { - if (s.equals("usages")) { - options.isUsages = true; - return true; - } - - if (s.equals("skipImports")) { - options.isSkipImportStatements = true; - return true; - } - - else if (s.equals("textOccurrences")) { - options.isSearchForTextOccurrences = true; - return true; - } - - return false; - } - - @NotNull - public abstract FindUsagesOptions parse(@NotNull String text, @NotNull Project project); - - @Nullable - public static OptionsParser getParserByPsiElementClass(@NotNull Class klass) { - if (klass == KtNamedFunction.class) { - return FUNCTION; - } - if (klass == KtProperty.class || klass == KtParameter.class) { - return PROPERTY; - } - if (klass == KtClass.class) { - return CLASS; - } - if (klass == PsiMethod.class) { - return JAVA_METHOD; - } - if (klass == PsiClass.class) { - return JAVA_CLASS; - } - if (klass == PsiField.class) { - return JAVA_FIELD; - } - if (klass == PsiPackage.class) { - return JAVA_PACKAGE; - } - if (klass == KtTypeParameter.class) { - return DEFAULT; - } - - return null; - } - } - - @NotNull - @Override - protected LightProjectDescriptor getProjectDescriptor() { - return KotlinWithJdkAndRuntimeLightProjectDescriptor.INSTANCE; - } - - @Override - public void setUp() { - super.setUp(); - myFixture.setTestDataPath(PluginTestCaseBase.getTestDataPathBase() + "/findUsages"); - } - - protected void extraConfig(@SuppressWarnings("UnusedParameters") @NotNull String path) { - - } - - protected void doTest(@NotNull String path) throws Exception { - File mainFile = new File(path); - final String mainFileName = mainFile.getName(); - String mainFileText = FileUtil.loadFile(mainFile, true); - final String prefix = mainFileName.substring(0, mainFileName.indexOf('.') + 1); - - boolean isPropertiesFile = FileUtilRt.getExtension(path).equals("properties"); - - Class caretElementClass; - if (!isPropertiesFile) { - List caretElementClassNames = InTextDirectivesUtils.findLinesWithPrefixesRemoved(mainFileText, "// PSI_ELEMENT: "); - assert caretElementClassNames.size() == 1; - //noinspection unchecked - caretElementClass = (Class)Class.forName(caretElementClassNames.get(0)); - } - else { - //noinspection unchecked - caretElementClass = (Class) (InTextDirectivesUtils.isDirectiveDefined(mainFileText, "## FIND_FILE_USAGES") - ? PropertiesFile.class - : Property.class); - } - - List fixtureClasses = InTextDirectivesUtils.findListWithPrefixes(mainFileText, "// FIXTURE_CLASS: "); - for (String fixtureClass : fixtureClasses) { - TestFixtureExtension.Companion.loadFixture(fixtureClass, myFixture.getModule()); - } - - try { - extraConfig(path); - - OptionsParser parser = OptionsParser.getParserByPsiElementClass(caretElementClass); - - String rootPath = path.substring(0, path.lastIndexOf("/") + 1); - - File rootDir = new File(rootPath); - File[] extraFiles = rootDir.listFiles( - new FilenameFilter() { - @Override - public boolean accept(@NotNull File dir, @NotNull String name) { - if (!name.startsWith(prefix) || name.equals(mainFileName)) return false; - - String ext = FileUtilRt.getExtension(name); - return ext.equals("kt") - || ext.equals("java") - || ext.equals("xml") - || ext.equals("properties") - || (ext.equals("txt") && !name.endsWith(".results.txt")); - } - } - ); - - assert extraFiles != null; - for (File file : extraFiles) { - myFixture.configureByFile(rootPath + file.getName()); - } - myFixture.configureByFile(path); - - PsiElement caretElement = - InTextDirectivesUtils.isDirectiveDefined(mainFileText, "// FIND_BY_REF") - ? TargetElementUtilBase.findTargetElement(myFixture.getEditor(), - TargetElementUtilBase.REFERENCED_ELEMENT_ACCEPTED | - JavaTargetElementEvaluator.NEW_AS_CONSTRUCTOR) - : myFixture.getElementAtCaret(); - assertNotNull(caretElement); - assertInstanceOf(caretElement, caretElementClass); - - PsiFile containingFile = caretElement.getContainingFile(); - boolean isLibraryElement = containingFile != null && ProjectRootsUtil.isLibraryFile(getProject(), containingFile.getVirtualFile()); - - FindUsagesOptions options = parser != null ? parser.parse(mainFileText, getProject()) : null; - - // Ensure that search by sources (if present) and decompiled declarations gives the same results - if (isLibraryElement) { - PsiElement originalElement = caretElement.getOriginalElement(); - findUsagesAndCheckResults(mainFileText, prefix, rootPath, originalElement, options); - - PsiElement navigationElement = caretElement.getNavigationElement(); - if (navigationElement != originalElement) { - findUsagesAndCheckResults(mainFileText, prefix, rootPath, navigationElement, options); - } - } - else { - findUsagesAndCheckResults(mainFileText, prefix, rootPath, caretElement, options); - } - } - finally { - for (String fixtureClass : fixtureClasses) { - TestFixtureExtension.Companion.unloadFixture(fixtureClass); - } - } - } - - private void findUsagesAndCheckResults( - @NotNull String mainFileText, - @NotNull String prefix, - @NotNull String rootPath, - @NotNull T caretElement, - @Nullable FindUsagesOptions options - ) throws ClassNotFoundException, InstantiationException, IllegalAccessException { - boolean highlightingMode = InTextDirectivesUtils.isDirectiveDefined(mainFileText, "// HIGHLIGHTING"); - - Collection usageInfos; - String log = null; - ArrayList logList = new ArrayList(); - try { - if (ExpressionsOfTypeProcessor.Companion.getMode() != ExpressionsOfTypeProcessor.Mode.ALWAYS_PLAIN) { - ExpressionsOfTypeProcessor.Companion.setTestLog(logList); - } - - usageInfos = findUsages(caretElement, options, highlightingMode); - } - finally { - ExpressionsOfTypeProcessor.Companion.setTestLog(null); - if (logList.size() > 0) { - Collections.sort(logList); - log = StringUtil.join(logList, "\n"); - } - } - - Collection filteringRules = instantiateClasses(mainFileText, "// FILTERING_RULES: "); - final Collection groupingRules = instantiateClasses(mainFileText, "// GROUPING_RULES: "); - - Collection filteredUsages = getUsageAdapters(filteringRules, usageInfos); - - List usageFiles = CollectionsKt.distinct( - CollectionsKt.map( - filteredUsages, - new Function1() { - @Override - public String invoke(UsageInfo2UsageAdapter adapter) { - return adapter.getFile().getName(); - } - } - ) - ); - final boolean appendFileName = usageFiles.size() > 1; - - Function convertToString = new Function() { - @Override - public String apply(@Nullable final UsageInfo2UsageAdapter usageAdapter) { - assert usageAdapter != null; - - String groupAsString = Joiner.on(", ").join( - CollectionsKt.map( - groupingRules, - new Function1() { - @Override - public String invoke(UsageGroupingRule rule) { - UsageGroup group = rule.groupUsage(usageAdapter); - return group != null ? group.getText(null) : ""; - } - } - ) - ); - if (!groupAsString.isEmpty()) { - groupAsString = "(" + groupAsString + ") "; - } - - UsageType usageType = getUsageType(usageAdapter.getElement()); - String usageTypeAsString = usageType == null ? "null" : usageType.toString(USAGE_VIEW_PRESENTATION); - - List usageChunks = new ArrayList(); - CollectionUtils.addAll(usageChunks, usageAdapter.getPresentation().getText()); - - // Add space after line number - usageChunks.add(1, new TextChunk(new TextAttributes(), " ")); - - return (appendFileName ? "[" + usageAdapter.getFile().getName() + "] " : "") + - usageTypeAsString + " " + - groupAsString + - Joiner.on("").join(usageChunks); - } - }; - - Collection finalUsages = Ordering.natural().sortedCopy(Collections2.transform(filteredUsages, convertToString)); - KotlinTestUtils.assertEqualsToFile(new File(rootPath, prefix + "results.txt"), StringUtil.join(finalUsages, "\n")); - - if (log != null) { - KotlinTestUtils.assertEqualsToFile(new File(rootPath, prefix + "log"), log); - - // if log is empty then compare results with plain search - try { - ExpressionsOfTypeProcessor.Companion.setMode(ExpressionsOfTypeProcessor.Mode.ALWAYS_PLAIN); - - findUsagesAndCheckResults(mainFileText, prefix, rootPath, caretElement, options); - } - finally { - ExpressionsOfTypeProcessor.Companion.setMode(ExpressionsOfTypeProcessor.Mode.ALWAYS_SMART); - } - } - } - - protected Collection findUsages( - @NotNull PsiElement targetElement, - @Nullable FindUsagesOptions options, - boolean highlightingMode - ) { - Project project = getProject(); - - FindUsagesHandler handler; - if (targetElement instanceof PsiMember) { - handler = new JavaFindUsagesHandler(targetElement, new JavaFindUsagesHandlerFactory(project)); - } - else if (targetElement instanceof KtDeclaration) { - handler = new KotlinFindUsagesHandlerFactory(project).createFindUsagesHandlerNoQuestions(targetElement); - } - else { - handler = ((FindManagerImpl) FindManager.getInstance(project)).getFindUsagesManager().getFindUsagesHandler(targetElement, false); - } - assert handler != null : "Cannot find handler for: " + targetElement; - - if (options == null) { - options = handler.getFindUsagesOptions(null); - } - - options.searchScope = GlobalSearchScope.allScope(project); - - CommonProcessors.CollectProcessor processor = new CommonProcessors.CollectProcessor(); - PsiElement[] psiElements = ArrayUtil.mergeArrays(handler.getPrimaryElements(), handler.getSecondaryElements()); - - for (PsiElement psiElement : psiElements) { - if (highlightingMode) { - for (PsiReference reference : handler.findReferencesToHighlight(psiElement, options.searchScope)) { - processor.process(new UsageInfo(reference)); - } - } - else { - handler.processElementUsages(psiElement, processor, options); - } - } - - return processor.getResults(); - } - - private static Collection getUsageAdapters( - final Collection filters, - Collection usageInfos - ) { - return Collections2.filter( - Collections2.transform(usageInfos, new Function() { - @Override - public UsageInfo2UsageAdapter apply(@Nullable UsageInfo usageInfo) { - assert (usageInfo != null); - - UsageInfo2UsageAdapter usageAdapter = new UsageInfo2UsageAdapter(usageInfo); - for (UsageFilteringRule filter : filters) { - if (!filter.isVisible(usageAdapter)) { - return null; - } - } - - return usageAdapter; - } - }), - Predicates.notNull()); - } - - @Nullable - private static UsageType getUsageType(@Nullable PsiElement element) { - if (element == null) return null; - - if (PsiTreeUtil.getParentOfType(element, PsiComment.class, false) != null) { - return UsageType.COMMENT_USAGE; - } - - UsageTypeProvider[] providers = Extensions.getExtensions(UsageTypeProvider.EP_NAME); - for (UsageTypeProvider provider : providers) { - UsageType usageType = provider.getUsageType(element); - if (usageType != null) { - return usageType; - } - } - - return UsageType.UNCLASSIFIED; - } - - private static Collection instantiateClasses(String mainFileText, String directive) - throws ClassNotFoundException, InstantiationException, IllegalAccessException { - List filteringRuleClassNames = InTextDirectivesUtils.findLinesWithPrefixesRemoved(mainFileText, directive); - - Collection filters = new ArrayList(); - for (String filteringRuleClassName : filteringRuleClassNames) { - //noinspection unchecked - Class klass = (Class) Class.forName(filteringRuleClassName); - filters.add(klass.newInstance()); - } - - return filters; - } -} diff --git a/idea/tests/org/jetbrains/kotlin/findUsages/AbstractFindUsagesTest.kt b/idea/tests/org/jetbrains/kotlin/findUsages/AbstractFindUsagesTest.kt new file mode 100644 index 00000000000..eccfa8884ee --- /dev/null +++ b/idea/tests/org/jetbrains/kotlin/findUsages/AbstractFindUsagesTest.kt @@ -0,0 +1,462 @@ +/* + * Copyright 2010-2015 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 org.jetbrains.kotlin.findUsages + +import com.intellij.codeInsight.JavaTargetElementEvaluator +import com.intellij.codeInsight.TargetElementUtilBase +import com.intellij.find.FindManager +import com.intellij.find.findUsages.* +import com.intellij.find.impl.FindManagerImpl +import com.intellij.lang.properties.psi.PropertiesFile +import com.intellij.lang.properties.psi.Property +import com.intellij.openapi.editor.markup.TextAttributes +import com.intellij.openapi.extensions.Extensions +import com.intellij.openapi.project.Project +import com.intellij.openapi.util.io.FileUtil +import com.intellij.openapi.util.io.FileUtilRt +import com.intellij.psi.* +import com.intellij.psi.search.GlobalSearchScope +import com.intellij.testFramework.LightProjectDescriptor +import com.intellij.testFramework.UsefulTestCase +import com.intellij.usageView.UsageInfo +import com.intellij.usages.TextChunk +import com.intellij.usages.UsageInfo2UsageAdapter +import com.intellij.usages.UsageViewPresentation +import com.intellij.usages.impl.rules.UsageType +import com.intellij.usages.impl.rules.UsageTypeProvider +import com.intellij.usages.rules.UsageFilteringRule +import com.intellij.usages.rules.UsageGroupingRule +import com.intellij.util.CommonProcessors +import org.jetbrains.kotlin.idea.findUsages.KotlinClassFindUsagesOptions +import org.jetbrains.kotlin.idea.findUsages.KotlinFindUsagesHandlerFactory +import org.jetbrains.kotlin.idea.findUsages.KotlinFunctionFindUsagesOptions +import org.jetbrains.kotlin.idea.findUsages.KotlinPropertyFindUsagesOptions +import org.jetbrains.kotlin.idea.search.usagesSearch.ExpressionsOfTypeProcessor +import org.jetbrains.kotlin.idea.test.KotlinLightCodeInsightFixtureTestCase +import org.jetbrains.kotlin.idea.test.KotlinWithJdkAndRuntimeLightProjectDescriptor +import org.jetbrains.kotlin.idea.test.PluginTestCaseBase +import org.jetbrains.kotlin.idea.test.TestFixtureExtension +import org.jetbrains.kotlin.idea.util.ProjectRootsUtil +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType +import org.jetbrains.kotlin.test.InTextDirectivesUtils +import org.jetbrains.kotlin.test.KotlinTestUtils +import java.io.File +import java.util.* + +abstract class AbstractFindUsagesTest : KotlinLightCodeInsightFixtureTestCase() { + + protected enum class OptionsParser { + CLASS { + override fun parse(text: String, project: Project): FindUsagesOptions { + return KotlinClassFindUsagesOptions(project).apply { + isUsages = false + isSearchForTextOccurrences = false + searchConstructorUsages = false + for (s in InTextDirectivesUtils.findListWithPrefixes(text, "// OPTIONS: ")) { + if (parseCommonOptions(this, s)) continue + + when (s) { + "constructorUsages" -> searchConstructorUsages = true + "derivedInterfaces" -> isDerivedInterfaces = true + "derivedClasses" -> isDerivedClasses = true + "functionUsages" -> isMethodsUsages = true + "propertyUsages" -> isFieldsUsages = true + else -> fail("Invalid option: " + s) + } + } + } + } + }, + FUNCTION { + override fun parse(text: String, project: Project): FindUsagesOptions { + return KotlinFunctionFindUsagesOptions(project).apply { + isUsages = false + for (s in InTextDirectivesUtils.findListWithPrefixes(text, "// OPTIONS: ")) { + if (parseCommonOptions(this, s)) continue + + when (s) { + "overrides" -> { + isOverridingMethods = true + isImplementingMethods = true + } + "overloadUsages" -> { + isIncludeOverloadUsages = true + isUsages = true + } + else -> fail("Invalid option: " + s) + } + } + } + } + }, + PROPERTY { + override fun parse(text: String, project: Project): FindUsagesOptions { + return KotlinPropertyFindUsagesOptions(project).apply { + isUsages = false + for (s in InTextDirectivesUtils.findListWithPrefixes(text, "// OPTIONS: ")) { + if (parseCommonOptions(this, s)) continue + + when (s) { + "overrides" -> searchOverrides = true + "skipRead" -> isReadAccess = false + "skipWrite" -> isWriteAccess = false + else -> fail("Invalid option: " + s) + } + } + } + } + }, + JAVA_CLASS { + override fun parse(text: String, project: Project): FindUsagesOptions { + return KotlinClassFindUsagesOptions(project).apply { + isUsages = false + searchConstructorUsages = false + for (s in InTextDirectivesUtils.findListWithPrefixes(text, "// OPTIONS: ")) { + if (parseCommonOptions(this, s)) continue + + when (s) { + "derivedInterfaces" -> isDerivedInterfaces = true + "derivedClasses" -> isDerivedClasses = true + "implementingClasses" -> isImplementingClasses = true + "methodUsages" -> isMethodsUsages = true + "fieldUsages" -> isFieldsUsages = true + else -> fail("Invalid option: " + s) + } + } + } + } + }, + JAVA_METHOD { + override fun parse(text: String, project: Project): FindUsagesOptions { + return JavaMethodFindUsagesOptions(project).apply { + isUsages = false + for (s in InTextDirectivesUtils.findListWithPrefixes(text, "// OPTIONS: ")) { + if (parseCommonOptions(this, s)) continue + + when (s) { + "overrides" -> { + isOverridingMethods = true + isImplementingMethods = true + } + else -> fail("Invalid option: " + s) + } + } + } + } + }, + JAVA_FIELD { + override fun parse(text: String, project: Project): FindUsagesOptions { + return JavaVariableFindUsagesOptions(project) + } + }, + JAVA_PACKAGE { + override fun parse(text: String, project: Project): FindUsagesOptions { + return JavaPackageFindUsagesOptions(project) + } + }, + DEFAULT { + override fun parse(text: String, project: Project): FindUsagesOptions { + return FindUsagesOptions(project) + } + }; + + abstract fun parse(text: String, project: Project): FindUsagesOptions + + companion object { + + protected fun parseCommonOptions(options: JavaFindUsagesOptions, s: String): Boolean { + when (s) { + "usages" -> { + options.isUsages = true + return true + } + "skipImports" -> { + options.isSkipImportStatements = true + return true + } + "textOccurrences" -> { + options.isSearchForTextOccurrences = true + return true + } + else -> return false + } + + } + + fun getParserByPsiElementClass(klass: Class): OptionsParser? { + return when (klass) { + KtNamedFunction::class.java -> FUNCTION + KtProperty::class.java, KtParameter::class.java -> PROPERTY + KtClass::class.java -> CLASS + PsiMethod::class.java -> JAVA_METHOD + PsiClass::class.java -> JAVA_CLASS + PsiField::class.java -> JAVA_FIELD + PsiPackage::class.java -> JAVA_PACKAGE + KtTypeParameter::class.java -> DEFAULT + else -> null + } + + } + } + } + + override fun getProjectDescriptor(): LightProjectDescriptor = KotlinWithJdkAndRuntimeLightProjectDescriptor.INSTANCE + + public override fun setUp() { + super.setUp() + myFixture.testDataPath = PluginTestCaseBase.getTestDataPathBase() + "/findUsages" + } + + // used in Spring tests (outside main project!) + protected open fun extraConfig(path: String) { + } + + protected fun doTest(path: String) { + val mainFile = File(path) + val mainFileName = mainFile.name + val mainFileText = FileUtil.loadFile(mainFile, true) + val prefix = mainFileName.substringBefore(".") + "." + + val isPropertiesFile = FileUtilRt.getExtension(path) == "properties" + + @Suppress("UNCHECKED_CAST") + val caretElementClass = (if (!isPropertiesFile) { + val caretElementClassNames = InTextDirectivesUtils.findLinesWithPrefixesRemoved(mainFileText, "// PSI_ELEMENT: ") + Class.forName(caretElementClassNames.single()) + } + else if (InTextDirectivesUtils.isDirectiveDefined(mainFileText, "## FIND_FILE_USAGES")) { + PropertiesFile::class.java + } + else { + Property::class.java + }) as Class + + val fixtureClasses = InTextDirectivesUtils.findListWithPrefixes(mainFileText, "// FIXTURE_CLASS: ") + for (fixtureClass in fixtureClasses) { + TestFixtureExtension.loadFixture(fixtureClass, myFixture.module) + } + + try { + extraConfig(path) + + val parser = OptionsParser.getParserByPsiElementClass(caretElementClass) + + val rootPath = path.substringBeforeLast("/") + "/" + + val rootDir = File(rootPath) + val extraFiles = rootDir.listFiles { dir, name -> + if (!name.startsWith(prefix) || name == mainFileName) return@listFiles false + + val ext = FileUtilRt.getExtension(name) + ext == "kt" + || ext == "java" + || ext == "xml" + || ext == "properties" + || ext == "txt" && !name.endsWith(".results.txt") + } + + for (file in extraFiles) { + myFixture.configureByFile(rootPath + file.name) + } + myFixture.configureByFile(path) + + val caretElement = if (InTextDirectivesUtils.isDirectiveDefined(mainFileText, "// FIND_BY_REF")) + TargetElementUtilBase.findTargetElement(myFixture.editor, + TargetElementUtilBase.REFERENCED_ELEMENT_ACCEPTED or JavaTargetElementEvaluator.NEW_AS_CONSTRUCTOR)!! + else + myFixture.elementAtCaret + UsefulTestCase.assertInstanceOf(caretElement, caretElementClass) + + val containingFile = caretElement.containingFile + val isLibraryElement = containingFile != null && ProjectRootsUtil.isLibraryFile(project, containingFile.virtualFile) + + val options = parser?.parse(mainFileText, project) + + // Ensure that search by sources (if present) and decompiled declarations gives the same results + if (isLibraryElement) { + val originalElement = caretElement.originalElement + findUsagesAndCheckResults(mainFileText, prefix, rootPath, originalElement, options) + + val navigationElement = caretElement.navigationElement + if (navigationElement !== originalElement) { + findUsagesAndCheckResults(mainFileText, prefix, rootPath, navigationElement, options) + } + } + else { + findUsagesAndCheckResults(mainFileText, prefix, rootPath, caretElement, options) + } + } + finally { + fixtureClasses.forEach { TestFixtureExtension.unloadFixture(it) } + } + } + + private fun findUsagesAndCheckResults( + mainFileText: String, + prefix: String, + rootPath: String, + caretElement: T, + options: FindUsagesOptions? + ) { + val highlightingMode = InTextDirectivesUtils.isDirectiveDefined(mainFileText, "// HIGHLIGHTING") + + var log: String? = null + val logList = ArrayList() + val usageInfos = try { + if (ExpressionsOfTypeProcessor.mode !== ExpressionsOfTypeProcessor.Mode.ALWAYS_PLAIN) { + ExpressionsOfTypeProcessor.testLog = logList + } + + findUsages(caretElement, options, highlightingMode) + } + finally { + ExpressionsOfTypeProcessor.testLog = null + if (logList.size > 0) { + log = logList.sorted().joinToString("\n") + } + } + + val filteringRules = instantiateClasses(mainFileText, "// FILTERING_RULES: ") + val groupingRules = instantiateClasses(mainFileText, "// GROUPING_RULES: ") + + val filteredUsages = getUsageAdapters(filteringRules, usageInfos) + + val usageFiles = filteredUsages.map { it.file.name }.distinct() + val appendFileName = usageFiles.size > 1 + + val convertToString: (UsageInfo2UsageAdapter) -> String = { usageAdapter -> + var groupAsString = groupingRules + .map { it.groupUsage(usageAdapter)?.getText(null) ?: "" } + .joinToString(", ") + if (!groupAsString.isEmpty()) { + groupAsString = "($groupAsString) " + } + + val usageType = getUsageType(usageAdapter.element) + val usageTypeAsString = usageType?.toString(USAGE_VIEW_PRESENTATION) ?: "null" + + val usageChunks = ArrayList() + usageChunks.addAll(usageAdapter.presentation.text.asList()) + usageChunks.add(1, TextChunk(TextAttributes(), " ")) // add space after line number + + buildString { + if (appendFileName) { + append("[").append(usageAdapter.file.name).append("] ") + } + append(usageTypeAsString) + append(" ") + append(groupAsString) + append(usageChunks.joinToString("")) + } + } + + val finalUsages = filteredUsages.map(convertToString).sorted() + KotlinTestUtils.assertEqualsToFile(File(rootPath, prefix + "results.txt"), finalUsages.joinToString("\n")) + + if (log != null) { + KotlinTestUtils.assertEqualsToFile(File(rootPath, prefix + "log"), log) + + // if log is empty then compare results with plain search + try { + ExpressionsOfTypeProcessor.mode = ExpressionsOfTypeProcessor.Mode.ALWAYS_PLAIN + + findUsagesAndCheckResults(mainFileText, prefix, rootPath, caretElement, options) + } + finally { + ExpressionsOfTypeProcessor.mode = ExpressionsOfTypeProcessor.Mode.ALWAYS_SMART + } + } + } + + protected fun findUsages( + targetElement: PsiElement, + options: FindUsagesOptions?, + highlightingMode: Boolean + ): Collection { + @Suppress("NAME_SHADOWING") + var options = options + val project = project + + val handler: FindUsagesHandler = (if (targetElement is PsiMember) { + JavaFindUsagesHandler(targetElement, JavaFindUsagesHandlerFactory(project)) + } + else if (targetElement is KtDeclaration) { + KotlinFindUsagesHandlerFactory(project).createFindUsagesHandlerNoQuestions(targetElement) + } + else { + (FindManager.getInstance(project) as FindManagerImpl).findUsagesManager.getFindUsagesHandler(targetElement, false) + }) ?: error("Cannot find handler for: $targetElement") + + if (options == null) { + options = handler.getFindUsagesOptions(null) + } + + options.searchScope = GlobalSearchScope.allScope(project) + + val processor = CommonProcessors.CollectProcessor() + for (psiElement in handler.primaryElements + handler.secondaryElements) { + if (highlightingMode) { + for (reference in handler.findReferencesToHighlight(psiElement, options.searchScope)) { + processor.process(UsageInfo(reference)) + } + } + else { + handler.processElementUsages(psiElement, processor, options) + } + } + + return processor.results + } + + companion object { + + val USAGE_VIEW_PRESENTATION = UsageViewPresentation() + + private fun getUsageAdapters( + filters: Collection, + usageInfos: Collection + ): Collection { + return usageInfos + .map(::UsageInfo2UsageAdapter) + .filter { usageAdapter -> filters.all { it.isVisible(usageAdapter) } } + } + + private fun getUsageType(element: PsiElement?): UsageType? { + if (element == null) return null + + if (element.getNonStrictParentOfType() != null) { + return UsageType.COMMENT_USAGE + } + + val providers = Extensions.getExtensions(UsageTypeProvider.EP_NAME) + return providers + .mapNotNull { it.getUsageType(element) } + .firstOrNull() + ?: UsageType.UNCLASSIFIED + } + + private fun instantiateClasses(mainFileText: String, directive: String): Collection { + val filteringRuleClassNames = InTextDirectivesUtils.findLinesWithPrefixesRemoved(mainFileText, directive) + return filteringRuleClassNames + .map { + @Suppress("UNCHECKED_CAST") + (Class.forName(it).newInstance() as T) + } + } + } +}