New J2K: introduce external code processing for new J2K
It will update usages from files not in conversion scope to a valid ones:
1. When converting (field, getter, setter) triple from Java to a Kotlin property,
it will:
1. Update every usage of getter/setter in Java/Kotlin code to a usage of actual property
2. If property is used in Java directly then will add @JvmField to it
2. When converting static method or static field without getter and setter, which has Java usages then add @JvmStatic to it
#KT-34164 fixed
This commit is contained in:
@@ -48,6 +48,7 @@ import com.intellij.psi.PsiManager
|
|||||||
import com.intellij.psi.util.PsiTreeUtil
|
import com.intellij.psi.util.PsiTreeUtil
|
||||||
import org.jetbrains.kotlin.idea.KotlinFileType
|
import org.jetbrains.kotlin.idea.KotlinFileType
|
||||||
import org.jetbrains.kotlin.idea.core.util.toPsiFile
|
import org.jetbrains.kotlin.idea.core.util.toPsiFile
|
||||||
|
import org.jetbrains.kotlin.idea.formatter.commitAndUnblockDocument
|
||||||
import org.jetbrains.kotlin.idea.j2k.IdeaJavaToKotlinServices
|
import org.jetbrains.kotlin.idea.j2k.IdeaJavaToKotlinServices
|
||||||
import org.jetbrains.kotlin.idea.j2k.J2kPostProcessor
|
import org.jetbrains.kotlin.idea.j2k.J2kPostProcessor
|
||||||
import org.jetbrains.kotlin.idea.util.application.executeWriteCommand
|
import org.jetbrains.kotlin.idea.util.application.executeWriteCommand
|
||||||
@@ -56,6 +57,7 @@ import org.jetbrains.kotlin.idea.util.isRunningInCidrIde
|
|||||||
import org.jetbrains.kotlin.j2k.*
|
import org.jetbrains.kotlin.j2k.*
|
||||||
import org.jetbrains.kotlin.psi.KtFile
|
import org.jetbrains.kotlin.psi.KtFile
|
||||||
import org.jetbrains.kotlin.psi.UserDataProperty
|
import org.jetbrains.kotlin.psi.UserDataProperty
|
||||||
|
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.util.*
|
import java.util.*
|
||||||
@@ -105,6 +107,7 @@ class JavaToKotlinAction : AnAction() {
|
|||||||
virtualFile.pathBeforeJ2K = virtualFile.path
|
virtualFile.pathBeforeJ2K = virtualFile.path
|
||||||
virtualFile.rename(this, fileName)
|
virtualFile.rename(this, fileName)
|
||||||
}
|
}
|
||||||
|
result += virtualFile
|
||||||
} catch (e: IOException) {
|
} catch (e: IOException) {
|
||||||
MessagesEx.error(psiFile.project, e.message ?: "").showLater()
|
MessagesEx.error(psiFile.project, e.message ?: "").showLater()
|
||||||
}
|
}
|
||||||
@@ -165,7 +168,7 @@ class JavaToKotlinAction : AnAction() {
|
|||||||
) return emptyList()
|
) return emptyList()
|
||||||
|
|
||||||
|
|
||||||
var externalCodeUpdate: (() -> Unit)? = null
|
var externalCodeUpdate: ((List<KtFile>) -> Unit)? = null
|
||||||
|
|
||||||
if (enableExternalCodeProcessing && converterResult!!.externalCodeProcessing != null) {
|
if (enableExternalCodeProcessing && converterResult!!.externalCodeProcessing != null) {
|
||||||
val question =
|
val question =
|
||||||
@@ -196,16 +199,18 @@ class JavaToKotlinAction : AnAction() {
|
|||||||
CommandProcessor.getInstance().markCurrentCommandAsGlobal(project)
|
CommandProcessor.getInstance().markCurrentCommandAsGlobal(project)
|
||||||
|
|
||||||
val newFiles = saveResults(javaFiles, converterResult!!.results)
|
val newFiles = saveResults(javaFiles, converterResult!!.results)
|
||||||
|
.map { it.toPsiFile(project) as KtFile }
|
||||||
|
.onEach { it.commitAndUnblockDocument() }
|
||||||
|
|
||||||
externalCodeUpdate?.invoke()
|
externalCodeUpdate?.invoke(newFiles)
|
||||||
|
|
||||||
PsiDocumentManager.getInstance(project).commitAllDocuments()
|
PsiDocumentManager.getInstance(project).commitAllDocuments()
|
||||||
|
|
||||||
newFiles.singleOrNull()?.let {
|
newFiles.singleOrNull()?.let {
|
||||||
FileEditorManager.getInstance(project).openFile(it, true)
|
FileEditorManager.getInstance(project).openFile(it.virtualFile, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
newFiles.map { it.toPsiFile(project) as KtFile }
|
newFiles
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ enum class ParseContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface ExternalCodeProcessing {
|
interface ExternalCodeProcessing {
|
||||||
fun prepareWriteOperation(progress: ProgressIndicator): () -> Unit
|
fun prepareWriteOperation(progress: ProgressIndicator?): (List<KtFile>) -> Unit
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -190,7 +190,8 @@ class OldJavaToKotlinConverter(
|
|||||||
if (map.isEmpty()) return null
|
if (map.isEmpty()) return null
|
||||||
|
|
||||||
return object : ExternalCodeProcessing {
|
return object : ExternalCodeProcessing {
|
||||||
override fun prepareWriteOperation(progress: ProgressIndicator): () -> Unit {
|
override fun prepareWriteOperation(progress: ProgressIndicator?): (List<KtFile>) -> Unit {
|
||||||
|
if (progress == null) error("Progress should not be null for old J2K")
|
||||||
val refs = ArrayList<ReferenceInfo>()
|
val refs = ArrayList<ReferenceInfo>()
|
||||||
|
|
||||||
progress.text = "Searching usages to update..."
|
progress.text = "Searching usages to update..."
|
||||||
@@ -322,7 +323,7 @@ class OldWithProgressProcessor(private val progress: ProgressIndicator?, private
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class ProgressPortionReporter(
|
class ProgressPortionReporter(
|
||||||
indicator: ProgressIndicator,
|
indicator: ProgressIndicator,
|
||||||
private val start: Double,
|
private val start: Double,
|
||||||
private val portion: Double
|
private val portion: Double
|
||||||
|
|||||||
@@ -58,11 +58,20 @@ class AccessorToPropertyProcessing(val accessorMethod: PsiMethod, val accessorKi
|
|||||||
if (accessorMethod.hasModifierProperty(PsiModifier.PRIVATE))
|
if (accessorMethod.hasModifierProperty(PsiModifier.PRIVATE))
|
||||||
emptyList()
|
emptyList()
|
||||||
else
|
else
|
||||||
listOf(AccessorToPropertyProcessor())
|
listOf(AccessorToPropertyProcessor(propertyName, accessorKind))
|
||||||
|
|
||||||
inner class AccessorToPropertyProcessor: ExternalCodeProcessor {
|
class AccessorToPropertyProcessor(
|
||||||
|
private val propertyName: String,
|
||||||
|
private val accessorKind: AccessorKind
|
||||||
|
) : ExternalCodeProcessor {
|
||||||
override fun processUsage(reference: PsiReference): Array<PsiReference>? {
|
override fun processUsage(reference: PsiReference): Array<PsiReference>? {
|
||||||
val nameExpr = reference.element as? KtSimpleNameExpression ?: return null
|
return processUsage(reference.element, propertyName, accessorKind)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun processUsage(element: PsiElement, propertyName: String, accessorKind: AccessorKind): Array<PsiReference>? {
|
||||||
|
val nameExpr = element as? KtSimpleNameExpression ?: return null
|
||||||
val callExpr = nameExpr.parent as? KtCallExpression ?: return null
|
val callExpr = nameExpr.parent as? KtCallExpression ?: return null
|
||||||
|
|
||||||
val arguments = callExpr.valueArguments
|
val arguments = callExpr.valueArguments
|
||||||
@@ -73,8 +82,7 @@ class AccessorToPropertyProcessing(val accessorMethod: PsiMethod, val accessorKi
|
|||||||
if (arguments.size != 0) return null // incorrect call
|
if (arguments.size != 0) return null // incorrect call
|
||||||
propertyNameExpr = callExpr.replace(propertyNameExpr) as KtSimpleNameExpression
|
propertyNameExpr = callExpr.replace(propertyNameExpr) as KtSimpleNameExpression
|
||||||
return propertyNameExpr.references
|
return propertyNameExpr.references
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
val value = arguments.singleOrNull()?.getArgumentExpression() ?: return null
|
val value = arguments.singleOrNull()?.getArgumentExpression() ?: return null
|
||||||
var assignment = factory.createExpression("a = b") as KtBinaryExpression
|
var assignment = factory.createExpression("a = b") as KtBinaryExpression
|
||||||
assignment.right!!.replace(value)
|
assignment.right!!.replace(value)
|
||||||
@@ -85,8 +93,7 @@ class AccessorToPropertyProcessing(val accessorMethod: PsiMethod, val accessorKi
|
|||||||
assignment.left!!.replace(qualifiedExpression)
|
assignment.left!!.replace(qualifiedExpression)
|
||||||
assignment = qualifiedExpression.replace(assignment) as KtBinaryExpression
|
assignment = qualifiedExpression.replace(assignment) as KtBinaryExpression
|
||||||
(assignment.left as KtQualifiedExpression).selectorExpression!!.references
|
(assignment.left as KtQualifiedExpression).selectorExpression!!.references
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
assignment.left!!.replace(propertyNameExpr)
|
assignment.left!!.replace(propertyNameExpr)
|
||||||
assignment = callExpr.replace(assignment) as KtBinaryExpression
|
assignment = callExpr.replace(assignment) as KtBinaryExpression
|
||||||
assignment.left!!.references
|
assignment.left!!.references
|
||||||
|
|||||||
+40
-5
@@ -27,9 +27,12 @@ import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
|
|||||||
import org.jetbrains.kotlin.idea.util.application.runReadAction
|
import org.jetbrains.kotlin.idea.util.application.runReadAction
|
||||||
import org.jetbrains.kotlin.js.resolve.diagnostics.findPsi
|
import org.jetbrains.kotlin.js.resolve.diagnostics.findPsi
|
||||||
import org.jetbrains.kotlin.lexer.KtTokens
|
import org.jetbrains.kotlin.lexer.KtTokens
|
||||||
import org.jetbrains.kotlin.nj2k.NewJ2kConverterContext
|
import org.jetbrains.kotlin.name.Name
|
||||||
import org.jetbrains.kotlin.nj2k.asGetterName
|
import org.jetbrains.kotlin.nj2k.*
|
||||||
import org.jetbrains.kotlin.nj2k.asSetterName
|
import org.jetbrains.kotlin.nj2k.externalCodeProcessing.JKFakeFieldData
|
||||||
|
import org.jetbrains.kotlin.nj2k.externalCodeProcessing.JKFieldData
|
||||||
|
import org.jetbrains.kotlin.nj2k.externalCodeProcessing.JKMethodData
|
||||||
|
import org.jetbrains.kotlin.nj2k.externalCodeProcessing.NewExternalCodeProcessing
|
||||||
import org.jetbrains.kotlin.nj2k.postProcessing.*
|
import org.jetbrains.kotlin.nj2k.postProcessing.*
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.*
|
import org.jetbrains.kotlin.psi.psiUtil.*
|
||||||
@@ -284,7 +287,7 @@ class ConvertGettersAndSettersToPropertyProcessing : ElementsBasedPostProcessing
|
|||||||
if (classesWithPropertiesData.isEmpty()) return
|
if (classesWithPropertiesData.isEmpty()) return
|
||||||
runUndoTransparentActionInEdt(inWriteAction = true) {
|
runUndoTransparentActionInEdt(inWriteAction = true) {
|
||||||
for ((klass, propertiesData) in classesWithPropertiesData) {
|
for ((klass, propertiesData) in classesWithPropertiesData) {
|
||||||
convertClass(klass, propertiesData)
|
convertClass(klass, propertiesData, converterContext.externalCodeProcessor)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -535,7 +538,11 @@ class ConvertGettersAndSettersToPropertyProcessing : ElementsBasedPostProcessing
|
|||||||
setName(newName)
|
setName(newName)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun convertClass(klass: KtClassOrObject, propertiesData: List<PropertyData>) {
|
private fun convertClass(
|
||||||
|
klass: KtClassOrObject,
|
||||||
|
propertiesData: List<PropertyData>,
|
||||||
|
externalCodeUpdater: NewExternalCodeProcessing
|
||||||
|
) {
|
||||||
val factory = KtPsiFactory(klass)
|
val factory = KtPsiFactory(klass)
|
||||||
val accessors = propertiesData.filterGettersAndSetters(klass, factory)
|
val accessors = propertiesData.filterGettersAndSetters(klass, factory)
|
||||||
|
|
||||||
@@ -558,6 +565,34 @@ class ConvertGettersAndSettersToPropertyProcessing : ElementsBasedPostProcessing
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
val propertyInfo = when (property) {
|
||||||
|
is RealProperty -> property.property.fqNameWithoutCompanions.let(externalCodeUpdater::getMember)
|
||||||
|
is MergedProperty -> property.mergeTo.fqNameWithoutCompanions.let(externalCodeUpdater::getMember)
|
||||||
|
is FakeProperty -> JKFakeFieldData(
|
||||||
|
isStatic = klass is KtObjectDeclaration,
|
||||||
|
kotlinElementPointer = null,
|
||||||
|
fqName = klass.fqNameWithoutCompanions.child(Name.identifier(property.name)),
|
||||||
|
name = property.name
|
||||||
|
).also { externalCodeUpdater.addMember(it) }
|
||||||
|
}?.also { it.name = property.name } as? JKFieldData
|
||||||
|
|
||||||
|
|
||||||
|
val getterFqName = getter.safeAs<RealGetter>()?.function?.fqNameWithoutCompanions
|
||||||
|
val setterFqName = setter.safeAs<RealSetter>()?.function?.fqNameWithoutCompanions
|
||||||
|
|
||||||
|
getterFqName?.let { fqName ->
|
||||||
|
externalCodeUpdater.getMember(fqName)?.safeAs<JKMethodData>()?.let {
|
||||||
|
it.usedAsAccessorOfProperty = propertyInfo ?: return@let
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setterFqName?.let { fqName ->
|
||||||
|
externalCodeUpdater.getMember(fqName)?.safeAs<JKMethodData>()?.let {
|
||||||
|
it.usedAsAccessorOfProperty = propertyInfo ?: return@let
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val isOpen = getter.safeAs<RealGetter>()?.function?.hasModifier(KtTokens.OPEN_KEYWORD) == true
|
val isOpen = getter.safeAs<RealGetter>()?.function?.hasModifier(KtTokens.OPEN_KEYWORD) == true
|
||||||
|| setter.safeAs<RealSetter>()?.function?.hasModifier(KtTokens.OPEN_KEYWORD) == true
|
|| setter.safeAs<RealSetter>()?.function?.hasModifier(KtTokens.OPEN_KEYWORD) == true
|
||||||
|
|
||||||
|
|||||||
@@ -18,11 +18,10 @@ import org.jetbrains.kotlin.nj2k.types.JKTypeFactory
|
|||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
|
|
||||||
|
|
||||||
class JKSymbolProvider(project: Project, module: Module?, contextElement: PsiElement) {
|
class JKSymbolProvider(private val resolver: JKResolver) {
|
||||||
private val symbolsByFqName = mutableMapOf<String, JKSymbol>()
|
private val symbolsByFqName = mutableMapOf<String, JKSymbol>()
|
||||||
val symbolsByPsi = mutableMapOf<PsiElement, JKSymbol>()
|
val symbolsByPsi = mutableMapOf<PsiElement, JKSymbol>()
|
||||||
private val symbolsByJK = mutableMapOf<JKDeclaration, JKSymbol>()
|
private val symbolsByJK = mutableMapOf<JKDeclaration, JKSymbol>()
|
||||||
private val resolver = JKResolver(project, module, contextElement)
|
|
||||||
|
|
||||||
private val elementVisitor = ElementVisitor()
|
private val elementVisitor = ElementVisitor()
|
||||||
lateinit var typeFactory: JKTypeFactory
|
lateinit var typeFactory: JKTypeFactory
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ package org.jetbrains.kotlin.nj2k
|
|||||||
import com.intellij.openapi.project.Project
|
import com.intellij.openapi.project.Project
|
||||||
import com.intellij.psi.PsiElement
|
import com.intellij.psi.PsiElement
|
||||||
import org.jetbrains.kotlin.j2k.*
|
import org.jetbrains.kotlin.j2k.*
|
||||||
|
import org.jetbrains.kotlin.nj2k.externalCodeProcessing.NewExternalCodeProcessing
|
||||||
import org.jetbrains.kotlin.nj2k.types.JKTypeFactory
|
import org.jetbrains.kotlin.nj2k.types.JKTypeFactory
|
||||||
|
|
||||||
data class NewJ2kConverterContext(
|
data class NewJ2kConverterContext(
|
||||||
@@ -16,7 +17,8 @@ data class NewJ2kConverterContext(
|
|||||||
val converter: NewJavaToKotlinConverter,
|
val converter: NewJavaToKotlinConverter,
|
||||||
val inConversionContext: (PsiElement) -> Boolean,
|
val inConversionContext: (PsiElement) -> Boolean,
|
||||||
val importStorage: JKImportStorage,
|
val importStorage: JKImportStorage,
|
||||||
val elementsInfoStorage: JKElementInfoStorage
|
val elementsInfoStorage: JKElementInfoStorage,
|
||||||
|
val externalCodeProcessor: NewExternalCodeProcessing
|
||||||
) : ConverterContext {
|
) : ConverterContext {
|
||||||
val project: Project
|
val project: Project
|
||||||
get() = converter.project
|
get() = converter.project
|
||||||
|
|||||||
@@ -31,12 +31,15 @@ import org.jetbrains.kotlin.idea.project.languageVersionSettings
|
|||||||
import org.jetbrains.kotlin.idea.util.application.runWriteAction
|
import org.jetbrains.kotlin.idea.util.application.runWriteAction
|
||||||
import org.jetbrains.kotlin.j2k.*
|
import org.jetbrains.kotlin.j2k.*
|
||||||
import org.jetbrains.kotlin.name.FqName
|
import org.jetbrains.kotlin.name.FqName
|
||||||
|
import org.jetbrains.kotlin.nj2k.conversions.JKResolver
|
||||||
|
import org.jetbrains.kotlin.nj2k.externalCodeProcessing.NewExternalCodeProcessing
|
||||||
import org.jetbrains.kotlin.nj2k.printing.JKCodeBuilder
|
import org.jetbrains.kotlin.nj2k.printing.JKCodeBuilder
|
||||||
import org.jetbrains.kotlin.nj2k.types.JKTypeFactory
|
import org.jetbrains.kotlin.nj2k.types.JKTypeFactory
|
||||||
import org.jetbrains.kotlin.psi.KtDeclaration
|
import org.jetbrains.kotlin.psi.KtDeclaration
|
||||||
import org.jetbrains.kotlin.psi.KtFile
|
import org.jetbrains.kotlin.psi.KtFile
|
||||||
import org.jetbrains.kotlin.psi.KtImportList
|
import org.jetbrains.kotlin.psi.KtImportList
|
||||||
import org.jetbrains.kotlin.psi.KtPsiFactory
|
import org.jetbrains.kotlin.psi.KtPsiFactory
|
||||||
|
import org.jetbrains.kotlin.psi.psiUtil.isAncestor
|
||||||
import org.jetbrains.kotlin.resolve.ImportPath
|
import org.jetbrains.kotlin.resolve.ImportPath
|
||||||
|
|
||||||
class NewJavaToKotlinConverter(
|
class NewJavaToKotlinConverter(
|
||||||
@@ -128,7 +131,8 @@ class NewJavaToKotlinConverter(
|
|||||||
override fun elementsToKotlin(inputElements: List<PsiElement>, processor: WithProgressProcessor): Result {
|
override fun elementsToKotlin(inputElements: List<PsiElement>, processor: WithProgressProcessor): Result {
|
||||||
val phaseDescription = "Converting Java code to Kotlin code"
|
val phaseDescription = "Converting Java code to Kotlin code"
|
||||||
val contextElement = inputElements.firstOrNull() ?: return Result(emptyList(), null, null)
|
val contextElement = inputElements.firstOrNull() ?: return Result(emptyList(), null, null)
|
||||||
val symbolProvider = JKSymbolProvider(project, targetModule, contextElement)
|
val resolver = JKResolver(project, targetModule, contextElement)
|
||||||
|
val symbolProvider = JKSymbolProvider(resolver)
|
||||||
val typeFactory = JKTypeFactory(symbolProvider)
|
val typeFactory = JKTypeFactory(symbolProvider)
|
||||||
symbolProvider.typeFactory = typeFactory
|
symbolProvider.typeFactory = typeFactory
|
||||||
symbolProvider.preBuildTree(inputElements)
|
symbolProvider.preBuildTree(inputElements)
|
||||||
@@ -152,14 +156,24 @@ class NewJavaToKotlinConverter(
|
|||||||
processor.updateState(i, 1, phaseDescription)
|
processor.updateState(i, 1, phaseDescription)
|
||||||
element to treeBuilder.buildTree(element, saveImports)
|
element to treeBuilder.buildTree(element, saveImports)
|
||||||
}
|
}
|
||||||
|
val inConversionContext = { element: PsiElement ->
|
||||||
|
inputElements.any { inputElement ->
|
||||||
|
if (inputElement == element) return@any true
|
||||||
|
inputElement.isAncestor(element, true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val externalCodeProcessing =
|
||||||
|
NewExternalCodeProcessing(oldConverterServices.referenceSearcher, inConversionContext)
|
||||||
|
|
||||||
val context = NewJ2kConverterContext(
|
val context = NewJ2kConverterContext(
|
||||||
symbolProvider,
|
symbolProvider,
|
||||||
typeFactory,
|
typeFactory,
|
||||||
this,
|
this,
|
||||||
{ it.containingFile in inputElements },
|
inConversionContext,
|
||||||
importStorage,
|
importStorage,
|
||||||
JKElementInfoStorage()
|
JKElementInfoStorage(),
|
||||||
|
externalCodeProcessing
|
||||||
)
|
)
|
||||||
ConversionsRunner.doApply(asts.withIndex().mapNotNull { (i, ast) ->
|
ConversionsRunner.doApply(asts.withIndex().mapNotNull { (i, ast) ->
|
||||||
processor.updateState(i, 1, phaseDescription)
|
processor.updateState(i, 1, phaseDescription)
|
||||||
@@ -182,7 +196,11 @@ class NewJavaToKotlinConverter(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return Result(results, null, context)
|
return Result(
|
||||||
|
results,
|
||||||
|
externalCodeProcessing.takeIf { it.isExternalProcessingNeeded() },
|
||||||
|
context
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun elementsToKotlin(inputElements: List<PsiElement>): Result {
|
override fun elementsToKotlin(inputElements: List<PsiElement>): Result {
|
||||||
|
|||||||
@@ -5,7 +5,10 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.nj2k.conversions
|
package org.jetbrains.kotlin.nj2k.conversions
|
||||||
|
|
||||||
|
import com.intellij.psi.PsiField
|
||||||
|
import org.jetbrains.kotlin.nj2k.externalCodeProcessing.JKFieldDataFromJava
|
||||||
import org.jetbrains.kotlin.nj2k.NewJ2kConverterContext
|
import org.jetbrains.kotlin.nj2k.NewJ2kConverterContext
|
||||||
|
import org.jetbrains.kotlin.nj2k.psi
|
||||||
import org.jetbrains.kotlin.nj2k.tree.*
|
import org.jetbrains.kotlin.nj2k.tree.*
|
||||||
|
|
||||||
|
|
||||||
@@ -13,6 +16,11 @@ class FieldToPropertyConversion(context: NewJ2kConverterContext) : RecursiveAppl
|
|||||||
override fun applyToElement(element: JKTreeElement): JKTreeElement {
|
override fun applyToElement(element: JKTreeElement): JKTreeElement {
|
||||||
if (element !is JKField) return recurse(element)
|
if (element !is JKField) return recurse(element)
|
||||||
element.mutability = if (element.modality == Modality.FINAL) Mutability.IMMUTABLE else Mutability.MUTABLE
|
element.mutability = if (element.modality == Modality.FINAL) Mutability.IMMUTABLE else Mutability.MUTABLE
|
||||||
|
|
||||||
|
element.psi<PsiField>()?.let { psi ->
|
||||||
|
context.externalCodeProcessor.addMember(JKFieldDataFromJava(psi))
|
||||||
|
}
|
||||||
|
|
||||||
element.modality = Modality.FINAL
|
element.modality = Modality.FINAL
|
||||||
return recurse(element)
|
return recurse(element)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,8 +16,11 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.nj2k.conversions
|
package org.jetbrains.kotlin.nj2k.conversions
|
||||||
|
|
||||||
|
import com.intellij.psi.PsiMethod
|
||||||
import org.jetbrains.kotlin.j2k.ast.Nullability
|
import org.jetbrains.kotlin.j2k.ast.Nullability
|
||||||
|
import org.jetbrains.kotlin.nj2k.externalCodeProcessing.JKMethodData
|
||||||
import org.jetbrains.kotlin.nj2k.NewJ2kConverterContext
|
import org.jetbrains.kotlin.nj2k.NewJ2kConverterContext
|
||||||
|
import org.jetbrains.kotlin.nj2k.psi
|
||||||
import org.jetbrains.kotlin.nj2k.throwAnnotation
|
import org.jetbrains.kotlin.nj2k.throwAnnotation
|
||||||
import org.jetbrains.kotlin.nj2k.tree.JKMethodImpl
|
import org.jetbrains.kotlin.nj2k.tree.JKMethodImpl
|
||||||
import org.jetbrains.kotlin.nj2k.tree.JKTreeElement
|
import org.jetbrains.kotlin.nj2k.tree.JKTreeElement
|
||||||
@@ -34,6 +37,11 @@ class JavaMethodToKotlinFunctionConversion(context: NewJ2kConverterContext) : Re
|
|||||||
symbolProvider
|
symbolProvider
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
element.psi<PsiMethod>()?.let { psi ->
|
||||||
|
context.externalCodeProcessor.addMember(JKMethodData(psi))
|
||||||
|
}
|
||||||
|
|
||||||
return recurse(element)
|
return recurse(element)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -26,6 +26,9 @@ class StaticsToCompanionExtractConversion(context: NewJ2kConverterContext) : Rec
|
|||||||
if (declaration is JKOtherModifiersOwner) {
|
if (declaration is JKOtherModifiersOwner) {
|
||||||
declaration.otherModifierElements -= declaration.elementByModifier(OtherModifier.STATIC)!!
|
declaration.otherModifierElements -= declaration.elementByModifier(OtherModifier.STATIC)!!
|
||||||
}
|
}
|
||||||
|
context.externalCodeProcessor.getMember(declaration)?.let {
|
||||||
|
it.isStatic = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return recurse(element)
|
return recurse(element)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ import org.jetbrains.kotlin.psi.psiUtil.getChildOfType
|
|||||||
import org.jetbrains.kotlin.resolve.ImportPath
|
import org.jetbrains.kotlin.resolve.ImportPath
|
||||||
|
|
||||||
|
|
||||||
class JKResolver(private val project: Project, module: Module?, private val contextElement: PsiElement) {
|
class JKResolver(val project: Project, module: Module?, private val contextElement: PsiElement) {
|
||||||
private val scope = module?.let {
|
private val scope = module?.let {
|
||||||
GlobalSearchScope.moduleWithDependenciesAndLibrariesScope(it)
|
GlobalSearchScope.moduleWithDependenciesAndLibrariesScope(it)
|
||||||
} ?: GlobalSearchScope.allScope(project)
|
} ?: GlobalSearchScope.allScope(project)
|
||||||
|
|||||||
@@ -0,0 +1,109 @@
|
|||||||
|
/*
|
||||||
|
* 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.externalCodeProcessing
|
||||||
|
|
||||||
|
import com.intellij.psi.PsiElement
|
||||||
|
import org.jetbrains.kotlin.j2k.AccessorKind
|
||||||
|
import org.jetbrains.kotlin.lexer.KtTokens
|
||||||
|
import org.jetbrains.kotlin.load.java.JvmAbi
|
||||||
|
import org.jetbrains.kotlin.name.FqName
|
||||||
|
import org.jetbrains.kotlin.psi.KtDeclaration
|
||||||
|
import org.jetbrains.kotlin.psi.KtElement
|
||||||
|
import org.jetbrains.kotlin.psi.KtProperty
|
||||||
|
import org.jetbrains.kotlin.psi.KtPsiFactory
|
||||||
|
|
||||||
|
internal class ExternalUsagesFixer(private val usages: List<JKMemberInfoWithUsages>) {
|
||||||
|
private val conversions = mutableListOf<JKExternalConversion>()
|
||||||
|
|
||||||
|
fun fix() {
|
||||||
|
usages.forEach { it.fix() }
|
||||||
|
conversions.sort()
|
||||||
|
conversions.forEach(JKExternalConversion::apply)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun JKMemberInfoWithUsages.fix() {
|
||||||
|
when (member) {
|
||||||
|
is JKFieldDataFromJava -> member.fix(javaUsages, kotlinUsages)
|
||||||
|
is JKMethodData -> member.fix(javaUsages, kotlinUsages)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun JKFieldDataFromJava.fix(javaUsages: List<PsiElement>, kotlinUsages: List<KtElement>) {
|
||||||
|
run {
|
||||||
|
val ktProperty = kotlinElement ?: return@run
|
||||||
|
when {
|
||||||
|
javaUsages.isNotEmpty() && ktProperty.isSimpleProperty() ->
|
||||||
|
ktProperty.addAnnotationIfThereAreNoJvmOnes(JvmAbi.JVM_FIELD_ANNOTATION_FQ_NAME)
|
||||||
|
|
||||||
|
javaUsages.isNotEmpty() && isStatic && !ktProperty.hasModifier(KtTokens.CONST_KEYWORD) ->
|
||||||
|
ktProperty.addAnnotationIfThereAreNoJvmOnes(JVM_STATIC_FQ_NAME)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wasRenamed) {
|
||||||
|
javaUsages.forEach { usage ->
|
||||||
|
conversions += PropertyRenamedJavaExternalUsageConversion(name, usage)
|
||||||
|
}
|
||||||
|
|
||||||
|
kotlinUsages.forEach { usage ->
|
||||||
|
conversions += PropertyRenamedKotlinExternalUsageConversion(name, usage)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun JKMethodData.fix(javaUsages: List<PsiElement>, kotlinUsages: List<KtElement>) {
|
||||||
|
usedAsAccessorOfProperty?.let { property ->
|
||||||
|
val ktProperty = property.kotlinElement
|
||||||
|
val accessorKind =
|
||||||
|
if (javaElement.name.startsWith("set")) AccessorKind.SETTER
|
||||||
|
else AccessorKind.GETTER
|
||||||
|
|
||||||
|
kotlinUsages.forEach { usage ->
|
||||||
|
conversions += AccessorToPropertyKotlinExternalConversion(property.name, accessorKind, usage)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ktProperty != null
|
||||||
|
&& javaUsages.isNotEmpty()
|
||||||
|
&& ktProperty.isSimpleProperty()
|
||||||
|
) javaUsages.forEach { usage ->
|
||||||
|
conversions += AccessorToPropertyJavaExternalConversion(property.name, accessorKind, usage)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (javaUsages.isNotEmpty() && isStatic) {
|
||||||
|
when (val accessorOf = usedAsAccessorOfProperty) {
|
||||||
|
null -> this
|
||||||
|
else -> accessorOf
|
||||||
|
}.kotlinElement?.addAnnotationIfThereAreNoJvmOnes(JVM_STATIC_FQ_NAME)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun KtProperty.isSimpleProperty() =
|
||||||
|
getter == null
|
||||||
|
&& setter == null
|
||||||
|
&& !hasModifier(KtTokens.CONST_KEYWORD)
|
||||||
|
|
||||||
|
private fun KtDeclaration.addAnnotationIfThereAreNoJvmOnes(fqName: FqName) {
|
||||||
|
// we don't want to resolve here and as we are working with fqNames, just by-text comparing is OK
|
||||||
|
if (annotationEntries.any { entry ->
|
||||||
|
USED_JVM_ANNOTATIONS.any { jvmAnnotation ->
|
||||||
|
entry.typeReference?.textMatches(jvmAnnotation.asString()) == true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
) return
|
||||||
|
addAnnotationEntry(KtPsiFactory(this).createAnnotationEntry("@${fqName.asString()}"))
|
||||||
|
}
|
||||||
|
|
||||||
|
internal data class JKMemberInfoWithUsages(
|
||||||
|
val member: JKMemberData<*>,
|
||||||
|
val javaUsages: List<PsiElement>,
|
||||||
|
val kotlinUsages: List<KtElement>
|
||||||
|
)
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val JVM_STATIC_FQ_NAME = FqName("kotlin.jvm.JvmStatic")
|
||||||
|
val USED_JVM_ANNOTATIONS = listOf(JVM_STATIC_FQ_NAME, JvmAbi.JVM_FIELD_ANNOTATION_FQ_NAME)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,101 @@
|
|||||||
|
/*
|
||||||
|
* 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.externalCodeProcessing
|
||||||
|
|
||||||
|
import com.intellij.psi.*
|
||||||
|
import org.jetbrains.kotlin.j2k.AccessorKind
|
||||||
|
import org.jetbrains.kotlin.j2k.usageProcessing.AccessorToPropertyProcessing
|
||||||
|
import org.jetbrains.kotlin.psi.KtElement
|
||||||
|
import org.jetbrains.kotlin.psi.KtPsiFactory
|
||||||
|
import org.jetbrains.kotlin.psi.KtSimpleNameExpression
|
||||||
|
import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf
|
||||||
|
import org.jetbrains.kotlin.utils.addToStdlib.cast
|
||||||
|
|
||||||
|
internal sealed class JKExternalConversion : Comparable<JKExternalConversion> {
|
||||||
|
abstract val usage: PsiElement
|
||||||
|
|
||||||
|
abstract fun apply()
|
||||||
|
|
||||||
|
private val depth by lazy(LazyThreadSafetyMode.NONE) { usage.parentsWithSelf.takeWhile { it !is PsiFile }.count() }
|
||||||
|
private val offset by lazy(LazyThreadSafetyMode.NONE) { usage.textRange.startOffset }
|
||||||
|
|
||||||
|
override fun compareTo(other: JKExternalConversion): Int {
|
||||||
|
val depth1 = depth
|
||||||
|
val depth2 = other.depth
|
||||||
|
if (depth1 != depth2) { // put deeper elements first to not invalidate them when processing ancestors
|
||||||
|
return -depth1.compareTo(depth2)
|
||||||
|
}
|
||||||
|
|
||||||
|
// process elements with the same deepness from right to left
|
||||||
|
// so that right-side of assignments is not invalidated by processing of the left one
|
||||||
|
return -offset.compareTo(other.offset)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class AccessorToPropertyKotlinExternalConversion(
|
||||||
|
private val name: String,
|
||||||
|
private val accessorKind: AccessorKind,
|
||||||
|
override val usage: PsiElement
|
||||||
|
) : JKExternalConversion() {
|
||||||
|
override fun apply() {
|
||||||
|
AccessorToPropertyProcessing.processUsage(usage, name, accessorKind)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class AccessorToPropertyJavaExternalConversion(
|
||||||
|
private val name: String,
|
||||||
|
private val accessorKind: AccessorKind,
|
||||||
|
override val usage: PsiElement
|
||||||
|
) : JKExternalConversion() {
|
||||||
|
override fun apply() {
|
||||||
|
if (usage !is PsiReferenceExpression) return
|
||||||
|
val methodCall = usage.parent as? PsiMethodCallExpression ?: return
|
||||||
|
|
||||||
|
val factory = PsiElementFactory.SERVICE.getInstance(usage.project)
|
||||||
|
val propertyAccess = factory.createReferenceExpression(usage.qualifierExpression)
|
||||||
|
val newExpression = when (accessorKind) {
|
||||||
|
AccessorKind.GETTER -> propertyAccess
|
||||||
|
AccessorKind.SETTER -> {
|
||||||
|
val value = methodCall.argumentList.expressions.singleOrNull() ?: return
|
||||||
|
factory.createAssignment(propertyAccess, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
methodCall.replace(newExpression)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun PsiElementFactory.createReferenceExpression(qualifier: PsiExpression?): PsiReferenceExpression =
|
||||||
|
createExpressionFromText(qualifier?.let { "qualifier." }.orEmpty() + name, usage).cast<PsiReferenceExpression>().apply {
|
||||||
|
qualifierExpression?.replace(qualifier ?: return@apply)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun PsiElementFactory.createAssignment(target: PsiExpression, value: PsiExpression): PsiAssignmentExpression =
|
||||||
|
createExpressionFromText("x = 1", usage).cast<PsiAssignmentExpression>().apply {
|
||||||
|
lExpression.replace(target)
|
||||||
|
rExpression!!.replace(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class PropertyRenamedKotlinExternalUsageConversion(
|
||||||
|
private val newName: String,
|
||||||
|
override val usage: KtElement
|
||||||
|
) : JKExternalConversion() {
|
||||||
|
override fun apply() {
|
||||||
|
if (usage !is KtSimpleNameExpression) return
|
||||||
|
val factory = KtPsiFactory(usage)
|
||||||
|
usage.getReferencedNameElement().replace(factory.createExpression(newName))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class PropertyRenamedJavaExternalUsageConversion(
|
||||||
|
private val newName: String,
|
||||||
|
override val usage: PsiElement
|
||||||
|
) : JKExternalConversion() {
|
||||||
|
override fun apply() {
|
||||||
|
if (usage !is PsiReferenceExpression) return
|
||||||
|
val factory = PsiElementFactory.SERVICE.getInstance(usage.project)
|
||||||
|
usage.referenceNameElement?.replace(factory.createExpressionFromText(newName, usage))
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
/*
|
||||||
|
* 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.externalCodeProcessing
|
||||||
|
|
||||||
|
import com.intellij.psi.PsiField
|
||||||
|
import com.intellij.psi.PsiMember
|
||||||
|
import com.intellij.psi.PsiMethod
|
||||||
|
import com.intellij.psi.SmartPsiElementPointer
|
||||||
|
import org.jetbrains.kotlin.idea.refactoring.fqName.getKotlinFqName
|
||||||
|
import org.jetbrains.kotlin.name.FqName
|
||||||
|
import org.jetbrains.kotlin.psi.KtDeclaration
|
||||||
|
import org.jetbrains.kotlin.psi.KtNamedFunction
|
||||||
|
import org.jetbrains.kotlin.psi.KtProperty
|
||||||
|
import org.jetbrains.kotlin.psi.psiUtil.isPrivate
|
||||||
|
|
||||||
|
interface JKMemberData<K : KtDeclaration> {
|
||||||
|
var kotlinElementPointer: SmartPsiElementPointer<K>?
|
||||||
|
var isStatic: Boolean
|
||||||
|
val fqName: FqName?
|
||||||
|
var name: String
|
||||||
|
|
||||||
|
val kotlinElement
|
||||||
|
get() = kotlinElementPointer?.element
|
||||||
|
|
||||||
|
val searchInJavaFiles: Boolean
|
||||||
|
get() = true
|
||||||
|
val searchInKotlinFiles: Boolean
|
||||||
|
get() = true
|
||||||
|
|
||||||
|
val searchingNeeded
|
||||||
|
get() = kotlinElement?.isPrivate() != true && (searchInJavaFiles || searchInKotlinFiles)
|
||||||
|
}
|
||||||
|
|
||||||
|
interface JKMemberDataCameFromJava<J : PsiMember, K : KtDeclaration> : JKMemberData<K> {
|
||||||
|
val javaElement: J
|
||||||
|
|
||||||
|
override val fqName
|
||||||
|
get() = javaElement.getKotlinFqName()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
interface JKFieldData : JKMemberData<KtProperty>
|
||||||
|
|
||||||
|
data class JKFakeFieldData(
|
||||||
|
override var isStatic: Boolean,
|
||||||
|
override var kotlinElementPointer: SmartPsiElementPointer<KtProperty>? = null,
|
||||||
|
override val fqName: FqName?,
|
||||||
|
override var name: String
|
||||||
|
) : JKFieldData {
|
||||||
|
override val searchInJavaFiles: Boolean
|
||||||
|
get() = false
|
||||||
|
override val searchInKotlinFiles: Boolean
|
||||||
|
get() = false
|
||||||
|
}
|
||||||
|
|
||||||
|
data class JKFieldDataFromJava(
|
||||||
|
override val javaElement: PsiField,
|
||||||
|
override var isStatic: Boolean = false,
|
||||||
|
override var kotlinElementPointer: SmartPsiElementPointer<KtProperty>? = null,
|
||||||
|
override var name: String = javaElement.name
|
||||||
|
) : JKMemberDataCameFromJava<PsiField, KtProperty>, JKFieldData {
|
||||||
|
override val searchInKotlinFiles: Boolean
|
||||||
|
get() = wasRenamed
|
||||||
|
|
||||||
|
val wasRenamed: Boolean
|
||||||
|
get() = javaElement.name != name
|
||||||
|
}
|
||||||
|
|
||||||
|
data class JKMethodData(
|
||||||
|
override val javaElement: PsiMethod,
|
||||||
|
override var isStatic: Boolean = false,
|
||||||
|
override var kotlinElementPointer: SmartPsiElementPointer<KtNamedFunction>? = null,
|
||||||
|
var usedAsAccessorOfProperty: JKFieldData? = null
|
||||||
|
) : JKMemberDataCameFromJava<PsiMethod, KtNamedFunction> {
|
||||||
|
override var name: String = javaElement.name
|
||||||
|
}
|
||||||
+118
@@ -0,0 +1,118 @@
|
|||||||
|
/*
|
||||||
|
* 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.externalCodeProcessing
|
||||||
|
|
||||||
|
import com.intellij.lang.java.JavaLanguage
|
||||||
|
import com.intellij.openapi.progress.ProgressIndicator
|
||||||
|
import com.intellij.openapi.progress.ProgressManager
|
||||||
|
import com.intellij.psi.PsiElement
|
||||||
|
import com.intellij.psi.PsiMember
|
||||||
|
import com.intellij.psi.SmartPointerManager
|
||||||
|
import org.jetbrains.kotlin.idea.core.ShortenReferences
|
||||||
|
import org.jetbrains.kotlin.idea.refactoring.fqName.getKotlinFqName
|
||||||
|
import org.jetbrains.kotlin.j2k.ExternalCodeProcessing
|
||||||
|
import org.jetbrains.kotlin.j2k.ProgressPortionReporter
|
||||||
|
import org.jetbrains.kotlin.j2k.ReferenceSearcher
|
||||||
|
import org.jetbrains.kotlin.name.FqName
|
||||||
|
import org.jetbrains.kotlin.nj2k.fqNameWithoutCompanions
|
||||||
|
import org.jetbrains.kotlin.nj2k.psi
|
||||||
|
import org.jetbrains.kotlin.nj2k.tree.JKDeclaration
|
||||||
|
import org.jetbrains.kotlin.psi.*
|
||||||
|
import org.jetbrains.kotlin.psi.psiUtil.forEachDescendantOfType
|
||||||
|
|
||||||
|
|
||||||
|
class NewExternalCodeProcessing(
|
||||||
|
private val referenceSearcher: ReferenceSearcher,
|
||||||
|
private val inConversionContext: (PsiElement) -> Boolean
|
||||||
|
) : ExternalCodeProcessing {
|
||||||
|
private val members = mutableMapOf<FqName, JKMemberData<*>>()
|
||||||
|
|
||||||
|
fun addMember(data: JKMemberData<*>) {
|
||||||
|
members[data.fqName ?: return] = data
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getMember(element: JKDeclaration) =
|
||||||
|
element.psi<PsiMember>()?.getKotlinFqName()?.let(members::get)
|
||||||
|
|
||||||
|
fun getMember(fqName: FqName) =
|
||||||
|
members[fqName]
|
||||||
|
|
||||||
|
fun isExternalProcessingNeeded(): Boolean =
|
||||||
|
members.values.any { it.searchingNeeded }
|
||||||
|
|
||||||
|
private fun List<KtFile>.bindJavaDeclarationsToConvertedKotlinOnes() {
|
||||||
|
forEach { file ->
|
||||||
|
file.forEachDescendantOfType<KtDeclaration> { declaration ->
|
||||||
|
val member = getMember(declaration.fqNameWithoutCompanions) ?: return@forEachDescendantOfType
|
||||||
|
when {
|
||||||
|
member is JKFieldData && declaration is KtProperty ->
|
||||||
|
member.kotlinElementPointer = SmartPointerManager.createPointer(declaration)
|
||||||
|
member is JKMethodData && declaration is KtNamedFunction ->
|
||||||
|
member.kotlinElementPointer = SmartPointerManager.createPointer(declaration)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun List<KtFile>.shortenJvmAnnotationsFqNames() {
|
||||||
|
val filter = filter@{ element: PsiElement ->
|
||||||
|
if (element !is KtUserType) return@filter ShortenReferences.FilterResult.GO_INSIDE
|
||||||
|
val isJvmAnnotation = ExternalUsagesFixer.USED_JVM_ANNOTATIONS.any { annotation ->
|
||||||
|
element.textMatches(annotation.asString())
|
||||||
|
}
|
||||||
|
if (isJvmAnnotation) ShortenReferences.FilterResult.PROCESS
|
||||||
|
else ShortenReferences.FilterResult.SKIP
|
||||||
|
}
|
||||||
|
for (file in this) {
|
||||||
|
ShortenReferences.DEFAULT.process(file, filter)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun prepareWriteOperation(progress: ProgressIndicator?): (List<KtFile>) -> Unit {
|
||||||
|
progress?.text = "Searching usages to update..."
|
||||||
|
|
||||||
|
val usages = mutableListOf<ExternalUsagesFixer.JKMemberInfoWithUsages>()
|
||||||
|
for ((index, member) in members.values.withIndex()) {
|
||||||
|
if (progress != null) {
|
||||||
|
progress.text2 = member.fqName?.shortName()?.identifier ?: continue
|
||||||
|
progress.checkCanceled()
|
||||||
|
|
||||||
|
ProgressManager.getInstance().runProcess(
|
||||||
|
{ usages += member.collectUsages() },
|
||||||
|
ProgressPortionReporter(progress, index / members.size.toDouble(), 1.0 / members.size)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
usages += member.collectUsages()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return { files ->
|
||||||
|
files.bindJavaDeclarationsToConvertedKotlinOnes()
|
||||||
|
ExternalUsagesFixer(usages).fix()
|
||||||
|
files.shortenJvmAnnotationsFqNames()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun JKMemberData<*>.collectUsages(): ExternalUsagesFixer.JKMemberInfoWithUsages {
|
||||||
|
val javaUsages = mutableListOf<PsiElement>()
|
||||||
|
val kotlinUsages = mutableListOf<KtElement>()
|
||||||
|
if (this is JKMemberDataCameFromJava<*, *>) referenceSearcher.findUsagesForExternalCodeProcessing(
|
||||||
|
javaElement,
|
||||||
|
searchJava = searchInJavaFiles,
|
||||||
|
searchKotlin = searchInKotlinFiles
|
||||||
|
).forEach { usage ->
|
||||||
|
val element = usage.element
|
||||||
|
if (inConversionContext(element)) return@forEach
|
||||||
|
when {
|
||||||
|
element is KtElement -> kotlinUsages += element
|
||||||
|
element.language == JavaLanguage.INSTANCE -> javaUsages += element
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ExternalUsagesFixer.JKMemberInfoWithUsages(this, javaUsages, kotlinUsages)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -15,10 +15,15 @@ import com.intellij.psi.util.PsiUtil
|
|||||||
import org.jetbrains.kotlin.j2k.ClassKind
|
import org.jetbrains.kotlin.j2k.ClassKind
|
||||||
import org.jetbrains.kotlin.j2k.ReferenceSearcher
|
import org.jetbrains.kotlin.j2k.ReferenceSearcher
|
||||||
import org.jetbrains.kotlin.j2k.isNullLiteral
|
import org.jetbrains.kotlin.j2k.isNullLiteral
|
||||||
|
import org.jetbrains.kotlin.name.Name
|
||||||
import org.jetbrains.kotlin.nj2k.tree.*
|
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.psi.psiUtil.parentsWithSelf
|
||||||
|
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||||
|
|
||||||
//copied from old j2k
|
//copied from old j2k
|
||||||
fun canKeepEqEq(left: PsiExpression, right: PsiExpression?): Boolean {
|
fun canKeepEqEq(left: PsiExpression, right: PsiExpression?): Boolean {
|
||||||
@@ -130,3 +135,9 @@ fun PsiClass.classKind(): JKClass.ClassKind =
|
|||||||
isInterface -> JKClass.ClassKind.INTERFACE
|
isInterface -> JKClass.ClassKind.INTERFACE
|
||||||
else -> JKClass.ClassKind.CLASS
|
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!!)) }
|
||||||
|
|||||||
Reference in New Issue
Block a user