How to add annotation on main constructor parameters in kotlin - kotlin

Having the following class:
data class TestMsg(
#Parse(";")
val someArray: Array<String>
)
And trying to get the annotation with
TestMsg::class.primaryConstructor!!.parameters.forEach{
println(it.findAnnotation<Parse>())
}
There is no annotation found. I had to move the annotation front of the parameter for make it working
data class TestMsg(
#Parse(";") val someArray: Array<String>
)
is it a parsing error of jetbrains or is it the normal behavior?
EDIT
You can find the annotation right here:
#Target(AnnotationTarget.FIELD, AnnotationTarget.PROPERTY, AnnotationTarget.TYPE_PARAMETER)
#Retention(AnnotationRetention.RUNTIME)
annotation class Parse(
val delimiter: String
)

change AnnotationTarget.PROPERTY to AnnotationTarget.VALUE_PARAMETER
#Target(AnnotationTarget.FIELD, AnnotationTarget.VALUE_PARAMETER, AnnotationTarget.TYPE_PARAMETER)
#Retention(AnnotationRetention.RUNTIME)
annotation class Parse(
val delimiter: String
)

Related

How to read kotlin annotation

I have annotation classes
annotation class Endpoint(val name: String)
#Target(AnnotationTarget.TYPE)
annotation class JsonObjectKey(val name: String)
These are used like so
#Endpoint("my-endpoint")
fun myEndpoint(): #JsonObjectKey("key") String {
return "value"
}
With a method: java.lang.reflect.Method object representing myEndpoint,
I am able to access the Endpoint-Annotation, but I fail to access the JsonObjectKey-Annotatation.
method.annotations only contains Endpoint
method.annotatedReturnType.annotations is empty
How to read JsonObjectKey (key in this scenario)?
Intention is to generate JsonObject {"key": "value"}
Use the Kotlin reflection API!
For example, this works:
fun main() {
println(::myEndpoint.returnType.annotations)
}
#Target(AnnotationTarget.TYPE)
annotation class JsonObjectKey(val name: String)
fun myEndpoint(): #JsonObjectKey("key") String {
return "value"
}
This outputs:
[#JsonObjectKey(name=key)]
If you only have a java.lang.reflect.Method for some reason, you can convert it to a KFunction using kotlinFunction:
println(someJavaMethod?.kotlinFunction?.returnType?.annotations)
It appears that annotations annotated in this position in Java:
public static #JsonObjectKey(name = "foo") String foo()
are very different from those annotated in this position in Kotlin:
fun foo(): #JsonObjectKey("key") String = ""
These seem to be two different positions. Kotlin reflection cannot see the annotation on the Java return type, and Java reflection cannot see the annotation on the Kotlin return type.
Compare how the annotations appear in the class file. For the Java method, the annotation shows up in the RuntimeVisibleTypeAnnotations attribute:
RuntimeVisibleTypeAnnotations:
0: #23(#24=s#20): METHOD_RETURN
JsonObjectKey(
name="foo"
)
On the other hand, the Kotlin method instead has the annotation stored as part of the kotlin.Metadata annotation:
RuntimeVisibleAnnotations:
0: #86(#87=[I#88,I#89,I#90],#91=I#92,#93=I#94,#95=[s#96],#97=[s#5,s#98,s#76,s#98,s#99,s#100,s#101,s#102])
kotlin.Metadata(
mv=[1,6,0]
k=2
xi=48
d1=["..."]
d2=["main","","myEndpoint","","LJsonObjectKey;","name","key","myproject"]
)

Can you define alternative shorthands for Kotlin Annotations?

I am using annotations and reflection to create a parser for some custom made files used in the project I work with
I have this annotation that will be used to annotate most data class constructor parameters
annotation class Element(val name: String = "",val type: ElementType = ElementType.Value)
the enum ElementType has these values
enum class XElementType {
Value,
Attribute,
Ignore
}
is there a way to create a shorthand or alternate so that instead of using
#Element(type=ElementType.Ignore)
val ignoredVariable:String
I can use something like
#IgnoreElement
val ignoredVariable:String
which will resolve to Element("",ElementType.Ignore) ?

Acquire annotation parameters (or annotation instance) from Kotlin PSI

I have a Kotlin annotation:
#Retention(AnnotationRetention.SOURCE)
#Target(AnnotationTarget.CLASS)
annotation class Type(
val type: String
)
It can be used on the Kotlin classes in two ways: using the named parameter syntax, or using the positional parameter syntax:
#Type(type = "named")
data class Named(
…
)
#Type("positional")
data class Positional
…
)
I use this annotation in my custom detekt rules for some extra checks. I need to extract the value of the type parameter to perform some check based on it. I do that like:
private fun getType(klass: KtClass): String? {
val annotation = klass
.annotationEntries
.find {
"Type" == it?.shortName?.asString()
}
val type = (annotation
?.valueArguments
?.find {
it.getArgumentName()?.asName?.asString() == "type"
}
?.getArgumentExpression() as? KtStringTemplateExpression)
?.takeUnless { it.hasInterpolation() }
?.plainContent
return type
}
But this code works only with "named" parameters syntax, and fails for the positional one. Is there any way to get the value of an annotation parameter no matter what syntax was used? It would be perfect if I could acquire my Type annotation instance directly from PSI / AST / KtElements and use it like usually. Is it possible to instantiate an annotation from the PSI tree?

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
}