Is it possible to pass a value from a meta annotation? - kotlin

Let's say I have an annotation like this:
#Target(AnnotationTarget.FUNCTION)
#Retention(AnnotationRetention.RUNTIME)
#EnumSource(value = MyEnum::class, mode = EnumSource.Mode.EXCLUDE)
annotation class TestEachValue
Is is possible to pass a value from my annotation class to one of the annotations on it? Something like:
#Target(AnnotationTarget.FUNCTION)
#Retention(AnnotationRetention.RUNTIME)
#EnumSource(value = MyEnum::class, mode = EnumSource.Mode.EXCLUDE, names=excludes_from_below)
annotation class TestEachValue(val excludes: Array<String>)
I would be willing to wrap the value in an annotation if that helps. Or maybe Kotlin has some magic comparable to the inline keyword? Any advice on how this can be done nicely would be greatly appreciated.

Related

Getting annotation of enum value

I know how to get an annotation of an enum value in Java.
However Kotlin has its own reflection library and I feel there should be a better way to do the job.
Could please anybody post an example.
Just to be specific let's define an enum class
enum class Enum {
#SerialName("constant")
Constant
}
I need a function f(e: Enum): String so that f(Enum.Constant) == "constant".
You can use a similar approach with java by getting the field by name and then reaching out to the annotation using annotation class.
So if you have below enum and annotation class definitions;
enum class Enum {
#SerialName("constant")
Constant
}
annotation class SerialName(val value: String)
Then you can define the below function and call it as shown below;
fun getAnnotationValue(enum:Enum):String = enum.declaringClass.getField(enum.name).getAnnotation(SerialName::class.java).value
fun main(args: Array<String>) {
println(getAnnotationValue(Enum.Constant))
}
Hope this helps.
I did only a little research here, but it appears there isn't support for this in the Kotlin reflection library. In fact, I discovered the linter doesn't even correctly suggest an annotation target of FIELD for your annotation if you give it one that doesn't work for Enum values, and instead incorrectly offers to automatically add a target of CLASS.
The problem is that Enum values are basically static member fields, which don't exist in Kotlin except in Enum classes. And the reflection classes don't seem to provide a way to access this special case.
I am struggling however to come up with a use case for Enum value annotations that can't be solved using properties in the Enum constructor(s).
enum class MyEnum(val someConstant: String? = null) {
SomeValue("myConstant")
}

Kotlin annotation only for annotation parameters

I'd like to have a Kotlin annotation that can only be used as a parameter on another annotation. Which target should I use for it?
// #Target <- ?
annotation class MyConfigurationEntry(
val option,
val value
)
#Target(AnnotationTarget.FUNCTION)
annotation class MyConfiguration(vararg val entries: MyConfigurationEntry)
I believe it is #Target(AnnotationTarget.ANNOTATION_CLASS) if you click on that constant you will read:
Annotation class only
Kotlin version 1.3.41

How to filter data class properties by kotlin annotation?

Implimentation of Annotation
#Target(AnnotationTarget.PROPERTY)
#Retention(AnnotationRetention.RUNTIME)
annotation class Returnable
Dummy Data class
data class DataClass(
val property: String
#Returnable
val annotatedProperty: String
)
Java Reflections filtering doesn't work
this::class.memberProperties
.filter{ it.annotations.map { ann -> ann.annotationClass }.contains(Returnable::class)}
Kotlin annotation isn't the same as Java annotations. So work with Kotlin reflection requires a bit different way compare to classic java. Here you can find a way of filtering properties of Kotlin data class by Kotlin annotations
DataClass("false","true")::class.members.filter {
it.findAnnotation<Returnable>() != null
}

Runtime annotations annotated on a filed in kotlin class are not generated correctly

Kotlin compiler remove the Java runtime annotation annotated on a field.The annotation is shown below.
#Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD, ElementType.FIELD, ElementType.TYPE, ElementType.PARAMETER})
#Retention(RetentionPolicy.RUNTIME)
#com.fasterxml.jackson.annotation.JacksonAnnotation
public #interface JsonDeserialize
I declared it on a field, as seen below.
#JsonSerialize(using = IDEncryptJsonSerializer::class)
#JsonDeserialize(using = IDDecryptJsonDeserializer::class)
#Column(name = "sku_id", nullable = false)
open var skuId: Long = 0L
The annotation doesn't work. Then, I take a fist look at the class file, as seen below.
#field:javax.persistence.Column public open var skuId: kotlin.Long
The JsonDeserialize and JsonSerialize annotation are dismiss.
The two annotations are work well in Java.
My kotlin version is 1.1.4.
How can I fix the problem?
Finally, I found the reason that result in the phenomenon.
If I declare a variable in class constructor, some of annotations annotate on that variable may cannot be compiled correctly.
Some of annotations may be lost because of kotlin compiler bug.
Then, I move the variable in the class body. Everything work well.

What is legitimate way to get annotations of a pure Kotlin property via reflection, are they always missing?

I'm trying to get annotations from Kotlin data class
package some.meaningless.package.name
import kotlin.reflect.full.memberProperties
annotation class MyAnnotation()
#MyAnnotation
data class TestDto(#MyAnnotation val answer: Int = 42)
fun main(args: Array<String>) {
TestDto::class.memberProperties.forEach { p -> println(p.annotations) }
println(TestDto::class.annotations)
}
I need to process class annotation to make a custom name serialization of GSON however no matter how I declare annotation class it never gets detected
The program always outputs
[]
[#some.meaningless.package.name.MyAnnotation()]
which means only class level annotations are present
Ok,
it seems that the culprit was, that Kotlin annotations have default #Target(AnnotationTarget.CLASS) which is not stressed enough in documentation.
After I added #Target to the annotation class it now works properly
#Target(AnnotationTarget.CLASS, AnnotationTarget.PROPERTY)
annotation class MyAnnotation()
Now it prints out
[#some.meaningless.package.name.MyAnnotation()]
[#some.meaningless.package.name.MyAnnotation()]
As a side affect it will force the compiler to check that the annotation is applied as required, in current version of Kotlin, if explicit #Targetis not present only class level annotations are kept but no validity checks performed.
As Kotlin reference said as below:
If you don't specify a use-site target, the target is chosen according to the #Target annotation of the annotation being used. If there are multiple applicable targets, the first applicable target from the following: param > property > field.
To make the annotation annotated on a property, you should use site target, for example:
#MyAnnotation
data class TestDto(#property:MyAnnotation val answer: Int = 42)
However, annotations with property target in Kotlin are not visible to Java, so you should double the annotation, for example:
#MyAnnotation // v--- used for property v--- used for params in Java
data class TestDto(#property:MyAnnotation #MyAnnotation val answer: Int = 42)