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

org.jetbrains.kotlin.psi.KtClassOrObject.kt Maven / Gradle / Ivy

There is a newer version: 2.1.0-Beta1
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.psi

import com.intellij.lang.ASTNode
import com.intellij.navigation.ItemPresentation
import com.intellij.navigation.ItemPresentationProviders
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiErrorElement
import com.intellij.psi.PsiWhiteSpace
import com.intellij.psi.impl.CheckUtil
import com.intellij.psi.stubs.IStubElementType
import com.intellij.psi.tree.TokenSet
import com.intellij.psi.util.PsiTreeUtil
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.psi.psiUtil.ClassIdCalculator
import org.jetbrains.kotlin.psi.psiUtil.isKtFile
import org.jetbrains.kotlin.psi.stubs.KotlinClassOrObjectStub
import org.jetbrains.kotlin.psi.stubs.elements.KtStubElementTypes

abstract class KtClassOrObject :
    KtTypeParameterListOwnerStub>, KtDeclarationContainer, KtNamedDeclaration,
    KtPureClassOrObject, KtClassLikeDeclaration {
    constructor(node: ASTNode) : super(node)
    constructor(stub: KotlinClassOrObjectStub, nodeType: IStubElementType<*, *>) : super(stub, nodeType)

    fun getColon(): PsiElement? = findChildByType(KtTokens.COLON)

    fun getSuperTypeList(): KtSuperTypeList? = getStubOrPsiChild(KtStubElementTypes.SUPER_TYPE_LIST)

    override fun getSuperTypeListEntries(): List = getSuperTypeList()?.entries.orEmpty()

    fun addSuperTypeListEntry(superTypeListEntry: KtSuperTypeListEntry): KtSuperTypeListEntry {
        getSuperTypeList()?.let {
            val single = it.entries.singleOrNull()
            if (single != null && single.typeReference?.typeElement == null) {
                return single.replace(superTypeListEntry) as KtSuperTypeListEntry
            }
            return EditCommaSeparatedListHelper.addItem(it, superTypeListEntries, superTypeListEntry)
        }

        val psiFactory = KtPsiFactory(project)
        val specifierListToAdd = psiFactory.createSuperTypeCallEntry("A()").replace(superTypeListEntry).parent
        val colon = addBefore(psiFactory.createColon(), getBody())
        return (addAfter(specifierListToAdd, colon) as KtSuperTypeList).entries.first()
    }

    fun removeSuperTypeListEntry(superTypeListEntry: KtSuperTypeListEntry) {
        val specifierList = getSuperTypeList() ?: return
        assert(superTypeListEntry.parent === specifierList)

        if (specifierList.entries.size > 1) {
            EditCommaSeparatedListHelper.removeItem(superTypeListEntry)
        } else {
            deleteChildRange(getColon() ?: specifierList, specifierList)
        }
    }

    fun getAnonymousInitializers(): List = getBody()?.anonymousInitializers.orEmpty()

    override fun getBody(): KtClassBody? = getStubOrPsiChild(KtStubElementTypes.CLASS_BODY)

    inline fun  addDeclaration(declaration: T): T {
        val body = getOrCreateBody()
        val anchor = PsiTreeUtil.skipSiblingsBackward(body.rBrace ?: body.lastChild!!, PsiWhiteSpace::class.java)
        return if (anchor?.nextSibling is PsiErrorElement) {
            body.addBefore(declaration, anchor)
        } else {
            body.addAfter(declaration, anchor)
        } as T
    }

    inline fun  addDeclarationAfter(declaration: T, anchor: PsiElement?): T {
        val anchorBefore = anchor ?: declarations.lastOrNull() ?: return addDeclaration(declaration)
        return getOrCreateBody().addAfter(declaration, anchorBefore) as T
    }

    inline fun  addDeclarationBefore(declaration: T, anchor: PsiElement?): T {
        val anchorAfter = anchor ?: declarations.firstOrNull() ?: return addDeclaration(declaration)
        return getOrCreateBody().addBefore(declaration, anchorAfter) as T
    }

    fun isTopLevel(): Boolean = greenStub?.isTopLevel() ?: isKtFile(parent)

    override fun getClassId(): ClassId? {
        greenStub?.let { return it.getClassId() }
        return ClassIdCalculator.calculateClassId(this)
    }

    override fun isLocal(): Boolean = greenStub?.isLocal() ?: KtPsiUtil.isLocal(this)

    fun isData(): Boolean = hasModifier(KtTokens.DATA_KEYWORD)

    override fun getDeclarations(): List = getBody()?.declarations.orEmpty()

    override fun getPresentation(): ItemPresentation? = ItemPresentationProviders.getItemPresentation(this)

    override fun getPrimaryConstructor(): KtPrimaryConstructor? = getStubOrPsiChild(KtStubElementTypes.PRIMARY_CONSTRUCTOR)

    override fun getPrimaryConstructorModifierList(): KtModifierList? = primaryConstructor?.modifierList

    fun getPrimaryConstructorParameterList(): KtParameterList? = primaryConstructor?.valueParameterList

    override fun getPrimaryConstructorParameters(): List = getPrimaryConstructorParameterList()?.parameters.orEmpty()

    override fun hasExplicitPrimaryConstructor(): Boolean = primaryConstructor != null

    override fun hasPrimaryConstructor(): Boolean = hasExplicitPrimaryConstructor() || !hasSecondaryConstructors()

    fun hasSecondaryConstructors(): Boolean = !secondaryConstructors.isEmpty()

    override fun getSecondaryConstructors(): List = getBody()?.secondaryConstructors.orEmpty()

    fun isAnnotation(): Boolean = hasModifier(KtTokens.ANNOTATION_KEYWORD)

    fun getDeclarationKeyword(): PsiElement? = findChildByType(classInterfaceObjectTokenSet)

    private val classInterfaceObjectTokenSet = TokenSet.create(
        KtTokens.CLASS_KEYWORD, KtTokens.INTERFACE_KEYWORD, KtTokens.OBJECT_KEYWORD
    )

    override fun delete() {
        CheckUtil.checkWritable(this)

        val file = containingKtFile
        if (!isTopLevel() || file.declarations.size > 1) {
            super.delete()
        } else {
            file.delete()
        }
    }

    override fun isEquivalentTo(another: PsiElement?): Boolean {
        if (this === another) {
            return true
        }

        if (another !is KtClassOrObject) {
            return false
        }

        val fq1 = getQualifiedName() ?: return false
        val fq2 = another.getQualifiedName() ?: return false
        if (fq1 == fq2) {
            val thisLocal = isLocal
            if (thisLocal != another.isLocal) {
                return false
            }

            // For non-local classes same fqn is enough
            // Consider different instances of local classes non-equivalent
            return !thisLocal
        }

        return false
    }

    protected fun getQualifiedName(): String? {
        val stub = greenStub
        if (stub != null) {
            val fqName = stub.getFqName()
            return fqName?.asString()
        }

        val parts = mutableListOf()
        var current: KtClassOrObject? = this
        while (current != null) {
            val name = current.name ?: return null
            parts.add(name)
            current = PsiTreeUtil.getParentOfType(current, KtClassOrObject::class.java)
        }
        val file = containingFile as? KtFile ?: return null
        val fileQualifiedName = file.packageFqName.asString()
        if (!fileQualifiedName.isEmpty()) {
            parts.add(fileQualifiedName)
        }
        parts.reverse()
        return parts.joinToString(separator = ".")
    }

    fun getContextReceiverList(): KtContextReceiverList? = getStubOrPsiChild(KtStubElementTypes.CONTEXT_RECEIVER_LIST)

    override fun getContextReceivers(): List =
        getContextReceiverList()?.let { return it.contextReceivers() } ?: emptyList()
}


fun KtClassOrObject.getOrCreateBody(): KtClassBody {
    getBody()?.let { return it }

    val newBody = KtPsiFactory(project).createEmptyClassBody()
    if (this is KtEnumEntry) return addAfter(newBody, initializerList ?: nameIdentifier) as KtClassBody
    return add(newBody) as KtClassBody
}

val KtClassOrObject.allConstructors
    get() = listOfNotNull(primaryConstructor) + secondaryConstructors




© 2015 - 2024 Weber Informatics LLC | Privacy Policy