All Downloads are FREE. Search and download functionalities are using the official Maven repository.

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)
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy