J2K: convert annotations in annotations parameters correctly
#KT-11126 Fixed
This commit is contained in:
@@ -9,3 +9,4 @@
|
|||||||
|
|
||||||
### Tools. J2K
|
### Tools. J2K
|
||||||
- Protected members used outside of inheritors are converted as public
|
- 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? {
|
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
|
val qualifiedName = annotation.qualifiedName
|
||||||
if (qualifiedName == CommonClassNames.JAVA_LANG_DEPRECATED && annotation.parameterList.attributes.isEmpty()) {
|
if (qualifiedName == CommonClassNames.JAVA_LANG_DEPRECATED && annotation.parameterList.attributes.isEmpty()) {
|
||||||
val deferredExpression = converter.deferredElement<Expression> { LiteralExpression("\"\"").assignNoPrototype() }
|
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) {
|
if (qualifiedName == CommonClassNames.JAVA_LANG_ANNOTATION_TARGET) {
|
||||||
val attributes = annotation.parameterList.attributes
|
val attributes = annotation.parameterList.attributes
|
||||||
@@ -128,8 +133,7 @@ class AnnotationConverter(private val converter: Converter) {
|
|||||||
Identifier(name, false).assignNoPrototype())
|
Identifier(name, false).assignNoPrototype())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Annotation(Identifier("Target").assignNoPrototype(),
|
return Identifier("Target").assignNoPrototype() to deferredExpressionList
|
||||||
deferredExpressionList, newLineAfter).assignPrototype(annotation)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val nameRef = annotation.nameReferenceElement
|
val nameRef = annotation.nameReferenceElement
|
||||||
@@ -148,12 +152,19 @@ class AnnotationConverter(private val converter: Converter) {
|
|||||||
|
|
||||||
attrValues.map { attrName to converter.deferredElement(it) }
|
attrValues.map { attrName to converter.deferredElement(it) }
|
||||||
}
|
}
|
||||||
return Annotation(name, arguments, newLineAfter).assignPrototype(annotation)
|
return name to arguments
|
||||||
}
|
}
|
||||||
|
|
||||||
fun convertAnnotationMethodDefault(method: PsiAnnotationMethod): DeferredElement<Expression>? {
|
fun convertAnnotationMethodDefault(method: PsiAnnotationMethod): DeferredElement<Expression>? {
|
||||||
val value = method.defaultValue ?: return null
|
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> {
|
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) })
|
else -> listOf({ codeConverter -> DummyStringExpression(value?.text ?: "").assignPrototype(value) })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -195,16 +212,20 @@ class AnnotationConverter(private val converter: Converter) {
|
|||||||
): Expression {
|
): Expression {
|
||||||
val expectedTypeConverted = converter.typeConverter.convertType(expectedType)
|
val expectedTypeConverted = converter.typeConverter.convertType(expectedType)
|
||||||
return if (expectedTypeConverted is ArrayType) {
|
return if (expectedTypeConverted is ArrayType) {
|
||||||
val array = createArrayInitializerExpression(expectedTypeConverted, componentGenerators.map { it(codeConverter) }, needExplicitType = false)
|
createArrayExpression(codeConverter, componentGenerators, expectedTypeConverted, isVararg).assignPrototype(value)
|
||||||
if (isVararg) {
|
|
||||||
StarExpression(array.assignNoPrototype()).assignPrototype(value)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
array.assignPrototype(value)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
DummyStringExpression(value.text!!).assignPrototype(value)
|
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)
|
builder.append(name)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
builder.append(name)
|
generateCall(builder)
|
||||||
.append("(")
|
|
||||||
.buildList(
|
|
||||||
generators = arguments.map {
|
|
||||||
{
|
|
||||||
if (it.first != null) {
|
|
||||||
builder append it.first!! append " = " append it.second
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
builder append it.second
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
separator = ", ")
|
|
||||||
.append(")")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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) {
|
override fun postGenerateCode(builder: CodeBuilder) {
|
||||||
// we add line break in postGenerateCode to keep comments attached to this element on the same line
|
// we add line break in postGenerateCode to keep comments attached to this element on the same line
|
||||||
builder.append(if (newLineAfter) "\n" else " ")
|
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() {
|
class Annotations(val annotations: List<Annotation>) : Element() {
|
||||||
override fun generateCode(builder: CodeBuilder) {
|
override fun generateCode(builder: CodeBuilder) {
|
||||||
builder.append(annotations, "")
|
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);
|
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")
|
@TestMetadata("annotationInterface1.java")
|
||||||
public void testAnnotationInterface1() throws Exception {
|
public void testAnnotationInterface1() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("j2k/testData/fileOrElement/annotations/annotationInterface1.java");
|
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);
|
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")
|
@TestMetadata("annotationInterface1.java")
|
||||||
public void testAnnotationInterface1() throws Exception {
|
public void testAnnotationInterface1() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("j2k/testData/fileOrElement/annotations/annotationInterface1.java");
|
String fileName = KotlinTestUtils.navigationMetadata("j2k/testData/fileOrElement/annotations/annotationInterface1.java");
|
||||||
|
|||||||
Reference in New Issue
Block a user