Java to Kotlin annotation target mapping: relevant fix for J2K
This commit is contained in:
@@ -20,13 +20,21 @@ import com.intellij.codeInsight.NullableNotNullManager
|
|||||||
import com.intellij.openapi.util.text.StringUtil
|
import com.intellij.openapi.util.text.StringUtil
|
||||||
import com.intellij.psi.*
|
import com.intellij.psi.*
|
||||||
import com.intellij.psi.javadoc.PsiDocTag
|
import com.intellij.psi.javadoc.PsiDocTag
|
||||||
|
import org.jetbrains.kotlin.descriptors.annotations.AnnotationTarget
|
||||||
import org.jetbrains.kotlin.j2k.ast.*
|
import org.jetbrains.kotlin.j2k.ast.*
|
||||||
import org.jetbrains.kotlin.j2k.ast.Annotation
|
import org.jetbrains.kotlin.j2k.ast.Annotation
|
||||||
|
import org.jetbrains.kotlin.load.java.components.JavaAnnotationTargetMapper
|
||||||
|
import java.lang.annotation.ElementType
|
||||||
|
import java.lang.annotation.Target
|
||||||
|
|
||||||
class AnnotationConverter(private val converter: Converter) {
|
class AnnotationConverter(private val converter: Converter) {
|
||||||
public val annotationsToRemove: Set<String> = (NullableNotNullManager.getInstance(converter.project).getNotNulls()
|
private val annotationsToRemove: Set<String> = (NullableNotNullManager.getInstance(converter.project).getNotNulls()
|
||||||
+ NullableNotNullManager.getInstance(converter.project).getNullables()
|
+ NullableNotNullManager.getInstance(converter.project).getNullables()
|
||||||
+ listOf(CommonClassNames.JAVA_LANG_OVERRIDE)).toSet()
|
+ listOf(CommonClassNames.JAVA_LANG_OVERRIDE, javaClass<ElementType>().name)).toSet()
|
||||||
|
|
||||||
|
public fun isImportRequired(annotationName: String): Boolean {
|
||||||
|
return annotationName in annotationsToRemove || annotationName == javaClass<Target>().name
|
||||||
|
}
|
||||||
|
|
||||||
public fun convertAnnotations(owner: PsiModifierListOwner): Annotations
|
public fun convertAnnotations(owner: PsiModifierListOwner): Annotations
|
||||||
= convertAnnotationsOnly(owner) + convertModifiersToAnnotations(owner)
|
= convertAnnotationsOnly(owner) + convertModifiersToAnnotations(owner)
|
||||||
@@ -88,12 +96,42 @@ class AnnotationConverter(private val converter: Converter) {
|
|||||||
PsiModifier.TRANSIENT to "transient"
|
PsiModifier.TRANSIENT to "transient"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
private fun mapTargetByName(expr: PsiReferenceExpression): Set<AnnotationTarget> {
|
||||||
|
return expr.referenceName?.let { JavaAnnotationTargetMapper.mapJavaTargetArgumentByName(it) } ?: emptySet()
|
||||||
|
}
|
||||||
|
|
||||||
public fun convertAnnotation(annotation: PsiAnnotation, withAt: Boolean, newLineAfter: Boolean): Annotation? {
|
public fun convertAnnotation(annotation: PsiAnnotation, withAt: Boolean, newLineAfter: Boolean): Annotation? {
|
||||||
val qualifiedName = annotation.getQualifiedName()
|
val qualifiedName = annotation.getQualifiedName()
|
||||||
if (qualifiedName == CommonClassNames.JAVA_LANG_DEPRECATED && annotation.getParameterList().getAttributes().isEmpty()) {
|
if (qualifiedName == CommonClassNames.JAVA_LANG_DEPRECATED && annotation.getParameterList().getAttributes().isEmpty()) {
|
||||||
val deferredExpression = converter.deferredElement<Expression> { LiteralExpression("\"\"").assignNoPrototype() }
|
val deferredExpression = converter.deferredElement<Expression> { LiteralExpression("\"\"").assignNoPrototype() }
|
||||||
return Annotation(Identifier("deprecated").assignNoPrototype(), listOf(null to deferredExpression), withAt, newLineAfter).assignPrototype(annotation) //TODO: insert comment
|
return Annotation(Identifier("deprecated").assignNoPrototype(), listOf(null to deferredExpression), withAt, newLineAfter).assignPrototype(annotation) //TODO: insert comment
|
||||||
}
|
}
|
||||||
|
if (qualifiedName == CommonClassNames.JAVA_LANG_ANNOTATION_TARGET) {
|
||||||
|
val attributes = annotation.parameterList.attributes
|
||||||
|
val arguments: Set<AnnotationTarget>
|
||||||
|
if (attributes.isEmpty()) {
|
||||||
|
arguments = setOf<AnnotationTarget>()
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
val value = attributes[0].value
|
||||||
|
arguments = when (value) {
|
||||||
|
is PsiArrayInitializerMemberValue -> value.getInitializers().filterIsInstance<PsiReferenceExpression>()
|
||||||
|
.flatMap { mapTargetByName(it) }
|
||||||
|
.toSet()
|
||||||
|
is PsiReferenceExpression -> mapTargetByName(value)
|
||||||
|
else -> setOf<AnnotationTarget>()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val deferredExpressionList = arguments.map {
|
||||||
|
val name = it.name()
|
||||||
|
null to converter.deferredElement<Expression> {
|
||||||
|
QualifiedExpression(Identifier("AnnotationTarget", false).assignNoPrototype(),
|
||||||
|
Identifier(name, false).assignNoPrototype())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Annotation(Identifier("target").assignNoPrototype(),
|
||||||
|
deferredExpressionList, withAt, newLineAfter).assignPrototype(annotation)
|
||||||
|
}
|
||||||
|
|
||||||
val nameRef = annotation.getNameReferenceElement()
|
val nameRef = annotation.getNameReferenceElement()
|
||||||
val name = Identifier((nameRef ?: return null).getText()!!).assignPrototype(nameRef)
|
val name = Identifier((nameRef ?: return null).getText()!!).assignPrototype(nameRef)
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ public fun Converter.convertImport(anImport: PsiImportStatementBase, filter: Boo
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun Converter.filterImport(name: String, ref: PsiJavaCodeReferenceElement): String? {
|
private fun Converter.filterImport(name: String, ref: PsiJavaCodeReferenceElement): String? {
|
||||||
if (name in annotationConverter.annotationsToRemove) return null
|
if (annotationConverter.isImportRequired(name)) return null
|
||||||
|
|
||||||
// If imported class has a kotlin analog, drop the import
|
// If imported class has a kotlin analog, drop the import
|
||||||
if (!JavaToKotlinClassMap.INSTANCE.mapPlatformClass(FqName(name)).isEmpty()) return null
|
if (!JavaToKotlinClassMap.INSTANCE.mapPlatformClass(FqName(name)).isEmpty()) return null
|
||||||
|
|||||||
@@ -14,3 +14,11 @@ import java.lang.annotation.Target;
|
|||||||
@Target({ElementType.CONSTRUCTOR, ElementType.FIELD})
|
@Target({ElementType.CONSTRUCTOR, ElementType.FIELD})
|
||||||
@interface I {
|
@interface I {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Target(ElementType.METHOD)
|
||||||
|
@interface J {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Target({})
|
||||||
|
@interface K {
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
import java.lang.annotation.ElementType
|
|
||||||
import java.lang.annotation.Target
|
|
||||||
|
|
||||||
annotation class Anon(public val stringArray: Array<String>, public val intArray: IntArray, // string
|
annotation class Anon(public val stringArray: Array<String>, public val intArray: IntArray, // string
|
||||||
public val string: String)
|
public val string: String)
|
||||||
|
|
||||||
Anon(string = "a", stringArray = arrayOf("a", "b"), intArray = intArrayOf(1, 2))
|
Anon(string = "a", stringArray = arrayOf("a", "b"), intArray = intArrayOf(1, 2))
|
||||||
Target(ElementType.CONSTRUCTOR, ElementType.FIELD)
|
target(AnnotationTarget.CONSTRUCTOR, AnnotationTarget.FIELD)
|
||||||
annotation class I
|
annotation class I
|
||||||
|
|
||||||
|
target(AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER)
|
||||||
|
annotation class J
|
||||||
|
|
||||||
|
target
|
||||||
|
annotation class K
|
||||||
|
|||||||
Reference in New Issue
Block a user