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

meta.rendering.DataClassRenderer.kt Maven / Gradle / Ivy

The newest version!
package de.peekandpoke.ultra.mutator.meta.rendering

import com.squareup.kotlinpoet.ClassName
import de.peekandpoke.ultra.meta.KotlinPrinter
import de.peekandpoke.ultra.meta.ProcessorUtils
import de.peekandpoke.ultra.meta.model.MType
import de.peekandpoke.ultra.meta.model.MVariable

class DataClassRenderer(

    override val ctx: ProcessorUtils.Context,
    private val classType: MType,
    private val renderers: PropertyRenderers

) : ProcessorUtils {

    private fun List.filtered(): List =
        // filter delegated properties (e.g. by lazy)
        filter { !it.isDelegate }
            // we only look at public properties
            .filter { classType.hasPublicGetterFor(it) }

    @Suppress("Detekt:LongMethod")
    private fun KotlinPrinter.renderFor(target: MType, mutatorClassName: ClassName) {

        // Get the short name for the mutator class
        val mutatorClassShort = mutatorClassName.simpleNames.joinToString("_")
        // Get the name for @JVMName annotation
        val jvmName = mutatorClassShort.replace("[^0-9a-zA-Z]+".toRegex(), "")
        // Import the target type
        val imported = target.import()

        // get all super interfaces
        val superMutators = target.directSuperTypes
            .filter { it.isInterface }
            .map { it.mutatorClassName.import() }
            .sorted()

        // create a template string from the super interfaces
        val superExtendsStr = when (superMutators.isEmpty()) {
            true -> ""
            else -> "${superMutators.joinToString(", ")} "
        }

        block(
            """
                @JvmName("mutate$jvmName")
                fun $imported.mutate(mutation: $mutatorClassShort.() -> Unit) = 
                    mutator({ x: $imported -> Unit }).apply(mutation).getResult()

            """.trimIndent()
        )

        when (target.isInterface) {
            //  Render an interface of a mutator  //////////////////////////////////////////////////////////////////////
            true -> {
                block(
                    """
                        @JvmName("mutator$jvmName")
                        fun $imported.mutator(onModify: OnModify<$imported> = {}): $mutatorClassShort = when (this) {
                    """.trimIndent()
                )

                indent {
                    target.directChildTypes.sortedBy { it.import() }.forEach {

                        val typeImported = it.import()
                        // TODO: code gen tests for mutator function in different package
                        val mutatorImported = it.import("mutator")

                        block(
                            """
                                is $typeImported -> $mutatorImported(onModify as OnModify<$typeImported>)
                            """.trimIndent()
                        )
                    }

                    block(
                        """
                            else -> error("Unknown child type ${"$"}{this::class}")
                        """.trimIndent()
                    )
                }

                // TODO: code gen tests for "override"
                block(
                    """
                        }
                        
                        interface $mutatorClassShort ${if (superExtendsStr.isNotEmpty()) ": $superExtendsStr" else ""}{
                            ${if (superExtendsStr.isNotEmpty()) "override " else ""}fun getResult(): $imported 
                        }
                        
                    """.trimIndent()
                )
            }

            //  Render a mutator class  ////////////////////////////////////////////////////////////////////////////////

            false -> {
                block(
                    """
                        @JvmName("mutator$jvmName")
                        fun $imported.mutator(onModify: OnModify<$imported> = {}) = 
                            $mutatorClassShort(this, onModify)
        
                        class $mutatorClassShort(
                            target: $imported, 
                            onModify: OnModify<$imported> = {}
                        ) : DataClassMutator<$imported>(target, onModify)${if (superExtendsStr.isNotEmpty()) ", $superExtendsStr" else ""} {
                        
                    """.trimIndent()
                )

                indent {
                    target.variables.filtered().forEach {
                        renderers.run { renderPropertyImplementation(it) }
                    }
                }

                append("}").newline()
            }
        }
    }

    fun render(printer: KotlinPrinter) = with(printer) {

        when {
            !classType.isParameterized ->
                renderFor(classType, classType.mutatorClassName)

            else ->
                classType.genericUsages.forEach { type ->

                    val mutatorClassName = type.mutatorClassName

                    divider()
                    line("// Mutator for ${type.import()} -> ${mutatorClassName.import()}")
                    divider()
                    newline()

                    renderFor(type, mutatorClassName)

                    newline()
                }
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy