1 /*
<lambda>null2  * Copyright (C) 2017 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.writer
18 
19 import androidx.room.compiler.codegen.VisibilityModifier
20 import androidx.room.compiler.codegen.XFunSpec
21 import androidx.room.compiler.codegen.XTypeSpec
22 import androidx.room.ext.CommonTypeNames
23 import androidx.room.ext.RoomTypeNames
24 import androidx.room.ext.SQLiteDriverTypeNames
25 import androidx.room.solver.CodeGenScope
26 import androidx.room.vo.DataClass
27 import androidx.room.vo.Properties
28 import androidx.room.vo.PropertyWithIndex
29 import androidx.room.vo.ShortcutEntity
30 import androidx.room.vo.columnNames
31 
32 class EntityUpdateAdapterWriter
33 private constructor(
34     val tableName: String,
35     val dataClass: DataClass,
36     val primaryKeyFields: Properties,
37     val onConflict: String
38 ) {
39     companion object {
40         fun create(entity: ShortcutEntity, onConflict: String) =
41             EntityUpdateAdapterWriter(
42                 tableName = entity.tableName,
43                 dataClass = entity.dataClass,
44                 primaryKeyFields = entity.primaryKey.properties,
45                 onConflict = onConflict
46             )
47     }
48 
49     fun createAnonymous(typeWriter: TypeWriter): XTypeSpec {
50         return XTypeSpec.anonymousClassBuilder()
51             .apply {
52                 superclass(
53                     RoomTypeNames.DELETE_OR_UPDATE_ADAPTER.parametrizedBy(dataClass.typeName)
54                 )
55                 addFunction(
56                     XFunSpec.builder(
57                             name = "createQuery",
58                             visibility = VisibilityModifier.PROTECTED,
59                             isOverride = true
60                         )
61                         .apply {
62                             returns(CommonTypeNames.STRING)
63                             val dataClassCols =
64                                 dataClass.columnNames.joinToString(",") { "`$it` = ?" }
65                             val pkFieldsCols =
66                                 primaryKeyFields.columnNames.joinToString(" AND ") { "`$it` = ?" }
67                             val query = buildString {
68                                 if (onConflict.isNotEmpty()) {
69                                     append("UPDATE OR $onConflict `$tableName` SET")
70                                 } else {
71                                     append("UPDATE `$tableName` SET")
72                                 }
73                                 append(" $dataClassCols")
74                                 append(" WHERE")
75                                 append(" $pkFieldsCols")
76                             }
77                             addStatement("return %S", query)
78                         }
79                         .build()
80                 )
81                 addFunction(
82                     XFunSpec.builder(
83                             name = "bind",
84                             visibility = VisibilityModifier.PROTECTED,
85                             isOverride = true
86                         )
87                         .apply {
88                             val stmtParam = "statement"
89                             addParameter(stmtParam, SQLiteDriverTypeNames.STATEMENT)
90                             val entityParam = "entity"
91                             addParameter(entityParam, dataClass.typeName)
92                             val mappedField = PropertyWithIndex.byOrder(dataClass.properties)
93                             val bindScope = CodeGenScope(writer = typeWriter)
94                             PropertyReadWriteWriter.bindToStatement(
95                                 ownerVar = entityParam,
96                                 stmtParamVar = stmtParam,
97                                 propertiesWithIndices = mappedField,
98                                 scope = bindScope
99                             )
100                             val pkeyStart = dataClass.properties.size
101                             val mappedPrimaryKeys =
102                                 primaryKeyFields.mapIndexed { index, field ->
103                                     PropertyWithIndex(
104                                         property = field,
105                                         indexVar = "${pkeyStart + index + 1}",
106                                         alwaysExists = true
107                                     )
108                                 }
109                             PropertyReadWriteWriter.bindToStatement(
110                                 ownerVar = entityParam,
111                                 stmtParamVar = stmtParam,
112                                 propertiesWithIndices = mappedPrimaryKeys,
113                                 scope = bindScope
114                             )
115                             addCode(bindScope.generate())
116                         }
117                         .build()
118                 )
119             }
120             .build()
121     }
122 }
123