diff --git a/Changelog.md b/Changelog.md index 7e418a36093..696957da3f5 100644 --- a/Changelog.md +++ b/Changelog.md @@ -9,3 +9,4 @@ ### Tools. J2K - Protected members used outside of inheritors are converted as public +- Support conversion for annotation constructor calls diff --git a/j2k/src/org/jetbrains/kotlin/j2k/AnnotationConverter.kt b/j2k/src/org/jetbrains/kotlin/j2k/AnnotationConverter.kt index e62458846ff..9e276eeab07 100644 --- a/j2k/src/org/jetbrains/kotlin/j2k/AnnotationConverter.kt +++ b/j2k/src/org/jetbrains/kotlin/j2k/AnnotationConverter.kt @@ -100,10 +100,15 @@ class AnnotationConverter(private val converter: Converter) { } fun convertAnnotation(annotation: PsiAnnotation, newLineAfter: Boolean): Annotation? { + val (name, arguments) = convertAnnotationValue(annotation) ?: return null + return Annotation(name, arguments, newLineAfter).assignPrototype(annotation) + } + + private fun convertAnnotationValue(annotation: PsiAnnotation): Pair>>>? { val qualifiedName = annotation.qualifiedName if (qualifiedName == CommonClassNames.JAVA_LANG_DEPRECATED && annotation.parameterList.attributes.isEmpty()) { val deferredExpression = converter.deferredElement { LiteralExpression("\"\"").assignNoPrototype() } - return Annotation(Identifier("Deprecated").assignNoPrototype(), listOf(null to deferredExpression), newLineAfter).assignPrototype(annotation) //TODO: insert comment + return Identifier("Deprecated").assignNoPrototype() to listOf(null to deferredExpression) //TODO: insert comment } if (qualifiedName == CommonClassNames.JAVA_LANG_ANNOTATION_TARGET) { val attributes = annotation.parameterList.attributes @@ -128,8 +133,7 @@ class AnnotationConverter(private val converter: Converter) { Identifier(name, false).assignNoPrototype()) } } - return Annotation(Identifier("Target").assignNoPrototype(), - deferredExpressionList, newLineAfter).assignPrototype(annotation) + return Identifier("Target").assignNoPrototype() to deferredExpressionList } val nameRef = annotation.nameReferenceElement @@ -148,12 +152,19 @@ class AnnotationConverter(private val converter: Converter) { attrValues.map { attrName to converter.deferredElement(it) } } - return Annotation(name, arguments, newLineAfter).assignPrototype(annotation) + return name to arguments } fun convertAnnotationMethodDefault(method: PsiAnnotationMethod): DeferredElement? { val value = method.defaultValue ?: return null - return converter.deferredElement(convertAttributeValue(value, method.returnType, false, false).single()) + val convertAttributeValue = convertAttributeValue(value, method.returnType, false, false) + if (method.returnType is PsiArrayType && value !is PsiArrayInitializerMemberValue) { + return converter.deferredElement { codeConverter -> + val convertedType = converter.typeConverter.convertType(method.returnType) as ArrayType + createArrayExpression(codeConverter, convertAttributeValue.toList(), convertedType, false) + } + } + return converter.deferredElement(convertAttributeValue.single()) } private fun convertAttributeValue(value: PsiAnnotationMemberValue?, expectedType: PsiType?, isVararg: Boolean, isUnnamed: Boolean): List<(CodeConverter) -> Expression> { @@ -171,6 +182,12 @@ class AnnotationConverter(private val converter: Converter) { } } + is PsiAnnotation -> { + listOf({ codeConverter -> + val (name, arguments) = convertAnnotationValue(value)!! + AnnotationConstructorCall(name, arguments).assignPrototype(value) + }) + } else -> listOf({ codeConverter -> DummyStringExpression(value?.text ?: "").assignPrototype(value) }) } } @@ -195,16 +212,20 @@ class AnnotationConverter(private val converter: Converter) { ): Expression { val expectedTypeConverted = converter.typeConverter.convertType(expectedType) return if (expectedTypeConverted is ArrayType) { - val array = createArrayInitializerExpression(expectedTypeConverted, componentGenerators.map { it(codeConverter) }, needExplicitType = false) - if (isVararg) { - StarExpression(array.assignNoPrototype()).assignPrototype(value) - } - else { - array.assignPrototype(value) - } + createArrayExpression(codeConverter, componentGenerators, expectedTypeConverted, isVararg).assignPrototype(value) } else { DummyStringExpression(value.text!!).assignPrototype(value) } } + + private fun createArrayExpression(codeConverter: CodeConverter, componentGenerators: List<(CodeConverter) -> Expression>, expectedType: ArrayType, isVararg: Boolean): Expression { + val array = createArrayInitializerExpression(expectedType, componentGenerators.map { it(codeConverter) }, needExplicitType = false) + return if (isVararg) { + StarExpression(array.assignNoPrototype()) + } + else { + array + } + } } diff --git a/j2k/src/org/jetbrains/kotlin/j2k/ast/Annotation.kt b/j2k/src/org/jetbrains/kotlin/j2k/ast/Annotation.kt index 8dac88b7be1..17c17af85e5 100644 --- a/j2k/src/org/jetbrains/kotlin/j2k/ast/Annotation.kt +++ b/j2k/src/org/jetbrains/kotlin/j2k/ast/Annotation.kt @@ -27,30 +27,42 @@ class Annotation(val name: Identifier, val arguments: List>>) : Expression() { + private val annotation = Annotation(name, arguments, false) + + override fun generateCode(builder: CodeBuilder) { + annotation.generateCall(builder) + } +} + class Annotations(val annotations: List) : Element() { override fun generateCode(builder: CodeBuilder) { builder.append(annotations, "") diff --git a/j2k/testData/fileOrElement/annotations/annotationArrayArgument.java b/j2k/testData/fileOrElement/annotations/annotationArrayArgument.java new file mode 100644 index 00000000000..67a28488c29 --- /dev/null +++ b/j2k/testData/fileOrElement/annotations/annotationArrayArgument.java @@ -0,0 +1,28 @@ +public @interface Ann { + Inner[] value(); + InnerParam[] test1() default @InnerParam(C.class); +} + +public @interface Inner { + +} + +public @interface InnerParam { + Class value(); +} + +@Ann(value = {@Inner, @Inner}, test1 = { @InnerParam(C.class) }) +public class C { +} + +@Ann({@Inner, @Inner}) +public class D { +} + +@Ann(value = @Inner) +public class E { +} + +@Ann(value = {@Inner}, test1 = { @InnerParam(value = C.class) }) +public class F { +} \ No newline at end of file diff --git a/j2k/testData/fileOrElement/annotations/annotationArrayArgument.kt b/j2k/testData/fileOrElement/annotations/annotationArrayArgument.kt new file mode 100644 index 00000000000..84daf461533 --- /dev/null +++ b/j2k/testData/fileOrElement/annotations/annotationArrayArgument.kt @@ -0,0 +1,19 @@ +import kotlin.reflect.KClass + +annotation class Ann(vararg val value: Inner, val test1: Array = arrayOf(InnerParam(C::class))) + +annotation class Inner + +annotation class InnerParam(val value: KClass<*>) + +@Ann(value = *arrayOf(Inner(), Inner()), test1 = arrayOf(InnerParam(C::class))) +class C + +@Ann(Inner(), Inner()) +class D + +@Ann(value = Inner()) +class E + +@Ann(value = *arrayOf(Inner()), test1 = arrayOf(InnerParam(value = C::class))) +class F \ No newline at end of file diff --git a/j2k/testData/fileOrElement/annotations/annotationDefault.java b/j2k/testData/fileOrElement/annotations/annotationDefault.java new file mode 100644 index 00000000000..dc9ff744113 --- /dev/null +++ b/j2k/testData/fileOrElement/annotations/annotationDefault.java @@ -0,0 +1,16 @@ +public @interface Ann { + int i() default 1; + int[] i2() default 1; + int[] i3() default { 1 }; + Class klass() default A.class; + Class[] klass2() default A.class; + Class[] klass3() default { A.class }; + Inner ann() default @Inner(); + Inner[] ann2() default @Inner; + Inner[] ann3() default { @Inner, @Inner() }; +} + +public class A +public @interface Inner { + +} diff --git a/j2k/testData/fileOrElement/annotations/annotationDefault.kt b/j2k/testData/fileOrElement/annotations/annotationDefault.kt new file mode 100644 index 00000000000..e05f0bb6684 --- /dev/null +++ b/j2k/testData/fileOrElement/annotations/annotationDefault.kt @@ -0,0 +1,6 @@ +import kotlin.reflect.KClass + +annotation class Ann(val i: Int = 1, val i2: IntArray = intArrayOf(1), val i3: IntArray = intArrayOf(1), val klass: KClass<*> = A::class, val klass2: Array> = arrayOf(A::class), val klass3: Array> = arrayOf(A::class), val ann: Inner = Inner(), val ann2: Array = arrayOf(Inner()), val ann3: Array = arrayOf(Inner(), Inner())) + +class A +annotation class Inner diff --git a/j2k/tests/org/jetbrains/kotlin/j2k/JavaToKotlinConverterForWebDemoTestGenerated.java b/j2k/tests/org/jetbrains/kotlin/j2k/JavaToKotlinConverterForWebDemoTestGenerated.java index 37ad44dfafb..c658f89efa8 100644 --- a/j2k/tests/org/jetbrains/kotlin/j2k/JavaToKotlinConverterForWebDemoTestGenerated.java +++ b/j2k/tests/org/jetbrains/kotlin/j2k/JavaToKotlinConverterForWebDemoTestGenerated.java @@ -43,6 +43,18 @@ public class JavaToKotlinConverterForWebDemoTestGenerated extends AbstractJavaTo KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("j2k/testData/fileOrElement/annotations"), Pattern.compile("^(.+)\\.java$"), true); } + @TestMetadata("annotationArrayArgument.java") + public void testAnnotationArrayArgument() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("j2k/testData/fileOrElement/annotations/annotationArrayArgument.java"); + doTest(fileName); + } + + @TestMetadata("annotationDefault.java") + public void testAnnotationDefault() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("j2k/testData/fileOrElement/annotations/annotationDefault.java"); + doTest(fileName); + } + @TestMetadata("annotationInterface1.java") public void testAnnotationInterface1() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("j2k/testData/fileOrElement/annotations/annotationInterface1.java"); diff --git a/j2k/tests/org/jetbrains/kotlin/j2k/JavaToKotlinConverterSingleFileTestGenerated.java b/j2k/tests/org/jetbrains/kotlin/j2k/JavaToKotlinConverterSingleFileTestGenerated.java index 97aa8f1aa78..e2927afc8fa 100644 --- a/j2k/tests/org/jetbrains/kotlin/j2k/JavaToKotlinConverterSingleFileTestGenerated.java +++ b/j2k/tests/org/jetbrains/kotlin/j2k/JavaToKotlinConverterSingleFileTestGenerated.java @@ -43,6 +43,18 @@ public class JavaToKotlinConverterSingleFileTestGenerated extends AbstractJavaTo KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("j2k/testData/fileOrElement/annotations"), Pattern.compile("^(.+)\\.java$"), true); } + @TestMetadata("annotationArrayArgument.java") + public void testAnnotationArrayArgument() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("j2k/testData/fileOrElement/annotations/annotationArrayArgument.java"); + doTest(fileName); + } + + @TestMetadata("annotationDefault.java") + public void testAnnotationDefault() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("j2k/testData/fileOrElement/annotations/annotationDefault.java"); + doTest(fileName); + } + @TestMetadata("annotationInterface1.java") public void testAnnotationInterface1() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("j2k/testData/fileOrElement/annotations/annotationInterface1.java");