diff --git a/j2k/src/org/jetbrains/jet/j2k/Converter.kt b/j2k/src/org/jetbrains/jet/j2k/Converter.kt index 8bb98e3d03f..142c9c8f741 100644 --- a/j2k/src/org/jetbrains/jet/j2k/Converter.kt +++ b/j2k/src/org/jetbrains/jet/j2k/Converter.kt @@ -72,6 +72,7 @@ public class Converter private(val project: Project, val settings: ConverterSett is PsiComment -> Comment(element.getText()!!) is PsiImportList -> convertImportList(element) is PsiImportStatementBase -> convertImport(element, false) + is PsiAnnotation -> convertAnnotation(element) is PsiPackageStatement -> PackageStatement(quoteKeywords(element.getPackageName() ?: "")) is PsiWhiteSpace -> WhiteSpace(element.getText()!!) else -> null @@ -100,7 +101,7 @@ public class Converter private(val project: Project, val settings: ConverterSett } public fun convertAnonymousClassBody(anonymousClass: PsiAnonymousClass): AnonymousClassBody { - return AnonymousClassBody(this, convertClassBody(anonymousClass), anonymousClass.getBaseClassType().resolve()?.isInterface() ?: false) + return AnonymousClassBody(convertClassBody(anonymousClass), anonymousClass.getBaseClassType().resolve()?.isInterface() ?: false) } private fun convertClassBody(psiClass: PsiClass): ClassBody { @@ -201,6 +202,7 @@ public class Converter private(val project: Project, val settings: ConverterSett } private fun convertClass(psiClass: PsiClass): Class { + val annotations = convertAnnotations(psiClass) val modifiers = convertModifiers(psiClass) val typeParameters = convertTypeParameterList(psiClass.getTypeParameterList()) val implementsTypes = convertToNotNullableTypes(psiClass.getImplementsListTypes()) @@ -209,9 +211,9 @@ public class Converter private(val project: Project, val settings: ConverterSett var classBody = convertClassBody(psiClass) when { - psiClass.isInterface() -> return Trait(this, name, getComments(psiClass), modifiers, typeParameters, extendsTypes, listOf(), implementsTypes, classBody) + psiClass.isInterface() -> return Trait(name, getComments(psiClass), annotations, modifiers, typeParameters, extendsTypes, listOf(), implementsTypes, classBody) - psiClass.isEnum() -> return Enum(this, name, getComments(psiClass), modifiers, typeParameters, listOf(), listOf(), implementsTypes, classBody) + psiClass.isEnum() -> return Enum(name, getComments(psiClass), annotations, modifiers, typeParameters, listOf(), listOf(), implementsTypes, classBody) else -> { if (psiClass.getPrimaryConstructor() == null && psiClass.getConstructors().size > 1) { @@ -238,7 +240,7 @@ public class Converter private(val project: Project, val settings: ConverterSett modifiers.add(Modifier.INNER) } - return Class(this, name, getComments(psiClass), modifiers, typeParameters, extendsTypes, baseClassParams, implementsTypes, classBody) + return Class(name, getComments(psiClass), annotations, modifiers, typeParameters, extendsTypes, baseClassParams, implementsTypes, classBody) } } } @@ -285,10 +287,10 @@ public class Converter private(val project: Project, val settings: ConverterSett //TODO: comments? val parameters = finalOrWithEmptyInitializerFields.map { field -> val varValModifier = if (field.isVal) Parameter.VarValModifier.Val else Parameter.VarValModifier.Var - Parameter(field.identifier, field.`type`, varValModifier, field.modifiers.filter { ACCESS_MODIFIERS.contains(it) }) + Parameter(field.identifier, field.`type`, varValModifier, field.annotations, field.modifiers.filter { ACCESS_MODIFIERS.contains(it) }) } - val primaryConstructor = PrimaryConstructor(this, MemberComments.Empty, setOf(Modifier.PRIVATE), ParameterList(parameters), Block.Empty) + val primaryConstructor = PrimaryConstructor(this, MemberComments.Empty, listOf(), setOf(Modifier.PRIVATE), ParameterList(parameters), Block.Empty) val updatedMembers = MemberList(classBody.normalMembers.elements.filter { !finalOrWithEmptyInitializerFields.contains(it) }) return ClassBody(primaryConstructor, classBody.secondaryConstructors, updatedMembers, classBody.classObjectMembers) } @@ -298,10 +300,12 @@ public class Converter private(val project: Project, val settings: ConverterSett } private fun convertField(field: PsiField): Field { + val annotations = convertAnnotations(field) val modifiers = convertModifiers(field) if (field is PsiEnumConstant) { return EnumConstant(Identifier(field.getName()!!), getComments(field), + annotations, modifiers, typeConverter.convertType(field.getType(), Nullability.NotNull), convertElement(field.getArgumentList())) @@ -309,6 +313,7 @@ public class Converter private(val project: Project, val settings: ConverterSett return Field(Identifier(field.getName()!!), getComments(field), + annotations, modifiers, typeConverter.convertVariableType(field), convertExpression(field.getInitializer(), field.getType()), @@ -323,17 +328,18 @@ public class Converter private(val project: Project, val settings: ConverterSett private fun doConvertMethod(method: PsiMethod, membersToRemove: MutableSet): Function { val returnType = typeConverter.convertMethodReturnType(method) + val annotations = convertAnnotations(method) val modifiers = convertModifiers(method) val comments = getComments(method) if (method.isConstructor()) { if (method.isPrimaryConstructor()) { - return convertPrimaryConstructor(method, modifiers, comments, membersToRemove) + return convertPrimaryConstructor(method, annotations, modifiers, comments, membersToRemove) } else { val params = convertParameterList(method.getParameterList()) - return SecondaryConstructor(this, comments, modifiers, params, convertBlock(method.getBody())) + return SecondaryConstructor(this, comments, annotations, modifiers, params, convertBlock(method.getBody())) } } else { @@ -364,6 +370,7 @@ public class Converter private(val project: Project, val settings: ConverterSett val correctedParameter = Parameter(Identifier("other"), ClassType(Identifier("Any"), listOf(), Nullability.Nullable, settings), Parameter.VarValModifier.None, + params.parameters.single().annotations, listOf()) params = ParameterList(listOf(correctedParameter)) } @@ -371,7 +378,7 @@ public class Converter private(val project: Project, val settings: ConverterSett val typeParameterList = convertTypeParameterList(method.getTypeParameterList()) val block = convertBlock(method.getBody()) - return Function(this, Identifier(method.getName()), comments, modifiers, returnType, typeParameterList, params, block, containingClass?.isInterface() ?: false) + return Function(this, Identifier(method.getName()), comments, annotations, modifiers, returnType, typeParameterList, params, block, containingClass?.isInterface() ?: false) } } @@ -411,6 +418,7 @@ public class Converter private(val project: Project, val settings: ConverterSett } private fun convertPrimaryConstructor(constructor: PsiMethod, + annotations: List, modifiers: Set, comments: MemberComments, membersToRemove: MutableSet): PrimaryConstructor { @@ -460,10 +468,11 @@ public class Converter private(val project: Project, val settings: ConverterSett Parameter(Identifier(field.getName()!!), `type`, if (field.hasModifierProperty(PsiModifier.FINAL)) Parameter.VarValModifier.Val else Parameter.VarValModifier.Var, + convertAnnotations(it) + convertAnnotations(field), convertModifiers(field).filter { ACCESS_MODIFIERS.contains(it) }) } }) - return PrimaryConstructor(this, comments, modifiers, parameterList, block) + return PrimaryConstructor(this, comments, annotations, modifiers, parameterList, block) } private fun findBackingFieldForConstructorParameter(parameter: PsiParameter, constructor: PsiMethod): Pair? { @@ -550,7 +559,7 @@ public class Converter private(val project: Project, val settings: ConverterSett Nullability.NotNull -> `type` = `type`.toNotNullType() Nullability.Nullable -> `type` = `type`.toNullableType() } - return Parameter(Identifier(parameter.getName()!!), `type`, varValModifier, modifiers) + return Parameter(Identifier(parameter.getName()!!), `type`, varValModifier, convertAnnotations(parameter), modifiers) } public fun convertExpression(argument: PsiExpression?, expectedType: PsiType?): Expression { @@ -601,6 +610,52 @@ public class Converter private(val project: Project, val settings: ConverterSett PsiModifier.PRIVATE to Modifier.PRIVATE ) + public fun convertAnnotations(owner: PsiModifierListOwner): List { + return owner.getModifierList()?.getAnnotations() + ?.filter { it.getQualifiedName() !in ANNOTATIONS_TO_REMOVE } + ?.map { convertAnnotation(it) } + ?.filterNotNull() ?: listOf() + } + + public fun convertAnnotation(annotation: PsiAnnotation): Annotation? { + val name = Identifier((annotation.getNameReferenceElement() ?: return null).getText()!!) + val annotationClass = annotation.getNameReferenceElement()?.resolve() as? PsiClass + val lastMethod = annotationClass?.getMethods()?.lastOrNull() + val arguments = annotation.getParameterList().getAttributes().flatMap { + val method = annotationClass?.findMethodsByName(it.getName() ?: "value", false)?.firstOrNull() + val expectedType = method?.getReturnType() + + val attrName = it.getName()?.let { Identifier(it) } + val value = it.getValue() + + val attrValues = when(value) { + is PsiExpression -> listOf(convertExpression(it.getValue() as? PsiExpression, expectedType)) + + is PsiArrayInitializerMemberValue -> { + val isVarArg = method == lastMethod /* converted to vararg in Kotlin */ + if (isVarArg && it.getName() == null) { + value.getInitializers().map { DummyStringExpression(it.getText()!!)/*TODO*/ } + } + else { + val expectedTypeConverted = typeConverter.convertType(expectedType) + if (expectedTypeConverted is ArrayType) { + val array = createArrayInitializerExpression(expectedTypeConverted, value.getInitializers().map { DummyStringExpression(it.getText()!!)/*TODO*/ }) + listOf(if (isVarArg) StarExpression(array) else array) + } + else { + listOf(DummyStringExpression(value.getText()!!)) + } + } + } + + else -> listOf(DummyStringExpression(value?.getText() ?: "")) + } + + attrValues.map { attrName to it } + } + return Annotation(name, arguments) + } + private val TYPE_MAP: Map = mapOf( JAVA_LANG_BYTE to "byte", JAVA_LANG_SHORT to "short", @@ -625,6 +680,7 @@ public class Converter private(val project: Project, val settings: ConverterSett val NOT_NULL_ANNOTATIONS: Set = setOf("org.jetbrains.annotations.NotNull", "com.sun.istack.internal.NotNull", "javax.annotation.Nonnull") val NULLABLE_ANNOTATIONS: Set = setOf("org.jetbrains.annotations.Nullable", "com.sun.istack.internal.Nullable", "javax.annotation.Nullable") +val ANNOTATIONS_TO_REMOVE: Set = HashSet(NOT_NULL_ANNOTATIONS + NULLABLE_ANNOTATIONS + listOf(CommonClassNames.JAVA_LANG_OVERRIDE)) val PRIMITIVE_TYPE_CONVERSIONS: Map = mapOf( "byte" to BYTE.asString(), diff --git a/j2k/src/org/jetbrains/jet/j2k/ast/Annotation.kt b/j2k/src/org/jetbrains/jet/j2k/ast/Annotation.kt new file mode 100644 index 00000000000..ef656749545 --- /dev/null +++ b/j2k/src/org/jetbrains/jet/j2k/ast/Annotation.kt @@ -0,0 +1,34 @@ +/* + * Copyright 2010-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.jet.j2k.ast + +import java.util.HashSet + +class Annotation(val name: Identifier, val arguments: List>) : Element { + override fun toKotlin(): String { + if (arguments.isEmpty()) return name.toKotlin() + + return name.toKotlin() + "(" + arguments.map { + if (it.first != null) + it.first!!.toKotlin() + " = " + it.second.toKotlin() + else + it.second.toKotlin() + }.makeString(", ") + ")" + } +} + +fun List.toKotlin(): String = if (isNotEmpty()) map { it.toKotlin() }.makeString("\n") + "\n" else "" diff --git a/j2k/src/org/jetbrains/jet/j2k/ast/AnonymousClassBody.kt b/j2k/src/org/jetbrains/jet/j2k/ast/AnonymousClassBody.kt index 3ce640ac8bd..60d2054379f 100644 --- a/j2k/src/org/jetbrains/jet/j2k/ast/AnonymousClassBody.kt +++ b/j2k/src/org/jetbrains/jet/j2k/ast/AnonymousClassBody.kt @@ -19,15 +19,7 @@ package org.jetbrains.jet.j2k.ast import org.jetbrains.jet.j2k.Converter import java.util.Collections -class AnonymousClassBody(converter: Converter, body: ClassBody, val extendsTrait: Boolean) -: Class(converter, - Identifier(""), - MemberComments.Empty, - setOf(), - TypeParameterList.Empty, - listOf(), - listOf(), - listOf(), - body) { +class AnonymousClassBody(body: ClassBody, val extendsTrait: Boolean) +: Class(Identifier(""), MemberComments.Empty, listOf(), setOf(), TypeParameterList.Empty, listOf(), listOf(), listOf(), body) { override fun toKotlin() = body.toKotlin(null) } diff --git a/j2k/src/org/jetbrains/jet/j2k/ast/Class.kt b/j2k/src/org/jetbrains/jet/j2k/ast/Class.kt index 96887af8117..544ebf3785b 100644 --- a/j2k/src/org/jetbrains/jet/j2k/ast/Class.kt +++ b/j2k/src/org/jetbrains/jet/j2k/ast/Class.kt @@ -16,23 +16,23 @@ package org.jetbrains.jet.j2k.ast -import org.jetbrains.jet.j2k.Converter import java.util.ArrayList open class Class( - val converter: Converter, val name: Identifier, comments: MemberComments, + annotations: List, modifiers: Set, val typeParameterList: TypeParameterList, val extendsTypes: List, val baseClassParams: List, val implementsTypes: List, val body: ClassBody -) : Member(comments, modifiers) { +) : Member(comments, annotations, modifiers) { override fun toKotlin(): String = commentsToKotlin() + + annotations.toKotlin() + modifiersToKotlin() + keyword + " " + name.toKotlin() + typeParameterList.toKotlin() + diff --git a/j2k/src/org/jetbrains/jet/j2k/ast/Constructors.kt b/j2k/src/org/jetbrains/jet/j2k/ast/Constructors.kt index 1227f1793e9..4169f22f9b5 100644 --- a/j2k/src/org/jetbrains/jet/j2k/ast/Constructors.kt +++ b/j2k/src/org/jetbrains/jet/j2k/ast/Constructors.kt @@ -23,17 +23,19 @@ import java.util.ArrayList abstract class Constructor( converter: Converter, comments: MemberComments, + annotations: List, modifiers: Set, parameterList: ParameterList, block: Block -) : Function(converter, Identifier.Empty, comments, modifiers, Type.Empty, TypeParameterList.Empty, parameterList, block, false) +) : Function(converter, Identifier.Empty, comments, annotations, modifiers, Type.Empty, TypeParameterList.Empty, parameterList, block, false) class PrimaryConstructor(converter: Converter, comments: MemberComments, + annotations: List, modifiers: Set, parameterList: ParameterList, block: Block) - : Constructor(converter, comments, modifiers, parameterList, block) { + : Constructor(converter, comments, annotations, modifiers, parameterList, block) { public fun signatureToKotlin(): String { val accessModifier = modifiers.accessModifier() @@ -46,10 +48,11 @@ class PrimaryConstructor(converter: Converter, class SecondaryConstructor(converter: Converter, comments: MemberComments, + annotations: List, modifiers: Set, parameterList: ParameterList, block: Block) - : Constructor(converter, comments, modifiers, parameterList, block) { + : Constructor(converter, comments, annotations, modifiers, parameterList, block) { public fun toInitFunction(containingClass: Class): Function { val modifiers = HashSet(modifiers) @@ -58,7 +61,7 @@ class SecondaryConstructor(converter: Converter, val block = Block(statements) val typeParameters = ArrayList() typeParameters.addAll(containingClass.typeParameterList.parameters) - return Function(converter, Identifier("create"), MemberComments.Empty, modifiers, + return Function(converter, Identifier("create"), comments, annotations, modifiers, ClassType(containingClass.name, typeParameters, Nullability.NotNull, converter.settings), TypeParameterList(typeParameters), parameterList, block, false) } diff --git a/j2k/src/org/jetbrains/jet/j2k/ast/Enum.kt b/j2k/src/org/jetbrains/jet/j2k/ast/Enum.kt index 1b816c665de..0f92b411590 100644 --- a/j2k/src/org/jetbrains/jet/j2k/ast/Enum.kt +++ b/j2k/src/org/jetbrains/jet/j2k/ast/Enum.kt @@ -16,26 +16,26 @@ package org.jetbrains.jet.j2k.ast -import org.jetbrains.jet.j2k.Converter - class Enum( - converter: Converter, name: Identifier, comments: MemberComments, + annotations: List, modifiers: Set, typeParameterList: TypeParameterList, extendsTypes: List, baseClassParams: List, implementsTypes: List, body: ClassBody -) : Class(converter, name, comments, modifiers, typeParameterList, +) : Class(name, comments, annotations, modifiers, typeParameterList, extendsTypes, baseClassParams, implementsTypes, body) { override fun primaryConstructorSignatureToKotlin(): String = body.primaryConstructor?.signatureToKotlin() ?: "" override fun toKotlin(): String { - return modifiersToKotlin() + + return commentsToKotlin() + + annotations.toKotlin() + + modifiersToKotlin() + "enum class " + name.toKotlin() + primaryConstructorSignatureToKotlin() + typeParameterList.toKotlin() + diff --git a/j2k/src/org/jetbrains/jet/j2k/ast/EnumConstant.kt b/j2k/src/org/jetbrains/jet/j2k/ast/EnumConstant.kt index c850c968683..60f2c77c072 100644 --- a/j2k/src/org/jetbrains/jet/j2k/ast/EnumConstant.kt +++ b/j2k/src/org/jetbrains/jet/j2k/ast/EnumConstant.kt @@ -19,17 +19,18 @@ package org.jetbrains.jet.j2k.ast class EnumConstant( identifier: Identifier, members: MemberComments, + annotations: List, modifiers: Set, `type`: Type, params: Element -) : Field(identifier, members, modifiers, `type`.toNotNullType(), params, true, false) { +) : Field(identifier, members, annotations, modifiers, `type`.toNotNullType(), params, true, false) { override fun toKotlin(): String { if (initializer.toKotlin().isEmpty()) { - return identifier.toKotlin() + return annotations.toKotlin() + identifier.toKotlin() } - return identifier.toKotlin() + " : " + `type`.toKotlin() + "(" + initializer.toKotlin() + ")" + return annotations.toKotlin() + identifier.toKotlin() + " : " + `type`.toKotlin() + "(" + initializer.toKotlin() + ")" } diff --git a/j2k/src/org/jetbrains/jet/j2k/ast/Expressions.kt b/j2k/src/org/jetbrains/jet/j2k/ast/Expressions.kt index 4082fa010b3..35415f9ccd6 100644 --- a/j2k/src/org/jetbrains/jet/j2k/ast/Expressions.kt +++ b/j2k/src/org/jetbrains/jet/j2k/ast/Expressions.kt @@ -104,6 +104,10 @@ class LambdaExpression(val arguments: String?, val statementList: StatementList) } } +class StarExpression(val methodCall: MethodCallExpression) : Expression() { + override fun toKotlin() = "*" + methodCall.toKotlin() +} + fun createArrayInitializerExpression(arrayType: ArrayType, initializers: List) : MethodCallExpression { val elementType = arrayType.elementType val createArrayFunction = if (elementType.isPrimitive()) { diff --git a/j2k/src/org/jetbrains/jet/j2k/ast/Field.kt b/j2k/src/org/jetbrains/jet/j2k/ast/Field.kt index 03fb319b21a..dece98e1dc3 100644 --- a/j2k/src/org/jetbrains/jet/j2k/ast/Field.kt +++ b/j2k/src/org/jetbrains/jet/j2k/ast/Field.kt @@ -22,15 +22,16 @@ import java.util.ArrayList open class Field( val identifier: Identifier, comments: MemberComments, + annotations: List, modifiers: Set, val `type`: Type, val initializer: Element, val isVal: Boolean, private val hasWriteAccesses: Boolean -) : Member(comments, modifiers) { +) : Member(comments, annotations, modifiers) { override fun toKotlin(): String { - val declaration = commentsToKotlin() + modifiersToKotlin() + (if (isVal) "val " else "var ") + identifier.toKotlin() + " : " + `type`.toKotlin() + val declaration = commentsToKotlin() + annotations.toKotlin() + modifiersToKotlin() + (if (isVal) "val " else "var ") + identifier.toKotlin() + " : " + `type`.toKotlin() return if (initializer.isEmpty) declaration + (if (isVal && hasWriteAccesses) "" else " = " + getDefaultInitializer(this)) else diff --git a/j2k/src/org/jetbrains/jet/j2k/ast/Function.kt b/j2k/src/org/jetbrains/jet/j2k/ast/Function.kt index a2f9a1e06e1..9ab8e42149e 100644 --- a/j2k/src/org/jetbrains/jet/j2k/ast/Function.kt +++ b/j2k/src/org/jetbrains/jet/j2k/ast/Function.kt @@ -23,13 +23,14 @@ open class Function( val converter: Converter, val name: Identifier, comments: MemberComments, + annotations: List, modifiers: Set, val `type`: Type, val typeParameterList: TypeParameterList, val parameterList: ParameterList, var block: Block?, val isInTrait: Boolean -) : Member(comments, modifiers) { +) : Member(comments, annotations, modifiers) { private fun modifiersToKotlin(): String { val resultingModifiers = ArrayList() @@ -58,11 +59,12 @@ open class Function( override fun toKotlin(): String { return commentsToKotlin() + - modifiersToKotlin() + - "fun ${typeParameterList.toKotlin().withSuffix(" ")}${name.toKotlin()}" + - "(${parameterList.toKotlin()})" + - returnTypeToKotlin() + - typeParameterList.whereToKotlin() + - block?.toKotlin() + annotations.toKotlin() + + modifiersToKotlin() + + "fun ${typeParameterList.toKotlin().withSuffix(" ")}${name.toKotlin()}" + + "(${parameterList.toKotlin()})" + + returnTypeToKotlin() + + typeParameterList.whereToKotlin() + + block?.toKotlin() } } diff --git a/j2k/src/org/jetbrains/jet/j2k/ast/Identifier.kt b/j2k/src/org/jetbrains/jet/j2k/ast/Identifier.kt index 35027e25253..f8e409769c9 100644 --- a/j2k/src/org/jetbrains/jet/j2k/ast/Identifier.kt +++ b/j2k/src/org/jetbrains/jet/j2k/ast/Identifier.kt @@ -36,6 +36,8 @@ class Identifier( private fun quote(str: String): String = "`" + str + "`" + override fun toString() = if (isNullable) "$name?" else name + class object { val Empty = Identifier("") diff --git a/j2k/src/org/jetbrains/jet/j2k/ast/Imports.kt b/j2k/src/org/jetbrains/jet/j2k/ast/Imports.kt index 3219dd22112..c1af1fd9b11 100644 --- a/j2k/src/org/jetbrains/jet/j2k/ast/Imports.kt +++ b/j2k/src/org/jetbrains/jet/j2k/ast/Imports.kt @@ -57,7 +57,7 @@ public fun Converter.convertImport(anImport: PsiImportStatementBase, filter: Boo } private fun filterImport(name: String, ref: PsiJavaCodeReferenceElement): String? { - if (name in NOT_NULL_ANNOTATIONS || name in NULLABLE_ANNOTATIONS) return null + if (name in ANNOTATIONS_TO_REMOVE) return null // If imported class has a kotlin analog, drop the import if (!JavaToKotlinClassMap.getInstance().mapPlatformClass(FqName(name)).isEmpty()) return null diff --git a/j2k/src/org/jetbrains/jet/j2k/ast/Initializer.kt b/j2k/src/org/jetbrains/jet/j2k/ast/Initializer.kt index f6616007bb1..0edf00c85c8 100644 --- a/j2k/src/org/jetbrains/jet/j2k/ast/Initializer.kt +++ b/j2k/src/org/jetbrains/jet/j2k/ast/Initializer.kt @@ -18,7 +18,7 @@ package org.jetbrains.jet.j2k.ast //TODO: is a member? -class Initializer(val block: Block, modifiers: Set) : Member(MemberComments.Empty, modifiers) { +class Initializer(val block: Block, modifiers: Set) : Member(MemberComments.Empty, listOf(), modifiers) { override fun toKotlin(): String { return block.toKotlin() } diff --git a/j2k/src/org/jetbrains/jet/j2k/ast/LocalVariable.kt b/j2k/src/org/jetbrains/jet/j2k/ast/LocalVariable.kt index d40d43fb34f..8b87543957d 100644 --- a/j2k/src/org/jetbrains/jet/j2k/ast/LocalVariable.kt +++ b/j2k/src/org/jetbrains/jet/j2k/ast/LocalVariable.kt @@ -20,6 +20,7 @@ import org.jetbrains.jet.j2k.ConverterSettings class LocalVariable( private val identifier: Identifier, + private val annotations: List, private val modifiers: Set, private val typeCalculator: () -> Type /* we use lazy type calculation for better performance */, private val initializer: Expression, @@ -28,16 +29,16 @@ class LocalVariable( ) : Element { override fun toKotlin(): String { - val varVal = if (isVal) "val" else "var" + val start = annotations.toKotlin() + if (isVal) "val" else "var" return if (initializer.isEmpty) { - "$varVal ${identifier.toKotlin()} : ${typeCalculator().toKotlin()}" + "$start ${identifier.toKotlin()} : ${typeCalculator().toKotlin()}" } else { val shouldSpecifyType = settings.specifyLocalVariableTypeByDefault if (shouldSpecifyType) - "$varVal ${identifier.toKotlin()} : ${typeCalculator().toKotlin()} = ${initializer.toKotlin()}" + "$start ${identifier.toKotlin()} : ${typeCalculator().toKotlin()} = ${initializer.toKotlin()}" else - "$varVal ${identifier.toKotlin()} = ${initializer.toKotlin()}" + "$start ${identifier.toKotlin()} = ${initializer.toKotlin()}" } } } diff --git a/j2k/src/org/jetbrains/jet/j2k/ast/Members.kt b/j2k/src/org/jetbrains/jet/j2k/ast/Members.kt index 5b293c61692..e30440da539 100644 --- a/j2k/src/org/jetbrains/jet/j2k/ast/Members.kt +++ b/j2k/src/org/jetbrains/jet/j2k/ast/Members.kt @@ -30,7 +30,7 @@ class MemberComments(elements: List) : WhiteSpaceSeparatedElementList(e } } -abstract class Member(val comments: MemberComments, val modifiers: Set) : Element { +abstract class Member(val comments: MemberComments, val annotations: List, val modifiers: Set) : Element { fun commentsToKotlin(): String = comments.toKotlin() } diff --git a/j2k/src/org/jetbrains/jet/j2k/ast/Parameter.kt b/j2k/src/org/jetbrains/jet/j2k/ast/Parameter.kt index 3e5758c0c12..770bfb6f06e 100644 --- a/j2k/src/org/jetbrains/jet/j2k/ast/Parameter.kt +++ b/j2k/src/org/jetbrains/jet/j2k/ast/Parameter.kt @@ -16,7 +16,11 @@ package org.jetbrains.jet.j2k.ast -class Parameter(val identifier: Identifier, val `type`: Type, val varVal: Parameter.VarValModifier, val modifiers: Collection) : Element { +class Parameter(val identifier: Identifier, + val `type`: Type, + val varVal: Parameter.VarValModifier, + val annotations: List, + val modifiers: Collection) : Element { public enum class VarValModifier { None Val @@ -26,6 +30,7 @@ class Parameter(val identifier: Identifier, val `type`: Type, val varVal: Parame override fun toKotlin(): String { val builder = StringBuilder() + builder.append(annotations.toKotlin()) builder.append(modifiers.toKotlin()) if (`type` is VarArgType) { diff --git a/j2k/src/org/jetbrains/jet/j2k/ast/Trait.kt b/j2k/src/org/jetbrains/jet/j2k/ast/Trait.kt index 49d03da2868..5f05c2f9f71 100644 --- a/j2k/src/org/jetbrains/jet/j2k/ast/Trait.kt +++ b/j2k/src/org/jetbrains/jet/j2k/ast/Trait.kt @@ -19,18 +19,16 @@ package org.jetbrains.jet.j2k.ast import org.jetbrains.jet.j2k.Converter import java.util.ArrayList -class Trait( - converter: Converter, - name: Identifier, - comments: MemberComments, - modifiers: Set, - typeParameterList: TypeParameterList, - extendsTypes: List, - baseClassParams: List, - implementsTypes: List, - body: ClassBody -) : Class(converter, name, comments, modifiers, typeParameterList, - extendsTypes, baseClassParams, implementsTypes, body) { +class Trait(name: Identifier, + comments: MemberComments, + annotations: List, + modifiers: Set, + typeParameterList: TypeParameterList, + extendsTypes: List, + baseClassParams: List, + implementsTypes: List, + body: ClassBody +) : Class(name, comments, annotations, modifiers, typeParameterList, extendsTypes, baseClassParams, implementsTypes, body) { override val keyword: String get() = "trait" diff --git a/j2k/src/org/jetbrains/jet/j2k/visitors/ElementVisitor.kt b/j2k/src/org/jetbrains/jet/j2k/visitors/ElementVisitor.kt index 5323567154e..8732312f2d4 100644 --- a/j2k/src/org/jetbrains/jet/j2k/visitors/ElementVisitor.kt +++ b/j2k/src/org/jetbrains/jet/j2k/visitors/ElementVisitor.kt @@ -28,11 +28,12 @@ class ElementVisitor(private val converter: Converter) : JavaElementVisitor() { override fun visitLocalVariable(variable: PsiLocalVariable) { result = LocalVariable(Identifier(variable.getName()!!), - converter.convertModifiers(variable), - { typeConverter.convertVariableType(variable) }, - converter.convertExpression(variable.getInitializer(), variable.getType()), - converter.settings.forceLocalVariableImmutability || variable.hasModifierProperty(PsiModifier.FINAL), - converter.settings) + converter.convertAnnotations(variable), + converter.convertModifiers(variable), + { typeConverter.convertVariableType(variable) }, + converter.convertExpression(variable.getInitializer(), variable.getType()), + converter.settings.forceLocalVariableImmutability || variable.hasModifierProperty(PsiModifier.FINAL), + converter.settings) } override fun visitExpressionList(list: PsiExpressionList) { diff --git a/j2k/tests/test/org/jetbrains/jet/j2k/test/AbstractJavaToKotlinConverterTest.kt b/j2k/tests/test/org/jetbrains/jet/j2k/test/AbstractJavaToKotlinConverterTest.kt index d1d2e21ea08..664f8435fb2 100644 --- a/j2k/tests/test/org/jetbrains/jet/j2k/test/AbstractJavaToKotlinConverterTest.kt +++ b/j2k/tests/test/org/jetbrains/jet/j2k/test/AbstractJavaToKotlinConverterTest.kt @@ -41,11 +41,17 @@ abstract class AbstractJavaToKotlinConverterTest() : LightIdeaTestCase() { override fun setUp() { super.setUp() + fun addFile(fileName: String, packageName: String) { + val code = FileUtil.loadFile(File("j2k/tests/testData/$fileName"), true) + val root = LightPlatformTestCase.getSourceRoot()!! + val dir = root.findChild(packageName) ?: root.createChildDirectory(null, packageName) + val file = dir.createChildData(null, fileName)!! + file.getOutputStream(null)!!.writer().use { it.write(code) } + } + ApplicationManager.getApplication()!!.runWriteAction{ - val kotlinApiFileName = "KotlinApi.kt" - val kotlinCode = FileUtil.loadFile(File("j2k/tests/testData/$kotlinApiFileName"), true) - val kotlinFile = LightPlatformTestCase.getSourceRoot()!!.createChildData(null, kotlinApiFileName)!! - kotlinFile.getOutputStream(null)!!.writer().use { it.write(kotlinCode) } + addFile("KotlinApi.kt", "kotlinApi") + addFile("JavaApi.java", "javaApi") } } diff --git a/j2k/tests/test/org/jetbrains/jet/j2k/test/JavaToKotlinConverterTestGenerated.java b/j2k/tests/test/org/jetbrains/jet/j2k/test/JavaToKotlinConverterTestGenerated.java index 21eebb3b267..520ad29f48b 100644 --- a/j2k/tests/test/org/jetbrains/jet/j2k/test/JavaToKotlinConverterTestGenerated.java +++ b/j2k/tests/test/org/jetbrains/jet/j2k/test/JavaToKotlinConverterTestGenerated.java @@ -43,6 +43,11 @@ 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("annotationUsages.java") + public void testAnnotationUsages() throws Exception { + doTest("j2k/tests/testData/ast/annotations/annotationUsages.java"); + } + @TestMetadata("jetbrainsNotNull.java") public void testJetbrainsNotNull() throws Exception { doTest("j2k/tests/testData/ast/annotations/jetbrainsNotNull.java"); diff --git a/j2k/tests/testData/JavaApi.java b/j2k/tests/testData/JavaApi.java new file mode 100644 index 00000000000..fe7d506ad72 --- /dev/null +++ b/j2k/tests/testData/JavaApi.java @@ -0,0 +1,37 @@ +package javaApi; + +public @interface Anon1 { + String[] value(); + String[] stringArray(); + int[] intArray(); + String string(); +} + +public @interface Anon2 { + String value(); + int intValue(); + char charValue(); +} + +public @interface Anon3 { + E e(); + String[] stringArray(); + String[] value(); +} + +public @interface Anon4 { + String[] value(); +} + +public @interface Anon5 { + int value(); +} + +public @interface Anon6 { + String[] value(); + int intValue() default 10; +} + +public enum E { + A, B, C +} diff --git a/j2k/tests/testData/ast/annotations/annotationUsages.java b/j2k/tests/testData/ast/annotations/annotationUsages.java new file mode 100644 index 00000000000..58ff9a6edaf --- /dev/null +++ b/j2k/tests/testData/ast/annotations/annotationUsages.java @@ -0,0 +1,11 @@ +//file +import javaApi.*; + +@Anon1(value = {"a"}, stringArray = {"b"}, intArray = {1, 2}, string = "x") +@Anon2(value = "a", intValue = 1, charValue = 'a') +@Anon3(e = E.A, stringArray = {}, value = {"a", "b"}) +@Anon4({"x", "y"}) +@Anon5(1) +@Anon6({"x", "y"}) +class C { +} diff --git a/j2k/tests/testData/ast/annotations/annotationUsages.kt b/j2k/tests/testData/ast/annotations/annotationUsages.kt new file mode 100644 index 00000000000..8d0d68d9a79 --- /dev/null +++ b/j2k/tests/testData/ast/annotations/annotationUsages.kt @@ -0,0 +1,9 @@ +import javaApi.* + +Anon1(value = array("a"), stringArray = array("b"), intArray = intArray(1, 2), string = "x") +Anon2(value = "a", intValue = 1, charValue = 'a') +Anon3(e = E.A, stringArray = array(), value = *array("a", "b")) +Anon4("x", "y") +Anon5(1) +Anon6(array("x", "y")) +class C() \ No newline at end of file