Implement ModuleSourceInfo.getDependentModules()

This commit is contained in:
Pavel V. Talanov
2014-09-19 14:45:35 +04:00
parent bf546c83fb
commit 5847c3559b
2 changed files with 136 additions and 4 deletions
@@ -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<ModuleSourceInfo> {
val dependents = getDependents(module)
if (isTests()) {
return dependents.mapTo(HashSet<ModuleSourceInfo>()) { it.testSourceInfo() }
}
else {
return dependents.flatMapTo(HashSet<ModuleSourceInfo>()) { listOf(it.productionSourceInfo(), it.testSourceInfo()) }
}
}
//NOTE: getDependents adapted from com.intellij.openapi.module.impl.scopes.ModuleWithDependentsScope#buildDependents()
private fun getDependents(module: Module): Set<Module> {
val result = THashSet<Module>()
result.add(module)
val processedExporting = THashSet<Module>()
val index = getModuleIndex(module.getProject())
val walkingQueue = Queue<Module>(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<Module, Module>()
val exportingUsages = MultiMap.create<Module, Module>()
}
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))
}!!
}
@@ -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<Library>(myProject) {