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

org.jetbrains.kotlin.javac.KotlinClassifiersCache.kt Maven / Gradle / Ivy

There is a newer version: 2.0.0
Show newest version
/*
 * Copyright 2010-2017 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.kotlin.javac

import com.intellij.openapi.vfs.VirtualFile
import com.intellij.psi.search.SearchScope
import org.jetbrains.kotlin.descriptors.Visibility
import org.jetbrains.kotlin.fileClasses.javaFileFacadeFqName
import org.jetbrains.kotlin.load.java.structure.*
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.KtClassOrObject
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.psiUtil.collectDescendantsOfType
import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
import org.jetbrains.kotlin.javac.wrappers.trees.find
import org.jetbrains.kotlin.javac.wrappers.trees.findInner
import org.jetbrains.kotlin.javac.wrappers.trees.tryToResolveByFqName
import org.jetbrains.kotlin.javac.wrappers.trees.tryToResolveInJavaLang
import org.jetbrains.kotlin.load.java.structure.impl.VirtualFileBoundJavaClass

class KotlinClassifiersCache(sourceFiles: Collection,
                             private val javac: JavacWrapper) {

    private val kotlinClasses: Map = sourceFiles.flatMap { ktFile ->
        ktFile.collectDescendantsOfType().map { it.fqName to it } +
        (ktFile.javaFileFacadeFqName to null)
    }.toMap()

    private val classifiers = hashMapOf()

    fun getKotlinClassifier(fqName: FqName) = classifiers[fqName] ?: createClassifier(fqName)

    private fun createClassifier(fqName: FqName): JavaClass? {
        if (!kotlinClasses.containsKey(fqName)) return null
        val kotlinClassifier = kotlinClasses[fqName] ?: return null

        return MockKotlinClassifier(fqName,
                                    kotlinClassifier,
                                    kotlinClassifier.typeParameters.isNotEmpty(),
                                    javac)
                .apply { classifiers[fqName] = this }
    }

}

class MockKotlinClassifier(override val fqName: FqName,
                           private val classOrObject: KtClassOrObject,
                           val hasTypeParameters: Boolean,
                           private val javac: JavacWrapper) : VirtualFileBoundJavaClass {

    override val isAbstract: Boolean
        get() = throw UnsupportedOperationException("Should not be called")

    override val isStatic: Boolean
        get() = throw UnsupportedOperationException("Should not be called")

    override val isFinal: Boolean
        get() = throw UnsupportedOperationException("Should not be called")

    override val visibility: Visibility
        get() = throw UnsupportedOperationException("Should not be called")

    override val typeParameters: List
        get() = throw UnsupportedOperationException("Should not be called")

    override val supertypes: Collection
        get() = classOrObject.superTypeListEntries
                .map { superTypeListEntry ->
                    val userType = superTypeListEntry.typeAsUserType
                    arrayListOf().apply {
                        userType?.referencedName?.let { add(it) }
                        var qualifier = userType?.qualifier
                        while (qualifier != null) {
                            qualifier.referencedName?.let { add(it) }
                            qualifier = qualifier.qualifier
                        }
                    }.reversed().joinToString(separator = ".") { it }
                }
                .mapNotNull { resolveSupertype(it, classOrObject, javac) }
                .map { MockKotlinClassifierType(it) }

    val innerClasses: Collection
        get() = classOrObject.declarations.filterIsInstance()
                .mapNotNull { nestedClassOrObject ->
                    nestedClassOrObject.fqName?.let {
                        javac.getKotlinClassifier(it)
                    }
                }

    override val outerClass: JavaClass?
        get() = throw UnsupportedOperationException("Should not be called")

    override val isInterface: Boolean
        get() = throw UnsupportedOperationException("Should not be called")

    override val isAnnotationType: Boolean
        get() = throw UnsupportedOperationException("Should not be called")

    override val isEnum: Boolean
        get() = throw UnsupportedOperationException("Should not be called")

    override val lightClassOriginKind
        get() = LightClassOriginKind.SOURCE

    override val virtualFile: VirtualFile?
        get() = null

    override val methods: Collection
        get() = throw UnsupportedOperationException("Should not be called")

    override val fields: Collection
        get() = throw UnsupportedOperationException("Should not be called")

    override val constructors: Collection
        get() = throw UnsupportedOperationException("Should not be called")

    override val name
        get() = fqName.shortNameOrSpecial()

    override val annotations
        get() = throw UnsupportedOperationException("Should not be called")

    override val isDeprecatedInJavaDoc: Boolean
        get() = throw UnsupportedOperationException("Should not be called")

    override fun isFromSourceCodeInScope(scope: SearchScope) = true

    override fun findAnnotation(fqName: FqName) =
            throw UnsupportedOperationException("Should not be called")

    override val innerClassNames
        get() = innerClasses.map(JavaClass::name)

    override fun findInnerClass(name: Name) =
            innerClasses.find { it.name == name }

}

class MockKotlinClassifierType(override val classifier: JavaClassifier) : JavaClassifierType {

    override val typeArguments: List
        get() = throw UnsupportedOperationException("Should not be called")

    override val isRaw: Boolean
        get() = throw UnsupportedOperationException("Should not be called")

    override val annotations: Collection
        get() = throw UnsupportedOperationException("Should not be called")

    override val classifierQualifiedName: String
        get() = throw UnsupportedOperationException("Should not be called")

    override val presentableText: String
        get() = throw UnsupportedOperationException("Should not be called")

    override fun findAnnotation(fqName: FqName) =
            throw UnsupportedOperationException("Should not be called")

    override val isDeprecatedInJavaDoc: Boolean
        get() = throw UnsupportedOperationException("Should not be called")

}

private fun resolveSupertype(name: String,
                             classOrObject: KtClassOrObject,
                             javac: JavacWrapper): JavaClass? {
    val nameParts = name.split(".")
    val ktFile = classOrObject.containingKtFile

    tryToResolveInner(name, classOrObject, javac, nameParts)?.let { return it }
    ktFile.tryToResolvePackageClass(name, javac, nameParts)?.let { return it }
    tryToResolveByFqName(name, javac)?.let { return it }
    ktFile.tryToResolveSingleTypeImport(name, javac, nameParts)?.let { return it }
    ktFile.tryToResolveTypeImportOnDemand(name, javac, nameParts)?.let { return it }
    tryToResolveInJavaLang(name, javac)?.let { return it }

    return null
}

private fun tryToResolveInner(name: String,
                              classOrObject: KtClassOrObject,
                              javac: JavacWrapper,
                              nameParts: List) =
        classOrObject.containingClassOrObject?.let { containingClass ->
            containingClass.fqName?.let {
                javac.findClass(it) ?: javac.getKotlinClassifier(it)
            }
        }?.findInner(name, javac, nameParts)

private fun KtFile.tryToResolvePackageClass(name: String,
                                     javac: JavacWrapper,
                                     nameParts: List = emptyList()): JavaClass? {
    if (nameParts.size > 1) {
        return find(FqName("${packageFqName.asString()}.${nameParts.first()}"), javac, nameParts)
    }
    else {
        return javac.findClass(FqName("${packageFqName.asString()}.$name"))
               ?: javac.getKotlinClassifier(FqName("${packageFqName.asString()}.$name"))
    }
}

private fun KtFile.tryToResolveSingleTypeImport(name: String,
                                                javac: JavacWrapper,
                                                nameParts: List = emptyList()): JavaClass? {
    if (nameParts.size > 1) {
        val foundImports = importDirectives.filter { it.text.endsWith(".${nameParts.first()}") }
        foundImports.forEach { importDirective ->
            importDirective.importedFqName?.let { importedFqName ->
                find(importedFqName, javac, nameParts)?.let { importedClass ->
                    return importedClass
                }
            }
        }
        return null
    }
    else {
        return importDirectives.find { importDirective ->
            importDirective.text.endsWith(".$name")
        }?.let { importDirective ->
            importDirective.importedFqName?.let { fqName ->
                javac.findClass(fqName) ?: javac.getKotlinClassifier(fqName)
            }
        }
    }
}

private fun KtFile.tryToResolveTypeImportOnDemand(name: String,
                                                  javac: JavacWrapper,
                                                  nameParts: List = emptyList()): JavaClass? {
    val packagesWithAsterisk = importDirectives.filter { it.text.endsWith("*") }

    if (nameParts.size > 1) {
        packagesWithAsterisk.forEach { importDirective ->
            find(FqName("${importDirective.importedFqName?.asString()}.${nameParts.first()}"),
                 javac,
                 nameParts)?.let { return it }
        }
        return null
    }
    else {
        packagesWithAsterisk.forEach { importDirective ->
            val fqName = "${importDirective.importedFqName?.asString()}.$name".let(::FqName)
            javac.findClass(fqName)?.let { return it } ?: javac.getKotlinClassifier(fqName)?.let { return it }
        }

        return null
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy