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

com.driver733.mapstructfluent.GenericProcessor.kt Maven / Gradle / Ivy

The newest version!
package com.driver733.mapstructfluent

import com.squareup.kotlinpoet.FunSpec
import org.mapstruct.AfterMapping
import org.mapstruct.BeforeMapping
import org.mapstruct.Mapper
import java.io.File
import javax.annotation.processing.ProcessingEnvironment
import javax.annotation.processing.RoundEnvironment
import javax.lang.model.element.ElementKind
import javax.lang.model.element.ExecutableElement
import javax.lang.model.element.Modifier.ABSTRACT
import javax.lang.model.element.Modifier.PUBLIC
import javax.lang.model.element.VariableElement
import javax.tools.Diagnostic

class GenericProcessor {

    fun process(roundEnv: RoundEnvironment?, procEnv: ProcessingEnvironment, processor: MapperMethodProcessor) =
        generatedSourcesDirPath(procEnv)
            .also { if (it == null) return false }
            .let { src -> findAndProcessMappers(roundEnv, src, processor) }
            .let { true }

    private fun generatedSourcesDirPath(env: ProcessingEnvironment) =
        env.options[KAPT_KOTLIN_GENERATED_OPTION_NAME]
            .let {
                if (it == null) {
                    env.messager.printMessage(
                        Diagnostic.Kind.ERROR,
                        "Can't find the target directory for generated Kotlin files"
                    )
                    null
                } else {
                    it
                }
            }

    private fun findAndProcessMappers(
        roundEnv: RoundEnvironment?,
        src: String?,
        processor: MapperMethodProcessor
    ) =
        roundEnv
            ?.getElementsAnnotatedWith(Mapper::class.java)
            ?.forEach { mapper ->
                mapper
                    .takeIf { it.modifiers.contains(ABSTRACT) }
                    ?.enclosedElements
                    ?.filter { it.kind == ElementKind.METHOD }
                    ?.map { it as ExecutableElement }
                    ?.filter { it.modifiers.contains(PUBLIC) }
                    ?.filter(b())
                    ?.also { methods ->
                        val fileSpecBuilder = processor.fileSpecBuilder(mapper)

                        methods.forEach { method ->
                            val receiver = method.parameters.first().kotlinType()
                            val otherParameters = method.parameters.drop(1).map(VariableElement::toParameterSpec)

                            processor.process(fileSpecBuilder, method, mapper)

                            fileSpecBuilder
                                .addFunction(
                                    FunSpec
                                        .builder("${method.simpleName}")
                                        .receiver(receiver)
                                        .addParameters(otherParameters)
                                        .addStatement(
                                            "return mapper.${method.simpleName}(this${otherParameters.joinToString()})"
                                        )
                                        .build()
                                )
                                .build()
                                .writeTo(File(src!!).apply { mkdir() })
                        }
                    }
            }

    private fun b() = { it: ExecutableElement ->
        it.modifiers.contains(ABSTRACT) || it.hasNotAnnotations(
            BeforeMapping::class.java,
            AfterMapping::class.java
        )
    }

    private fun  ExecutableElement.hasNotAnnotations(vararg classes: Class) =
        classes.all {
            hasNotAnnotation(it)
        }

    private fun  ExecutableElement.hasNotAnnotation(clazz: Class) =
        !hasAnnotation(clazz)

    private fun  ExecutableElement.hasAnnotation(clazz: Class) =
        getAnnotation(clazz) != null
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy