All Downloads are FREE. Search and download functionalities are using the official Maven repository.
Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.zeoflow.depot.compiler.processing.javac.JavacAnnotationBox.kt Maven / Gradle / Ivy
/*
* Copyright (C) 2021 ZeoFlow SRL
*
* 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 com.zeoflow.depot.compiler.processing.javac
import com.zeoflow.depot.compiler.processing.XAnnotationBox
import com.zeoflow.depot.compiler.processing.XNullability
import com.zeoflow.depot.compiler.processing.XType
import com.google.auto.common.AnnotationMirrors
import java.lang.reflect.Proxy
import javax.lang.model.element.AnnotationMirror
import javax.lang.model.element.AnnotationValue
import javax.lang.model.element.VariableElement
import javax.lang.model.type.TypeMirror
import javax.lang.model.util.SimpleAnnotationValueVisitor6
internal interface JavacClassGetter {
fun getAsType(methodName: String): XType?
fun getAsTypeList(methodName: String): List
fun getAsAnnotationBox(methodName: String): XAnnotationBox
fun getAsAnnotationBoxArray(methodName: String): Array>
}
/**
* Class that helps to read values from annotations. Simple types as string, int, lists can
* be read from [value]. If you need to read classes or another annotations from annotation use
* [getAsType], [getAsAnnotationBox] and [getAsAnnotationBoxArray] correspondingly.
*/
internal class JavacAnnotationBox(obj: Any) : XAnnotationBox {
private val classGetter = obj as JavacClassGetter
@Suppress("UNCHECKED_CAST")
override val value: T = obj as T
override fun getAsType(methodName: String): XType? = classGetter.getAsType(methodName)
override fun getAsTypeList(methodName: String): List =
classGetter.getAsTypeList(methodName)
override fun getAsAnnotationBox(methodName: String): XAnnotationBox {
return classGetter.getAsAnnotationBox(methodName)
}
override fun getAsAnnotationBoxArray(
methodName: String
): Array> {
return classGetter.getAsAnnotationBoxArray(methodName)
}
}
internal fun AnnotationMirror.box(
env: JavacProcessingEnv,
cl: Class
): JavacAnnotationBox {
if (!cl.isAnnotation) {
throw IllegalArgumentException("$cl is not annotation")
}
val map = cl.declaredMethods.associate { method ->
val value = AnnotationMirrors.getAnnotationValue(this, method.name)
val returnType = method.returnType
val defaultValue = method.defaultValue
val result: Any? = when {
returnType == Boolean::class.java -> value.getAsBoolean(defaultValue as Boolean)
returnType == String::class.java -> value.getAsString(defaultValue as String?)
returnType == Array::class.java -> value.getAsStringList().toTypedArray()
returnType == emptyArray>()::class.java -> value.toListOfClassTypes(env)
returnType == IntArray::class.java -> value.getAsIntList().toIntArray()
returnType == Class::class.java -> {
try {
value.toClassType(env)
} catch (notPresent: TypeNotPresentException) {
null
}
}
returnType == Int::class.java -> value.getAsInt(defaultValue as Int?)
returnType.isAnnotation -> {
@Suppress("UNCHECKED_CAST")
AnnotationClassVisitor(env, returnType as Class).visit(value)
}
returnType.isArray && returnType.componentType.isAnnotation -> {
@Suppress("UNCHECKED_CAST")
AnnotationListVisitor(env, returnType.componentType as Class)
.visit(value)
}
returnType.isArray && returnType.componentType.isEnum -> {
@Suppress("UNCHECKED_CAST")
EnumListVisitor(returnType.componentType as Class>).visit(value)
}
returnType.isEnum -> {
@Suppress("UNCHECKED_CAST")
value.getAsEnum(returnType as Class>)
}
else -> {
throw UnsupportedOperationException("$returnType isn't supported")
}
}
method.name to result
}
return JavacAnnotationBox(
Proxy.newProxyInstance(
JavacClassGetter::class.java.classLoader,
arrayOf(cl, JavacClassGetter::class.java)
) { _, method, args ->
when (method.name) {
JavacClassGetter::getAsType.name -> map[args[0]]
JavacClassGetter::getAsTypeList.name -> map[args[0]]
"getAsAnnotationBox" -> map[args[0]]
"getAsAnnotationBoxArray" -> map[args[0]]
else -> map[method.name]
}
}
)
}
@Suppress("DEPRECATION")
private val ANNOTATION_VALUE_TO_INT_VISITOR = object : SimpleAnnotationValueVisitor6() {
override fun visitInt(i: Int, p: Void?): Int? {
return i
}
}
@Suppress("DEPRECATION")
private val ANNOTATION_VALUE_TO_BOOLEAN_VISITOR = object :
SimpleAnnotationValueVisitor6() {
override fun visitBoolean(b: Boolean, p: Void?): Boolean? {
return b
}
}
@Suppress("DEPRECATION")
private val ANNOTATION_VALUE_TO_STRING_VISITOR = object :
SimpleAnnotationValueVisitor6() {
override fun visitString(s: String?, p: Void?): String? {
return s
}
}
@Suppress("DEPRECATION")
private val ANNOTATION_VALUE_STRING_ARR_VISITOR = object :
SimpleAnnotationValueVisitor6, Void>() {
override fun visitArray(vals: MutableList?, p: Void?): List {
return vals?.mapNotNull {
ANNOTATION_VALUE_TO_STRING_VISITOR.visit(it)
} ?: emptyList()
}
}
@Suppress("DEPRECATION")
private val ANNOTATION_VALUE_INT_ARR_VISITOR = object :
SimpleAnnotationValueVisitor6, Void>() {
override fun visitArray(vals: MutableList?, p: Void?): List {
return vals?.mapNotNull {
ANNOTATION_VALUE_TO_INT_VISITOR.visit(it)
} ?: emptyList()
}
}
private fun AnnotationValue.getAsInt(def: Int? = null): Int? {
return ANNOTATION_VALUE_TO_INT_VISITOR.visit(this) ?: def
}
private fun AnnotationValue.getAsIntList(): List {
return ANNOTATION_VALUE_INT_ARR_VISITOR.visit(this)
}
private fun AnnotationValue.getAsString(def: String? = null): String? {
return ANNOTATION_VALUE_TO_STRING_VISITOR.visit(this) ?: def
}
private fun AnnotationValue.getAsBoolean(def: Boolean): Boolean {
return ANNOTATION_VALUE_TO_BOOLEAN_VISITOR.visit(this) ?: def
}
private fun AnnotationValue.getAsStringList(): List {
return ANNOTATION_VALUE_STRING_ARR_VISITOR.visit(this)
}
// code below taken from dagger2
// compiler/src/main/java/dagger/internal/codegen/ConfigurationAnnotations.java
@Suppress("DEPRECATION")
private val TO_LIST_OF_TYPES = object :
SimpleAnnotationValueVisitor6, Void?>() {
override fun visitArray(values: MutableList?, p: Void?): List {
return values?.mapNotNull {
val tmp = TO_TYPE.visit(it)
tmp
} ?: emptyList()
}
override fun defaultAction(o: Any?, p: Void?): List? {
return emptyList()
}
}
@Suppress("DEPRECATION")
private val TO_TYPE = object : SimpleAnnotationValueVisitor6() {
override fun visitType(t: TypeMirror, p: Void?): TypeMirror {
return t
}
override fun defaultAction(o: Any?, p: Void?): TypeMirror {
throw TypeNotPresentException(o!!.toString(), null)
}
}
private fun AnnotationValue.toListOfClassTypes(env: JavacProcessingEnv): List {
return TO_LIST_OF_TYPES.visit(this).map {
env.wrap(
typeMirror = it,
kotlinType = null,
elementNullability = XNullability.UNKNOWN
)
}
}
private fun AnnotationValue.toClassType(env: JavacProcessingEnv): XType? {
return TO_TYPE.visit(this)?.let {
env.wrap(
typeMirror = it,
kotlinType = null,
elementNullability = XNullability.UNKNOWN
)
}
}
@Suppress("DEPRECATION")
private class AnnotationListVisitor(
private val env: JavacProcessingEnv,
private val annotationClass: Class
) :
SimpleAnnotationValueVisitor6>, Void?>() {
override fun visitArray(
values: MutableList?,
void: Void?
): Array> {
val visitor = AnnotationClassVisitor(env, annotationClass)
return values?.mapNotNull { visitor.visit(it) }?.toTypedArray() ?: emptyArray()
}
}
@Suppress("DEPRECATION")
private class EnumListVisitor>(private val enumClass: Class) :
SimpleAnnotationValueVisitor6, Void?>() {
override fun visitArray(
values: MutableList?,
void: Void?
): Array {
val result = values?.map { it.getAsEnum(enumClass) }
@Suppress("UNCHECKED_CAST")
val resultArray = java.lang.reflect.Array
.newInstance(enumClass, result?.size ?: 0) as Array
result?.forEachIndexed { index, value ->
resultArray[index] = value
}
return resultArray
}
}
@Suppress("DEPRECATION")
private class AnnotationClassVisitor(
private val env: JavacProcessingEnv,
private val annotationClass: Class
) :
SimpleAnnotationValueVisitor6?, Void?>() {
override fun visitAnnotation(a: AnnotationMirror?, v: Void?) = a?.box(env, annotationClass)
}
@Suppress("UNCHECKED_CAST", "DEPRECATION", "BanUncheckedReflection")
private fun > AnnotationValue.getAsEnum(enumClass: Class): T {
return object : SimpleAnnotationValueVisitor6() {
override fun visitEnumConstant(value: VariableElement?, p: Void?): T {
return enumClass.getDeclaredMethod("valueOf", String::class.java)
.invoke(null, value!!.simpleName.toString()) as T
}
}.visit(this)
}