Refactor JvmDependenciesIndex

- Move collectKnownClassNamesInPackage to the only place where it's used
- Fix warnings/inspections and simplify implementation a bit
This commit is contained in:
Alexander Udalov
2017-03-26 13:25:27 +03:00
parent 801a93edbc
commit f1a1ebae01
4 changed files with 47 additions and 72 deletions
@@ -121,7 +121,22 @@ class KotlinCliJavaFileManagerImpl(private val myPsiManager: PsiManager) : CoreJ
return findClassInPsiFile(classNameWithInnerClasses, file) return findClassInPsiFile(classNameWithInnerClasses, file)
} }
override fun knownClassNamesInPackage(packageFqName: FqName) = index.collectKnownClassNamesInPackage(packageFqName) override fun knownClassNamesInPackage(packageFqName: FqName): Set<String> {
val result = hashSetOf<String>()
index.traverseDirectoriesInPackage(packageFqName, continueSearch = {
dir, _ ->
for (child in dir.children) {
if (child.extension == "class" || child.extension == "java") {
result.add(child.nameWithoutExtension)
}
}
true
})
return result
}
companion object { companion object {
private val LOG = Logger.getInstance(KotlinCliJavaFileManagerImpl::class.java) private val LOG = Logger.getInstance(KotlinCliJavaFileManagerImpl::class.java)
@@ -23,7 +23,7 @@ import java.util.concurrent.locks.ReentrantReadWriteLock
import kotlin.concurrent.read import kotlin.concurrent.read
import kotlin.concurrent.write import kotlin.concurrent.write
class JvmDependenciesDynamicCompoundIndex() : JvmDependenciesIndex { class JvmDependenciesDynamicCompoundIndex : JvmDependenciesIndex {
private val indices = arrayListOf<JvmDependenciesIndex>() private val indices = arrayListOf<JvmDependenciesIndex>()
private val lock = ReentrantReadWriteLock() private val lock = ReentrantReadWriteLock()
@@ -36,13 +36,9 @@ class JvmDependenciesDynamicCompoundIndex() : JvmDependenciesIndex {
fun addNewIndexForRoots(roots: Iterable<JavaRoot>): JvmDependenciesIndex? = fun addNewIndexForRoots(roots: Iterable<JavaRoot>): JvmDependenciesIndex? =
lock.read { lock.read {
val alreadyIndexed = indexedRoots.toHashSet() val alreadyIndexed = indexedRoots.toHashSet()
val newRoots = roots.filter { !alreadyIndexed.contains(it) } val newRoots = roots.filter { root -> root !in alreadyIndexed }
if (newRoots.isEmpty()) null if (newRoots.isEmpty()) null
else { else JvmDependenciesIndexImpl(newRoots).also(this::addIndex)
val index = JvmDependenciesIndexImpl(newRoots)
addIndex(index)
index
}
} }
override val indexedRoots: Sequence<JavaRoot> get() = indices.asSequence().flatMap { it.indexedRoots } override val indexedRoots: Sequence<JavaRoot> get() = indices.asSequence().flatMap { it.indexedRoots }
@@ -51,22 +47,15 @@ class JvmDependenciesDynamicCompoundIndex() : JvmDependenciesIndex {
classId: ClassId, classId: ClassId,
acceptedRootTypes: Set<JavaRoot.RootType>, acceptedRootTypes: Set<JavaRoot.RootType>,
findClassGivenDirectory: (VirtualFile, JavaRoot.RootType) -> T? findClassGivenDirectory: (VirtualFile, JavaRoot.RootType) -> T?
): T? = ): T? = lock.read {
lock.read { indices.asSequence().mapNotNull { it.findClass(classId, acceptedRootTypes, findClassGivenDirectory) }.firstOrNull()
indices.asSequence().mapNotNull { it.findClass(classId, acceptedRootTypes, findClassGivenDirectory) }.firstOrNull() }
}
override fun traverseDirectoriesInPackage( override fun traverseDirectoriesInPackage(
packageFqName: FqName, packageFqName: FqName,
acceptedRootTypes: Set<JavaRoot.RootType>, acceptedRootTypes: Set<JavaRoot.RootType>,
continueSearch: (VirtualFile, JavaRoot.RootType) -> Boolean continueSearch: (VirtualFile, JavaRoot.RootType) -> Boolean
) { ) = lock.read {
lock.read { indices.forEach { it.traverseDirectoriesInPackage(packageFqName, acceptedRootTypes, continueSearch) }
indices.forEach { it.traverseDirectoriesInPackage(packageFqName, acceptedRootTypes, continueSearch) }
}
}
override fun collectKnownClassNamesInPackage(packageFqName: FqName): Set<String> = lock.read {
indices.flatMapTo(hashSetOf()) { it.collectKnownClassNamesInPackage(packageFqName) }
} }
} }
@@ -35,10 +35,6 @@ interface JvmDependenciesIndex {
acceptedRootTypes: Set<JavaRoot.RootType> = JavaRoot.SourceAndBinary, acceptedRootTypes: Set<JavaRoot.RootType> = JavaRoot.SourceAndBinary,
continueSearch: (VirtualFile, JavaRoot.RootType) -> Boolean continueSearch: (VirtualFile, JavaRoot.RootType) -> Boolean
) )
fun collectKnownClassNamesInPackage(
packageFqName: FqName
): Set<String>
} }
data class JavaRoot(val file: VirtualFile, val type: RootType, val prefixFqName: FqName? = null) { data class JavaRoot(val file: VirtualFile, val type: RootType, val prefixFqName: FqName? = null) {
@@ -39,24 +39,21 @@ class JvmDependenciesIndexImpl(_roots: List<JavaRoot>): JvmDependenciesIndex {
private class Cache { private class Cache {
private val innerPackageCaches = HashMap<String, Cache>() private val innerPackageCaches = HashMap<String, Cache>()
operator fun get(name: String) = innerPackageCaches.getOrPut(name) { Cache() } operator fun get(name: String) = innerPackageCaches.getOrPut(name, ::Cache)
// indices of roots that are known to contain this package // indices of roots that are known to contain this package
// if this list contains [1, 3, 5] then roots with indices 1, 3 and 5 are known to contain this package, 2 and 4 are known not to (no information about roots 6 or higher) // if this list contains [1, 3, 5] then roots with indices 1, 3 and 5 are known to contain this package, 2 and 4 are known not to (no information about roots 6 or higher)
// if this list contains maxIndex that means that all roots containing this package are known // if this list contains maxIndex that means that all roots containing this package are known
val rootIndices = IntArrayList() val rootIndices = IntArrayList(2)
} }
// root "Cache" object corresponds to DefaultPackage which exists in every root. Roots with non-default fqname are also listed here but // root "Cache" object corresponds to DefaultPackage which exists in every root. Roots with non-default fqname are also listed here but
// they will be ignored on requests with invalid fqname prefix. // they will be ignored on requests with invalid fqname prefix.
private val rootCache: Cache by lazy { private val rootCache: Cache by lazy {
with(Cache()) { Cache().apply {
roots.indices.forEach { roots.indices.forEach(rootIndices::add)
rootIndices.add(it)
}
rootIndices.add(maxIndex) rootIndices.add(maxIndex)
rootIndices.trimToSize() rootIndices.trimToSize()
this
} }
} }
@@ -72,7 +69,7 @@ class JvmDependenciesIndexImpl(_roots: List<JavaRoot>): JvmDependenciesIndex {
acceptedRootTypes: Set<JavaRoot.RootType>, acceptedRootTypes: Set<JavaRoot.RootType>,
findClassGivenDirectory: (VirtualFile, JavaRoot.RootType) -> T? findClassGivenDirectory: (VirtualFile, JavaRoot.RootType) -> T?
): T? { ): T? {
return search(FindClassRequest(classId, acceptedRootTypes)) { dir, rootType -> return findClassCached(classId, acceptedRootTypes) { dir, rootType ->
val found = findClassGivenDirectory(dir, rootType) val found = findClassGivenDirectory(dir, rootType)
HandleResult(found, continueSearch = found == null) HandleResult(found, continueSearch = found == null)
} }
@@ -88,73 +85,51 @@ class JvmDependenciesIndexImpl(_roots: List<JavaRoot>): JvmDependenciesIndex {
} }
} }
override fun collectKnownClassNamesInPackage(
packageFqName: FqName
): Set<String> {
val result = hashSetOf<String>()
traverseDirectoriesInPackage(packageFqName, continueSearch = {
dir, _ ->
for (child in dir.children) {
if (child.extension != "class" && child.extension != "java") continue
result.add(child.nameWithoutExtension)
}
true
})
return result
}
private data class HandleResult<out T : Any>(val result: T?, val continueSearch: Boolean) private data class HandleResult<out T : Any>(val result: T?, val continueSearch: Boolean)
private fun <T : Any> search( private fun <T : Any> findClassCached(
request: SearchRequest, classId: ClassId,
acceptedRootTypes: Set<JavaRoot.RootType>,
handler: (VirtualFile, JavaRoot.RootType) -> HandleResult<T> handler: (VirtualFile, JavaRoot.RootType) -> HandleResult<T>
): T? { ): T? {
// make a decision based on information saved from last class search // make a decision based on information saved from last class search
if (request !is FindClassRequest || lastClassSearch?.first?.classId != request.classId) { if (lastClassSearch?.first?.classId != classId) {
return doSearch(request, handler) return search(FindClassRequest(classId, acceptedRootTypes), handler)
} }
val (cachedRequest, cachedResult) = lastClassSearch!! val (cachedRequest, cachedResult) = lastClassSearch!!
return when (cachedResult) { return when (cachedResult) {
is SearchResult.NotFound -> { is SearchResult.NotFound -> {
val limitedRootTypes = request.acceptedRootTypes.toHashSet() val limitedRootTypes = acceptedRootTypes - cachedRequest.acceptedRootTypes
limitedRootTypes.removeAll(cachedRequest.acceptedRootTypes)
if (limitedRootTypes.isEmpty()) { if (limitedRootTypes.isEmpty()) {
null null
} }
else { else {
doSearch(FindClassRequest(request.classId, limitedRootTypes), handler) search(FindClassRequest(classId, limitedRootTypes), handler)
} }
} }
is SearchResult.Found -> { is SearchResult.Found -> {
if (cachedRequest.acceptedRootTypes == request.acceptedRootTypes) { if (cachedRequest.acceptedRootTypes == acceptedRootTypes) {
handler(cachedResult.packageDirectory, cachedResult.root.type).result handler(cachedResult.packageDirectory, cachedResult.root.type).result
} }
else { else {
doSearch(request, handler) search(FindClassRequest(classId, acceptedRootTypes), handler)
} }
} }
} }
} }
private fun <T : Any> doSearch(request: SearchRequest, handler: (VirtualFile, JavaRoot.RootType) -> HandleResult<T>): T? { private fun <T : Any> search(request: SearchRequest, handler: (VirtualFile, JavaRoot.RootType) -> HandleResult<T>): T? {
val findClassRequest = request as? FindClassRequest fun <T : Any> found(packageDirectory: VirtualFile, root: JavaRoot, result: T): T = result.also {
if (request is FindClassRequest) {
fun <T : Any> found(packageDirectory: VirtualFile, root: JavaRoot, result: T): T { lastClassSearch = Pair(request, SearchResult.Found(packageDirectory, root))
if (findClassRequest != null) {
lastClassSearch = Pair(findClassRequest, SearchResult.Found(packageDirectory, root))
} }
return result
} }
fun <T : Any> notFound(): T? { fun <T : Any> notFound(): T? = null.also {
if (findClassRequest != null) { if (request is FindClassRequest) {
lastClassSearch = Pair(findClassRequest, SearchResult.NotFound) lastClassSearch = Pair(request, SearchResult.NotFound)
} }
return null
} }
fun handle(root: JavaRoot, targetDirInRoot: VirtualFile): T? { fun handle(root: JavaRoot, targetDirInRoot: VirtualFile): T? {
@@ -197,7 +172,7 @@ class JvmDependenciesIndexImpl(_roots: List<JavaRoot>): JvmDependenciesIndex {
return notFound() return notFound()
} }
// try to find a target directory corresponding to package represented by packagesPath in a given root reprenting by index // try to find a target directory corresponding to package represented by packagesPath in a given root represented by index
// possibly filling "Cache" objects with new information // possibly filling "Cache" objects with new information
private fun travelPath(rootIndex: Int, packagesPath: List<String>, fillCachesAfter: Int, cachesPath: List<Cache>): VirtualFile? { private fun travelPath(rootIndex: Int, packagesPath: List<String>, fillCachesAfter: Int, cachesPath: List<Cache>): VirtualFile? {
if (rootIndex >= maxIndex) { if (rootIndex >= maxIndex) {