Fixed KT-5247 Java to Kotlin: convert java @interfaces into Kotlin annotation classes

#KT-5247 Fixed
This commit is contained in:
Valentin Kipyatkov
2014-07-18 18:48:04 +04:00
parent d7e857ed0a
commit 3c33b7dcfa
14 changed files with 177 additions and 39 deletions
@@ -47,15 +47,15 @@ class AnnotationConverter(private val converter: Converter) {
}
}
val list = annotations.map { convertAnnotation(it, owner is PsiLocalVariable) }.filterNotNull() //TODO: brackets are also needed for local classes
return Annotations(list, newLines).assignNoPrototype()
val list = annotations.map { convertAnnotation(it, owner is PsiLocalVariable, newLines) }.filterNotNull() //TODO: brackets are also needed for local classes
return Annotations(list).assignNoPrototype()
}
private fun convertModifiersToAnnotations(owner: PsiModifierListOwner): Annotations {
val list = MODIFIER_TO_ANNOTATION
.filter { owner.hasModifierProperty(it.first) }
.map { org.jetbrains.jet.j2k.ast.Annotation(Identifier(it.second).assignNoPrototype(), listOf(), false).assignNoPrototype() }
return Annotations(list, false).assignNoPrototype()
.map { Annotation(Identifier(it.second).assignNoPrototype(), listOf(), false, false).assignNoPrototype() }
return Annotations(list).assignNoPrototype()
}
private val MODIFIER_TO_ANNOTATION = listOf(
@@ -65,10 +65,10 @@ class AnnotationConverter(private val converter: Converter) {
PsiModifier.TRANSIENT to "transient"
)
public fun convertAnnotation(annotation: PsiAnnotation, brackets: Boolean): org.jetbrains.jet.j2k.ast.Annotation? {
public fun convertAnnotation(annotation: PsiAnnotation, brackets: Boolean, newLineAfter: Boolean): Annotation? {
val qualifiedName = annotation.getQualifiedName()
if (qualifiedName == CommonClassNames.JAVA_LANG_DEPRECATED && annotation.getParameterList().getAttributes().isEmpty()) {
return org.jetbrains.jet.j2k.ast.Annotation(Identifier("deprecated").assignNoPrototype(), listOf(null to LiteralExpression("\"\"").assignNoPrototype()), brackets).assignPrototype(annotation) //TODO: insert comment
return Annotation(Identifier("deprecated").assignNoPrototype(), listOf(null to LiteralExpression("\"\"").assignNoPrototype()), brackets, newLineAfter).assignPrototype(annotation) //TODO: insert comment
}
val nameRef = annotation.getNameReferenceElement()
@@ -87,7 +87,12 @@ class AnnotationConverter(private val converter: Converter) {
attrValues.map { attrName to it }
}
return org.jetbrains.jet.j2k.ast.Annotation(name, arguments, brackets).assignPrototype(annotation)
return Annotation(name, arguments, brackets, newLineAfter).assignPrototype(annotation)
}
public fun convertAnnotationMethodDefault(method: PsiAnnotationMethod): Expression? {
val value = method.getDefaultValue() ?: return null
return convertAttributeValue(value, method.getReturnType(), false, false).single()
}
private fun convertAttributeValue(value: PsiAnnotationMemberValue?, expectedType: PsiType?, isVararg: Boolean, isUnnamed: Boolean): List<Expression> {
@@ -89,6 +89,7 @@ class CodeBuilder(private val topElement: PsiElement?) {
if (topElement == null || element.prototypes!!.isEmpty()) {
element.generateCode(this)
element.postGenerateCode(this)
return this
}
@@ -135,6 +136,8 @@ class CodeBuilder(private val topElement: PsiElement?) {
postfixElements.forEach { appendCommentOrWhiteSpace(it) }
element.postGenerateCode(this)
return this
}
+47 -3
View File
@@ -117,7 +117,7 @@ public class Converter private(val project: Project,
is PsiExpression -> convertExpression(element)
is PsiImportList -> convertImportList(element)
is PsiImportStatementBase -> convertImport(element, false)
is PsiAnnotation -> annotationConverter.convertAnnotation(element, false)
is PsiAnnotation -> annotationConverter.convertAnnotation(element, false, false)
is PsiPackageStatement -> PackageStatement(quoteKeywords(element.getPackageName() ?: "")).assignPrototype(element)
else -> null
}
@@ -164,6 +164,8 @@ public class Converter private(val project: Project,
val convertedMembers = LinkedHashMap<PsiMember, Member>()
for (element in psiClass.getChildren()) {
if (element is PsiMember) {
if (element is PsiAnnotationMethod) continue // converted in convertAnnotationType()
val converted = convertMember(element, membersToRemove, constructorConverter)
if (converted != null && !converted.isEmpty) {
convertedMembers.put(element, converted)
@@ -240,6 +242,10 @@ public class Converter private(val project: Project,
= annotationConverter.convertAnnotations(owner)
fun convertClass(psiClass: PsiClass): Class {
if (psiClass.isAnnotationType()) {
return convertAnnotationType(psiClass)
}
val annotations = annotationConverter.convertAnnotations(psiClass)
var modifiers = convertModifiers(psiClass)
val typeParameters = convertTypeParameterList(psiClass.getTypeParameterList())
@@ -270,6 +276,44 @@ public class Converter private(val project: Project,
}.assignPrototype(psiClass)
}
private fun convertAnnotationType(psiClass: PsiClass): Class {
val paramModifiers = Modifiers(listOf(Modifier.PUBLIC)).assignNoPrototype()
val noBlankLinesInheritance = CommentsAndSpacesInheritance(blankLinesBefore = false)
val annotationMethods = psiClass.getMethods().filterIsInstance(javaClass<PsiAnnotationMethod>())
val parameters = annotationMethods
.map { method ->
val returnType = method.getReturnType()
val typeConverted = if (method == annotationMethods.last && returnType is PsiArrayType)
VarArgType(typeConverter.convertType(returnType.getComponentType(), Nullability.NotNull).assignNoPrototype())
else
typeConverter.convertType(returnType, Nullability.NotNull)
typeConverted.assignPrototype(method.getReturnTypeElement(), noBlankLinesInheritance)
Parameter(method.declarationIdentifier(),
typeConverted,
Parameter.VarValModifier.Val,
convertAnnotations(method),
paramModifiers,
annotationConverter.convertAnnotationMethodDefault(method)).assignPrototype(method, noBlankLinesInheritance)
}
val parameterList = ParameterList(parameters).assignNoPrototype()
val constructorSignature = PrimaryConstructorSignature(Annotations.Empty, Modifiers.Empty, parameterList).assignNoPrototype()
// to convert fields and nested types - they are not allowed in Kotlin but we convert them and let user refactor code
var classBody = convertBody(psiClass, null)
classBody = ClassBody(constructorSignature, classBody.members, classBody.classObjectMembers, listOf(), classBody.lBrace, classBody.rBrace)
val annotationAnnotation = Annotation(Identifier("annotation").assignNoPrototype(), listOf(), false, false).assignNoPrototype()
return Class(psiClass.declarationIdentifier(),
(convertAnnotations(psiClass) + Annotations(listOf(annotationAnnotation))).assignNoPrototype(),
convertModifiers(psiClass).without(Modifier.ABSTRACT),
TypeParameterList.Empty,
listOf(),
listOf(),
listOf(),
classBody).assignPrototype(psiClass)
}
private fun convertInitializer(initializer: PsiClassInitializer): Initializer {
return Initializer(convertBlock(initializer.getBody()), convertModifiers(initializer)).assignPrototype(initializer)
}
@@ -606,8 +650,8 @@ public class Converter private(val project: Project,
val convertedType = typeConverter.convertType(types[it], Nullability.NotNull)
null to MethodCallExpression.buildNotNull(null, "javaClass", listOf(), listOf(convertedType)).assignPrototype(refElements[it])
}
val annotation = Annotation(Identifier("throws").assignNoPrototype(), arguments, false)
return Annotations(listOf(annotation.assignPrototype(throwsList)), true).assignPrototype(throwsList)
val annotation = Annotation(Identifier("throws").assignNoPrototype(), arguments, false, true)
return Annotations(listOf(annotation.assignPrototype(throwsList))).assignPrototype(throwsList)
}
}
+27 -26
View File
@@ -18,7 +18,7 @@ package org.jetbrains.jet.j2k.ast
import org.jetbrains.jet.j2k.*
class Annotation(val name: Identifier, val arguments: List<Pair<Identifier?, Expression>>, val brackets: Boolean) : Element() {
class Annotation(val name: Identifier, val arguments: List<Pair<Identifier?, Expression>>, val brackets: Boolean, val newLineAfter: Boolean) : Element() {
private fun CodeBuilder.surroundWithBrackets(action: () -> Unit) {
if (brackets) append("[")
action()
@@ -28,44 +28,45 @@ class Annotation(val name: Identifier, val arguments: List<Pair<Identifier?, Exp
override fun generateCode(builder: CodeBuilder) {
if (arguments.isEmpty()) {
builder.surroundWithBrackets { builder.append(name) }
return
}
else {
builder.surroundWithBrackets {
builder.append(name)
.append("(")
.append(arguments.map {
{
if (it.first != null) {
builder append it.first!! append " = " append it.second
}
else {
builder append it.second
}
}
}, ", ")
.append(")")
}
}
}
builder.surroundWithBrackets {
builder.append(name)
.append("(")
.append(arguments.map {
{
if (it.first != null) {
builder append it.first!! append " = " append it.second
}
else {
builder append it.second
}
}
}, ", ")
.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 Annotations(val annotations: List<Annotation>, val newLines: Boolean) : Element() {
private val br = if (newLines) "\n" else " "
class Annotations(val annotations: List<Annotation>) : Element() {
override fun generateCode(builder: CodeBuilder) {
if (annotations.isNotEmpty()) {
builder.append(annotations, br, "", br)
}
builder.append(annotations, "")
}
override val isEmpty: Boolean = annotations.isEmpty()
fun plus(other: Annotations) = Annotations(annotations + other.annotations, newLines || other.newLines)
fun plus(other: Annotations) = Annotations(annotations + other.annotations)
class object {
val Empty = Annotations(listOf(), false)
val Empty = Annotations(listOf())
}
}
fun Annotations.withBrackets(): Annotations
= Annotations(annotations.map { Annotation(it.name, it.arguments, true).assignPrototypesFrom(it) }, newLines).assignPrototypesFrom(this)
= Annotations(annotations.map { Annotation(it.name, it.arguments, true, it.newLineAfter).assignPrototypesFrom(it) }).assignPrototypesFrom(this)
@@ -72,6 +72,8 @@ abstract class Element {
/** This method should not be used anywhere except for CodeBuilder! Use CodeBuilder.append instead. */
public abstract fun generateCode(builder: CodeBuilder)
public open fun postGenerateCode(builder: CodeBuilder) { }
public open val isEmpty: Boolean get() = false
object Empty : Element() {
@@ -34,7 +34,6 @@ class Parameter(val identifier: Identifier,
builder.append(annotations).appendWithSpaceAfter(modifiers)
if (`type` is VarArgType) {
assert(varVal == VarValModifier.None)
builder.append("vararg ")
}
@@ -43,6 +43,21 @@ public class JavaToKotlinConverterTestGenerated extends AbstractJavaToKotlinConv
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.TestsPackage", new File("j2k/tests/testData/ast/annotations"), Pattern.compile("^(.+)\\.java$"), true);
}
@TestMetadata("annotationInterface1.java")
public void testAnnotationInterface1() throws Exception {
doTest("j2k/tests/testData/ast/annotations/annotationInterface1.java");
}
@TestMetadata("annotationInterface2.java")
public void testAnnotationInterface2() throws Exception {
doTest("j2k/tests/testData/ast/annotations/annotationInterface2.java");
}
@TestMetadata("annotationInterface3.java")
public void testAnnotationInterface3() throws Exception {
doTest("j2k/tests/testData/ast/annotations/annotationInterface3.java");
}
@TestMetadata("annotationUsages.java")
public void testAnnotationUsages() throws Exception {
doTest("j2k/tests/testData/ast/annotations/annotationUsages.java");
@@ -0,0 +1,16 @@
import java.lang.annotation.ElementType;
import java.lang.annotation.Target;
@interface Anon {
String[] stringArray();
int[] intArray();
// string
String string();
}
@Anon(string = "a", stringArray = { "a", "b" }, intArray = { 1, 2 })
@Target({ElementType.CONSTRUCTOR, ElementType.FIELD})
@interface I {
}
@@ -0,0 +1,9 @@
import java.lang.annotation.ElementType
import java.lang.annotation.Target
annotation class Anon(public val stringArray: Array<String>, public val intArray: IntArray, // string
public val string: String)
Anon(string = "a", stringArray = array("a", "b"), intArray = intArray(1, 2))
Target(ElementType.CONSTRUCTOR, ElementType.FIELD)
annotation class I
@@ -0,0 +1,8 @@
@interface Anon {
String s() default "a";
String[] stringArray() default { "a", "b" };
int[] intArray();
}
@Anon(intArray = {1, 2})
class A{ }
@@ -0,0 +1,4 @@
annotation class Anon(public val s: String = "a", public val stringArray: Array<String> = array("a", "b"), public vararg val intArray: Int)
Anon(intArray = *intArray(1, 2))
class A
@@ -0,0 +1,14 @@
@interface Anon {
String value();
enum E {
A, B
}
E field = E.A;
}
@Anon("a")
interface I {
Anon.E e = Anon.field;
}
@@ -0,0 +1,19 @@
annotation class Anon(public val value: String) {
public enum class E {
A
B
}
class object {
public val field: E = E.A
}
}
Anon("a")
trait I {
class object {
public val e: Anon.E = Anon.field
}
}
@@ -1,7 +1,6 @@
class A {
deprecated("")
volatile
var field1 = 0
volatile var field1 = 0
transient var field2 = 1