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

builder.extra.RuntimeExceptionSpecBuilder.kt Maven / Gradle / Ivy

There is a newer version: 0.1.0
Show newest version
package io.toolisticon.kotlin.generation.builder.extra

import com.squareup.kotlinpoet.ClassName
import com.squareup.kotlinpoet.CodeBlock
import com.squareup.kotlinpoet.ExperimentalKotlinPoetApi
import com.squareup.kotlinpoet.KModifier
import com.squareup.kotlinpoet.TypeName
import com.squareup.kotlinpoet.TypeSpec
import com.squareup.kotlinpoet.asTypeName
import io.toolisticon.kotlin.generation.KotlinCodeGeneration.buildParameter
import io.toolisticon.kotlin.generation.KotlinCodeGeneration.format.FORMAT_NAME
import io.toolisticon.kotlin.generation.KotlinCodeGeneration.format.FORMAT_STRING_TEMPLATE
import io.toolisticon.kotlin.generation.KotlinCodeGeneration.name.nullable
import io.toolisticon.kotlin.generation.KotlinCodeGeneration.simpleClassName
import io.toolisticon.kotlin.generation.builder.*
import io.toolisticon.kotlin.generation.builder.KotlinConstructorPropertySpecBuilder.Companion.primaryConstructorWithProperties
import io.toolisticon.kotlin.generation.poet.FunSpecBuilder
import io.toolisticon.kotlin.generation.poet.KDoc
import io.toolisticon.kotlin.generation.spec.*
import io.toolisticon.kotlin.generation.spec.toList
import kotlin.reflect.KClass

/**
 * Builder for a special [io.toolisticon.kotlin.generation.spec.KotlinClassSpec] that represents
 * an [RuntimeException] with String-Message-Template.
 */
@ExperimentalKotlinPoetApi
class RuntimeExceptionSpecBuilder internal constructor(
  private val delegate: KotlinClassSpecBuilder,
) : KotlinGeneratorTypeSpecBuilder,
  KotlinAnnotatableDocumentableModifiableBuilder,
  KotlinConstructorPropertySupport,
  KotlinSuperInterfaceSupport {

  companion object {
    private val NULLABLE_THROWABLE = Throwable::class.asTypeName().nullable()
    private val FIND_TEMPLATE_PARAMS = Regex("\\$(\\w+)")

    /**
     * Creates new builder.
     */
    fun builder(name: String): RuntimeExceptionSpecBuilder = builder(simpleClassName(name))

    /**
     * Creates new builder.
     */
    fun builder(className: ClassName): RuntimeExceptionSpecBuilder = RuntimeExceptionSpecBuilder(className = className)

    /**
     * Gets template parameters as names.
     */
    private fun findTemplateParams(template: String) = FIND_TEMPLATE_PARAMS.findAll(template).map { it.value }
      .map { it.removePrefix("$") }
  }

  private lateinit var _messageTemplate: String
  private var _cause: Pair = false to "cause"
  private val _messageTemplateParameters = LinkedHashMap()

  internal constructor(className: ClassName) : this(delegate = KotlinClassSpecBuilder(className = className)) {
    addTag(ClassSpecType.EXCEPTION)
    delegate.superclass(RuntimeException::class)
  }

  /**
   * Sets a string message template. Placeholders `\$foo` are parsed and automatically used
   * as parameters of type `Any`, unless explicitly overwritten by `addParameter` or `addConstructorProperty`.
   */
  fun messageTemplate(messageTemplate: String) = apply {
    _messageTemplate = messageTemplate

    findTemplateParams(_messageTemplate).forEach {
      _messageTemplateParameters.putIfAbsent(it, Any::class.asTypeName())
    }
  }

  /**
   * Define the type of a placeholder parameter in the message template.
   */
  fun addParameter(name: String, type: KClass<*>) = addParameter(name, type.asTypeName())

  /**
   * Define the type of a placeholder parameter in the message template.
   */
  fun addParameter(name: String, type: TypeName) = apply {
    _messageTemplateParameters[name] = type
  }

  /**
   * Include the cause (Throwable) in the constructor.
   */
  fun includeCause(name: String? = null) = apply {
    _cause = true to (name ?: _cause.second)
  }

  override fun build(): KotlinClassSpec {
    require(::_messageTemplate.isInitialized) { "Message template must be initialized." }
    delegate.addSuperclassConstructorParameter(FORMAT_STRING_TEMPLATE, _messageTemplate)

    val constructorProperties = delegate.constructorProperties

    val constructorBuilder: FunSpecBuilder = if (constructorProperties.isNotEmpty()) {
      delegate.delegate.primaryConstructorWithProperties(toList(constructorProperties.values))
    } else {
      FunSpecBuilder.constructorBuilder()
    }

    _messageTemplateParameters.entries.filterNot { constructorProperties.containsKey(it.key) }.forEach { (name, type) ->
      constructorBuilder.addParameter(name, type)
    }

    if (_cause.first) {
      delegate.addSuperclassConstructorParameter(FORMAT_NAME, _cause.second)
      val nullableCauseParameter = buildParameter(_cause.second, NULLABLE_THROWABLE) {
        defaultValue("null")
      }

      constructorBuilder.addParameter(nullableCauseParameter.get())
    }

    // TODO bypass classSpecBuild until issue 47 is solved
    return KotlinClassSpec(
      className = delegate.className,
      spec = delegate.delegate
        .primaryConstructor(constructorBuilder.build())
        .build()
    )
  }

  // 
  override fun addAnnotation(spec: KotlinAnnotationSpecSupplier) = apply { delegate.addAnnotation(spec) }
  override fun addConstructorProperty(spec: KotlinConstructorPropertySpecSupplier) = apply { delegate.addConstructorProperty(spec) }
  override fun addKdoc(kdoc: KDoc) = apply { delegate.addKdoc(kdoc) }
  override fun addModifiers(vararg modifiers: KModifier) = apply { delegate.addModifiers(*modifiers) }
  override fun addSuperinterface(superinterface: TypeName, constructorParameter: String) = apply { delegate.addSuperinterface(superinterface, constructorParameter) }
  override fun addSuperinterface(superinterface: TypeName, delegate: CodeBlock): RuntimeExceptionSpecBuilder = apply { this.delegate.addSuperinterface(superinterface, delegate) }
  override fun addTag(type: KClass<*>, tag: Any?) = apply { delegate.addTag(type, tag) }
  override fun builder(block: TypeSpec.Builder.() -> Unit): RuntimeExceptionSpecBuilder = apply { delegate.builder(block) }
  // 
}

@ExperimentalKotlinPoetApi
typealias RuntimeExceptionSpecBuilderReceiver = RuntimeExceptionSpecBuilder.() -> Unit




© 2015 - 2024 Weber Informatics LLC | Privacy Policy