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