/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package androidx.room.writer
import androidx.room.compiler.codegen.CodeLanguage
import androidx.room.compiler.codegen.VisibilityModifier
import androidx.room.compiler.codegen.XClassName
import androidx.room.compiler.codegen.XCodeBlock
import androidx.room.compiler.codegen.XCodeBlock.Builder.Companion.addLocalVal
import androidx.room.compiler.codegen.XFunSpec
import androidx.room.compiler.codegen.XFunSpec.Builder.Companion.apply
import androidx.room.compiler.codegen.XPropertySpec
import androidx.room.compiler.codegen.XTypeName
import androidx.room.compiler.codegen.XTypeSpec
import androidx.room.compiler.codegen.XTypeSpec.Builder.Companion.addOriginatingElement
import androidx.room.compiler.processing.XElement
import androidx.room.compiler.processing.XMethodElement
import androidx.room.compiler.processing.XType
import androidx.room.ext.CommonTypeNames
import androidx.room.ext.RoomMemberNames
import androidx.room.ext.RoomTypeNames
import androidx.room.ext.RoomTypeNames.DELETE_OR_UPDATE_ADAPTER
import androidx.room.ext.RoomTypeNames.DELETE_OR_UPDATE_ADAPTER_COMPAT
import androidx.room.ext.RoomTypeNames.INSERT_ADAPTER
import androidx.room.ext.RoomTypeNames.INSERT_ADAPTER_COMPAT
import androidx.room.ext.RoomTypeNames.ROOM_DB
import androidx.room.ext.RoomTypeNames.UPSERT_ADAPTER
import androidx.room.ext.RoomTypeNames.UPSERT_ADAPTER_COMPAT
import androidx.room.ext.SupportDbTypeNames
import androidx.room.processor.OnConflictProcessor
import androidx.room.solver.CodeGenScope
import androidx.room.solver.KotlinBoxedPrimitiveMethodDelegateBinder
import androidx.room.solver.KotlinDefaultMethodDelegateBinder
import androidx.room.solver.types.getRequiredTypeConverters
import androidx.room.vo.Dao
import androidx.room.vo.DeleteOrUpdateShortcutMethod
import androidx.room.vo.InsertMethod
import androidx.room.vo.KotlinBoxedPrimitiveMethodDelegate
import androidx.room.vo.KotlinDefaultMethodDelegate
import androidx.room.vo.RawQueryMethod
import androidx.room.vo.ReadQueryMethod
import androidx.room.vo.ShortcutEntity
import androidx.room.vo.TransactionMethod
import androidx.room.vo.UpdateMethod
import androidx.room.vo.UpsertMethod
import androidx.room.vo.WriteQueryMethod
/**
* Creates the implementation for a class annotated with Dao.
*/
class DaoWriter(
val dao: Dao,
private val dbElement: XElement,
codeLanguage: CodeLanguage,
javaLambdaSyntaxAvailable: Boolean
) : TypeWriter(codeLanguage, javaLambdaSyntaxAvailable) {
private val declaredDao = dao.element.type
// TODO nothing prevents this from conflicting, we should fix.
private val dbProperty: XPropertySpec = XPropertySpec
.builder(codeLanguage, DB_PROPERTY_NAME, ROOM_DB, VisibilityModifier.PRIVATE)
.build()
private val companionTypeBuilder = lazy {
XTypeSpec.companionObjectBuilder(codeLanguage)
}
companion object {
const val GET_LIST_OF_TYPE_CONVERTERS_METHOD = "getRequiredConverters"
const val DB_PROPERTY_NAME = "__db"
private fun shortcutEntityFieldNamePart(shortcutEntity: ShortcutEntity): String {
fun typeNameToFieldName(typeName: XClassName): String {
return typeName.simpleNames.last()
}
return if (shortcutEntity.isPartialEntity) {
typeNameToFieldName(shortcutEntity.pojo.className) + "As" +
typeNameToFieldName(shortcutEntity.entityClassName)
} else {
typeNameToFieldName(shortcutEntity.entityClassName)
}
}
}
override fun createTypeSpecBuilder(): XTypeSpec.Builder {
val builder = XTypeSpec.classBuilder(codeLanguage, dao.implTypeName)
val preparedQueries = dao.queryMethods.filterIsInstance<WriteQueryMethod>()
val shortcutMethods = buildList {
addAll(createInsertMethods())
addAll(createDeleteMethods())
addAll(createUpdateMethods())
addAll(createTransactionMethods())
addAll(createUpsertMethods())
}
builder.apply {
addOriginatingElement(dbElement)
setVisibility(
if (dao.element.isInternal()) {
VisibilityModifier.INTERNAL
} else {
VisibilityModifier.PUBLIC
}
)
if (dao.element.isInterface()) {
addSuperinterface(dao.typeName)
} else {
superclass(dao.typeName)
}
addProperty(dbProperty)
setPrimaryConstructor(
createConstructor(
shortcutMethods,
dao.constructorParamType != null
)
)
shortcutMethods.forEach {
addFunction(it.functionImpl)
}
dao.queryMethods.filterIsInstance<ReadQueryMethod>().forEach { method ->
addFunction(createSelectMethod(method))
}
preparedQueries.forEach {
addFunction(createPreparedQueryMethod(it))
}
dao.rawQueryMethods.forEach {
addFunction(createRawQueryMethod(it))
}
if (codeLanguage == CodeLanguage.JAVA) {
dao.kotlinDefaultMethodDelegates.forEach {
addFunction(createDefaultImplMethodDelegate(it))
}
dao.kotlinBoxedPrimitiveMethodDelegates.forEach {
addFunction(createBoxedPrimitiveBridgeMethodDelegate(it))
}
}
// Keep this the last one to be generated because used custom converters will
// register fields with a payload which we collect in dao to report used
// Type Converters.
addConverterListMethod(this)
if (companionTypeBuilder.isInitialized()) {
addType(companionTypeBuilder.value.build())
}
}
return builder
}
private fun addConverterListMethod(typeSpecBuilder: XTypeSpec.Builder) {
when (codeLanguage) {
// For Java a static method is created
CodeLanguage.JAVA -> typeSpecBuilder.addFunction(createConverterListMethod())
// For Kotlin a function in the companion object is created
CodeLanguage.KOTLIN -> companionTypeBuilder.value
.addFunction(createConverterListMethod())
.build()
}
}
private fun createConverterListMethod(): XFunSpec {
val body = XCodeBlock.builder(codeLanguage).apply {
val requiredTypeConverters = getRequiredTypeConverters()
if (requiredTypeConverters.isEmpty()) {
when (language) {
CodeLanguage.JAVA ->
addStatement("return %T.emptyList()", CommonTypeNames.COLLECTIONS)
CodeLanguage.KOTLIN ->
addStatement("return emptyList()")
}
} else {
val placeholders = requiredTypeConverters.joinToString(",") { "%L" }
val requiredTypeConvertersLiterals = requiredTypeConverters.map {
when (language) {
CodeLanguage.JAVA -> XCodeBlock.ofJavaClassLiteral(language, it)
CodeLanguage.KOTLIN -> XCodeBlock.ofKotlinClassLiteral(language, it)
}
}.toTypedArray()
when (language) {
CodeLanguage.JAVA ->
addStatement("return %T.asList($placeholders)",
CommonTypeNames.ARRAYS,
*requiredTypeConvertersLiterals
)
CodeLanguage.KOTLIN ->
addStatement(
"return listOf($placeholders)",
*requiredTypeConvertersLiterals
)
}
}
}.build()
return XFunSpec.builder(
codeLanguage,
GET_LIST_OF_TYPE_CONVERTERS_METHOD,
VisibilityModifier.PUBLIC
).apply(
javaMethodBuilder = {
addModifiers(javax.lang.model.element.Modifier.STATIC)
},
kotlinFunctionBuilder = {},
).apply {
returns(
CommonTypeNames.LIST.parametrizedBy(
when (language) {
CodeLanguage.JAVA -> CommonTypeNames.JAVA_CLASS
CodeLanguage.KOTLIN -> CommonTypeNames.KOTLIN_CLASS
}.parametrizedBy(XTypeName.ANY_WILDCARD)
)
)
addCode(body)
}.build()
}
private fun createTransactionMethods(): List<PreparedStmtQuery> {
return dao.transactionMethods.map {
PreparedStmtQuery(emptyMap(), createTransactionMethodBody(it))
}
}
private fun createTransactionMethodBody(method: TransactionMethod): XFunSpec {
val scope = CodeGenScope(this)
method.methodBinder.executeAndReturn(
parameterNames = method.parameterNames,
daoName = dao.typeName,
daoImplName = dao.implTypeName,
dbProperty = dbProperty,
scope = scope
)
return overrideWithoutAnnotations(method.element, declaredDao)
.addCode(scope.generate())
.build()
}
private fun createConstructor(
shortcutMethods: List<PreparedStmtQuery>,
callSuper: Boolean
): XFunSpec {
val body = XCodeBlock.builder(codeLanguage).apply {
addStatement("this.%N = %L", dbProperty, dbProperty.name)
shortcutMethods.asSequence().filterNot {
it.fields.isEmpty()
}.map {
it.fields.values
}.flatten().groupBy {
it.first.name
}.map {
it.value.first()
}.forEach { (propertySpec, initExpression) ->
addStatement("this.%L = %L", propertySpec.name, initExpression)
}
}.build()
return XFunSpec.constructorBuilder(codeLanguage, VisibilityModifier.PUBLIC).apply {
addParameter(
typeName = dao.constructorParamType ?: ROOM_DB,
name = dbProperty.name
)
if (callSuper) {
callSuperConstructor(XCodeBlock.of(language, "%L", dbProperty.name))
}
addCode(body)
}.build()
}
private fun createSelectMethod(method: ReadQueryMethod): XFunSpec {
return overrideWithoutAnnotations(method.element, declaredDao)
.addCode(createQueryMethodBody(method))
.build()
}
private fun createRawQueryMethod(method: RawQueryMethod): XFunSpec {
val body = XCodeBlock.builder(codeLanguage).apply {
val scope = CodeGenScope(this@DaoWriter)
val roomSQLiteQueryVar: String
val queryParam = method.runtimeQueryParam
val shouldReleaseQuery: Boolean
if (queryParam?.isSupportQuery() == true) {
roomSQLiteQueryVar = queryParam.paramName
shouldReleaseQuery = false
} else if (queryParam?.isString() == true) {
roomSQLiteQueryVar = scope.getTmpVar("_statement")
shouldReleaseQuery = true
addLocalVariable(
name = roomSQLiteQueryVar,
typeName = RoomTypeNames.ROOM_SQL_QUERY,
assignExpr = XCodeBlock.of(
codeLanguage,
"%M(%L, 0)",
RoomMemberNames.ROOM_SQL_QUERY_ACQUIRE,
queryParam.paramName
),
)
} else {
// try to generate compiling code. we would've already reported this error
roomSQLiteQueryVar = scope.getTmpVar("_statement")
shouldReleaseQuery = false
addLocalVariable(
name = roomSQLiteQueryVar,
typeName = RoomTypeNames.ROOM_SQL_QUERY,
assignExpr = XCodeBlock.of(
codeLanguage,
"%M(%S, 0)",
RoomMemberNames.ROOM_SQL_QUERY_ACQUIRE,
"missing query parameter"
),
)
}
if (method.returnsValue) {
// don't generate code because it will create 1 more error. The original error is
// already reported by the processor.
method.queryResultBinder.convertAndReturn(
roomSQLiteQueryVar = roomSQLiteQueryVar,
canReleaseQuery = shouldReleaseQuery,
dbProperty = dbProperty,
inTransaction = method.inTransaction,
scope = scope
)
}
add(scope.generate())
}.build()
return overrideWithoutAnnotations(method.element, declaredDao)
.addCode(body)
.build()
}
private fun createPreparedQueryMethod(method: WriteQueryMethod): XFunSpec {
return overrideWithoutAnnotations(method.element, declaredDao)
.addCode(createPreparedQueryMethodBody(method))
.build()
}
/**
* Groups all insert methods based on the insert statement they will use then creates all
* field specs, EntityInsertAdapterWriter and actual insert methods.
*/
private fun createInsertMethods(): List<PreparedStmtQuery> {
return dao.insertMethods
.map { insertMethod ->
val onConflict = OnConflictProcessor.onConflictText(insertMethod.onConflict)
val entities = insertMethod.entities
val useDriverApi = insertMethod.methodBinder?.isMigratedToDriver() == true
val fields = entities.mapValues {
val spec = getOrCreateProperty(
InsertMethodProperty(it.value, onConflict, useDriverApi))
val impl = EntityInsertAdapterWriter.create(it.value, onConflict)
.createAnonymous(this@DaoWriter, dbProperty, useDriverApi)
spec to impl
}
val methodImpl = overrideWithoutAnnotations(
insertMethod.element,
declaredDao
).apply {
addCode(createInsertMethodBody(insertMethod, fields))
}.build()
PreparedStmtQuery(fields, methodImpl)
}
}
private fun createInsertMethodBody(
method: InsertMethod,
insertAdapters: Map<String, Pair<XPropertySpec, XTypeSpec>>
): XCodeBlock {
if (insertAdapters.isEmpty() || method.methodBinder == null) {
return XCodeBlock.builder(codeLanguage).build()
}
val useDriverApi = method.methodBinder.isMigratedToDriver()
val scope = CodeGenScope(writer = this, useDriverApi = useDriverApi)
if (useDriverApi) {
method.methodBinder.convertAndReturn(
parameters = method.parameters,
adapters = insertAdapters,
dbProperty = dbProperty,
scope = scope
)
} else {
method.methodBinder.convertAndReturnCompat(
parameters = method.parameters,
adapters = insertAdapters,
dbProperty = dbProperty,
scope = scope
)
}
return scope.generate()
}
/**
* Creates EntityUpdateAdapter for each delete method.
*/
private fun createDeleteMethods(): List<PreparedStmtQuery> {
return createShortcutMethods(dao.deleteMethods, "delete") { deleteMethod, entity ->
val useDriverApi = deleteMethod.methodBinder?.isMigratedToDriver() == true
EntityDeleteAdapterWriter.create(entity)
.createAnonymous(this@DaoWriter, dbProperty.name, useDriverApi)
}
}
/**
* Creates EntityUpdateAdapter for each @Update method.
*/
private fun createUpdateMethods(): List<PreparedStmtQuery> {
return createShortcutMethods(dao.updateMethods, "update") { update, entity ->
val onConflict = OnConflictProcessor.onConflictText(update.onConflictStrategy)
val useDriverApi = update.methodBinder?.isMigratedToDriver() == true
EntityUpdateAdapterWriter.create(entity, onConflict)
.createAnonymous(this@DaoWriter, dbProperty.name, useDriverApi)
}
}
private fun <T : DeleteOrUpdateShortcutMethod> createShortcutMethods(
methods: List<T>,
methodPrefix: String,
implCallback: (T, ShortcutEntity) -> XTypeSpec
): List<PreparedStmtQuery> {
return methods.mapNotNull { method ->
val entities = method.entities
if (entities.isEmpty()) {
null
} else {
val onConflict = if (method is UpdateMethod) {
OnConflictProcessor.onConflictText(method.onConflictStrategy)
} else {
""
}
val useDriverApi = method.methodBinder?.isMigratedToDriver() == true
val fields = entities.mapValues {
val spec = getOrCreateProperty(
DeleteOrUpdateAdapterProperty(
it.value,
methodPrefix,
onConflict,
useDriverApi
)
)
val impl = implCallback(method, it.value)
spec to impl
}
val methodSpec = overrideWithoutAnnotations(method.element, declaredDao).apply {
addCode(createDeleteOrUpdateMethodBody(method, fields))
}.build()
PreparedStmtQuery(fields, methodSpec)
}
}
}
private fun createDeleteOrUpdateMethodBody(
method: DeleteOrUpdateShortcutMethod,
adapters: Map<String, Pair<XPropertySpec, XTypeSpec>>
): XCodeBlock {
if (adapters.isEmpty() || method.methodBinder == null) {
return XCodeBlock.builder(codeLanguage).build()
}
val useDriverApi = method.methodBinder.isMigratedToDriver()
val scope = CodeGenScope(writer = this, useDriverApi = useDriverApi)
if (useDriverApi) {
method.methodBinder.convertAndReturn(
parameters = method.parameters,
adapters = adapters,
dbProperty = dbProperty,
scope = scope
)
} else {
method.methodBinder.convertAndReturnCompat(
parameters = method.parameters,
adapters = adapters,
dbProperty = dbProperty,
scope = scope
)
}
return scope.generate()
}
/**
* Groups all upsert methods based on the upsert statement they will use then creates all
* field specs, EntityUpsertAdapterWriter and actual upsert methods.
*/
private fun createUpsertMethods(): List<PreparedStmtQuery> {
return dao.upsertMethods
.map { upsertMethod ->
val entities = upsertMethod.entities
val fields = entities.mapValues {
val useDriverApi = upsertMethod.methodBinder?.isMigratedToDriver() == true
val spec = getOrCreateProperty(UpsertAdapterProperty(it.value, useDriverApi))
val impl = EntityUpsertAdapterWriter.create(it.value)
.createConcrete(it.value, this@DaoWriter, dbProperty, useDriverApi)
spec to impl
}
val methodImpl = overrideWithoutAnnotations(
upsertMethod.element,
declaredDao
).apply {
addCode(createUpsertMethodBody(upsertMethod, fields))
}.build()
PreparedStmtQuery(fields, methodImpl)
}
}
private fun createUpsertMethodBody(
method: UpsertMethod,
upsertAdapters: Map<String, Pair<XPropertySpec, XCodeBlock>>
): XCodeBlock {
if (upsertAdapters.isEmpty() || method.methodBinder == null) {
return XCodeBlock.builder(codeLanguage).build()
}
val useDriverApi = method.methodBinder.isMigratedToDriver()
val scope = CodeGenScope(writer = this, useDriverApi = useDriverApi)
if (useDriverApi) {
method.methodBinder.convertAndReturn(
parameters = method.parameters,
adapters = upsertAdapters,
dbProperty = dbProperty,
scope = scope
)
} else {
method.methodBinder.convertAndReturnCompat(
parameters = method.parameters,
adapters = upsertAdapters,
dbProperty = dbProperty,
scope = scope
)
}
return scope.generate()
}
private fun createPreparedQueryMethodBody(method: WriteQueryMethod): XCodeBlock {
if (!method.preparedQueryResultBinder.isMigratedToDriver()) {
return compatCreatePreparedQueryMethodBody(method)
}
val scope = CodeGenScope(this, useDriverApi = true)
val queryWriter = QueryWriter(method)
val sqlVar = scope.getTmpVar("_sql")
val listSizeArgs = queryWriter.prepareQuery(sqlVar, scope)
method.preparedQueryResultBinder.executeAndReturn(
sqlQueryVar = sqlVar,
dbProperty = dbProperty,
bindStatement = { stmtVar -> queryWriter.bindArgs(stmtVar, listSizeArgs, this) },
returnTypeName = method.returnType.asTypeName(),
scope = scope
)
return scope.generate()
}
private fun compatCreatePreparedQueryMethodBody(method: WriteQueryMethod): XCodeBlock {
val scope = CodeGenScope(this)
method.preparedQueryResultBinder.executeAndReturn(
prepareQueryStmtBlock = {
val queryWriter = QueryWriter(method)
val sqlVar = getTmpVar("_sql")
val stmtVar = getTmpVar("_stmt")
val listSizeArgs = queryWriter.prepareQuery(sqlVar, this)
builder.addLocalVal(
stmtVar,
SupportDbTypeNames.SQLITE_STMT,
"%N.compileStatement(%L)",
dbProperty,
sqlVar
)
queryWriter.bindArgs(stmtVar, listSizeArgs, this)
stmtVar
},
preparedStmtProperty = null,
dbProperty = dbProperty,
scope = scope
)
return scope.builder.build()
}
private fun createQueryMethodBody(method: ReadQueryMethod): XCodeBlock {
if (!method.queryResultBinder.isMigratedToDriver()) {
return compatCreateQueryMethodBody(method)
}
val scope = CodeGenScope(this, useDriverApi = true)
val queryWriter = QueryWriter(method)
val sqlVar = scope.getTmpVar("_sql")
val listSizeArgs = queryWriter.prepareQuery(sqlVar, scope)
method.queryResultBinder.convertAndReturn(
sqlQueryVar = sqlVar,
dbProperty = dbProperty,
bindStatement = { stmtVar ->
queryWriter.bindArgs(stmtVar, listSizeArgs, this)
},
returnTypeName = method.returnType.asTypeName(),
inTransaction = method.inTransaction,
scope = scope
)
return scope.generate()
}
private fun compatCreateQueryMethodBody(method: ReadQueryMethod): XCodeBlock {
val queryWriter = QueryWriter(method)
val scope = CodeGenScope(this)
val sqlVar = scope.getTmpVar("_sql")
val roomSQLiteQueryVar = scope.getTmpVar("_statement")
queryWriter.prepareReadAndBind(sqlVar, roomSQLiteQueryVar, scope)
method.queryResultBinder.convertAndReturn(
roomSQLiteQueryVar = roomSQLiteQueryVar,
canReleaseQuery = true,
dbProperty = dbProperty,
inTransaction = method.inTransaction,
scope = scope
)
return scope.generate()
}
// TODO(b/251459654): Handle @JvmOverloads in delegating functions with Kotlin codegen.
private fun createDefaultImplMethodDelegate(method: KotlinDefaultMethodDelegate): XFunSpec {
val scope = CodeGenScope(this)
return overrideWithoutAnnotations(method.element, declaredDao).apply {
KotlinDefaultMethodDelegateBinder.executeAndReturn(
daoName = dao.typeName,
daoImplName = dao.implTypeName,
methodName = method.element.jvmName,
returnType = method.element.returnType,
parameterNames = method.element.parameters.map { it.name },
scope = scope
)
addCode(scope.generate())
}.build()
}
private fun createBoxedPrimitiveBridgeMethodDelegate(
method: KotlinBoxedPrimitiveMethodDelegate
): XFunSpec {
val scope = CodeGenScope(this)
return overrideWithoutAnnotations(method.element, declaredDao).apply {
KotlinBoxedPrimitiveMethodDelegateBinder.execute(
methodName = method.element.jvmName,
returnType = method.element.returnType,
parameters = method.concreteMethod.parameters.map {
it.type.asTypeName() to it.name
},
scope = scope
)
addCode(scope.generate())
}.build()
}
private fun overrideWithoutAnnotations(
elm: XMethodElement,
owner: XType
): XFunSpec.Builder {
return XFunSpec.overridingBuilder(codeLanguage, elm, owner)
}
/**
* Represents a query statement prepared in Dao implementation.
*
* @param fields This map holds all the member properties necessary for this query. The key is
* the corresponding parameter name in the defining query method. The value is a pair from the
* property declaration to definition.
* @param functionImpl The body of the query method implementation.
*/
data class PreparedStmtQuery(
val fields: Map<String, Pair<XPropertySpec, Any>>,
val functionImpl: XFunSpec
) {
companion object {
// The key to be used in `fields` where the method requires a field that is not
// associated with any of its parameters
const val NO_PARAM_FIELD = "-"
}
}
private class InsertMethodProperty(
val shortcutEntity: ShortcutEntity,
val onConflictText: String,
val useDriverApi: Boolean
) : SharedPropertySpec(
baseName = if (useDriverApi) {
"insertAdapterOf${shortcutEntityFieldNamePart(shortcutEntity)}"
} else {
"insertionAdapterOf${shortcutEntityFieldNamePart(shortcutEntity)}"
},
type = if (useDriverApi) {
INSERT_ADAPTER
} else {
INSERT_ADAPTER_COMPAT
}.parametrizedBy(shortcutEntity.pojo.typeName)
) {
override fun getUniqueKey(): String {
return "${shortcutEntity.pojo.typeName}-${shortcutEntity.entityTypeName}" +
"$onConflictText-$useDriverApi"
}
override fun prepare(writer: TypeWriter, builder: XPropertySpec.Builder) {
}
}
class DeleteOrUpdateAdapterProperty(
val shortcutEntity: ShortcutEntity,
val methodPrefix: String,
val onConflictText: String,
val useDriverApi: Boolean
) : SharedPropertySpec(
baseName = if (useDriverApi) {
"${methodPrefix}AdapterOf${shortcutEntityFieldNamePart(shortcutEntity)}"
} else {
"${methodPrefix}CompatAdapterOf${shortcutEntityFieldNamePart(shortcutEntity)}"
},
type = if (useDriverApi) {
DELETE_OR_UPDATE_ADAPTER.parametrizedBy(shortcutEntity.pojo.typeName)
} else {
DELETE_OR_UPDATE_ADAPTER_COMPAT.parametrizedBy(shortcutEntity.pojo.typeName)
}
) {
override fun prepare(writer: TypeWriter, builder: XPropertySpec.Builder) {
}
override fun getUniqueKey(): String {
return "${shortcutEntity.pojo.typeName}-${shortcutEntity.entityTypeName}" +
"$methodPrefix$onConflictText-$useDriverApi"
}
}
class UpsertAdapterProperty(
val shortcutEntity: ShortcutEntity,
val useDriverApi: Boolean
) : SharedPropertySpec(
baseName = if (useDriverApi) {
"upsertAdapterOf${shortcutEntityFieldNamePart(shortcutEntity)}"
} else {
"upsertionAdapterOf${shortcutEntityFieldNamePart(shortcutEntity)}"
},
type = if (useDriverApi) {
UPSERT_ADAPTER.parametrizedBy(shortcutEntity.pojo.typeName)
} else {
UPSERT_ADAPTER_COMPAT.parametrizedBy(shortcutEntity.pojo.typeName)
}
) {
override fun getUniqueKey(): String {
return "${shortcutEntity.pojo.typeName}-${shortcutEntity.entityTypeName}-$useDriverApi"
}
override fun prepare(writer: TypeWriter, builder: XPropertySpec.Builder) {
}
}
}