diff --git a/j2k/j2k.iml b/j2k/j2k.iml index 41c3c2ee98c..a5e5d67ecd7 100644 --- a/j2k/j2k.iml +++ b/j2k/j2k.iml @@ -11,6 +11,7 @@ + diff --git a/j2k/src/org/jetbrains/jet/j2k/Converter.kt b/j2k/src/org/jetbrains/jet/j2k/Converter.kt index 63c20ab46be..aa4fd8fd8d8 100644 --- a/j2k/src/org/jetbrains/jet/j2k/Converter.kt +++ b/j2k/src/org/jetbrains/jet/j2k/Converter.kt @@ -160,7 +160,7 @@ public class Converter(val project: Project, val settings: ConverterSettings) { var keepStatement = true if (statement is AssignmentExpression) { val assignee = statement.left - if (assignee is CallChainExpression) { + if (assignee is QualifiedExpression) { for (field in finalOrWithEmptyInitializerFields) { val id = field.identifier.toKotlin() if (assignee.identifier.toKotlin().endsWith("." + id)) { @@ -600,8 +600,8 @@ public class Converter(val project: Project, val settings: ConverterSettings) { private fun convertToNotNullableTypes(types: Array): List = types.map { convertType(it, Nullability.NotNull) } - public fun convertParameterList(parameters: PsiParameterList): ParameterList - = ParameterList(parameters.getParameters().map { convertParameter(it) }) + public fun convertParameterList(parameterList: PsiParameterList): ParameterList + = ParameterList(parameterList.getParameters().map { convertParameter(it) }) public fun convertParameter(parameter: PsiParameter, nullability: Nullability = Nullability.Default, diff --git a/j2k/src/org/jetbrains/jet/j2k/ast/MethodCallExpression.kt b/j2k/src/org/jetbrains/jet/j2k/ast/MethodCallExpression.kt index 89c330f407f..fba0dda99fd 100644 --- a/j2k/src/org/jetbrains/jet/j2k/ast/MethodCallExpression.kt +++ b/j2k/src/org/jetbrains/jet/j2k/ast/MethodCallExpression.kt @@ -36,7 +36,7 @@ open class MethodCallExpression( class object { fun build(receiver: Expression, methodName: String, arguments: List = ArrayList()): MethodCallExpression { - return MethodCallExpression(CallChainExpression(receiver, Identifier(methodName, false)), + return MethodCallExpression(QualifiedExpression(receiver, Identifier(methodName, false)), arguments, ArrayList(), false) } diff --git a/j2k/src/org/jetbrains/jet/j2k/ast/CallChainExpression.kt b/j2k/src/org/jetbrains/jet/j2k/ast/QualifiedExpression.kt similarity index 94% rename from j2k/src/org/jetbrains/jet/j2k/ast/CallChainExpression.kt rename to j2k/src/org/jetbrains/jet/j2k/ast/QualifiedExpression.kt index d18af29a2a6..0bfabe30f96 100644 --- a/j2k/src/org/jetbrains/jet/j2k/ast/CallChainExpression.kt +++ b/j2k/src/org/jetbrains/jet/j2k/ast/QualifiedExpression.kt @@ -16,7 +16,7 @@ package org.jetbrains.jet.j2k.ast -class CallChainExpression(val expression: Expression, val identifier: Expression) : Expression() { +class QualifiedExpression(val expression: Expression, val identifier: Expression) : Expression() { override val isNullable: Boolean get() { if (!expression.isEmpty && expression.isNullable) return true diff --git a/j2k/src/org/jetbrains/jet/j2k/visitors/ExpressionVisitor.kt b/j2k/src/org/jetbrains/jet/j2k/visitors/ExpressionVisitor.kt index d11d75331a4..93ce24e3765 100644 --- a/j2k/src/org/jetbrains/jet/j2k/visitors/ExpressionVisitor.kt +++ b/j2k/src/org/jetbrains/jet/j2k/visitors/ExpressionVisitor.kt @@ -25,6 +25,8 @@ import com.intellij.psi.CommonClassNames.* import com.intellij.psi.util.PsiTreeUtil import org.jetbrains.jet.lang.types.lang.PrimitiveType import org.jetbrains.jet.j2k.* +import org.jetbrains.jet.lang.resolve.java.jetAsJava.KotlinLightMethod +import org.jetbrains.jet.lang.psi.JetProperty open class ExpressionVisitor(protected val converter: Converter, private val usageReplacementMap: Map = mapOf()) : JavaElementVisitor() { @@ -75,8 +77,7 @@ open class ExpressionVisitor(protected val converter: Converter, result = MethodCallExpression.build(lhs, "ushr", listOf(rhs)) } else { - result = BinaryExpression(lhs, rhs, - getOperatorString(expression.getOperationSign().getTokenType())) + result = BinaryExpression(lhs, rhs, getOperatorString(expression.getOperationSign().getTokenType())) } } @@ -137,15 +138,38 @@ open class ExpressionVisitor(protected val converter: Converter, } override fun visitMethodCallExpression(expression: PsiMethodCallExpression) { - convertMethodCallExpression(expression) - } + val methodExpr = expression.getMethodExpression() + val target = methodExpr.resolve() + if (target is KotlinLightMethod) { + val property = target.origin as? JetProperty + if (property != null) { + val parameterCount = target.getParameterList().getParameters().size + val argCount = expression.getArgumentList().getExpressions().size + if (parameterCount == argCount) { + val qualifier = converter.convertExpression(methodExpr.getQualifierExpression()) + val propertyName = Identifier(property.getName()!!, false) + val propertyAccess = QualifiedExpression(qualifier, propertyName) + when(parameterCount) { + 0 /* getter */ -> { + result = propertyAccess + return + } + + 1 /* setter */ -> { + val argument = converter.convertExpression(expression.getArgumentList().getExpressions().single()) + result = AssignmentExpression(propertyAccess, argument, "=") + return + } + } + } + } + } - protected fun convertMethodCallExpression(expression: PsiMethodCallExpression) { if (!expression.isSuperConstructorCall() || !expression.isInsidePrimaryConstructor()) { - result = MethodCallExpression(converter.convertExpression(expression.getMethodExpression()), - converter.convertArguments(expression), - converter.convertTypes(expression.getTypeArguments()), - converter.convertType(expression.getType()).isNullable) + result = MethodCallExpression(converter.convertExpression(methodExpr), + converter.convertArguments(expression), + converter.convertTypes(expression.getTypeArguments()), + converter.convertType(expression.getType()).isNullable) } } @@ -177,7 +201,7 @@ open class ExpressionVisitor(protected val converter: Converter, val reference = expression.getClassReference() val typeParameters = if (reference != null) converter.convertTypes(reference.getTypeParameters()) else listOf() - return CallChainExpression(Identifier(constructor.getName(), false), + return QualifiedExpression(Identifier(constructor.getName(), false), MethodCallExpression(Identifier("init"), converter.convertExpressions(arguments), typeParameters, false)) } @@ -222,7 +246,7 @@ open class ExpressionVisitor(protected val converter: Converter, val qualifier = expression.getQualifierExpression() if (addReceiver) { - identifier = CallChainExpression(Identifier("__", false), Identifier(referencedName, isNullable)) + identifier = QualifiedExpression(Identifier("__", false), Identifier(referencedName, isNullable)) } else if (insideSecondaryConstructor && expression.isThisConstructorCall()) { identifier = Identifier("val __ = " + (containingConstructor?.getContainingClass()?.getNameIdentifier()?.getText() ?: "")) @@ -263,7 +287,7 @@ open class ExpressionVisitor(protected val converter: Converter, } } - result = CallChainExpression(converter.convertExpression(qualifier), identifier) + result = QualifiedExpression(converter.convertExpression(qualifier), identifier) } private fun PsiReference.nullability(): Nullability { diff --git a/j2k/tests/test/org/jetbrains/jet/j2k/test/AbstractJavaToKotlinConverterTest.kt b/j2k/tests/test/org/jetbrains/jet/j2k/test/AbstractJavaToKotlinConverterTest.kt index 63363c814fd..b988bff7328 100644 --- a/j2k/tests/test/org/jetbrains/jet/j2k/test/AbstractJavaToKotlinConverterTest.kt +++ b/j2k/tests/test/org/jetbrains/jet/j2k/test/AbstractJavaToKotlinConverterTest.kt @@ -32,10 +32,22 @@ import com.intellij.psi.codeStyle.CodeStyleManager import org.jetbrains.jet.JetTestUtils import com.intellij.openapi.project.Project import com.intellij.openapi.command.WriteCommandAction +import com.intellij.openapi.application.ApplicationManager abstract class AbstractJavaToKotlinConverterTest() : LightIdeaTestCase() { val testHeaderPattern = Pattern.compile("//(element|expression|statement|method|class|file|comp)\n") + override fun setUp() { + super.setUp() + + ApplicationManager.getApplication()!!.runWriteAction{ + val kotlinApiFileName = "KotlinApi.kt" + val kotlinCode = FileUtil.loadFile(File("j2k/tests/testData/$kotlinApiFileName"), true) + val kotlinFile = LightPlatformTestCase.getSourceRoot()!!.createChildData(null, kotlinApiFileName)!! + kotlinFile.getOutputStream(null)!!.writer().use { it.write(kotlinCode) } + } + } + public fun doTest(javaPath: String) { val project = LightPlatformTestCase.getProject()!! val javaFile = File(javaPath) diff --git a/j2k/tests/test/org/jetbrains/jet/j2k/test/JavaToKotlinConverterTestGenerated.java b/j2k/tests/test/org/jetbrains/jet/j2k/test/JavaToKotlinConverterTestGenerated.java index 6b74bb3104b..3dcbfc9bb01 100644 --- a/j2k/tests/test/org/jetbrains/jet/j2k/test/JavaToKotlinConverterTestGenerated.java +++ b/j2k/tests/test/org/jetbrains/jet/j2k/test/JavaToKotlinConverterTestGenerated.java @@ -31,7 +31,7 @@ import org.jetbrains.jet.j2k.test.AbstractJavaToKotlinConverterTest; /** This class is generated by {@link org.jetbrains.jet.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */ @SuppressWarnings("all") @TestMetadata("j2k/tests/testData/ast") -@InnerTestClasses({JavaToKotlinConverterTestGenerated.Annotations.class, JavaToKotlinConverterTestGenerated.AnonymousBlock.class, JavaToKotlinConverterTestGenerated.ArrayAccessExpression.class, JavaToKotlinConverterTestGenerated.ArrayInitializerExpression.class, JavaToKotlinConverterTestGenerated.ArrayType.class, JavaToKotlinConverterTestGenerated.AssertStatement.class, JavaToKotlinConverterTestGenerated.AssignmentExpression.class, JavaToKotlinConverterTestGenerated.BinaryExpression.class, JavaToKotlinConverterTestGenerated.BoxedType.class, JavaToKotlinConverterTestGenerated.BreakStatement.class, JavaToKotlinConverterTestGenerated.CallChainExpression.class, JavaToKotlinConverterTestGenerated.Class.class, JavaToKotlinConverterTestGenerated.ClassExpression.class, JavaToKotlinConverterTestGenerated.ConditionalExpression.class, JavaToKotlinConverterTestGenerated.Constructors.class, JavaToKotlinConverterTestGenerated.ContinueStatement.class, JavaToKotlinConverterTestGenerated.DeclarationStatement.class, JavaToKotlinConverterTestGenerated.DoWhileStatement.class, JavaToKotlinConverterTestGenerated.Enum.class, JavaToKotlinConverterTestGenerated.Field.class, JavaToKotlinConverterTestGenerated.For.class, JavaToKotlinConverterTestGenerated.ForeachStatement.class, JavaToKotlinConverterTestGenerated.Formatting.class, JavaToKotlinConverterTestGenerated.Function.class, JavaToKotlinConverterTestGenerated.Identifier.class, JavaToKotlinConverterTestGenerated.IfStatement.class, JavaToKotlinConverterTestGenerated.ImportStatement.class, JavaToKotlinConverterTestGenerated.InProjectionType.class, JavaToKotlinConverterTestGenerated.Inheritance.class, JavaToKotlinConverterTestGenerated.IsOperator.class, JavaToKotlinConverterTestGenerated.Issues.class, JavaToKotlinConverterTestGenerated.KotlinExclusion.class, JavaToKotlinConverterTestGenerated.LabelStatement.class, JavaToKotlinConverterTestGenerated.List.class, JavaToKotlinConverterTestGenerated.LiteralExpression.class, JavaToKotlinConverterTestGenerated.LocalVariable.class, JavaToKotlinConverterTestGenerated.MethodCallExpression.class, JavaToKotlinConverterTestGenerated.Misc.class, JavaToKotlinConverterTestGenerated.NewClassExpression.class, JavaToKotlinConverterTestGenerated.Nullability.class, JavaToKotlinConverterTestGenerated.ObjectLiteral.class, JavaToKotlinConverterTestGenerated.OutProjectionType.class, JavaToKotlinConverterTestGenerated.PackageStatement.class, JavaToKotlinConverterTestGenerated.ParenthesizedExpression.class, JavaToKotlinConverterTestGenerated.PolyadicExpression.class, JavaToKotlinConverterTestGenerated.PostfixOperator.class, JavaToKotlinConverterTestGenerated.PrefixOperator.class, JavaToKotlinConverterTestGenerated.RawGenerics.class, JavaToKotlinConverterTestGenerated.ReturnStatement.class, JavaToKotlinConverterTestGenerated.Settings.class, JavaToKotlinConverterTestGenerated.StarProjectionType.class, JavaToKotlinConverterTestGenerated.SuperExpression.class, JavaToKotlinConverterTestGenerated.Switch.class, JavaToKotlinConverterTestGenerated.SynchronizedStatement.class, JavaToKotlinConverterTestGenerated.ThisExpression.class, JavaToKotlinConverterTestGenerated.ThrowStatement.class, JavaToKotlinConverterTestGenerated.Trait.class, JavaToKotlinConverterTestGenerated.TryStatement.class, JavaToKotlinConverterTestGenerated.TypeCastExpression.class, JavaToKotlinConverterTestGenerated.TypeParameters.class, JavaToKotlinConverterTestGenerated.VarArg.class, JavaToKotlinConverterTestGenerated.WhileStatement.class}) +@InnerTestClasses({JavaToKotlinConverterTestGenerated.Annotations.class, JavaToKotlinConverterTestGenerated.AnonymousBlock.class, JavaToKotlinConverterTestGenerated.ArrayAccessExpression.class, JavaToKotlinConverterTestGenerated.ArrayInitializerExpression.class, JavaToKotlinConverterTestGenerated.ArrayType.class, JavaToKotlinConverterTestGenerated.AssertStatement.class, JavaToKotlinConverterTestGenerated.AssignmentExpression.class, JavaToKotlinConverterTestGenerated.BinaryExpression.class, JavaToKotlinConverterTestGenerated.BoxedType.class, JavaToKotlinConverterTestGenerated.BreakStatement.class, JavaToKotlinConverterTestGenerated.CallChainExpression.class, JavaToKotlinConverterTestGenerated.Class.class, JavaToKotlinConverterTestGenerated.ClassExpression.class, JavaToKotlinConverterTestGenerated.ConditionalExpression.class, JavaToKotlinConverterTestGenerated.Constructors.class, JavaToKotlinConverterTestGenerated.ContinueStatement.class, JavaToKotlinConverterTestGenerated.DeclarationStatement.class, JavaToKotlinConverterTestGenerated.DoWhileStatement.class, JavaToKotlinConverterTestGenerated.Enum.class, JavaToKotlinConverterTestGenerated.Field.class, JavaToKotlinConverterTestGenerated.For.class, JavaToKotlinConverterTestGenerated.ForeachStatement.class, JavaToKotlinConverterTestGenerated.Formatting.class, JavaToKotlinConverterTestGenerated.Function.class, JavaToKotlinConverterTestGenerated.Identifier.class, JavaToKotlinConverterTestGenerated.IfStatement.class, JavaToKotlinConverterTestGenerated.ImportStatement.class, JavaToKotlinConverterTestGenerated.InProjectionType.class, JavaToKotlinConverterTestGenerated.Inheritance.class, JavaToKotlinConverterTestGenerated.IsOperator.class, JavaToKotlinConverterTestGenerated.Issues.class, JavaToKotlinConverterTestGenerated.KotlinApiAccess.class, JavaToKotlinConverterTestGenerated.KotlinExclusion.class, JavaToKotlinConverterTestGenerated.LabelStatement.class, JavaToKotlinConverterTestGenerated.List.class, JavaToKotlinConverterTestGenerated.LiteralExpression.class, JavaToKotlinConverterTestGenerated.LocalVariable.class, JavaToKotlinConverterTestGenerated.MethodCallExpression.class, JavaToKotlinConverterTestGenerated.Misc.class, JavaToKotlinConverterTestGenerated.NewClassExpression.class, JavaToKotlinConverterTestGenerated.Nullability.class, JavaToKotlinConverterTestGenerated.ObjectLiteral.class, JavaToKotlinConverterTestGenerated.OutProjectionType.class, JavaToKotlinConverterTestGenerated.PackageStatement.class, JavaToKotlinConverterTestGenerated.ParenthesizedExpression.class, JavaToKotlinConverterTestGenerated.PolyadicExpression.class, JavaToKotlinConverterTestGenerated.PostfixOperator.class, JavaToKotlinConverterTestGenerated.PrefixOperator.class, JavaToKotlinConverterTestGenerated.RawGenerics.class, JavaToKotlinConverterTestGenerated.ReturnStatement.class, JavaToKotlinConverterTestGenerated.Settings.class, JavaToKotlinConverterTestGenerated.StarProjectionType.class, JavaToKotlinConverterTestGenerated.SuperExpression.class, JavaToKotlinConverterTestGenerated.Switch.class, JavaToKotlinConverterTestGenerated.SynchronizedStatement.class, JavaToKotlinConverterTestGenerated.ThisExpression.class, JavaToKotlinConverterTestGenerated.ThrowStatement.class, JavaToKotlinConverterTestGenerated.Trait.class, JavaToKotlinConverterTestGenerated.TryStatement.class, JavaToKotlinConverterTestGenerated.TypeCastExpression.class, JavaToKotlinConverterTestGenerated.TypeParameters.class, JavaToKotlinConverterTestGenerated.VarArg.class, JavaToKotlinConverterTestGenerated.WhileStatement.class}) public class JavaToKotlinConverterTestGenerated extends AbstractJavaToKotlinConverterTest { public void testAllFilesPresentInAst() throws Exception { JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.TestsPackage", new File("j2k/tests/testData/ast"), Pattern.compile("^(.+)\\.java$"), true); @@ -1550,6 +1550,24 @@ public class JavaToKotlinConverterTestGenerated extends AbstractJavaToKotlinConv } + @TestMetadata("j2k/tests/testData/ast/kotlinApiAccess") + public static class KotlinApiAccess extends AbstractJavaToKotlinConverterTest { + public void testAllFilesPresentInKotlinApiAccess() throws Exception { + JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.TestsPackage", new File("j2k/tests/testData/ast/kotlinApiAccess"), Pattern.compile("^(.+)\\.java$"), true); + } + + @TestMetadata("InheritedProperty.java") + public void testInheritedProperty() throws Exception { + doTest("j2k/tests/testData/ast/kotlinApiAccess/InheritedProperty.java"); + } + + @TestMetadata("Property.java") + public void testProperty() throws Exception { + doTest("j2k/tests/testData/ast/kotlinApiAccess/Property.java"); + } + + } + @TestMetadata("j2k/tests/testData/ast/kotlinExclusion") public static class KotlinExclusion extends AbstractJavaToKotlinConverterTest { public void testAllFilesPresentInKotlinExclusion() throws Exception { @@ -2497,6 +2515,7 @@ public class JavaToKotlinConverterTestGenerated extends AbstractJavaToKotlinConv suite.addTestSuite(Inheritance.class); suite.addTestSuite(IsOperator.class); suite.addTestSuite(Issues.class); + suite.addTestSuite(KotlinApiAccess.class); suite.addTestSuite(KotlinExclusion.class); suite.addTestSuite(LabelStatement.class); suite.addTestSuite(List.class); diff --git a/j2k/tests/testData/KotlinApi.kt b/j2k/tests/testData/KotlinApi.kt new file mode 100644 index 00000000000..2e026a7240e --- /dev/null +++ b/j2k/tests/testData/KotlinApi.kt @@ -0,0 +1,5 @@ +package kotlinApi + +public open class KotlinClass { + public var property: String +} \ No newline at end of file diff --git a/j2k/tests/testData/ast/kotlinApiAccess/InheritedProperty.java b/j2k/tests/testData/ast/kotlinApiAccess/InheritedProperty.java new file mode 100644 index 00000000000..93ffccf2b02 --- /dev/null +++ b/j2k/tests/testData/ast/kotlinApiAccess/InheritedProperty.java @@ -0,0 +1,9 @@ +//file +import kotlinApi.* + +class C extends KotlinClass { + void foo() { + System.out.println(getProperty()); + setProperty("a") + } +} \ No newline at end of file diff --git a/j2k/tests/testData/ast/kotlinApiAccess/InheritedProperty.kt b/j2k/tests/testData/ast/kotlinApiAccess/InheritedProperty.kt new file mode 100644 index 00000000000..4fe3001a917 --- /dev/null +++ b/j2k/tests/testData/ast/kotlinApiAccess/InheritedProperty.kt @@ -0,0 +1,8 @@ +import kotlinApi.* + +class C() : KotlinClass() { + fun foo() { + System.out.println(property) + property = "a" + } +} \ No newline at end of file diff --git a/j2k/tests/testData/ast/kotlinApiAccess/Property.java b/j2k/tests/testData/ast/kotlinApiAccess/Property.java new file mode 100644 index 00000000000..396f99b1660 --- /dev/null +++ b/j2k/tests/testData/ast/kotlinApiAccess/Property.java @@ -0,0 +1,9 @@ +//file +import kotlinApi.* + +class C { + void foo(KotlinClass k) { + System.out.println(k.getProperty()); + k.setProperty("a") + } +} \ No newline at end of file diff --git a/j2k/tests/testData/ast/kotlinApiAccess/Property.kt b/j2k/tests/testData/ast/kotlinApiAccess/Property.kt new file mode 100644 index 00000000000..c66d3484503 --- /dev/null +++ b/j2k/tests/testData/ast/kotlinApiAccess/Property.kt @@ -0,0 +1,8 @@ +import kotlinApi.* + +class C() { + fun foo(k: KotlinClass) { + System.out.println(k.property) + k.property = "a" + } +} \ No newline at end of file