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

se.ansman.dagger.auto.compiler.common.rendering.HiltJavaModuleBuilder.kt Maven / Gradle / Ivy

package se.ansman.dagger.auto.compiler.common.rendering

import com.squareup.javapoet.*
import dagger.Binds
import dagger.BindsOptionalOf
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.codegen.OriginatingElement
import se.ansman.dagger.auto.compiler.common.applyEach
import se.ansman.dagger.auto.compiler.common.generatedFileComment
import se.ansman.dagger.auto.compiler.common.models.HiltModule
import se.ansman.dagger.auto.compiler.common.rawType
import se.ansman.dagger.auto.compiler.common.rendering.HiltModuleBuilder.ProviderMode
import javax.annotation.processing.Generated
import javax.lang.model.element.Element
import javax.lang.model.element.Modifier

class HiltJavaModuleBuilder private constructor(
    private val info: HiltModule,
) : HiltModuleBuilder {
    private val nameAllocator = NameAllocator()
    private val typeSpec = TypeSpec.classBuilder(info.moduleName)
        .addOriginatingElement(info.originatingElement)
        .addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT)
        .addMethod(
            MethodSpec.constructorBuilder()
                .addModifiers(Modifier.PRIVATE)
                .build()
        )
        .addAnnotation(AnnotationSpec.builder(Generated::class.java)
            .addMember("value", "\$S", info.processor.name)
            .build())
        .addAnnotation(Module::class.java)
        .addAnnotation(
            when (val installation = info.installation) {
                is HiltModuleBuilder.Installation.InstallIn ->
                    AnnotationSpec.builder(InstallIn::class.java)
                        .applyEach(installation.components) { addMember("value", "\$T.class", it) }
                        .build()

                is HiltModuleBuilder.Installation.TestInstallIn ->
                    AnnotationSpec.builder(ClassName.get("dagger.hilt.testing", "TestInstallIn"))
                        .applyEach(installation.components) { addMember("components", "\$T.class", it) }
                        .applyEach(installation.replaces) { addMember("replaces", "\$T.class", it) }
                        .build()
            }
        )
        .addAnnotation(
            AnnotationSpec.builder(OriginatingElement::class.java)
                .addMember("topLevelClass", "\$T.class", info.originatingTopLevelClassName)
                .build()
        )

    override fun addProvider(
        name: String,
        returnType: HiltModuleBuilder.DaggerType,
        isPublic: Boolean,
        parameters: List>,
        mode: ProviderMode,
        contents: (List) -> CodeBlock,
    ) = apply {
        val parameterNameAllocator = NameAllocator()
        val params = parameters.map { it.toParameterSpec(parameterNameAllocator) }
        typeSpec.addMethod(
            MethodSpec.methodBuilder(nameAllocator.newName(name))
                .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
                .addAnnotation(Provides::class.java)
                .addAnnotations(mode.asAnnotations())
                .addParameters(params)
                .addStatement(contents(params))
                .addAnnotations(returnType.qualifiers)
                .returns(returnType.type)
                .build()
        )
    }

    override fun addBinding(
        name: String,
        sourceType: HiltModuleBuilder.DaggerType,
        returnType: HiltModuleBuilder.DaggerType,
        isPublic: Boolean,
        mode: ProviderMode
    ) = addBinding(
        bindingAnnotation = Binds::class.java,
        name = name,
        sourceType = sourceType,
        returnType = returnType,
        mode = mode,
    )

    override fun addOptionalBinding(
        name: String,
        type: HiltModuleBuilder.DaggerType,
        isPublic: Boolean
    ) = addBinding(
        bindingAnnotation = BindsOptionalOf::class.java,
        name = name,
        sourceType = null,
        returnType = type,
        mode = ProviderMode.Single
    )

    private fun addBinding(
        bindingAnnotation: Class,
        name: String,
        sourceType: HiltModuleBuilder.DaggerType?,
        returnType: HiltModuleBuilder.DaggerType,
        mode: ProviderMode
    ) = apply {
        typeSpec.addMethod(
            MethodSpec.methodBuilder(nameAllocator.newName(name))
                .addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT)
                .addAnnotation(bindingAnnotation)
                .addAnnotations(mode.asAnnotations())
                .apply {
                    if (sourceType != null) {
                        addParameter(sourceType.toParameterSpec(NameAllocator()))
                    }
                }
                .addAnnotations(returnType.qualifiers)
                .returns(returnType.type)
                .build()
        )
    }

    override fun build(): JavaFile =
        JavaFile.builder(info.moduleName.packageName(), typeSpec.build())
            .addFileComment(generatedFileComment)
            .build()

    companion object {
        val Factory = HiltModuleBuilder.Factory(::HiltJavaModuleBuilder)
    }
}

private fun HiltModuleBuilder.Parameter.toParameterSpec(
    nameAllocator: NameAllocator,
): ParameterSpec =
    ParameterSpec
        .builder(asTypeName(), nameAllocator.newName(asParameterName()))
        .addAnnotations(qualifiers)
        .build()

private fun ProviderMode.asAnnotations() =
    asAnnotations(AnnotationSpec::get)

private fun HiltModuleBuilder.Parameter.asParameterName(): String =
    asParameterName { rawType().simpleName() }

private fun HiltModuleBuilder.Parameter.asTypeName(): TypeName =
    asTypeName { rawType, arguments ->
        ParameterizedTypeName.get(ClassName.get(rawType.java), *arguments.map { it.box() }.toTypedArray())
    }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy