blob: c6400a6bdcbc5ca9363fa11422f6983d016ae800 [file] [log] [blame]
/*
* Copyright 2018 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.processor
import androidx.room.ext.KotlinTypeNames
import androidx.room.ext.L
import androidx.room.ext.N
import androidx.room.ext.RoomCoroutinesTypeNames
import androidx.room.ext.T
import androidx.room.parser.ParsedQuery
import androidx.room.compiler.processing.XDeclaredType
import androidx.room.compiler.processing.XMethodElement
import androidx.room.compiler.processing.XMethodType
import androidx.room.compiler.processing.XSuspendMethodType
import androidx.room.compiler.processing.XType
import androidx.room.compiler.processing.XVariableElement
import androidx.room.compiler.processing.isSuspendFunction
import androidx.room.solver.prepared.binder.CallablePreparedQueryResultBinder.Companion.createPreparedBinder
import androidx.room.solver.prepared.binder.PreparedQueryResultBinder
import androidx.room.solver.query.result.CoroutineResultBinder
import androidx.room.solver.query.result.QueryResultBinder
import androidx.room.solver.shortcut.binder.CallableDeleteOrUpdateMethodBinder.Companion.createDeleteOrUpdateBinder
import androidx.room.solver.shortcut.binder.CallableInsertMethodBinder.Companion.createInsertBinder
import androidx.room.solver.shortcut.binder.DeleteOrUpdateMethodBinder
import androidx.room.solver.shortcut.binder.InsertMethodBinder
import androidx.room.solver.transaction.binder.CoroutineTransactionMethodBinder
import androidx.room.solver.transaction.binder.InstantTransactionMethodBinder
import androidx.room.solver.transaction.binder.TransactionMethodBinder
import androidx.room.solver.transaction.result.TransactionMethodAdapter
import androidx.room.vo.QueryParameter
import androidx.room.vo.ShortcutQueryParameter
import androidx.room.vo.TransactionMethod
/**
* Delegate class with common functionality for DAO method processors.
*/
abstract class MethodProcessorDelegate(
val context: Context,
val containing: XDeclaredType,
val executableElement: XMethodElement
) {
abstract fun extractReturnType(): XType
abstract fun extractParams(): List<XVariableElement>
fun extractQueryParams(): List<QueryParameter> {
return extractParams().map { variableElement ->
QueryParameterProcessor(
baseContext = context,
containing = containing,
element = variableElement,
sqlName = variableElement.name
).process()
}
}
abstract fun findResultBinder(returnType: XType, query: ParsedQuery): QueryResultBinder
abstract fun findPreparedResultBinder(
returnType: XType,
query: ParsedQuery
): PreparedQueryResultBinder
abstract fun findInsertMethodBinder(
returnType: XType,
params: List<ShortcutQueryParameter>
): InsertMethodBinder
abstract fun findDeleteOrUpdateMethodBinder(returnType: XType): DeleteOrUpdateMethodBinder
abstract fun findTransactionMethodBinder(
callType: TransactionMethod.CallType
): TransactionMethodBinder
companion object {
fun createFor(
context: Context,
containing: XDeclaredType,
executableElement: XMethodElement
): MethodProcessorDelegate {
val asMember = executableElement.asMemberOf(containing)
return if (asMember.isSuspendFunction()) {
val hasCoroutineArtifact = context.processingEnv
.findTypeElement(RoomCoroutinesTypeNames.COROUTINES_ROOM.toString()) != null
if (!hasCoroutineArtifact) {
context.logger.e(ProcessorErrors.MISSING_ROOM_COROUTINE_ARTIFACT)
}
SuspendMethodProcessorDelegate(
context,
containing,
executableElement,
asMember
)
} else {
DefaultMethodProcessorDelegate(
context,
containing,
executableElement,
asMember
)
}
}
}
}
/**
* Default delegate for DAO methods.
*/
class DefaultMethodProcessorDelegate(
context: Context,
containing: XDeclaredType,
executableElement: XMethodElement,
val executableType: XMethodType
) : MethodProcessorDelegate(context, containing, executableElement) {
override fun extractReturnType(): XType {
return executableType.returnType
}
override fun extractParams() = executableElement.parameters
override fun findResultBinder(returnType: XType, query: ParsedQuery) =
context.typeAdapterStore.findQueryResultBinder(returnType, query)
override fun findPreparedResultBinder(
returnType: XType,
query: ParsedQuery
) = context.typeAdapterStore.findPreparedQueryResultBinder(returnType, query)
override fun findInsertMethodBinder(
returnType: XType,
params: List<ShortcutQueryParameter>
) = context.typeAdapterStore.findInsertMethodBinder(returnType, params)
override fun findDeleteOrUpdateMethodBinder(returnType: XType) =
context.typeAdapterStore.findDeleteOrUpdateMethodBinder(returnType)
override fun findTransactionMethodBinder(callType: TransactionMethod.CallType) =
InstantTransactionMethodBinder(
TransactionMethodAdapter(executableElement.name, callType))
}
/**
* Delegate for DAO methods that are a suspend function.
*/
class SuspendMethodProcessorDelegate(
context: Context,
containing: XDeclaredType,
executableElement: XMethodElement,
val executableType: XSuspendMethodType
) : MethodProcessorDelegate(context, containing, executableElement) {
private val continuationParam: XVariableElement by lazy {
val continuationType = context.processingEnv
.requireType(KotlinTypeNames.CONTINUATION.toString()).rawType
executableElement.parameters.last {
it.type.rawType == continuationType
}
}
override fun extractReturnType(): XType {
return executableType.getSuspendFunctionReturnType()
}
override fun extractParams() =
executableElement.parameters.filterNot {
it == continuationParam
}
override fun findResultBinder(returnType: XType, query: ParsedQuery) =
CoroutineResultBinder(
typeArg = returnType,
adapter = context.typeAdapterStore.findQueryResultAdapter(returnType, query),
continuationParamName = continuationParam.name
)
override fun findPreparedResultBinder(
returnType: XType,
query: ParsedQuery
) = createPreparedBinder(
returnType = returnType,
adapter = context.typeAdapterStore.findPreparedQueryResultAdapter(returnType, query)
) { callableImpl, dbField ->
addStatement(
"return $T.execute($N, $L, $L, $N)",
RoomCoroutinesTypeNames.COROUTINES_ROOM,
dbField,
"true", // inTransaction
callableImpl,
continuationParam.name
)
}
override fun findInsertMethodBinder(
returnType: XType,
params: List<ShortcutQueryParameter>
) = createInsertBinder(
typeArg = returnType,
adapter = context.typeAdapterStore.findInsertAdapter(returnType, params)
) { callableImpl, dbField ->
addStatement(
"return $T.execute($N, $L, $L, $N)",
RoomCoroutinesTypeNames.COROUTINES_ROOM,
dbField,
"true", // inTransaction
callableImpl,
continuationParam.name
)
}
override fun findDeleteOrUpdateMethodBinder(returnType: XType) =
createDeleteOrUpdateBinder(
typeArg = returnType,
adapter = context.typeAdapterStore.findDeleteOrUpdateAdapter(returnType)
) { callableImpl, dbField ->
addStatement(
"return $T.execute($N, $L, $L, $N)",
RoomCoroutinesTypeNames.COROUTINES_ROOM,
dbField,
"true", // inTransaction
callableImpl,
continuationParam.name
)
}
override fun findTransactionMethodBinder(callType: TransactionMethod.CallType) =
CoroutineTransactionMethodBinder(
adapter = TransactionMethodAdapter(executableElement.name, callType),
continuationParamName = continuationParam.name
)
}