Push Down: Support moving members from Java to Kotlin class
#KT-9485 Fixed
This commit is contained in:
@@ -711,6 +711,10 @@
|
||||
language="kotlin"
|
||||
implementationClass="org.jetbrains.kotlin.idea.refactoring.memberInfo.KotlinClassMembersRefactoringSupport"/>
|
||||
|
||||
<refactoring.pushDown
|
||||
language="kotlin"
|
||||
implementationClass="org.jetbrains.kotlin.idea.refactoring.pushDown.JavaToKotlinPushDownDelegate"/>
|
||||
|
||||
<problemFileHighlightFilter implementation="org.jetbrains.kotlin.idea.projectView.KotlinProblemFileHighlightFilter"/>
|
||||
|
||||
<codeInsight.typeInfo language="kotlin" implementationClass="org.jetbrains.kotlin.idea.codeInsight.KotlinExpressionTypeProvider"/>
|
||||
|
||||
+116
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
* Copyright 2010-2016 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.idea.refactoring.pushDown
|
||||
|
||||
import com.intellij.psi.*
|
||||
import com.intellij.refactoring.memberPushDown.JavaPushDownDelegate
|
||||
import com.intellij.refactoring.memberPushDown.NewSubClassData
|
||||
import com.intellij.refactoring.memberPushDown.PushDownData
|
||||
import com.intellij.refactoring.util.RefactoringUtil
|
||||
import com.intellij.refactoring.util.classMembers.MemberInfo
|
||||
import com.intellij.util.containers.MultiMap
|
||||
import org.jetbrains.kotlin.asJava.unwrapped
|
||||
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.getJavaClassDescriptor
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.getJavaMemberDescriptor
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
|
||||
import org.jetbrains.kotlin.idea.core.getOrCreateCompanionObject
|
||||
import org.jetbrains.kotlin.idea.refactoring.isInterfaceClass
|
||||
import org.jetbrains.kotlin.idea.refactoring.j2k
|
||||
import org.jetbrains.kotlin.idea.refactoring.j2kText
|
||||
import org.jetbrains.kotlin.idea.refactoring.pullUp.addMemberToTarget
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.psi.KtCallableDeclaration
|
||||
import org.jetbrains.kotlin.psi.KtClass
|
||||
import org.jetbrains.kotlin.psi.KtClassOrObject
|
||||
import org.jetbrains.kotlin.psi.KtPsiFactory
|
||||
import org.jetbrains.kotlin.types.TypeSubstitutor
|
||||
import org.jetbrains.kotlin.types.substitutions.getTypeSubstitutor
|
||||
|
||||
class JavaToKotlinPushDownDelegate : JavaPushDownDelegate() {
|
||||
override fun checkTargetClassConflicts(
|
||||
targetClass: PsiElement?,
|
||||
pushDownData: PushDownData<MemberInfo, PsiMember>,
|
||||
conflicts: MultiMap<PsiElement, String>,
|
||||
subClassData: NewSubClassData?
|
||||
) {
|
||||
super.checkTargetClassConflicts(targetClass, pushDownData, conflicts, subClassData)
|
||||
|
||||
val ktClass = targetClass?.unwrapped as? KtClassOrObject ?: return
|
||||
val resolutionFacade = ktClass.getResolutionFacade()
|
||||
val targetClassDescriptor = resolutionFacade.resolveToDescriptor(ktClass) as ClassDescriptor
|
||||
for (memberInfo in pushDownData.membersToMove) {
|
||||
val member = memberInfo.member ?: continue
|
||||
checkExternalUsages(conflicts, member, targetClassDescriptor, resolutionFacade)
|
||||
}
|
||||
}
|
||||
|
||||
override fun pushDownToClass(targetClass: PsiElement, pushDownData: PushDownData<MemberInfo, PsiMember>) {
|
||||
val superClass = pushDownData.sourceClass as? PsiClass ?: return
|
||||
val subClass = targetClass.unwrapped as? KtClassOrObject ?: return
|
||||
val resolutionFacade = subClass.getResolutionFacade()
|
||||
val superClassDescriptor = superClass.getJavaClassDescriptor(resolutionFacade) ?: return
|
||||
val subClassDescriptor = resolutionFacade.resolveToDescriptor(subClass) as ClassDescriptor
|
||||
val substitutor = getTypeSubstitutor(superClassDescriptor.defaultType, subClassDescriptor.defaultType) ?: TypeSubstitutor.EMPTY
|
||||
val psiFactory = KtPsiFactory(subClass)
|
||||
var hasAbstractMembers = false
|
||||
members@ for (memberInfo in pushDownData.membersToMove) {
|
||||
val member = memberInfo.member
|
||||
val memberDescriptor = member.getJavaMemberDescriptor(resolutionFacade) ?: continue
|
||||
when (member) {
|
||||
is PsiMethod, is PsiField -> {
|
||||
val ktMember = member.j2k() as? KtCallableDeclaration ?: continue@members
|
||||
ktMember.removeModifier(KtTokens.DEFAULT_VISIBILITY_KEYWORD)
|
||||
val isStatic = member.hasModifierProperty(PsiModifier.STATIC)
|
||||
val targetMemberClass = if (isStatic && subClass is KtClass) subClass.getOrCreateCompanionObject() else subClass
|
||||
val targetMemberClassDescriptor = resolutionFacade.resolveToDescriptor(targetMemberClass) as ClassDescriptor
|
||||
if (member.hasModifierProperty(PsiModifier.ABSTRACT)) {
|
||||
hasAbstractMembers = true
|
||||
}
|
||||
moveCallableMemberToClass(
|
||||
ktMember,
|
||||
memberDescriptor as CallableMemberDescriptor,
|
||||
targetMemberClass,
|
||||
targetMemberClassDescriptor,
|
||||
substitutor,
|
||||
memberInfo.isToAbstract
|
||||
).apply {
|
||||
if (subClass.isInterfaceClass()) {
|
||||
removeModifier(KtTokens.ABSTRACT_KEYWORD)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
is PsiClass -> {
|
||||
if (memberInfo.overrides != null) {
|
||||
val typeText = RefactoringUtil.findReferenceToClass(superClass.implementsList, member)?.j2kText() ?: continue@members
|
||||
subClass.addSuperTypeListEntry(psiFactory.createSuperTypeEntry(typeText))
|
||||
}
|
||||
else {
|
||||
val ktClass = member.j2k() as? KtClassOrObject ?: continue@members
|
||||
addMemberToTarget(ktClass, subClass)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (hasAbstractMembers && !subClass.isInterfaceClass()) {
|
||||
subClass.addModifier(KtTokens.ABSTRACT_KEYWORD)
|
||||
}
|
||||
}
|
||||
}
|
||||
+10
-31
@@ -29,28 +29,24 @@ import com.intellij.usageView.UsageViewDescriptor
|
||||
import org.jetbrains.kotlin.asJava.unwrapped
|
||||
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.getJavaClassDescriptor
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
|
||||
import org.jetbrains.kotlin.idea.codeInsight.shorten.addToShorteningWaitSet
|
||||
import org.jetbrains.kotlin.idea.refactoring.runSynchronouslyWithProgress
|
||||
import org.jetbrains.kotlin.idea.refactoring.memberInfo.KotlinMemberInfo
|
||||
import org.jetbrains.kotlin.idea.refactoring.memberInfo.KtPsiClassWrapper
|
||||
import org.jetbrains.kotlin.idea.refactoring.pullUp.*
|
||||
import org.jetbrains.kotlin.idea.refactoring.runSynchronouslyWithProgress
|
||||
import org.jetbrains.kotlin.idea.search.declarationsSearch.HierarchySearchRequest
|
||||
import org.jetbrains.kotlin.idea.search.declarationsSearch.searchInheritors
|
||||
import org.jetbrains.kotlin.idea.util.application.runReadAction
|
||||
import org.jetbrains.kotlin.lexer.KtModifierKeywordToken
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.source.getPsi
|
||||
import org.jetbrains.kotlin.types.TypeSubstitutor
|
||||
import org.jetbrains.kotlin.types.substitutions.getTypeSubstitutor
|
||||
import org.jetbrains.kotlin.util.findCallableMemberBySignature
|
||||
import org.jetbrains.kotlin.utils.keysToMap
|
||||
import java.util.ArrayList
|
||||
import java.util.*
|
||||
|
||||
class KotlinPushDownContext(
|
||||
val sourceClass: KtClass,
|
||||
@@ -141,31 +137,14 @@ class KotlinPushDownProcessor(
|
||||
is KtProperty, is KtNamedFunction -> {
|
||||
memberDescriptor as CallableMemberDescriptor
|
||||
|
||||
val targetMemberDescriptor = memberDescriptor.substitute(substitutor)?.let {
|
||||
targetClassDescriptor.findCallableMemberBySignature(it as CallableMemberDescriptor)
|
||||
}
|
||||
val targetMember = targetMemberDescriptor?.source?.getPsi() as? KtCallableDeclaration
|
||||
targetMember?.apply {
|
||||
if (memberDescriptor.modality != Modality.ABSTRACT && memberInfo.isToAbstract) {
|
||||
addModifier(KtTokens.OVERRIDE_KEYWORD)
|
||||
}
|
||||
else if (memberDescriptor.overriddenDescriptors.isEmpty()) {
|
||||
removeModifier(KtTokens.OVERRIDE_KEYWORD)
|
||||
}
|
||||
else {
|
||||
addModifier(KtTokens.OVERRIDE_KEYWORD)
|
||||
}
|
||||
} ?: addMemberToTarget(member, targetClass).apply {
|
||||
if (this@KotlinPushDownProcessor.context.sourceClassDescriptor.kind == ClassKind.INTERFACE) {
|
||||
if (targetClassDescriptor.kind != ClassKind.INTERFACE && memberDescriptor.modality == Modality.ABSTRACT) {
|
||||
addModifier(KtTokens.ABSTRACT_KEYWORD)
|
||||
}
|
||||
}
|
||||
if (memberDescriptor.modality != Modality.ABSTRACT && memberInfo.isToAbstract) {
|
||||
KtTokens.VISIBILITY_MODIFIERS.types.forEach { removeModifier(it as KtModifierKeywordToken) }
|
||||
addModifier(KtTokens.OVERRIDE_KEYWORD)
|
||||
}
|
||||
}
|
||||
moveCallableMemberToClass(
|
||||
member as KtCallableDeclaration,
|
||||
memberDescriptor,
|
||||
targetClass,
|
||||
targetClassDescriptor,
|
||||
substitutor,
|
||||
memberInfo.isToAbstract
|
||||
)
|
||||
}
|
||||
|
||||
is KtClassOrObject, is KtPsiClassWrapper -> {
|
||||
|
||||
@@ -28,6 +28,7 @@ import org.jetbrains.kotlin.idea.refactoring.memberInfo.KtPsiClassWrapper
|
||||
import org.jetbrains.kotlin.idea.refactoring.pullUp.renderForConflicts
|
||||
import org.jetbrains.kotlin.idea.references.KtReference
|
||||
import org.jetbrains.kotlin.idea.references.mainReference
|
||||
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getQualifiedExpressionForReceiver
|
||||
@@ -97,7 +98,7 @@ private fun checkConflicts(
|
||||
for (member in membersToPush) {
|
||||
checkMemberClashing(conflicts, context, member, membersToKeepAbstract, substitutor, targetClass, targetClassDescriptor)
|
||||
checkSuperCalls(conflicts, context, member, membersToPush)
|
||||
checkExternalUsages(conflicts, context, member, targetClassDescriptor)
|
||||
checkExternalUsages(conflicts, member, targetClassDescriptor, context.resolutionFacade)
|
||||
checkVisibility(conflicts, context, member, targetClassDescriptor)
|
||||
}
|
||||
}
|
||||
@@ -169,15 +170,15 @@ private fun checkSuperCalls(
|
||||
)
|
||||
}
|
||||
|
||||
private fun checkExternalUsages(
|
||||
internal fun checkExternalUsages(
|
||||
conflicts: MultiMap<PsiElement, String>,
|
||||
context: KotlinPushDownContext,
|
||||
member: KtNamedDeclaration,
|
||||
targetClassDescriptor: ClassDescriptor
|
||||
member: PsiElement,
|
||||
targetClassDescriptor: ClassDescriptor,
|
||||
resolutionFacade: ResolutionFacade
|
||||
): Unit {
|
||||
for (ref in ReferencesSearch.search(member, member.resolveScope, false)) {
|
||||
val calleeExpr = ref.element as? KtSimpleNameExpression ?: continue
|
||||
val resolvedCall = calleeExpr.getResolvedCall(context.resolutionFacade.analyze(calleeExpr)) ?: continue
|
||||
val resolvedCall = calleeExpr.getResolvedCall(resolutionFacade.analyze(calleeExpr)) ?: continue
|
||||
val callElement = resolvedCall.call.callElement
|
||||
val dispatchReceiver = resolvedCall.dispatchReceiver
|
||||
if (dispatchReceiver == null || dispatchReceiver is Qualifier) continue
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright 2010-2016 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.idea.refactoring.pushDown
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.idea.refactoring.pullUp.addMemberToTarget
|
||||
import org.jetbrains.kotlin.lexer.KtModifierKeywordToken
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.psi.KtCallableDeclaration
|
||||
import org.jetbrains.kotlin.psi.KtClassOrObject
|
||||
import org.jetbrains.kotlin.resolve.source.getPsi
|
||||
import org.jetbrains.kotlin.types.TypeSubstitutor
|
||||
import org.jetbrains.kotlin.util.findCallableMemberBySignature
|
||||
|
||||
internal fun moveCallableMemberToClass(
|
||||
member: KtCallableDeclaration,
|
||||
memberDescriptor: CallableMemberDescriptor,
|
||||
targetClass: KtClassOrObject,
|
||||
targetClassDescriptor: ClassDescriptor,
|
||||
substitutor: TypeSubstitutor,
|
||||
makeAbstract: Boolean
|
||||
): KtCallableDeclaration {
|
||||
val targetMemberDescriptor = memberDescriptor.substitute(substitutor)?.let {
|
||||
targetClassDescriptor.findCallableMemberBySignature(it as CallableMemberDescriptor)
|
||||
}
|
||||
val targetMember = targetMemberDescriptor?.source?.getPsi() as? KtCallableDeclaration
|
||||
return targetMember?.apply {
|
||||
if (memberDescriptor.modality != Modality.ABSTRACT && makeAbstract) {
|
||||
addModifier(KtTokens.OVERRIDE_KEYWORD)
|
||||
}
|
||||
else if (memberDescriptor.overriddenDescriptors.isEmpty()) {
|
||||
removeModifier(KtTokens.OVERRIDE_KEYWORD)
|
||||
}
|
||||
else {
|
||||
addModifier(KtTokens.OVERRIDE_KEYWORD)
|
||||
}
|
||||
} ?: addMemberToTarget(member, targetClass).apply {
|
||||
val sourceClassDescriptor = memberDescriptor.containingDeclaration as? ClassDescriptor
|
||||
if (sourceClassDescriptor?.kind == ClassKind.INTERFACE) {
|
||||
if (targetClassDescriptor.kind != ClassKind.INTERFACE && memberDescriptor.modality == Modality.ABSTRACT) {
|
||||
addModifier(KtTokens.ABSTRACT_KEYWORD)
|
||||
}
|
||||
}
|
||||
if (memberDescriptor.modality != Modality.ABSTRACT && makeAbstract) {
|
||||
KtTokens.VISIBILITY_MODIFIERS.types.forEach { removeModifier(it as KtModifierKeywordToken) }
|
||||
addModifier(KtTokens.OVERRIDE_KEYWORD)
|
||||
}
|
||||
} as KtCallableDeclaration
|
||||
}
|
||||
Reference in New Issue
Block a user