J2K: convert annotations in annotations parameters correctly

#KT-11126 Fixed
This commit is contained in:
Natalia Ukhorskaya
2016-02-20 17:55:30 +03:00
parent 7d594970ff
commit 405b0b1df6
9 changed files with 154 additions and 27 deletions
+1
View File
@@ -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");