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.writer
18 
19 import androidx.room.compiler.codegen.VisibilityModifier
20 import androidx.room.compiler.codegen.XFunSpec
21 import androidx.room.compiler.codegen.XTypeName
22 import androidx.room.compiler.codegen.XTypeSpec
23 import androidx.room.compiler.processing.XNullability
24 import androidx.room.ext.CommonTypeNames
25 import androidx.room.ext.RoomTypeNames
26 import androidx.room.ext.SQLiteDriverTypeNames
27 import androidx.room.solver.CodeGenScope
28 import androidx.room.vo.DataClass
29 import androidx.room.vo.PropertyWithIndex
30 import androidx.room.vo.ShortcutEntity
31 import androidx.room.vo.columnNames
32 
33 class EntityInsertAdapterWriter
34 private constructor(
35     val tableName: String,
36     val dataClass: DataClass,
37     val primitiveAutoGenerateColumn: String?,
38     val onConflict: String
39 ) {
40     companion object {
41         fun create(entity: ShortcutEntity, onConflict: String): EntityInsertAdapterWriter {
42             // If there is an auto-increment primary key with primitive type, we consider 0 as
43             // not set. For such fields, we must generate a slightly different insertion SQL.
44             val primitiveAutoGenerateField =
45                 if (entity.primaryKey.autoGenerateId) {
46                     entity.primaryKey.properties.firstOrNull()?.let { field ->
47                         field.statementBinder?.typeMirror()?.let { binderType ->
48                             if (binderType.nullability == XNullability.NONNULL) {
49                                 field
50                             } else {
51                                 null
52                             }
53                         }
54                     }
55                 } else {
56                     null
57                 }
58             return EntityInsertAdapterWriter(
59                 tableName = entity.tableName,
60                 dataClass = entity.dataClass,
61                 primitiveAutoGenerateColumn = primitiveAutoGenerateField?.columnName,
62                 onConflict = onConflict
63             )
64         }
65     }
66 
67     fun createAnonymous(
68         typeWriter: TypeWriter,
69     ): XTypeSpec {
70         return XTypeSpec.anonymousClassBuilder()
71             .apply {
72                 superclass(RoomTypeNames.INSERT_ADAPTER.parametrizedBy(dataClass.typeName))
73                 addFunction(
74                     XFunSpec.builder(
75                             name = "createQuery",
76                             visibility = VisibilityModifier.PROTECTED,
77                             isOverride = true
78                         )
79                         .apply {
80                             returns(CommonTypeNames.STRING)
81                             val query = buildString {
82                                 if (onConflict.isNotEmpty()) {
83                                     append("INSERT OR $onConflict INTO `$tableName`")
84                                 } else {
85                                     append("INSERT INTO `$tableName`")
86                                 }
87                                 append(" (${dataClass.columnNames.joinToString(",") { "`$it`" }})")
88                                 append(" VALUES (")
89                                 append(
90                                     dataClass.properties.joinToString(",") {
91                                         if (it.columnName == primitiveAutoGenerateColumn) {
92                                             "nullif(?, 0)"
93                                         } else {
94                                             "?"
95                                         }
96                                     }
97                                 )
98                                 append(")")
99                             }
100                             addStatement("return %S", query)
101                         }
102                         .build()
103                 )
104                 addFunction(
105                     XFunSpec.builder(
106                             name = "bind",
107                             visibility = VisibilityModifier.PROTECTED,
108                             isOverride = true
109                         )
110                         .apply {
111                             returns(XTypeName.UNIT_VOID)
112                             val stmtParam = "statement"
113                             addParameter(stmtParam, SQLiteDriverTypeNames.STATEMENT)
114                             val entityParam = "entity"
115                             addParameter(entityParam, dataClass.typeName)
116                             val mapped = PropertyWithIndex.byOrder(dataClass.properties)
117                             val bindScope = CodeGenScope(writer = typeWriter)
118                             PropertyReadWriteWriter.bindToStatement(
119                                 ownerVar = entityParam,
120                                 stmtParamVar = stmtParam,
121                                 propertiesWithIndices = mapped,
122                                 scope = bindScope
123                             )
124                             addCode(bindScope.generate())
125                         }
126                         .build()
127                 )
128             }
129             .build()
130     }
131 }
132