1 /*
<lambda>null2  * Copyright (C) 2016 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package androidx.room.solver.query.result
18 
19 import androidx.room.compiler.codegen.XCodeBlock
20 import androidx.room.compiler.codegen.XFunSpec
21 import androidx.room.compiler.codegen.XMemberName.Companion.packageMember
22 import androidx.room.compiler.codegen.XTypeName
23 import androidx.room.compiler.codegen.compat.XConverters.toString
24 import androidx.room.compiler.processing.XType
25 import androidx.room.ext.ArrayLiteral
26 import androidx.room.ext.CommonTypeNames
27 import androidx.room.ext.RoomTypeNames.STATEMENT_UTIL
28 import androidx.room.ext.SQLiteDriverTypeNames
29 import androidx.room.solver.CodeGenScope
30 import androidx.room.vo.ColumnIndexVar
31 import androidx.room.vo.Entity
32 import androidx.room.vo.columnNames
33 import androidx.room.writer.EntityStatementConverterWriter
34 
35 class EntityRowAdapter(val entity: Entity, out: XType) : QueryMappedRowAdapter(out) {
36     override val mapping = EntityMapping(entity)
37 
38     private lateinit var functionSpec: XFunSpec
39 
40     private var stmtDelegateVarName: String? = null
41 
42     private val indexAdapter =
43         object : IndexAdapter {
44 
45             private var indexVars: List<ColumnIndexVar>? = null
46 
47             override fun onStatementReady(stmtVarName: String, scope: CodeGenScope) {
48                 indexVars =
49                     entity.columnNames.map { columnName ->
50                         val packageMember = STATEMENT_UTIL.packageMember("getColumnIndex")
51                         ColumnIndexVar(
52                             column = columnName,
53                             indexVar =
54                                 XCodeBlock.of("%M(%L, %S)", packageMember, stmtVarName, columnName)
55                                     // indexVar expects a string, and that depends on the language.
56                                     // We should change the function signature to accept XCodeBlock.
57                                     .toString(scope.language)
58                         )
59                     }
60             }
61 
62             override fun getIndexVars() = indexVars ?: emptyList()
63         }
64 
65     override fun onStatementReady(
66         stmtVarName: String,
67         scope: CodeGenScope,
68         indices: List<ColumnIndexVar>
69     ) {
70         // Check if given indices are the default ones, i.e. onStatementReady() was called without
71         // an indices argument and these are the default parameter ones, which means a wrapped
72         // statement is not needed since the generated entity statement converter has access to the
73         // original statement.
74         if (indices.isNotEmpty() && indices != indexAdapter.getIndexVars()) {
75             // Due to entity converter code being shared and using getColumnIndex() we can't
76             // generate code that uses the mapping directly. Instead we create a wrapped statement
77             // that is solely used in the shared converter function and whose getColumnIndex() is
78             // overridden to return the resolved column index.
79             stmtDelegateVarName = scope.getTmpVar("_wrappedStmt")
80             val entityColumnNamesParam =
81                 ArrayLiteral(CommonTypeNames.STRING, *entity.columnNames.toTypedArray())
82             val entityColumnIndicesParam =
83                 ArrayLiteral(XTypeName.PRIMITIVE_INT, *indices.map { it.indexVar }.toTypedArray())
84             val wrapperTypeName = SQLiteDriverTypeNames.STATEMENT
85             val packageMember = STATEMENT_UTIL.packageMember("wrapMappedColumns")
86             scope.builder.addLocalVariable(
87                 checkNotNull(stmtDelegateVarName),
88                 wrapperTypeName,
89                 assignExpr =
90                     XCodeBlock.of(
91                         "%M(%L, %L, %L)",
92                         packageMember,
93                         stmtVarName,
94                         entityColumnNamesParam,
95                         entityColumnIndicesParam
96                     )
97             )
98         }
99         functionSpec =
100             scope.writer.getOrCreateFunction(EntityStatementConverterWriter(entity = entity))
101     }
102 
103     override fun convert(outVarName: String, stmtVarName: String, scope: CodeGenScope) {
104         scope.builder.addStatement(
105             "%L = %N(%L)",
106             outVarName,
107             functionSpec,
108             stmtDelegateVarName ?: stmtVarName
109         )
110     }
111 
112     override fun getDefaultIndexAdapter() = indexAdapter
113 
114     data class EntityMapping(val entity: Entity) : Mapping() {
115         override val usedColumns: List<String> = entity.columnNames
116     }
117 }
118