com.dbobjekts.codegen.writer.TableDetailsSourceBuilder.kt Maven / Gradle / Ivy
package com.dbobjekts.codegen.writer
import com.dbobjekts.api.PackageName
import com.dbobjekts.statement.WriteQueryAccessors
import com.dbobjekts.codegen.metadata.*
import com.dbobjekts.codegen.metadata.DBGeneratedPrimaryKey
import com.dbobjekts.metadata.column.NullableColumn
import com.dbobjekts.statement.insert.InsertBuilderBase
import com.dbobjekts.statement.update.UpdateBuilderBase
import com.dbobjekts.util.StringUtil
data class FieldData(
val field: String,
val columnName: String,
val fieldType: String,
val defaultClause: String,
val nullable: Boolean,
val autoGenPK: Boolean,
val singlePrimaryKey: Boolean,
val compositePrimaryKey: Boolean
)
class TableDetailsSourceBuilder(val tableDefinition: DBTableDefinition) {
private val tableName = tableDefinition.asClassName()
val fields: List
val allFieldsExceptAutoPK: List
val nonNullFields: List
val singlePrimaryKey: FieldData?
val allPrimaryKeys: List
val autoPrimaryKey: FieldData?
init {
fields = tableDefinition.columns.map { colDef ->
val fieldName = colDef.columnName.fieldName
val isNullable = colDef.column is NullableColumn<*>
val dataType = StringUtil.classToString(colDef.column.valueClass) + (if (isNullable) "?" else "")
val defaultClause: String = getDefaultValue(colDef)?.let { " = $it" } ?: ""
val autoGenPk = colDef is DBGeneratedPrimaryKey
FieldData(
fieldName,
colDef.columnName.value,
dataType,
defaultClause,
isNullable,
autoGenPk,
colDef.isSinglePrimaryKey,
colDef.isCompositePrimaryKey
)
}
allPrimaryKeys = fields.filter { it.singlePrimaryKey || it.compositePrimaryKey }
allFieldsExceptAutoPK = fields.filterNot { it.autoGenPK }
nonNullFields = allFieldsExceptAutoPK.filterNot { it.nullable || it.autoGenPK }
singlePrimaryKey = fields.filter { it.singlePrimaryKey }.firstOrNull()
autoPrimaryKey = fields.filter { it.autoGenPK }.firstOrNull()
}
fun sourceForTableComment(): String {
val (foreignKeys, references) = tableDefinition.linkedTables.partition { (s, t) -> s == tableDefinition.schema && t == tableDefinition.tableName }
val composite = tableDefinition.columns.filter { it.isCompositePrimaryKey }
val pks = if (composite.isEmpty()) (singlePrimaryKey?.columnName ?: "none") else composite.map { it.columnName.value }
return """
/**
* Auto-generated metadata object for db table ${tableDefinition.schema.value}.${tableDefinition.tableName}.
*
* Do not edit this file manually! Always use [com.dbobjekts.codegen.CodeGenerator] when the metadata model is no longer in sync with the database.
*
* Primary keys: $pks
*
* Foreign keys to: ${foreignKeys.map { it.first.value + "." + it.second.value }.joinToString(",")}
* References by: ${references.map { it.first.value + "." + it.second.value }.joinToString(",")}
*/
""".trimIndent()
}
fun sourceForToValue(): String {
val elements = fields.mapIndexed { i, field ->
"values[$i] as ${field.fieldType}"
}.joinToString(",")
return " override fun toValue(values: List) = ${tableName}Row($elements)"
}
fun sourceForUpdateRowMethod(): String {
if (allPrimaryKeys.isEmpty())
return """
/**
* Warning: this method will throw a StatementBuilderException at runtime because $tableName does not have a primary key.
*/
override fun updateRow(rowData: TableRowData<*, *>): Long =
throw com.dbobjekts.api.exception.StatementBuilderException("Sorry, but you cannot use row-based updates for table ${tableName}. At least one column must be marked as primary key.")
"""
val elements = fields.mapIndexed { _, field ->
" add($tableName.${field.field}, rowData.${field.field})"
}.joinToString("\n")
val pk1 = allPrimaryKeys[0].field
val whereclause = StringBuilder("${tableName}.$pk1.eq(rowData.$pk1)")
allPrimaryKeys.forEachIndexed { i, r ->
if (i > 0) {
val field = r.field
whereclause.append(".and(${tableName}.$field.eq(rowData.$field))")
}
}
val source = """
/**
* FOR INTERNAL USE ONLY
*/
override fun updateRow(rowData: TableRowData<*, *>): Long {
rowData as ${tableName}Row
$elements
return where($whereclause)
}
"""
return source
}
fun sourceForRowDataClass(): String {
val elements = mutableListOf()
if (autoPrimaryKey != null)
elements += "val ${autoPrimaryKey.field}: ${autoPrimaryKey.fieldType} = 0"
elements.addAll(allFieldsExceptAutoPK.map { f ->
" val ${f.field}: ${f.fieldType}"
})
val pks = allPrimaryKeys.map { "Pair($tableName.${it.field}, ${it.field})" }.joinToString(",")
val fieldStr = elements.joinToString(",\n")
val source = """
data class ${tableName}Row(
$fieldStr
) : TableRowData<${tableName}UpdateBuilder, ${tableName}InsertBuilder>(${tableName}.metadata()){
override val primaryKeys = listOf>($pks)
}
"""
return source
}
private fun sourceForInsertRowMethod(): String {
val elements = allFieldsExceptAutoPK.mapIndexed { _, field ->
" add($tableName.${field.field}, rowData.${field.field})"
}.joinToString("\n")
val source = """
override fun insertRow(rowData: TableRowData<*, *>): Long {
rowData as ${tableName}Row
$elements
return execute()
}
"""
return source
}
private fun sourceForMandatoryColumnsMethod(): String {
return if (nonNullFields.isEmpty()) "" else
"""
fun mandatoryColumns(${nonNullFields.map { f -> "${f.field}: ${f.fieldType}" }.joinToString(", ")}) : ${tableName}InsertBuilder {
${nonNullFields.map { f -> " mandatory($tableName.${f.field}, ${f.field})" }.joinToString("\n")}
return this
}
"""
}
fun sourceForMetaDataVal(): String {
val accessorClass = WriteQueryAccessors::class.java.simpleName
val updateBuilder = "${tableName}UpdateBuilder"
val insertBuilder = "${tableName}InsertBuilder"
return " override fun metadata(): $accessorClass<$updateBuilder, $insertBuilder> = $accessorClass($updateBuilder(), $insertBuilder())"
}
fun sourceForBuilderClasses(): String {
val updateBuilderBase = UpdateBuilderBase::class.java.simpleName
val insertBuilderBase = InsertBuilderBase::class.java.simpleName
fun writeMethod(data: FieldData, returnType: String) =
" fun ${data.field}(value: ${data.fieldType}): $returnType = put($tableName.${data.field}, value)"
val updateRowMethodSource = sourceForUpdateRowMethod()
val insertRowMethodSource = sourceForInsertRowMethod()
val mandatoryColumnsMethod = sourceForMandatoryColumnsMethod()
val updateBuilder = "${tableName}UpdateBuilder"
val insertBuilder = "${tableName}InsertBuilder"
return """
class $updateBuilder() : $updateBuilderBase($tableName) {
${allFieldsExceptAutoPK.map { d -> writeMethod(d, updateBuilder) }.joinToString("\n")}
$updateRowMethodSource
}
class $insertBuilder():$insertBuilderBase(){
${allFieldsExceptAutoPK.map { d -> writeMethod(d, insertBuilder) }.joinToString("\n")}
$mandatoryColumnsMethod
$insertRowMethodSource
}
"""
}
private fun getDefaultValue(colDef: DBColumnDefinition): String? {
if (!(colDef.column is NullableColumn<*>))
return null
val clz = colDef.column.valueClass
return if (!clz.isPrimitive) "null"
else {
when (clz.simpleName) {
"byte" -> "0"
"short" -> "0"
"int" -> "0"
"float" -> "0f"
"long" -> "0l"
"double" -> "0d"
"boolean" -> "false"
else -> null
}
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy