All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jetbrains.kotlin.asJava.classes.KotlinClassInnerStuffCache.kt Maven / Gradle / Ivy

There is a newer version: 2.1.0-RC
Show newest version
/*
 * Copyright 2010-2024 JetBrains s.r.o. and Kotlin Programming Language contributors.
 * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
 */

package org.jetbrains.kotlin.asJava.classes

import com.intellij.openapi.diagnostic.Logger
import com.intellij.psi.*
import com.intellij.psi.augment.PsiAugmentProvider
import com.intellij.psi.impl.PsiClassImplUtil
import com.intellij.psi.impl.PsiImplUtil
import com.intellij.psi.util.PsiUtil
import com.intellij.util.ArrayUtil
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap
import org.jetbrains.kotlin.utils.addIfNotNull
import java.util.*

class KotlinClassInnerStuffCache(
    private val myClass: KtExtensibleLightClass,
    private val dependencies: List,
    private val lazyCreator: LazyCreator,
    private val generateEnumMethods: Boolean = true,
) {
    abstract class LazyCreator {
        abstract fun  get(initializer: () -> T, dependencies: List): Lazy
    }

    private fun  cache(initializer: () -> T): Lazy = lazyCreator.get(initializer, dependencies)

    private val constructorsCache = cache { PsiImplUtil.getConstructors(myClass) }

    val constructors: Array
        get() = copy(constructorsCache.value)

    private val fieldsCache = cache {
        val own = myClass.ownFields
        val ext = collectAugments(myClass, PsiField::class.java)
        ArrayUtil.mergeCollections(own, ext, PsiField.ARRAY_FACTORY)
    }

    val fields: Array
        get() = copy(fieldsCache.value)

    private val methodsCache = cache {
        val own = myClass.ownMethods
        var ext = collectAugments(myClass, PsiMethod::class.java)
        if (generateEnumMethods && myClass.isEnum) {
            ext = ArrayList(ext.size + 2).also {
                it += ext
                it.addIfNotNull(getValuesMethod())
                it.addIfNotNull(getValueOfMethod())
            }
        }

        ArrayUtil.mergeCollections(own, ext, PsiMethod.ARRAY_FACTORY)
    }

    val methods: Array
        get() = copy(methodsCache.value)

    private val innerClassesCache = cache {
        val own = myClass.ownInnerClasses
        val ext = collectAugments(myClass, PsiClass::class.java)
        ArrayUtil.mergeCollections(own, ext, PsiClass.ARRAY_FACTORY)
    }

    val innerClasses: Array
        get() = copy(innerClassesCache.value)

    private val fieldByNameCache = cache {
        val fields = this.fields.takeIf { it.isNotEmpty() } ?: return@cache emptyMap()
        Collections.unmodifiableMap(Object2ObjectOpenHashMap(fields.size).apply {
            for (field in fields) {
                putIfAbsent(field.name, field)
            }
        })
    }

    fun findFieldByName(name: String, checkBases: Boolean): PsiField? {
        return if (checkBases) {
            PsiClassImplUtil.findFieldByName(myClass, name, true)
        } else {
            fieldByNameCache.value[name]
        }
    }

    private val methodByNameCache = cache {
        val methods = this.methods.takeIf { it.isNotEmpty() } ?: return@cache emptyMap()
        Collections.unmodifiableMap(Object2ObjectOpenHashMap>().apply {
            for ((key, list) in methods.groupByTo(HashMap()) { it.name }) {
                put(key, list.toTypedArray())
            }
        })
    }

    fun findMethodsByName(name: String, checkBases: Boolean): Array {
        return if (checkBases) {
            PsiClassImplUtil.findMethodsByName(myClass, name, true)
        } else {
            copy(methodByNameCache.value[name] ?: PsiMethod.EMPTY_ARRAY)
        }
    }

    private val innerClassByNameCache = cache {
        val classes = this.innerClasses.takeIf { it.isNotEmpty() } ?: return@cache emptyMap()

        Collections.unmodifiableMap(Object2ObjectOpenHashMap().apply {
            for (psiClass in classes) {
                val name = psiClass.name
                if (name == null) {
                    Logger.getInstance(KotlinClassInnerStuffCache::class.java).error("$psiClass has no name")
                } else if (psiClass !is ExternallyDefinedPsiElement || !containsKey(name)) {
                    put(name, psiClass)
                }
            }
        })
    }

    fun findInnerClassByName(name: String, checkBases: Boolean): PsiClass? {
        return if (checkBases) {
            PsiClassImplUtil.findInnerByName(myClass, name, true)
        } else {
            innerClassByNameCache.value[name]
        }
    }

    private val valuesMethodCache = cache { getEnumValuesPsiMethod(myClass) }

    private fun getValuesMethod(): PsiMethod? {
        if (myClass.isEnum && !myClass.isAnonymous && !isClassNameSealed()) {
            return valuesMethodCache.value
        }

        return null
    }

    private val valueOfMethodCache = cache { getEnumValueOfPsiMethod(myClass) }

    fun getValueOfMethod(): PsiMethod? {
        if (myClass.isEnum && !myClass.isAnonymous) {
            return valueOfMethodCache.value
        }

        return null
    }

    private fun isClassNameSealed(): Boolean {
        return myClass.name == PsiKeyword.SEALED && PsiUtil.getLanguageLevel(myClass).toJavaVersion().feature >= 16
    }
}

private val PsiClass.isAnonymous: Boolean
    get() = name == null || this is PsiAnonymousClass

private fun  copy(value: Array): Array {
    return if (value.isEmpty()) value else value.clone()
}

private fun  collectAugments(element: PsiElement, type: Class): List {
    return PsiAugmentProvider.collectAugments(element, type, null)
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy