io.micronaut.kotlin.processing.annotation.KotlinAnnotationMetadataBuilder.kt Maven / Gradle / Ivy
/*
* Copyright 2017-2022 original authors
*
* 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
*
* https://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 io.micronaut.kotlin.processing.annotation
import com.google.devtools.ksp.getClassDeclarationByName
import com.google.devtools.ksp.getDeclaredProperties
import com.google.devtools.ksp.isConstructor
import com.google.devtools.ksp.isDefault
import com.google.devtools.ksp.processing.Resolver
import com.google.devtools.ksp.processing.SymbolProcessorEnvironment
import com.google.devtools.ksp.symbol.*
import io.micronaut.context.annotation.Property
import io.micronaut.core.annotation.AnnotationClassValue
import io.micronaut.core.annotation.AnnotationUtil
import io.micronaut.core.annotation.AnnotationValue
import io.micronaut.core.reflect.ReflectionUtils
import io.micronaut.core.util.ArrayUtils
import io.micronaut.core.util.clhm.ConcurrentLinkedHashMap
import io.micronaut.inject.annotation.AbstractAnnotationMetadataBuilder
import io.micronaut.inject.annotation.MutableAnnotationMetadata
import io.micronaut.inject.visitor.VisitorContext
import io.micronaut.kotlin.processing.getBinaryName
import io.micronaut.kotlin.processing.getClassDeclaration
import io.micronaut.kotlin.processing.visitor.KotlinVisitorContext
import java.lang.annotation.RetentionPolicy
import java.lang.reflect.Method
import java.util.*
internal class KotlinAnnotationMetadataBuilder(private val symbolProcessorEnvironment: SymbolProcessorEnvironment,
private val resolver: Resolver,
private val visitorContext: KotlinVisitorContext): AbstractAnnotationMetadataBuilder() {
private val annotationDefaultsCache: ConcurrentLinkedHashMap> =
ConcurrentLinkedHashMap.Builder>().maximumWeightedCapacity(200).build()
companion object {
private fun getTypeForAnnotation(annotationMirror: KSAnnotation, visitorContext: KotlinVisitorContext): KSClassDeclaration {
return annotationMirror.annotationType.resolve().declaration.getClassDeclaration(visitorContext)
}
fun getAnnotationTypeName(annotationMirror: KSAnnotation, visitorContext: KotlinVisitorContext): String {
val type = getTypeForAnnotation(annotationMirror, visitorContext)
return if (type.qualifiedName != null) {
type.qualifiedName!!.asString()
} else {
annotationMirror.shortName.asString()
}
}
}
override fun getTypeForAnnotation(annotationMirror: KSAnnotation): KSClassDeclaration {
return Companion.getTypeForAnnotation(annotationMirror, visitorContext)
}
override fun hasAnnotation(element: KSAnnotated, annotation: Class): Boolean {
return hasAnnotation(element, annotation.name)
}
override fun hasAnnotation(element: KSAnnotated, annotation: String): Boolean {
return element.annotations.map {
it.annotationType.resolve().declaration.qualifiedName
}.any {
it?.asString() == annotation
}
}
override fun hasAnnotations(element: KSAnnotated): Boolean {
return if (element is KSPropertyDeclaration) {
element.annotations.iterator().hasNext() ||
element.getter?.annotations?.iterator()?.hasNext() ?: false
} else {
element.annotations.iterator().hasNext()
}
}
override fun getAnnotationTypeName(annotationMirror: KSAnnotation): String {
return Companion.getAnnotationTypeName(annotationMirror, visitorContext)
}
override fun getElementName(element: KSAnnotated): String {
if (element is KSDeclaration) {
return if (element is KSClassDeclaration) {
element.qualifiedName!!.asString()
} else {
element.simpleName.asString()
}
}
TODO("Not yet implemented")
}
override fun getAnnotationsForType(element: KSAnnotated): MutableList {
val annotationMirrors : MutableList = mutableListOf()
when (element) {
is KSValueParameter -> {
// fuse annotations for setter and property
val parent = element.parent
if (parent is KSPropertySetter) {
val property = parent.parent
if (property is KSPropertyDeclaration) {
annotationMirrors.addAll(property.annotations)
}
annotationMirrors.addAll(parent.annotations)
}
annotationMirrors.addAll(element.annotations)
}
is KSPropertyGetter, is KSPropertySetter -> {
val property = element.parent
if (property is KSPropertyDeclaration) {
annotationMirrors.addAll(property.annotations)
}
annotationMirrors.addAll(element.annotations)
}
is KSPropertyDeclaration -> {
val parent : KSClassDeclaration? = findClassDeclaration(element)
if (parent is KSClassDeclaration) {
if (parent.classKind == ClassKind.ANNOTATION_CLASS) {
annotationMirrors.addAll(element.annotations)
val getter = element.getter
if (getter != null) {
annotationMirrors.addAll(getter.annotations)
}
}
}
annotationMirrors.addAll(element.annotations)
}
else -> {
annotationMirrors.addAll(element.annotations)
}
}
val expanded : MutableList = mutableListOf()
for (ann in annotationMirrors) {
val annotationName = getAnnotationTypeName(ann)
var repeateable = false
var hasOtherMembers = false
for (arg in ann.arguments) {
if ("value" == arg.name?.asString()) {
val value = arg.value
if (value is Iterable<*>) {
for (nested in value) {
if (nested is KSAnnotation) {
val repeatableName = getRepeatableName(nested)
if (repeatableName != null && repeatableName == annotationName) {
expanded.add(nested)
repeateable = true
}
}
}
}
} else {
hasOtherMembers = true
}
}
if (!repeateable || hasOtherMembers) {
expanded.add(ann)
}
}
return expanded
}
private fun findClassDeclaration(element: KSPropertyDeclaration): KSClassDeclaration? {
var parent = element.parent
while (parent != null) {
if (parent is KSClassDeclaration) {
return parent
}
parent = parent.parent
}
return null
}
override fun postProcess(annotationMetadata: MutableAnnotationMetadata, element: KSAnnotated) {
if (element is KSValueParameter && element.type.resolve().isMarkedNullable) {
annotationMetadata.addDeclaredAnnotation(AnnotationUtil.NULLABLE, emptyMap())
} else if (element is KSFunctionDeclaration) {
val markedNullable = element.returnType?.resolve()?.isMarkedNullable
if (markedNullable != null && markedNullable) {
annotationMetadata.addDeclaredAnnotation(AnnotationUtil.NULLABLE, emptyMap())
}
} else if (element is KSPropertyDeclaration) {
val markedNullable = element.type.resolve().isMarkedNullable
if (markedNullable) {
annotationMetadata.addDeclaredAnnotation(AnnotationUtil.NULLABLE, emptyMap())
}
} else if (element is KSPropertySetter) {
if (!annotationMetadata.hasAnnotation(JvmField::class.java) && (annotationMetadata.hasStereotype(AnnotationUtil.QUALIFIER) || annotationMetadata.hasAnnotation(Property::class.java))) {
// implicitly inject
annotationMetadata.addDeclaredAnnotation(AnnotationUtil.INJECT, emptyMap())
}
}
}
override fun buildHierarchy(
element: KSAnnotated,
inheritTypeAnnotations: Boolean,
declaredOnly: Boolean
): MutableList {
if (declaredOnly) {
return mutableListOf(element)
}
when (element) {
is KSValueParameter -> {
val parent = element.parent
return if (parent is KSFunctionDeclaration) {
if (parent.isConstructor()) {
mutableListOf(element)
} else {
val parameters = parent.parameters
val parameterIndex =
parameters.indexOf(parameters.find { it.name!!.asString() == element.name!!.asString() })
methodsHierarchy(parent)
.map { if (it == parent) element else it.parameters[parameterIndex] }
.toMutableList()
}
} else { // Setter
mutableListOf(element)
}
}
is KSClassDeclaration -> {
val hierarchy = mutableListOf()
hierarchy.add(element)
if (element.classKind == ClassKind.ANNOTATION_CLASS) {
return hierarchy
}
populateTypeHierarchy(element, hierarchy)
hierarchy.reverse()
return hierarchy
}
is KSFunctionDeclaration -> {
val methodsHierarchy = methodsHierarchy(element)
val hierarchy = mutableListOf()
hierarchy.addAll(methodsHierarchy)
return hierarchy
}
else -> {
return mutableListOf(element)
}
}
}
private fun methodsHierarchy(element: KSFunctionDeclaration): List =
if (element.isConstructor()) {
listOf(element)
} else {
val hierarchy = mutableListOf(element)
var overriden = element.findOverridee() as KSFunctionDeclaration?
while (overriden != null) {
hierarchy.add(overriden)
overriden = overriden.findOverridee() as KSFunctionDeclaration?
}
hierarchy.reverse()
hierarchy
}
override fun readAnnotationRawValues(
originatingElement: KSAnnotated,
annotationName: String,
member: KSAnnotated,
memberName: String,
annotationValue: Any,
annotationValues: MutableMap
) {
if (!annotationValues.containsKey(memberName)) {
val value = readAnnotationValue(originatingElement, member, annotationName, memberName, annotationValue)
if (value != null) {
validateAnnotationValue(originatingElement, annotationName, member, memberName, value)
annotationValues[memberName] = value
}
}
}
override fun isValidationRequired(member: KSAnnotated?): Boolean {
if (member != null) {
return member.annotations.any {
val name = it.annotationType.resolve().declaration.qualifiedName?.asString()
if (name != null) {
return name.startsWith("jakarta.validation")
} else {
return false
}
}
}
return false
}
override fun addError(originatingElement: KSAnnotated, error: String) {
symbolProcessorEnvironment.logger.error(error, originatingElement)
}
override fun addWarning(originatingElement: KSAnnotated, warning: String) {
symbolProcessorEnvironment.logger.warn(warning, originatingElement)
}
override fun readAnnotationValue(
originatingElement: KSAnnotated,
member: KSAnnotated,
annotationName: String,
memberName: String,
annotationValue: Any
): Any? {
return when (annotationValue) {
is Collection<*> -> {
toArray(annotationValue, originatingElement)
}
is Array<*> -> {
toArray(annotationValue.toList(), originatingElement)
}
else -> {
if (isEvaluatedExpression(annotationValue)) {
return buildEvaluatedExpressionReference(
originatingElement,
annotationName,
memberName,
annotationValue
)
} else {
return readAnnotationValue(originatingElement, annotationValue)
}
}
}
}
private fun toArray(
annotationValue: Collection<*>,
originatingElement: KSAnnotated
): Array? {
var valueType = Any::class.java
val collection = annotationValue.mapNotNull {
val v = readAnnotationValue(originatingElement, it)
if (v != null) {
valueType = v.javaClass
}
v
} // annotation values can't be null
return ArrayUtils.toArray(collection, valueType)
}
override fun readAnnotationDefaultValues(
annotationName: String,
annotationType: KSAnnotated
): MutableMap {
// issue getting default values for an annotation here
// TODO: awful hack due to https://github.com/google/ksp/issues/642 and not being able to access annotation defaults for a type
val classDeclaration = annotationType.getClassDeclaration(visitorContext)
val qualifiedName = classDeclaration.qualifiedName
return if (qualifiedName != null) {
annotationDefaultsCache.computeIfAbsent(qualifiedName.asString()) {
readDefaultValuesReflectively(
classDeclaration,
annotationType,
"getDescriptor",
"getJClass",
"getMethods"
)
}
} else {
mutableMapOf()
}
}
override fun getOriginatingClassName(orginatingElement: KSAnnotated): String? {
val binaryName = if (orginatingElement is KSClassDeclaration) {
orginatingElement.getBinaryName(resolver, visitorContext)
} else {
val classDeclaration = orginatingElement.getClassDeclaration(visitorContext)
classDeclaration.getBinaryName(resolver, visitorContext)
}
return if (binaryName != Object::javaClass.name) {
binaryName
} else {
null
}
}
private fun readDefaultValuesReflectively(classDeclaration : KSClassDeclaration, annotationType: KSAnnotated, vararg path : String): MutableMap {
var o: Any? = findValueReflectively(annotationType, *path)
val declaredProperties = classDeclaration.getDeclaredProperties()
val map = mutableMapOf()
if (o != null) {
if (o is Iterable<*>) {
for (m in o) {
if (m != null) {
val name = findValueReflectively(m, "getName")
// currently only handles JavaLiteralAnnotationArgument but probably should handle others
val value =
findValueReflectively(m, "getAnnotationParameterDefaultValue", "getValue")
if (value != null && name != null) {
val ksPropertyDeclaration = declaredProperties.find { it.simpleName.asString() == name.toString() }
if (ksPropertyDeclaration != null) {
map[ksPropertyDeclaration] = value
}
}
}
}
}
}
return map
}
private fun findValueReflectively(
root: Any,
vararg path : String
): Any? {
var m: Method?
var o: Any = root
for (p in path) {
m = ReflectionUtils.findMethod(o.javaClass, p).orElse(null)
if (m == null) {
return null
} else {
try {
o = m.invoke(o)
if (o == null) {
return null
}
} catch (e: Exception) {
return null
}
}
}
return o
}
override fun readAnnotationRawValues(annotationMirror: KSAnnotation): MutableMap {
val map = mutableMapOf()
val declaration = annotationMirror.annotationType.resolve().declaration.getClassDeclaration(visitorContext)
declaration.getAllProperties().forEach { prop ->
val argument = annotationMirror.arguments.find { it.name == prop.simpleName }
if (argument?.value != null && !argument.isDefault()) {
val value = argument.value!!
map[prop] = value
}
}
return map
}
override fun getAnnotationValues(
originatingElement: KSAnnotated,
member: KSAnnotated?,
annotationType: Class
): Optional> {
val annotationMirrors: MutableList = (member as KSPropertyDeclaration).getter!!.annotations.toMutableList()
annotationMirrors.addAll(member.annotations.toList())
val annotationName = annotationType.name
for (annotationMirror in annotationMirrors) {
if (annotationMirror.annotationType.resolve().declaration.qualifiedName?.asString() == annotationName) {
val values: Map = readAnnotationRawValues(annotationMirror)
val converted: MutableMap = mutableMapOf()
for ((key, value1) in values) {
var value = value1!!
val memberName = key.simpleName.asString()
if (isEvaluatedExpression(value)) {
value = buildEvaluatedExpressionReference(
originatingElement,
annotationName,
memberName,
value
)
}
readAnnotationRawValues(
originatingElement,
annotationName,
key,
memberName,
value,
converted
)
}
return Optional.of(
AnnotationValue.builder(annotationType).members(converted).build()
)
}
}
return Optional.empty()
}
override fun getRepeatableName(annotationMirror: KSAnnotation): String? {
return getRepeatableNameForType(annotationMirror.annotationType)
}
override fun getRepeatableNameForType(annotationType: KSAnnotated): String? {
val name = java.lang.annotation.Repeatable::class.java.name
val repeatable = annotationType.getClassDeclaration(visitorContext).annotations.find {
it.annotationType.resolve().declaration.qualifiedName?.asString() == name
}
if (repeatable != null) {
val value = repeatable.arguments.find { it.name?.asString() == "value" }?.value
if (value != null) {
val declaration = (value as KSType).declaration.getClassDeclaration(visitorContext)
return declaration.getBinaryName(resolver, visitorContext)
}
}
return null
}
override fun getAnnotationMirror(annotationName: String): Optional {
return Optional.ofNullable(resolver.getClassDeclarationByName(annotationName))
}
override fun getAnnotationMember(annotationElement: KSAnnotated, member: CharSequence): KSAnnotated? {
if (annotationElement is KSClassDeclaration) {
return annotationElement.getAllProperties().find { it.simpleName.asString() == member }
}
throw IllegalStateException("Unknown annotation element: $annotationElement")
}
override fun getAnnotationMemberName(member: KSAnnotated): String {
if (member is KSPropertyDeclaration) {
return member.simpleName.asString()
}
throw IllegalStateException("Unknown annotation member element: $member")
}
override fun createVisitorContext(): VisitorContext {
return KotlinVisitorContext(symbolProcessorEnvironment, resolver)
}
override fun getRetentionPolicy(annotation: KSAnnotated): RetentionPolicy {
var retention = annotation.annotations.find {
getAnnotationTypeName(it) == java.lang.annotation.Retention::class.java.name
}
if (retention != null) {
val value = retention.arguments.find { it.name?.asString() == "value" }?.value
if (value is KSType) {
return toRetentionPolicy(value)
}
} else {
retention = annotation.annotations.find {
getAnnotationTypeName(it) == Retention::class.java.name
}
if (retention != null) {
val value = retention.arguments.find { it.name?.asString() == "value" }?.value
if (value is KSType) {
return toJavaRetentionPolicy(value)
}
}
}
return RetentionPolicy.RUNTIME
}
private fun toRetentionPolicy(value: KSType) =
RetentionPolicy.valueOf(value.declaration.qualifiedName!!.getShortName())
private fun toJavaRetentionPolicy(value: KSType) =
when (AnnotationRetention.valueOf(value.declaration.qualifiedName!!.getShortName())) {
AnnotationRetention.RUNTIME -> {
RetentionPolicy.RUNTIME
}
AnnotationRetention.SOURCE -> {
RetentionPolicy.SOURCE
}
AnnotationRetention.BINARY -> {
RetentionPolicy.CLASS
}
}
private fun populateTypeHierarchy(element: KSClassDeclaration, hierarchy: MutableList) {
element.superTypes.forEach {
val t = it.resolve()
if (t != resolver.builtIns.anyType) {
val declaration = t.declaration
if (!hierarchy.contains(declaration)) {
hierarchy.add(declaration)
populateTypeHierarchy(declaration.getClassDeclaration(visitorContext), hierarchy)
}
}
}
}
private fun readAnnotationValue(originatingElement: KSAnnotated, value: Any?): Any? {
if (value == null) {
return null
}
if (value is KSType) {
val declaration = value.declaration
if (declaration is KSClassDeclaration) {
if (declaration.classKind == ClassKind.ENUM_ENTRY) {
return declaration.qualifiedName?.getShortName()
}
if (declaration.classKind == ClassKind.CLASS ||
declaration.classKind == ClassKind.INTERFACE ||
declaration.classKind == ClassKind.ANNOTATION_CLASS) {
return AnnotationClassValue(declaration.getBinaryName(resolver, visitorContext))
}
}
}
if (value is KSAnnotation) {
return readNestedAnnotationValue(originatingElement, value)
}
return value
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy