J2K: convert annotations in annotations parameters correctly
#KT-11126 Fixed
This commit is contained in:
@@ -9,3 +9,4 @@
|
||||
|
||||
### Tools. J2K
|
||||
- Protected members used outside of inheritors are converted as public
|
||||
- Support conversion for annotation constructor calls
|
||||
|
||||
@@ -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<Identifier, List<Pair<Identifier?, DeferredElement<Expression>>>>? {
|
||||
val qualifiedName = annotation.qualifiedName
|
||||
if (qualifiedName == CommonClassNames.JAVA_LANG_DEPRECATED && annotation.parameterList.attributes.isEmpty()) {
|
||||
val deferredExpression = converter.deferredElement<Expression> { 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<Expression>? {
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,30 +27,42 @@ class Annotation(val name: Identifier, val arguments: List<Pair<Identifier?, Def
|
||||
builder.append(name)
|
||||
}
|
||||
else {
|
||||
builder.append(name)
|
||||
.append("(")
|
||||
.buildList(
|
||||
generators = arguments.map {
|
||||
{
|
||||
if (it.first != null) {
|
||||
builder append it.first!! append " = " append it.second
|
||||
}
|
||||
else {
|
||||
builder append it.second
|
||||
}
|
||||
}
|
||||
},
|
||||
separator = ", ")
|
||||
.append(")")
|
||||
generateCall(builder)
|
||||
}
|
||||
}
|
||||
|
||||
fun generateCall(builder: CodeBuilder) {
|
||||
builder.append(name)
|
||||
.append("(")
|
||||
.buildList(
|
||||
generators = arguments.map {
|
||||
{
|
||||
if (it.first != null) {
|
||||
builder append it.first!! append " = " append it.second
|
||||
}
|
||||
else {
|
||||
builder append it.second
|
||||
}
|
||||
}
|
||||
},
|
||||
separator = ", ")
|
||||
.append(")")
|
||||
}
|
||||
|
||||
override fun postGenerateCode(builder: CodeBuilder) {
|
||||
// we add line break in postGenerateCode to keep comments attached to this element on the same line
|
||||
builder.append(if (newLineAfter) "\n" else " ")
|
||||
}
|
||||
}
|
||||
|
||||
class AnnotationConstructorCall(name: Identifier, arguments: List<Pair<Identifier?, DeferredElement<Expression>>>) : Expression() {
|
||||
private val annotation = Annotation(name, arguments, false)
|
||||
|
||||
override fun generateCode(builder: CodeBuilder) {
|
||||
annotation.generateCall(builder)
|
||||
}
|
||||
}
|
||||
|
||||
class Annotations(val annotations: List<Annotation>) : Element() {
|
||||
override fun generateCode(builder: CodeBuilder) {
|
||||
builder.append(annotations, "")
|
||||
|
||||
@@ -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 {
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
annotation class Ann(vararg val value: Inner, val test1: Array<InnerParam> = 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
|
||||
@@ -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 {
|
||||
|
||||
}
|
||||
@@ -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<KClass<*>> = arrayOf(A::class), val klass3: Array<KClass<*>> = arrayOf(A::class), val ann: Inner = Inner(), val ann2: Array<Inner> = arrayOf(Inner()), val ann3: Array<Inner> = arrayOf(Inner(), Inner()))
|
||||
|
||||
class A
|
||||
annotation class Inner
|
||||
@@ -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");
|
||||
|
||||
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user