diff --git a/j2k/src/org/jetbrains/kotlin/j2k/ClassBodyConverter.kt b/j2k/src/org/jetbrains/kotlin/j2k/ClassBodyConverter.kt index 4905b18bdb7..fc9347b4037 100644 --- a/j2k/src/org/jetbrains/kotlin/j2k/ClassBodyConverter.kt +++ b/j2k/src/org/jetbrains/kotlin/j2k/ClassBodyConverter.kt @@ -19,7 +19,7 @@ package org.jetbrains.kotlin.j2k import com.intellij.psi.* import org.jetbrains.kotlin.j2k.ast.* import org.jetbrains.kotlin.j2k.usageProcessing.AccessorToPropertyProcessing -import org.jetbrains.kotlin.j2k.usageProcessing.MethodIntoObjectProcessing +import org.jetbrains.kotlin.j2k.usageProcessing.MemberIntoObjectProcessing import org.jetbrains.kotlin.j2k.usageProcessing.ToObjectWithOnlyMethodsProcessing import org.jetbrains.kotlin.load.java.JvmAbi import org.jetbrains.kotlin.name.SpecialNames @@ -100,9 +100,8 @@ class ClassBodyConverter(private val psiClass: PsiClass, } else { for (psiMember in psiMembers) { - if (psiMember is PsiMethod /* fields in object can be accessed as fields from java */ - && !psiMember.hasModifierProperty(PsiModifier.PRIVATE)) { - converter.addUsageProcessing(MethodIntoObjectProcessing(psiMember, JvmAbi.INSTANCE_FIELD)) + if (!psiMember.hasModifierProperty(PsiModifier.PRIVATE)) { + converter.addUsageProcessing(MemberIntoObjectProcessing(psiMember, JvmAbi.INSTANCE_FIELD)) } } } @@ -123,9 +122,8 @@ class ClassBodyConverter(private val psiClass: PsiClass, } else if (useCompanionObject && member !is Class && psiMember !is PsiEnumConstant && psiMember.hasModifierProperty(PsiModifier.STATIC)) { companionObjectMembers.add(member) - if (psiMember is PsiMethod /* fields in companion object can be accessed as fields from java */ - && !psiMember.hasModifierProperty(PsiModifier.PRIVATE)) { - converter.addUsageProcessing(MethodIntoObjectProcessing(psiMember, SpecialNames.DEFAULT_NAME_FOR_COMPANION_OBJECT.identifier)) + if (!psiMember.hasModifierProperty(PsiModifier.PRIVATE)) { + converter.addUsageProcessing(MemberIntoObjectProcessing(psiMember, SpecialNames.DEFAULT_NAME_FOR_COMPANION_OBJECT.identifier)) } } else { diff --git a/j2k/src/org/jetbrains/kotlin/j2k/JavaToKotlinConverter.kt b/j2k/src/org/jetbrains/kotlin/j2k/JavaToKotlinConverter.kt index 4669adbef15..3994d4c5bc8 100644 --- a/j2k/src/org/jetbrains/kotlin/j2k/JavaToKotlinConverter.kt +++ b/j2k/src/org/jetbrains/kotlin/j2k/JavaToKotlinConverter.kt @@ -152,7 +152,7 @@ class JavaToKotlinConverter( val map: Map> = usageProcessings.values .flatMap { it } - .filter { it.javaCodeProcessor != null || it.kotlinCodeProcessor != null } + .filter { it.javaCodeProcessors != null || it.kotlinCodeProcessors != null } .groupBy { it.targetElement } if (map.isEmpty()) return null @@ -171,8 +171,8 @@ class JavaToKotlinConverter( ProgressManager.getInstance().runProcess( { - val searchJava = processings.any { it.javaCodeProcessor != null } - val searchKotlin = processings.any { it.kotlinCodeProcessor != null } + val searchJava = processings.any { it.javaCodeProcessors != null } + val searchKotlin = processings.any { it.kotlinCodeProcessors != null } services.referenceSearcher.findUsagesForExternalCodeProcessing(psiElement, searchJava, searchKotlin) .filterNot { inConversionScope(it.element) } .mapTo(refs) { ReferenceInfo(it, psiElement, it.element.containingFile, processings) } @@ -191,8 +191,8 @@ class JavaToKotlinConverter( ReferenceLoop@ for ((reference, target, file, processings) in fileRefs.sortedWith(ReferenceComparator)) { val processors = when (reference.element.language) { - JavaLanguage.INSTANCE -> processings.mapNotNull { it.javaCodeProcessor } - KotlinLanguage.INSTANCE -> processings.mapNotNull { it.kotlinCodeProcessor } + JavaLanguage.INSTANCE -> processings.flatMap { it.javaCodeProcessors } + KotlinLanguage.INSTANCE -> processings.flatMap { it.kotlinCodeProcessors } else -> continue@ReferenceLoop } diff --git a/j2k/src/org/jetbrains/kotlin/j2k/usageProcessing/AccessorToPropertyProcessing.kt b/j2k/src/org/jetbrains/kotlin/j2k/usageProcessing/AccessorToPropertyProcessing.kt index fb9f23d0462..10db31dcf45 100644 --- a/j2k/src/org/jetbrains/kotlin/j2k/usageProcessing/AccessorToPropertyProcessing.kt +++ b/j2k/src/org/jetbrains/kotlin/j2k/usageProcessing/AccessorToPropertyProcessing.kt @@ -21,11 +21,12 @@ import org.jetbrains.kotlin.j2k.AccessorKind import org.jetbrains.kotlin.j2k.CodeConverter import org.jetbrains.kotlin.j2k.ast.* import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.utils.addToStdlib.singletonList class AccessorToPropertyProcessing(val accessorMethod: PsiMethod, val accessorKind: AccessorKind, val propertyName: String) : UsageProcessing { override val targetElement: PsiElement get() = accessorMethod - override val convertedCodeProcessor = object: ConvertedCodeProcessor { + override val convertedCodeProcessor = object : ConvertedCodeProcessor { override fun convertMethodUsage(methodCall: PsiMethodCallExpression, codeConverter: CodeConverter): Expression? { val isNullable = codeConverter.typeConverter.methodNullability(accessorMethod).isNullable(codeConverter.settings) @@ -47,44 +48,47 @@ class AccessorToPropertyProcessing(val accessorMethod: PsiMethod, val accessorKi } } - override val javaCodeProcessor: ExternalCodeProcessor? = null + override val javaCodeProcessors = emptyList() - override val kotlinCodeProcessor: ExternalCodeProcessor? = if (accessorMethod.hasModifierProperty(PsiModifier.PRIVATE)) - null - else - object : ExternalCodeProcessor { - override fun processUsage(reference: PsiReference): Array? { - val nameExpr = reference.element as? KtSimpleNameExpression ?: return null - val callExpr = nameExpr.parent as? KtCallExpression ?: return null + override val kotlinCodeProcessors = + if (accessorMethod.hasModifierProperty(PsiModifier.PRIVATE)) + emptyList() + else + AccessorToPropertyProcessor().singletonList() - val arguments = callExpr.valueArguments + inner class AccessorToPropertyProcessor: ExternalCodeProcessor { + override fun processUsage(reference: PsiReference): Array? { + val nameExpr = reference.element as? KtSimpleNameExpression ?: return null + val callExpr = nameExpr.parent as? KtCallExpression ?: return null - val factory = KtPsiFactory(nameExpr.project) - var propertyNameExpr = factory.createSimpleName(propertyName) - if (accessorKind == AccessorKind.GETTER) { - if (arguments.size != 0) return null // incorrect call - propertyNameExpr = callExpr.replace(propertyNameExpr) as KtSimpleNameExpression - return propertyNameExpr.references + val arguments = callExpr.valueArguments + + val factory = KtPsiFactory(nameExpr.project) + var propertyNameExpr = factory.createSimpleName(propertyName) + if (accessorKind == AccessorKind.GETTER) { + if (arguments.size != 0) return null // incorrect call + propertyNameExpr = callExpr.replace(propertyNameExpr) as KtSimpleNameExpression + return propertyNameExpr.references + } + else { + val value = arguments.singleOrNull()?.getArgumentExpression() ?: return null + var assignment = factory.createExpression("a = b") as KtBinaryExpression + assignment.right!!.replace(value) + + val qualifiedExpression = callExpr.parent as? KtQualifiedExpression + if (qualifiedExpression != null && qualifiedExpression.selectorExpression == callExpr) { + callExpr.replace(propertyNameExpr) + assignment.left!!.replace(qualifiedExpression) + assignment = qualifiedExpression.replace(assignment) as KtBinaryExpression + return (assignment.left as KtQualifiedExpression).selectorExpression!!.references } else { - val value = arguments.singleOrNull()?.getArgumentExpression() ?: return null - var assignment = factory.createExpression("a = b") as KtBinaryExpression - assignment.right!!.replace(value) - - val qualifiedExpression = callExpr.parent as? KtQualifiedExpression - if (qualifiedExpression != null && qualifiedExpression.selectorExpression == callExpr) { - callExpr.replace(propertyNameExpr) - assignment.left!!.replace(qualifiedExpression) - assignment = qualifiedExpression.replace(assignment) as KtBinaryExpression - return (assignment.left as KtQualifiedExpression).selectorExpression!!.references - } - else { - assignment.left!!.replace(propertyNameExpr) - assignment = callExpr.replace(assignment) as KtBinaryExpression - return assignment.left!!.references - } + assignment.left!!.replace(propertyNameExpr) + assignment = callExpr.replace(assignment) as KtBinaryExpression + return assignment.left!!.references } - } + } + } } diff --git a/j2k/src/org/jetbrains/kotlin/j2k/usageProcessing/FieldToPropertyProcessing.kt b/j2k/src/org/jetbrains/kotlin/j2k/usageProcessing/FieldToPropertyProcessing.kt index c0b7df7a53c..c7f3fde3a15 100644 --- a/j2k/src/org/jetbrains/kotlin/j2k/usageProcessing/FieldToPropertyProcessing.kt +++ b/j2k/src/org/jetbrains/kotlin/j2k/usageProcessing/FieldToPropertyProcessing.kt @@ -21,6 +21,7 @@ import com.intellij.util.IncorrectOperationException import org.jetbrains.kotlin.j2k.AccessorKind import org.jetbrains.kotlin.j2k.CodeConverter import org.jetbrains.kotlin.j2k.ast.* +import org.jetbrains.kotlin.utils.addToStdlib.singletonList class FieldToPropertyProcessing(val field: PsiField, val propertyName: String, val isNullable: Boolean) : UsageProcessing { override val targetElement: PsiElement get() = this.field @@ -28,16 +29,19 @@ class FieldToPropertyProcessing(val field: PsiField, val propertyName: String, v override val convertedCodeProcessor: ConvertedCodeProcessor? = if (field.name != propertyName) MyConvertedCodeProcessor() else null - override var javaCodeProcessor = if (field.hasModifierProperty(PsiModifier.PRIVATE)) - null - else if (!field.hasModifierProperty(PsiModifier.STATIC)) - UseAccessorsJavaCodeProcessor() - else if (field.name != propertyName) - ElementRenamedCodeProcessor(propertyName) - else - null + override var javaCodeProcessors = + if (field.hasModifierProperty(PsiModifier.PRIVATE)) + emptyList() + else if (field.name != propertyName) + listOf(ElementRenamedCodeProcessor(propertyName), UseAccessorsJavaCodeProcessor()) + else + UseAccessorsJavaCodeProcessor().singletonList() - override val kotlinCodeProcessor = if (field.name != propertyName) ElementRenamedCodeProcessor(propertyName) else null + override val kotlinCodeProcessors = + if (field.name != propertyName) + ElementRenamedCodeProcessor(propertyName).singletonList() + else + emptyList() private inner class MyConvertedCodeProcessor : ConvertedCodeProcessor { override fun convertVariableUsage(expression: PsiReferenceExpression, codeConverter: CodeConverter): Expression? { diff --git a/j2k/src/org/jetbrains/kotlin/j2k/usageProcessing/MethodIntoObjectProcessing.kt b/j2k/src/org/jetbrains/kotlin/j2k/usageProcessing/MemberIntoObjectProcessing.kt similarity index 79% rename from j2k/src/org/jetbrains/kotlin/j2k/usageProcessing/MethodIntoObjectProcessing.kt rename to j2k/src/org/jetbrains/kotlin/j2k/usageProcessing/MemberIntoObjectProcessing.kt index 85e6cd26fd3..55cba32a629 100644 --- a/j2k/src/org/jetbrains/kotlin/j2k/usageProcessing/MethodIntoObjectProcessing.kt +++ b/j2k/src/org/jetbrains/kotlin/j2k/usageProcessing/MemberIntoObjectProcessing.kt @@ -17,17 +17,22 @@ package org.jetbrains.kotlin.j2k.usageProcessing import com.intellij.psi.* +import org.jetbrains.kotlin.utils.addToStdlib.singletonList -class MethodIntoObjectProcessing(private val method: PsiMethod, private val objectName: String) : UsageProcessing { - override val targetElement: PsiElement get() = method +class MemberIntoObjectProcessing(private val member: PsiMember, private val objectName: String) : UsageProcessing { + override val targetElement: PsiElement get() = member override val convertedCodeProcessor: ConvertedCodeProcessor? get() = null - override val javaCodeProcessor = object: ExternalCodeProcessor { + override val javaCodeProcessors = AppendObjectNameProcessor().singletonList() + + override val kotlinCodeProcessors = emptyList() + + inner class AppendObjectNameProcessor : ExternalCodeProcessor { override fun processUsage(reference: PsiReference): Array? { val refExpr = reference.element as? PsiReferenceExpression ?: return null val qualifier = refExpr.qualifierExpression - val factory = PsiElementFactory.SERVICE.getInstance(method.project) + val factory = PsiElementFactory.SERVICE.getInstance(member.project) if (qualifier != null) { val newQualifier = factory.createExpressionFromText(qualifier.text + "." + objectName, null) qualifier.replace(newQualifier) @@ -40,6 +45,4 @@ class MethodIntoObjectProcessing(private val method: PsiMethod, private val obje } } } - - override val kotlinCodeProcessor: ExternalCodeProcessor? get() = null } \ No newline at end of file diff --git a/j2k/src/org/jetbrains/kotlin/j2k/usageProcessing/ToObjectWithOnlyMethodsProcessing.kt b/j2k/src/org/jetbrains/kotlin/j2k/usageProcessing/ToObjectWithOnlyMethodsProcessing.kt index 751a3161557..08eb0000a04 100644 --- a/j2k/src/org/jetbrains/kotlin/j2k/usageProcessing/ToObjectWithOnlyMethodsProcessing.kt +++ b/j2k/src/org/jetbrains/kotlin/j2k/usageProcessing/ToObjectWithOnlyMethodsProcessing.kt @@ -18,13 +18,18 @@ package org.jetbrains.kotlin.j2k.usageProcessing import com.intellij.psi.* import org.jetbrains.kotlin.load.java.JvmAbi +import org.jetbrains.kotlin.utils.addToStdlib.singletonList class ToObjectWithOnlyMethodsProcessing(private val psiClass: PsiClass) : UsageProcessing { override val targetElement: PsiElement get() = psiClass override val convertedCodeProcessor: ConvertedCodeProcessor? get() = null - override val javaCodeProcessor = object: ExternalCodeProcessor { + override val javaCodeProcessors = ToObjectWithOnlyMethodsProcessor().singletonList() + + override val kotlinCodeProcessors = emptyList() + + inner class ToObjectWithOnlyMethodsProcessor: ExternalCodeProcessor { override fun processUsage(reference: PsiReference): Array? { val refExpr = reference.element as? PsiReferenceExpression ?: return null val factory = PsiElementFactory.SERVICE.getInstance(psiClass.project) @@ -33,6 +38,4 @@ class ToObjectWithOnlyMethodsProcessing(private val psiClass: PsiClass) : UsageP return arrayOf(qualifiedExpr) } } - - override val kotlinCodeProcessor: ExternalCodeProcessor? get() = null } \ No newline at end of file diff --git a/j2k/src/org/jetbrains/kotlin/j2k/usageProcessing/UsageProcessing.kt b/j2k/src/org/jetbrains/kotlin/j2k/usageProcessing/UsageProcessing.kt index 69e83c48c4b..9c0562c81c4 100644 --- a/j2k/src/org/jetbrains/kotlin/j2k/usageProcessing/UsageProcessing.kt +++ b/j2k/src/org/jetbrains/kotlin/j2k/usageProcessing/UsageProcessing.kt @@ -24,8 +24,8 @@ import org.jetbrains.kotlin.j2k.ast.Expression interface UsageProcessing { val targetElement: PsiElement val convertedCodeProcessor: ConvertedCodeProcessor? - val javaCodeProcessor: ExternalCodeProcessor? - val kotlinCodeProcessor: ExternalCodeProcessor? + val javaCodeProcessors: List + val kotlinCodeProcessors: List } interface ConvertedCodeProcessor { diff --git a/j2k/testData/multiFile/FieldToProperty/JavaClass.java b/j2k/testData/multiFile/FieldToProperty/JavaClass.java index fda94197cbb..1bf5da8f4bb 100644 --- a/j2k/testData/multiFile/FieldToProperty/JavaClass.java +++ b/j2k/testData/multiFile/FieldToProperty/JavaClass.java @@ -8,4 +8,6 @@ public class JavaClass { public int getProperty() { return myProperty; } + + public static final String NAME = "Bar"; } \ No newline at end of file diff --git a/j2k/testData/multiFile/FieldToProperty/JavaClass.kt b/j2k/testData/multiFile/FieldToProperty/JavaClass.kt index 7e200738f83..525d9ab7b75 100644 --- a/j2k/testData/multiFile/FieldToProperty/JavaClass.kt +++ b/j2k/testData/multiFile/FieldToProperty/JavaClass.kt @@ -5,4 +5,9 @@ open class JavaClass { var property = 0 protected set + + companion object { + + val NAME = "Bar" + } } \ No newline at end of file diff --git a/j2k/testData/multiFile/FieldToProperty/external/JavaFile.java b/j2k/testData/multiFile/FieldToProperty/external/JavaFile.java index acea054e9f7..46e8e484300 100644 --- a/j2k/testData/multiFile/FieldToProperty/external/JavaFile.java +++ b/j2k/testData/multiFile/FieldToProperty/external/JavaFile.java @@ -9,5 +9,7 @@ public class C extends JavaClass { field = myProperty; field *= 2; field = field * 2; + + String s = JavaClass.NAME; } } \ No newline at end of file diff --git a/j2k/testData/multiFile/FieldToProperty/external/JavaFile.java.expected b/j2k/testData/multiFile/FieldToProperty/external/JavaFile.java.expected index 525ee76ca4f..b4e95df7974 100644 --- a/j2k/testData/multiFile/FieldToProperty/external/JavaFile.java.expected +++ b/j2k/testData/multiFile/FieldToProperty/external/JavaFile.java.expected @@ -9,5 +9,7 @@ public class C extends JavaClass { setField(getProperty()); setField(getField() * 2); setField(getField() * 2); + + String s = JavaClass.Companion.getNAME(); } } \ No newline at end of file diff --git a/j2k/testData/multiFile/ToCompanionObject/ClassWithStatics.java b/j2k/testData/multiFile/ToCompanionObject/ClassWithStatics.java index 2ab98a31eb4..645c3ff2d01 100644 --- a/j2k/testData/multiFile/ToCompanionObject/ClassWithStatics.java +++ b/j2k/testData/multiFile/ToCompanionObject/ClassWithStatics.java @@ -6,6 +6,7 @@ class ClassWithStatics { public static void staticMethod(int p) {} public static final int staticField = 1; + public static int staticNonFinalField = 1; protected static int ourValue = 0; diff --git a/j2k/testData/multiFile/ToCompanionObject/ClassWithStatics.kt b/j2k/testData/multiFile/ToCompanionObject/ClassWithStatics.kt index c65be26612d..fe099452d83 100644 --- a/j2k/testData/multiFile/ToCompanionObject/ClassWithStatics.kt +++ b/j2k/testData/multiFile/ToCompanionObject/ClassWithStatics.kt @@ -10,6 +10,7 @@ internal open class ClassWithStatics { } val staticField = 1 + var staticNonFinalField = 1 var value = 0 } diff --git a/j2k/testData/multiFile/ToCompanionObject/external/JavaFile.java b/j2k/testData/multiFile/ToCompanionObject/external/JavaFile.java index 8bc33206773..ff041da45c2 100644 --- a/j2k/testData/multiFile/ToCompanionObject/external/JavaFile.java +++ b/j2k/testData/multiFile/ToCompanionObject/external/JavaFile.java @@ -4,7 +4,7 @@ class C { void foo(ClassWithStatics c) { ClassWithStatics.staticMethod(ClassWithStatics.staticField); c.instanceMethod(); - ClassWithStatics.staticField += 2; + ClassWithStatics.staticNonFinalField += 2; } void methodReferences() { diff --git a/j2k/testData/multiFile/ToCompanionObject/external/JavaFile.java.expected b/j2k/testData/multiFile/ToCompanionObject/external/JavaFile.java.expected index 7c39f106ce9..c4734e41f05 100644 --- a/j2k/testData/multiFile/ToCompanionObject/external/JavaFile.java.expected +++ b/j2k/testData/multiFile/ToCompanionObject/external/JavaFile.java.expected @@ -2,9 +2,9 @@ package test; class C { void foo(ClassWithStatics c) { - ClassWithStatics.Companion.staticMethod(ClassWithStatics.staticField); + ClassWithStatics.Companion.staticMethod(ClassWithStatics.Companion.getStaticField()); c.instanceMethod(); - ClassWithStatics.staticField += 2; + ClassWithStatics.Companion.setStaticNonFinalField(ClassWithStatics.Companion.getStaticNonFinalField() + 2); } void methodReferences() { @@ -15,7 +15,7 @@ class C { class D extends ClassWithStatics { void foo() { - Companion.staticMethod(staticField); - value *= 2; + Companion.staticMethod(Companion.getStaticField()); + Companion.setValue(Companion.getValue() * 2); } } \ No newline at end of file diff --git a/j2k/testData/multiFile/ToObject/external/JavaFile.java.expected b/j2k/testData/multiFile/ToObject/external/JavaFile.java.expected index 59a81219f26..b07f8071c73 100644 --- a/j2k/testData/multiFile/ToObject/external/JavaFile.java.expected +++ b/j2k/testData/multiFile/ToObject/external/JavaFile.java.expected @@ -2,8 +2,8 @@ package test; class C { void foo() { - Utils.INSTANCE.foo1(Utils.staticField); - Utils.staticField += Utils.INSTANCE.foo2(); + Utils.INSTANCE.foo1(Utils.INSTANCE.getStaticField()); + Utils.INSTANCE.setStaticField(Utils.INSTANCE.getStaticField() + Utils.INSTANCE.foo2()); PureUtils.INSTANCE.foo1(PureUtils.INSTANCE.foo2()) } }