Java to Kotlin converter: converting usages of Kotlin global and extension functions and properties
This commit is contained in:
@@ -12,6 +12,7 @@
|
||||
<orderEntry type="module" module-name="descriptor.loader.java" />
|
||||
<orderEntry type="library" name="idea-full" level="project" />
|
||||
<orderEntry type="module" module-name="frontend.java" />
|
||||
<orderEntry type="module" module-name="jet.as.java.psi" />
|
||||
</component>
|
||||
</module>
|
||||
|
||||
|
||||
@@ -58,7 +58,7 @@ public class Converter(val project: Project, val settings: ConverterSettings) {
|
||||
is PsiExpression -> convertExpression(element)
|
||||
is PsiComment -> Comment(element.getText()!!)
|
||||
is PsiImportList -> convertImportList(element)
|
||||
is PsiImportStatementBase -> convertImport(element)
|
||||
is PsiImportStatementBase -> convertImport(element, false)
|
||||
is PsiPackageStatement -> PackageStatement(quoteKeywords(element.getPackageName() ?: ""))
|
||||
is PsiWhiteSpace -> WhiteSpace(element.getText()!!)
|
||||
else -> null
|
||||
@@ -615,20 +615,6 @@ public class Converter(val project: Project, val settings: ConverterSettings) {
|
||||
return Parameter(Identifier(parameter.getName()!!), `type`, varValModifier, modifiers)
|
||||
}
|
||||
|
||||
public fun convertArguments(expression: PsiCallExpression): List<Expression> {
|
||||
val arguments = expression.getArgumentList()?.getExpressions() ?: array()
|
||||
val resolved = expression.resolveMethod()
|
||||
val expectedTypes = if (resolved != null)
|
||||
resolved.getParameterList().getParameters().map { it.getType() }
|
||||
else
|
||||
listOf()
|
||||
|
||||
return if (arguments.size == expectedTypes.size())
|
||||
(0..expectedTypes.lastIndex).map { i -> convertExpression(arguments[i], expectedTypes[i]) }
|
||||
else
|
||||
arguments.map { convertExpression(it) }
|
||||
}
|
||||
|
||||
public fun convertExpression(argument: PsiExpression?, expectedType: PsiType?): Expression {
|
||||
if (argument == null) return Identifier.Empty
|
||||
|
||||
|
||||
@@ -21,41 +21,51 @@ import org.jetbrains.jet.j2k.*
|
||||
import com.intellij.psi.PsiImportList
|
||||
import org.jetbrains.jet.lang.resolve.name.FqName
|
||||
import org.jetbrains.jet.lang.resolve.java.mapping.JavaToKotlinClassMap
|
||||
import org.jetbrains.jet.lang.resolve.name.isValidJavaFqName
|
||||
|
||||
import com.intellij.psi.PsiJavaCodeReferenceElement
|
||||
import org.jetbrains.jet.asJava.KotlinLightClassForPackage
|
||||
|
||||
class Import(val name: String) : Element {
|
||||
override fun toKotlin() = "import " + name
|
||||
}
|
||||
|
||||
class ImportList(val imports: List<Import>) : Element {
|
||||
val filteredImports = imports.filter {
|
||||
!it.name.isEmpty() && it.name !in NOT_NULL_ANNOTATIONS && it.name !in NULLABLE_ANNOTATIONS
|
||||
}.filter {
|
||||
// If name is invalid, like with star imports, don't try to filter
|
||||
if (!isValidJavaFqName(it.name))
|
||||
true
|
||||
else {
|
||||
// If imported class has a kotlin analog, drop the import
|
||||
val kotlinAnalogsForClass = JavaToKotlinClassMap.getInstance().mapPlatformClass(FqName(it.name))
|
||||
kotlinAnalogsForClass.isEmpty()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class ImportList(private val imports: List<Import>) : Element {
|
||||
override val isEmpty: Boolean
|
||||
get() = filteredImports.isEmpty()
|
||||
get() = imports.isEmpty()
|
||||
|
||||
override fun toKotlin() = filteredImports.toKotlin("\n")
|
||||
override fun toKotlin() = imports.toKotlin("\n")
|
||||
}
|
||||
|
||||
fun Converter.convertImportList(importList: PsiImportList): ImportList =
|
||||
ImportList(importList.getAllImportStatements() map { convertImport(it) })
|
||||
public fun Converter.convertImportList(importList: PsiImportList): ImportList =
|
||||
ImportList(importList.getAllImportStatements().map { convertImport(it, true) }.filterNotNull())
|
||||
|
||||
fun Converter.convertImport(i: PsiImportStatementBase): Import {
|
||||
val reference = i.getImportReference()
|
||||
if (reference != null) {
|
||||
return Import(quoteKeywords(reference.getQualifiedName()!!) + if (i.isOnDemand()) ".*" else "")
|
||||
public fun Converter.convertImport(anImport: PsiImportStatementBase, filter: Boolean): Import? {
|
||||
val reference = anImport.getImportReference()
|
||||
if (reference == null) return null
|
||||
val qualifiedName = quoteKeywords(reference.getQualifiedName()!!)
|
||||
if (anImport.isOnDemand()) {
|
||||
return Import(qualifiedName + ".*")
|
||||
}
|
||||
return Import("")
|
||||
}
|
||||
else {
|
||||
return if (filter) {
|
||||
val filteredName = filterImport(qualifiedName, reference)
|
||||
if (filteredName != null) Import(filteredName) else null
|
||||
}
|
||||
else {
|
||||
Import(qualifiedName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun filterImport(name: String, ref: PsiJavaCodeReferenceElement): String? {
|
||||
if (name in NOT_NULL_ANNOTATIONS || name in NULLABLE_ANNOTATIONS) return null
|
||||
|
||||
// If imported class has a kotlin analog, drop the import
|
||||
if (!JavaToKotlinClassMap.getInstance().mapPlatformClass(FqName(name)).isEmpty()) return null
|
||||
|
||||
val target = ref.resolve()
|
||||
if (target != null && target is KotlinLightClassForPackage) {
|
||||
return quoteKeywords(target.getFqName().parent().toString()) + ".*"
|
||||
}
|
||||
|
||||
return name
|
||||
}
|
||||
|
||||
@@ -27,6 +27,11 @@ 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
|
||||
import org.jetbrains.jet.lang.psi.JetFunction
|
||||
import org.jetbrains.jet.lang.psi.JetClassOrObject
|
||||
import org.jetbrains.jet.lang.psi.psiUtil.getParentByType
|
||||
import org.jetbrains.jet.lang.psi.psiUtil.isExtensionDeclaration
|
||||
import org.jetbrains.jet.lang.psi.JetPropertyAccessor
|
||||
|
||||
open class ExpressionVisitor(protected val converter: Converter,
|
||||
private val usageReplacementMap: Map<PsiVariable, String> = mapOf()) : JavaElementVisitor() {
|
||||
@@ -138,39 +143,73 @@ open class ExpressionVisitor(protected val converter: Converter,
|
||||
}
|
||||
|
||||
override fun visitMethodCallExpression(expression: PsiMethodCallExpression) {
|
||||
if (expression.isSuperConstructorCall() && expression.isInsidePrimaryConstructor()) return // skip it
|
||||
|
||||
val methodExpr = expression.getMethodExpression()
|
||||
val target = methodExpr.resolve()
|
||||
if (target is KotlinLightMethod) {
|
||||
val property = target.origin as? JetProperty
|
||||
if (property != null) {
|
||||
val origin = target.origin
|
||||
val isTopLevel = origin?.getParentByType(javaClass<JetClassOrObject>(), true) == null
|
||||
if (origin is JetProperty || origin is JetPropertyAccessor) {
|
||||
val property = if (origin is JetProperty) origin else origin.getParent() as JetProperty
|
||||
val parameterCount = target.getParameterList().getParameters().size
|
||||
val argCount = expression.getArgumentList().getExpressions().size
|
||||
if (parameterCount == argCount) {
|
||||
val qualifier = converter.convertExpression(methodExpr.getQualifierExpression())
|
||||
val arguments = expression.getArgumentList().getExpressions()
|
||||
if (parameterCount == arguments.size) {
|
||||
val propertyName = Identifier(property.getName()!!, false)
|
||||
val propertyAccess = QualifiedExpression(qualifier, propertyName)
|
||||
when(parameterCount) {
|
||||
var isExtension = false
|
||||
val propertyAccess = if (isTopLevel) {
|
||||
if (property.isExtensionDeclaration()) {
|
||||
isExtension = true
|
||||
QualifiedExpression(converter.convertExpression(arguments.firstOrNull()), propertyName)
|
||||
}
|
||||
else {
|
||||
propertyName
|
||||
}
|
||||
}
|
||||
else {
|
||||
QualifiedExpression(converter.convertExpression(methodExpr.getQualifierExpression()), propertyName)
|
||||
}
|
||||
|
||||
when(if (isExtension) parameterCount - 1 else parameterCount) {
|
||||
0 /* getter */ -> {
|
||||
result = propertyAccess
|
||||
return
|
||||
}
|
||||
|
||||
1 /* setter */ -> {
|
||||
val argument = converter.convertExpression(expression.getArgumentList().getExpressions().single())
|
||||
val argument = converter.convertExpression(arguments[if (isExtension) 1 else 0])
|
||||
result = AssignmentExpression(propertyAccess, argument, "=")
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (origin is JetFunction) {
|
||||
if (isTopLevel) {
|
||||
result = if (origin.isExtensionDeclaration()) {
|
||||
val arguments = expression.getArgumentList().getExpressions()
|
||||
val qualifier = converter.convertExpression(arguments.firstOrNull())
|
||||
MethodCallExpression(QualifiedExpression(qualifier, Identifier(origin.getName()!!, false)),
|
||||
convertArguments(expression, isExtension = true),
|
||||
converter.convertTypes(expression.getTypeArguments()),
|
||||
converter.convertType(expression.getType()).isNullable)
|
||||
}
|
||||
else {
|
||||
MethodCallExpression(Identifier(origin.getName()!!, false),
|
||||
convertArguments(expression),
|
||||
converter.convertTypes(expression.getTypeArguments()),
|
||||
converter.convertType(expression.getType()).isNullable)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (!expression.isSuperConstructorCall() || !expression.isInsidePrimaryConstructor()) {
|
||||
result = MethodCallExpression(converter.convertExpression(methodExpr),
|
||||
converter.convertArguments(expression),
|
||||
converter.convertTypes(expression.getTypeArguments()),
|
||||
converter.convertType(expression.getType()).isNullable)
|
||||
}
|
||||
result = MethodCallExpression(converter.convertExpression(methodExpr),
|
||||
convertArguments(expression),
|
||||
converter.convertTypes(expression.getTypeArguments()),
|
||||
converter.convertType(expression.getType()).isNullable)
|
||||
}
|
||||
|
||||
override fun visitNewExpression(expression: PsiNewExpression) {
|
||||
@@ -194,7 +233,7 @@ open class ExpressionVisitor(protected val converter: Converter,
|
||||
var arguments = argumentList?.getExpressions() ?: array()
|
||||
if (constructor == null || constructor.isPrimaryConstructor() || isNotConvertedClass) {
|
||||
return NewClassExpression(converter.convertElement(classReference),
|
||||
converter.convertArguments(expression),
|
||||
convertArguments(expression),
|
||||
converter.convertExpression(expression.getQualifier()),
|
||||
if (anonymousClass != null) converter.convertAnonymousClass(anonymousClass) else null)
|
||||
}
|
||||
@@ -236,16 +275,15 @@ open class ExpressionVisitor(protected val converter: Converter,
|
||||
}
|
||||
|
||||
override fun visitReferenceExpression(expression: PsiReferenceExpression) {
|
||||
val containingConstructor = expression.getContainingConstructor()
|
||||
val insideSecondaryConstructor = containingConstructor != null && !containingConstructor.isPrimaryConstructor()
|
||||
val addReceiver = insideSecondaryConstructor && (expression.getReference()?.resolve() as? PsiField)?.getContainingClass() == containingConstructor!!.getContainingClass()
|
||||
|
||||
val isNullable = converter.convertType(expression.getType(), expression.nullability()).isNullable
|
||||
val referencedName = expression.getReferenceName()!!
|
||||
var identifier: Expression = Identifier(referencedName, isNullable)
|
||||
val qualifier = expression.getQualifierExpression()
|
||||
|
||||
if (addReceiver) {
|
||||
val containingConstructor = expression.getContainingConstructor()
|
||||
val insideSecondaryConstructor = containingConstructor != null && !containingConstructor.isPrimaryConstructor()
|
||||
|
||||
if (insideSecondaryConstructor && (expression.getReference()?.resolve() as? PsiField)?.getContainingClass() == containingConstructor!!.getContainingClass()) {
|
||||
identifier = QualifiedExpression(Identifier("__", false), Identifier(referencedName, isNullable))
|
||||
}
|
||||
else if (insideSecondaryConstructor && expression.isThisConstructorCall()) {
|
||||
@@ -269,7 +307,7 @@ open class ExpressionVisitor(protected val converter: Converter,
|
||||
&& target.getContainingClass() != null
|
||||
&& PsiTreeUtil.getParentOfType(expression, javaClass<PsiClass>()) != target.getContainingClass()
|
||||
&& !isStaticallyImported(target, expression)) {
|
||||
var member = target as PsiMember
|
||||
var member: PsiMember = target
|
||||
var code = Identifier(referencedName).toKotlin()
|
||||
while (member.getContainingClass() != null) {
|
||||
code = Identifier(member.getContainingClass()!!.getName()!!).toKotlin() + "." + code
|
||||
@@ -285,6 +323,7 @@ open class ExpressionVisitor(protected val converter: Converter,
|
||||
identifier = Identifier(replacement, isNullable)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
result = QualifiedExpression(converter.convertExpression(qualifier), identifier)
|
||||
@@ -332,6 +371,23 @@ open class ExpressionVisitor(protected val converter: Converter,
|
||||
result = PolyadicExpression(parameters, getOperatorString(expression.getOperationTokenType()))
|
||||
}
|
||||
|
||||
private fun convertArguments(expression: PsiCallExpression, isExtension: Boolean = false): List<Expression> {
|
||||
var arguments = expression.getArgumentList()?.getExpressions()?.toList() ?: listOf()
|
||||
if (isExtension && arguments.isNotEmpty()) {
|
||||
arguments = arguments.drop(1)
|
||||
}
|
||||
val resolved = expression.resolveMethod()
|
||||
val expectedTypes = if (resolved != null)
|
||||
resolved.getParameterList().getParameters().map { it.getType() }
|
||||
else
|
||||
listOf()
|
||||
|
||||
return if (arguments.size == expectedTypes.size())
|
||||
(0..expectedTypes.lastIndex).map { i -> converter.convertExpression(arguments[i], expectedTypes[i]) }
|
||||
else
|
||||
arguments.map { converter.convertExpression(it) }
|
||||
}
|
||||
|
||||
private fun getOperatorString(tokenType: IElementType): String {
|
||||
return when(tokenType) {
|
||||
JavaTokenType.EQEQ -> "=="
|
||||
|
||||
@@ -1556,6 +1556,36 @@ public class JavaToKotlinConverterTestGenerated extends AbstractJavaToKotlinConv
|
||||
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.TestsPackage", new File("j2k/tests/testData/ast/kotlinApiAccess"), Pattern.compile("^(.+)\\.java$"), true);
|
||||
}
|
||||
|
||||
@TestMetadata("ExtensionFunction.java")
|
||||
public void testExtensionFunction() throws Exception {
|
||||
doTest("j2k/tests/testData/ast/kotlinApiAccess/ExtensionFunction.java");
|
||||
}
|
||||
|
||||
@TestMetadata("ExtensionProperty.java")
|
||||
public void testExtensionProperty() throws Exception {
|
||||
doTest("j2k/tests/testData/ast/kotlinApiAccess/ExtensionProperty.java");
|
||||
}
|
||||
|
||||
@TestMetadata("GlobalFunction.java")
|
||||
public void testGlobalFunction() throws Exception {
|
||||
doTest("j2k/tests/testData/ast/kotlinApiAccess/GlobalFunction.java");
|
||||
}
|
||||
|
||||
@TestMetadata("GlobalGenericFunction.java")
|
||||
public void testGlobalGenericFunction() throws Exception {
|
||||
doTest("j2k/tests/testData/ast/kotlinApiAccess/GlobalGenericFunction.java");
|
||||
}
|
||||
|
||||
@TestMetadata("GlobalValue1.java")
|
||||
public void testGlobalValue1() throws Exception {
|
||||
doTest("j2k/tests/testData/ast/kotlinApiAccess/GlobalValue1.java");
|
||||
}
|
||||
|
||||
@TestMetadata("GlobalValue2.java")
|
||||
public void testGlobalValue2() throws Exception {
|
||||
doTest("j2k/tests/testData/ast/kotlinApiAccess/GlobalValue2.java");
|
||||
}
|
||||
|
||||
@TestMetadata("InheritedProperty.java")
|
||||
public void testInheritedProperty() throws Exception {
|
||||
doTest("j2k/tests/testData/ast/kotlinApiAccess/InheritedProperty.java");
|
||||
|
||||
@@ -1,5 +1,19 @@
|
||||
package kotlinApi
|
||||
|
||||
public open class KotlinClass {
|
||||
public var property: String
|
||||
}
|
||||
public var property: String = ""
|
||||
}
|
||||
|
||||
public fun globalFunction(s: String): String = s
|
||||
public fun globalGenericFunction<T>(t: T): T = t
|
||||
|
||||
public fun Int.extensionFunction(): String = toString()
|
||||
|
||||
public var globalValue1: Int = 1
|
||||
public var globalValue2: Int
|
||||
get() = 0
|
||||
set(value) {}
|
||||
|
||||
public var String.extensionProperty: Int
|
||||
get() = 1
|
||||
set(value) {}
|
||||
@@ -0,0 +1,8 @@
|
||||
//file
|
||||
import kotlinApi.KotlinApiPackage;
|
||||
|
||||
class C {
|
||||
void foo() {
|
||||
String s = KotlinApiPackage.extensionFunction(1);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import kotlinApi.*
|
||||
|
||||
class C() {
|
||||
fun foo() {
|
||||
val s = 1.extensionFunction()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
//file
|
||||
import kotlinApi.KotlinApiPackage;
|
||||
|
||||
class C {
|
||||
int foo() {
|
||||
KotlinApiPackage.setExtensionProperty("a", 1);
|
||||
return KotlinApiPackage.getExtensionProperty("b");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import kotlinApi.*
|
||||
|
||||
class C() {
|
||||
fun foo(): Int {
|
||||
"a".extensionProperty = 1
|
||||
return "b".extensionProperty
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
//file
|
||||
import kotlinApi.KotlinApiPackage;
|
||||
|
||||
class C {
|
||||
void foo() {
|
||||
String s = KotlinApiPackage.globalFunction("x");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import kotlinApi.*
|
||||
|
||||
class C() {
|
||||
fun foo() {
|
||||
val s = globalFunction("x")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
//file
|
||||
import kotlinApi.KotlinApiPackage;
|
||||
|
||||
class C {
|
||||
void foo() {
|
||||
int v = KotlinApiPackage.<Integer>globalGenericFunction(1);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import kotlinApi.*
|
||||
|
||||
class C() {
|
||||
fun foo() {
|
||||
val v = globalGenericFunction<Int>(1)!!
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
//file
|
||||
import kotlinApi.KotlinApiPackage;
|
||||
|
||||
class C {
|
||||
int foo() {
|
||||
KotlinApiPackage.setGlobalValue1(0)
|
||||
return KotlinApiPackage.getGlobalValue1();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import kotlinApi.*
|
||||
|
||||
class C() {
|
||||
fun foo(): Int {
|
||||
globalValue1 = 0
|
||||
return globalValue1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
//file
|
||||
import kotlinApi.KotlinApiPackage;
|
||||
|
||||
class C {
|
||||
int foo() {
|
||||
KotlinApiPackage.setGlobalValue2(0)
|
||||
return KotlinApiPackage.getGlobalValue2();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import kotlinApi.*
|
||||
|
||||
class C() {
|
||||
fun foo(): Int {
|
||||
globalValue2 = 0
|
||||
return globalValue2
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user