From 5847c3559b980470bcd7019315b3a31fb3c5ef76 Mon Sep 17 00:00:00 2001 From: "Pavel V. Talanov" Date: Fri, 19 Sep 2014 14:45:35 +0400 Subject: [PATCH] Implement ModuleSourceInfo.getDependentModules() --- .../plugin/caches/resolve/moduleDependents.kt | 90 +++++++++++++++++++ .../caches/resolve/IdeaModuleInfoTest.kt | 50 ++++++++++- 2 files changed, 136 insertions(+), 4 deletions(-) create mode 100644 idea/idea-analysis/src/org/jetbrains/jet/plugin/caches/resolve/moduleDependents.kt diff --git a/idea/idea-analysis/src/org/jetbrains/jet/plugin/caches/resolve/moduleDependents.kt b/idea/idea-analysis/src/org/jetbrains/jet/plugin/caches/resolve/moduleDependents.kt new file mode 100644 index 00000000000..b0f56c8efc1 --- /dev/null +++ b/idea/idea-analysis/src/org/jetbrains/jet/plugin/caches/resolve/moduleDependents.kt @@ -0,0 +1,90 @@ +/* + * Copyright 2010-2014 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.jet.plugin.caches.resolve + +import com.intellij.openapi.module.Module +import com.intellij.util.containers.MultiMap +import com.intellij.openapi.project.Project +import com.intellij.openapi.module.ModuleManager +import com.intellij.openapi.roots.ModuleRootManager +import com.intellij.openapi.roots.ModuleOrderEntry +import gnu.trove.THashSet +import com.intellij.util.containers.Queue +import com.intellij.psi.util.CachedValuesManager +import com.intellij.psi.util.CachedValueProvider +import com.intellij.openapi.roots.ProjectRootModificationTracker +import java.util.HashSet + +//NOTE: this is an approximation that may contain more module infos then the exact solution +public fun ModuleSourceInfo.getDependentModules(): Set { + val dependents = getDependents(module) + if (isTests()) { + return dependents.mapTo(HashSet()) { it.testSourceInfo() } + } + else { + return dependents.flatMapTo(HashSet()) { listOf(it.productionSourceInfo(), it.testSourceInfo()) } + } +} + +//NOTE: getDependents adapted from com.intellij.openapi.module.impl.scopes.ModuleWithDependentsScope#buildDependents() +private fun getDependents(module: Module): Set { + val result = THashSet() + result.add(module) + + val processedExporting = THashSet() + + val index = getModuleIndex(module.getProject()) + + val walkingQueue = Queue(10) + walkingQueue.addLast(module) + + while (!walkingQueue.isEmpty()) { + val current = walkingQueue.pullFirst() + processedExporting.add(current!!) + result.addAll(index.plainUsages[current]) + for (dependent in index.exportingUsages[current]) { + result.add(dependent) + if (processedExporting.add(dependent)) { + walkingQueue.addLast(dependent) + } + } + } + return result +} + +private class ModuleIndex { + val plainUsages = MultiMap.create() + val exportingUsages = MultiMap.create() +} + +private fun getModuleIndex(project: Project): ModuleIndex { + return CachedValuesManager.getManager(project).getCachedValue(project) { + val index = ModuleIndex() + for (module in ModuleManager.getInstance(project).getModules()) { + for (orderEntry in ModuleRootManager.getInstance(module).getOrderEntries()) { + if (orderEntry is ModuleOrderEntry) { + val referenced = orderEntry.getModule() + if (referenced != null) { + val map = if (orderEntry.isExported()) index.exportingUsages else index.plainUsages + map.putValue(referenced, module) + } + } + } + } + CachedValueProvider.Result(index, ProjectRootModificationTracker.getInstance(project)) + }!! +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/jet/plugin/caches/resolve/IdeaModuleInfoTest.kt b/idea/tests/org/jetbrains/jet/plugin/caches/resolve/IdeaModuleInfoTest.kt index fcc5c6bce1a..a3bca4084b2 100644 --- a/idea/tests/org/jetbrains/jet/plugin/caches/resolve/IdeaModuleInfoTest.kt +++ b/idea/tests/org/jetbrains/jet/plugin/caches/resolve/IdeaModuleInfoTest.kt @@ -21,10 +21,11 @@ import com.intellij.openapi.module.StdModuleTypes import com.intellij.openapi.module.Module import com.intellij.openapi.roots.ModuleRootModificationUtil import com.intellij.openapi.roots.DependencyScope -import junit.framework.Assert +import org.junit.Assert import com.intellij.openapi.roots.impl.libraries.ProjectLibraryTable import com.intellij.openapi.roots.libraries.Library import com.intellij.openapi.command.WriteCommandAction +import com.intellij.testFramework.UsefulTestCase class IdeaModuleInfoTest : ModuleTestCase() { @@ -182,6 +183,43 @@ class IdeaModuleInfoTest : ModuleTestCase() { c.test.assertDependenciesEqual(c.test, c.production, b.test, b.production, a.test, a.production) } + fun testDependents() { + //NOTE: we do not differ between dependency kinds + val (a, b, c) = modules(name1 = "a", name2 = "b", name3 = "c") + val (d, e, f) = modules(name1 = "d", name2 = "e", name3 = "f") + + b.addDependency(a, exported = true) + + c.addDependency(a) + + d.addDependency(c, exported = true) + + e.addDependency(b) + + f.addDependency(d) + f.addDependency(e) + + + a.test.assertDependentsEqual(a.test, b.test, c.test, e.test) + a.production.assertDependentsEqual(a.production, a.test, b.production, b.test, c.production, c.test, e.production, e.test) + + b.test.assertDependentsEqual(b.test, e.test) + b.production.assertDependentsEqual(b.production, b.test, e.production, e.test) + + + c.test.assertDependentsEqual(c.test, d.test, f.test) + c.production.assertDependentsEqual(c.production, c.test, d.production, d.test, f.production, f.test) + + d.test.assertDependentsEqual(d.test, f.test) + d.production.assertDependentsEqual(d.production, d.test, f.production, f.test) + + e.test.assertDependentsEqual(e.test, f.test) + e.production.assertDependentsEqual(e.production, e.test, f.production, f.test) + + f.test.assertDependentsEqual(f.test) + f.production.assertDependentsEqual(f.production, f.test) + } + private fun Module.addDependency( other: Module, dependencyScope: DependencyScope = DependencyScope.COMPILE, @@ -189,13 +227,13 @@ class IdeaModuleInfoTest : ModuleTestCase() { ) = ModuleRootModificationUtil.addDependency(this, other, dependencyScope, exported) private val Module.production: ModuleProductionSourceInfo - get() = productionSourceInfo() + get() = productionSourceInfo() private val Module.test: ModuleTestSourceInfo - get() = testSourceInfo() + get() = testSourceInfo() private val Library.classes: LibraryInfo - get() = LibraryInfo(getProject()!!, this) + get() = LibraryInfo(getProject()!!, this) private fun Module.addDependency( lib: Library, @@ -213,6 +251,10 @@ class IdeaModuleInfoTest : ModuleTestCase() { Assert.assertEquals(dependencies.toList(), this.dependencies()) } + private fun ModuleSourceInfo.assertDependentsEqual(vararg dependents: ModuleSourceInfo) { + UsefulTestCase.assertSameElements(this.getDependentModules(), dependents.toList()) + } + private fun projectLibrary(name: String = "lib"): Library { val libraryTable = ProjectLibraryTable.getInstance(myProject)!! return WriteCommandAction.runWriteCommandAction(myProject) {