blob: c7909ee2d6f2de820e4128c4d5913753033a8bb5 [file] [log] [blame]
/*
* Copyright 2010-2019 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.nj2k
import com.intellij.codeInsight.generation.GenerateEqualsHelper.getEqualsSignature
import com.intellij.lang.jvm.JvmClassKind
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.command.CommandProcessor
import com.intellij.psi.*
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.psi.util.InheritanceUtil
import com.intellij.psi.util.MethodSignatureUtil
import com.intellij.psi.util.PsiUtil
import org.jetbrains.kotlin.idea.util.application.runWriteAction
import org.jetbrains.kotlin.j2k.ClassKind
import org.jetbrains.kotlin.j2k.ReferenceSearcher
import org.jetbrains.kotlin.j2k.isNullLiteral
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.nj2k.tree.*
import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.psi.KtObjectDeclaration
import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
//copied from old j2k
fun canKeepEqEq(left: PsiExpression, right: PsiExpression?): Boolean {
if (left.isNullLiteral() || (right?.isNullLiteral() == true)) return true
when (val type = left.type) {
is PsiPrimitiveType, is PsiArrayType -> return true
is PsiClassType -> {
if (right?.type is PsiPrimitiveType) return true
val psiClass = type.resolve() ?: return false
if (!psiClass.hasModifierProperty(PsiModifier.FINAL)) return false
if (psiClass.isEnum) return true
val equalsSignature = getEqualsSignature(left.project, GlobalSearchScope.allScope(left.project))
val equalsMethod = MethodSignatureUtil.findMethodBySignature(psiClass, equalsSignature, true)
if (equalsMethod != null && equalsMethod.containingClass?.qualifiedName != CommonClassNames.JAVA_LANG_OBJECT) return false
return true
}
else -> return false
}
}
internal fun PsiMember.visibility(
referenceSearcher: ReferenceSearcher,
assignNonCodeElements: ((JKFormattingOwner, PsiElement) -> Unit)?
): JKVisibilityModifierElement =
modifierList?.children?.mapNotNull { child ->
if (child !is PsiKeyword) return@mapNotNull null
when (child.text) {
PsiModifier.PACKAGE_LOCAL -> Visibility.INTERNAL
PsiModifier.PRIVATE -> Visibility.PRIVATE
PsiModifier.PROTECTED -> handleProtectedVisibility(referenceSearcher)
PsiModifier.PUBLIC -> Visibility.PUBLIC
else -> null
}?.let {
JKVisibilityModifierElement(it)
}?.also { modifier ->
assignNonCodeElements?.also { it(modifier, child) }
}
}?.firstOrNull() ?: JKVisibilityModifierElement(Visibility.INTERNAL)
fun PsiMember.modality(assignNonCodeElements: ((JKFormattingOwner, PsiElement) -> Unit)?) =
modifierList?.children?.mapNotNull { child ->
if (child !is PsiKeyword) return@mapNotNull null
when (child.text) {
PsiModifier.FINAL -> Modality.FINAL
PsiModifier.ABSTRACT -> Modality.ABSTRACT
else -> null
}?.let {
JKModalityModifierElement(it)
}?.also { modifier ->
assignNonCodeElements?.let { it(modifier, child) }
}
}?.firstOrNull() ?: JKModalityModifierElement(Modality.OPEN)
fun JvmClassKind.toJk() = when (this) {
JvmClassKind.CLASS -> JKClass.ClassKind.CLASS
JvmClassKind.INTERFACE -> JKClass.ClassKind.INTERFACE
JvmClassKind.ANNOTATION -> JKClass.ClassKind.ANNOTATION
JvmClassKind.ENUM -> JKClass.ClassKind.ENUM
}
private fun PsiMember.handleProtectedVisibility(referenceSearcher: ReferenceSearcher): Visibility {
val originalClass = containingClass ?: return Visibility.PROTECTED
// Search for usages only in Java because java-protected member cannot be used in Kotlin from same package
val usages = referenceSearcher.findUsagesForExternalCodeProcessing(this, searchJava = true, searchKotlin = false)
return if (usages.any { !allowProtected(it.element, this, originalClass) })
Visibility.PUBLIC
else Visibility.PROTECTED
}
private fun allowProtected(element: PsiElement, member: PsiMember, originalClass: PsiClass): Boolean {
if (element.parent is PsiNewExpression && member is PsiMethod && member.isConstructor) {
// calls to for protected constructors are allowed only within same class or as super calls
return element.parentsWithSelf.contains(originalClass)
}
return element.parentsWithSelf.filterIsInstance<PsiClass>().any { accessContainingClass ->
if (!InheritanceUtil.isInheritorOrSelf(accessContainingClass, originalClass, true)) return@any false
if (element !is PsiReferenceExpression) return@any true
val qualifierExpression = element.qualifierExpression ?: return@any true
// super.foo is allowed if 'foo' is protected
if (qualifierExpression is PsiSuperExpression) return@any true
val receiverType = qualifierExpression.type ?: return@any true
val resolvedClass = PsiUtil.resolveGenericsClassInType(receiverType).element ?: return@any true
// receiver type should be subtype of containing class
InheritanceUtil.isInheritorOrSelf(resolvedClass, accessContainingClass, true)
}
}
fun PsiClass.classKind(): JKClass.ClassKind =
when {
isAnnotationType -> JKClass.ClassKind.ANNOTATION
isEnum -> JKClass.ClassKind.ENUM
isInterface -> JKClass.ClassKind.INTERFACE
else -> JKClass.ClassKind.CLASS
}
val KtDeclaration.fqNameWithoutCompanions
get() = generateSequence(this) { it.containingClassOrObject }
.filter { it.safeAs<KtObjectDeclaration>()?.isCompanion() != true && it.name != null }
.toList()
.foldRight(containingKtFile.packageFqName) { container, acc -> acc.child(Name.identifier(container.name!!)) }
internal fun <T> runUndoTransparentActionInEdt(inWriteAction: Boolean, action: () -> T): T {
var result: T? = null
ApplicationManager.getApplication().invokeAndWait {
CommandProcessor.getInstance().runUndoTransparentAction {
result = when {
inWriteAction -> runWriteAction(action)
else -> action()
}
}
}
return result!!
}