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)
}
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 {
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.write
class JvmDependenciesDynamicCompoundIndex() : JvmDependenciesIndex {
class JvmDependenciesDynamicCompoundIndex : JvmDependenciesIndex {
private val indices = arrayListOf<JvmDependenciesIndex>()
private val lock = ReentrantReadWriteLock()
@@ -36,13 +36,9 @@ class JvmDependenciesDynamicCompoundIndex() : JvmDependenciesIndex {
fun addNewIndexForRoots(roots: Iterable<JavaRoot>): JvmDependenciesIndex? =
lock.read {
val alreadyIndexed = indexedRoots.toHashSet()
val newRoots = roots.filter { !alreadyIndexed.contains(it) }
val newRoots = roots.filter { root -> root !in alreadyIndexed }
if (newRoots.isEmpty()) null
else {
val index = JvmDependenciesIndexImpl(newRoots)
addIndex(index)
index
}
else JvmDependenciesIndexImpl(newRoots).also(this::addIndex)
}
override val indexedRoots: Sequence<JavaRoot> get() = indices.asSequence().flatMap { it.indexedRoots }
@@ -51,22 +47,15 @@ class JvmDependenciesDynamicCompoundIndex() : JvmDependenciesIndex {
classId: ClassId,
acceptedRootTypes: Set<JavaRoot.RootType>,
findClassGivenDirectory: (VirtualFile, JavaRoot.RootType) -> T?
): T? =
lock.read {
indices.asSequence().mapNotNull { it.findClass(classId, acceptedRootTypes, findClassGivenDirectory) }.firstOrNull()
}
): T? = lock.read {
indices.asSequence().mapNotNull { it.findClass(classId, acceptedRootTypes, findClassGivenDirectory) }.firstOrNull()
}
override fun traverseDirectoriesInPackage(
packageFqName: FqName,
acceptedRootTypes: Set<JavaRoot.RootType>,
continueSearch: (VirtualFile, JavaRoot.RootType) -> Boolean
) {
lock.read {
indices.forEach { it.traverseDirectoriesInPackage(packageFqName, acceptedRootTypes, continueSearch) }
}
}
override fun collectKnownClassNamesInPackage(packageFqName: FqName): Set<String> = lock.read {
indices.flatMapTo(hashSetOf()) { it.collectKnownClassNamesInPackage(packageFqName) }
) = lock.read {
indices.forEach { it.traverseDirectoriesInPackage(packageFqName, acceptedRootTypes, continueSearch) }
}
}
@@ -35,10 +35,6 @@ interface JvmDependenciesIndex {
acceptedRootTypes: Set<JavaRoot.RootType> = JavaRoot.SourceAndBinary,
continueSearch: (VirtualFile, JavaRoot.RootType) -> Boolean
)
fun collectKnownClassNamesInPackage(
packageFqName: FqName
): Set<String>
}
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 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
// 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
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
// they will be ignored on requests with invalid fqname prefix.
private val rootCache: Cache by lazy {
with(Cache()) {
roots.indices.forEach {
rootIndices.add(it)
}
Cache().apply {
roots.indices.forEach(rootIndices::add)
rootIndices.add(maxIndex)
rootIndices.trimToSize()
this
}
}
@@ -72,7 +69,7 @@ class JvmDependenciesIndexImpl(_roots: List<JavaRoot>): JvmDependenciesIndex {
acceptedRootTypes: Set<JavaRoot.RootType>,
findClassGivenDirectory: (VirtualFile, JavaRoot.RootType) -> T?
): T? {
return search(FindClassRequest(classId, acceptedRootTypes)) { dir, rootType ->
return findClassCached(classId, acceptedRootTypes) { dir, rootType ->
val found = findClassGivenDirectory(dir, rootType)
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 fun <T : Any> search(
request: SearchRequest,
private fun <T : Any> findClassCached(
classId: ClassId,
acceptedRootTypes: Set<JavaRoot.RootType>,
handler: (VirtualFile, JavaRoot.RootType) -> HandleResult<T>
): T? {
// make a decision based on information saved from last class search
if (request !is FindClassRequest || lastClassSearch?.first?.classId != request.classId) {
return doSearch(request, handler)
if (lastClassSearch?.first?.classId != classId) {
return search(FindClassRequest(classId, acceptedRootTypes), handler)
}
val (cachedRequest, cachedResult) = lastClassSearch!!
return when (cachedResult) {
is SearchResult.NotFound -> {
val limitedRootTypes = request.acceptedRootTypes.toHashSet()
limitedRootTypes.removeAll(cachedRequest.acceptedRootTypes)
val limitedRootTypes = acceptedRootTypes - cachedRequest.acceptedRootTypes
if (limitedRootTypes.isEmpty()) {
null
}
else {
doSearch(FindClassRequest(request.classId, limitedRootTypes), handler)
search(FindClassRequest(classId, limitedRootTypes), handler)
}
}
is SearchResult.Found -> {
if (cachedRequest.acceptedRootTypes == request.acceptedRootTypes) {
if (cachedRequest.acceptedRootTypes == acceptedRootTypes) {
handler(cachedResult.packageDirectory, cachedResult.root.type).result
}
else {
doSearch(request, handler)
search(FindClassRequest(classId, acceptedRootTypes), handler)
}
}
}
}
private fun <T : Any> doSearch(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 {
if (findClassRequest != null) {
lastClassSearch = Pair(findClassRequest, SearchResult.Found(packageDirectory, root))
private fun <T : Any> search(request: SearchRequest, handler: (VirtualFile, JavaRoot.RootType) -> HandleResult<T>): T? {
fun <T : Any> found(packageDirectory: VirtualFile, root: JavaRoot, result: T): T = result.also {
if (request is FindClassRequest) {
lastClassSearch = Pair(request, SearchResult.Found(packageDirectory, root))
}
return result
}
fun <T : Any> notFound(): T? {
if (findClassRequest != null) {
lastClassSearch = Pair(findClassRequest, SearchResult.NotFound)
fun <T : Any> notFound(): T? = null.also {
if (request is FindClassRequest) {
lastClassSearch = Pair(request, SearchResult.NotFound)
}
return null
}
fun handle(root: JavaRoot, targetDirInRoot: VirtualFile): T? {
@@ -197,7 +172,7 @@ class JvmDependenciesIndexImpl(_roots: List<JavaRoot>): JvmDependenciesIndex {
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
private fun travelPath(rootIndex: Int, packagesPath: List<String>, fillCachesAfter: Int, cachesPath: List<Cache>): VirtualFile? {
if (rootIndex >= maxIndex) {