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.solver.query.result 18 19 import androidx.room.compiler.processing.XType 20 import androidx.room.parser.ParsedQuery 21 import androidx.room.processor.Context 22 import androidx.room.processor.ProcessorErrors 23 import androidx.room.solver.CodeGenScope 24 import androidx.room.verifier.QueryResultInfo 25 import androidx.room.vo.ColumnIndexVar 26 import androidx.room.vo.DataClass 27 import androidx.room.vo.Property 28 import androidx.room.vo.PropertyWithIndex 29 import androidx.room.vo.RelationCollector 30 import androidx.room.writer.PropertyReadWriteWriter 31 32 /** 33 * Creates the entity from the given info. 34 * 35 * The info comes from the query processor so we know about the order of columns in the result etc. 36 */ 37 class DataClassRowAdapter( 38 private val context: Context, 39 private val info: QueryResultInfo?, 40 private val query: ParsedQuery?, 41 val dataClass: DataClass, 42 out: XType 43 ) : QueryMappedRowAdapter(out) { 44 override val mapping: DataClassMapping 45 val relationCollectors: List<RelationCollector> 46 47 private val indexAdapter: DataClassIndexAdapter 48 49 // Set when statement is ready. 50 private lateinit var fieldsWithIndices: List<PropertyWithIndex> 51 52 init { 53 val remainingFields = dataClass.properties.toMutableList() 54 val unusedColumns = arrayListOf<String>() 55 val matchedFields: List<Property> 56 if (info != null) { 57 matchedFields = 58 info.columns.mapNotNull { column -> 59 val field = remainingFields.firstOrNull { it.columnName == column.name } 60 if (field == null) { 61 unusedColumns.add(column.name) 62 null 63 } else { 64 remainingFields.remove(field) 65 field 66 } 67 } 68 val nonNulls = remainingFields.filter { it.nonNull } 69 if (nonNulls.isNotEmpty()) { 70 context.logger.e( 71 ProcessorErrors.dataClassMissingNonNull( 72 dataClassTypeName = dataClass.typeName.toString(context.codeLanguage), 73 missingDataClassProperties = nonNulls.map { it.name }, 74 allQueryColumns = info.columns.map { it.name } 75 ) 76 ) 77 } 78 if (matchedFields.isEmpty()) { 79 context.logger.e( 80 ProcessorErrors.cannotFindQueryResultAdapter( 81 out.asTypeName().toString(context.codeLanguage) 82 ) 83 ) 84 } 85 } else { 86 matchedFields = remainingFields.map { it } 87 remainingFields.clear() 88 } 89 relationCollectors = RelationCollector.createCollectors(context, dataClass.relations) 90 91 mapping = 92 DataClassMapping( 93 dataClass = dataClass, 94 matchedFields = matchedFields, 95 unusedColumns = unusedColumns, 96 unusedFields = remainingFields 97 ) 98 99 indexAdapter = DataClassIndexAdapter(mapping, info, query) 100 } 101 102 fun relationTableNames(): List<String> { 103 return relationCollectors 104 .flatMap { 105 val queryTableNames = it.loadAllQuery.tables.map { it.name } 106 if (it.rowAdapter is DataClassRowAdapter) { 107 it.rowAdapter.relationTableNames() + queryTableNames 108 } else { 109 queryTableNames 110 } 111 } 112 .distinct() 113 } 114 115 override fun onStatementReady( 116 stmtVarName: String, 117 scope: CodeGenScope, 118 indices: List<ColumnIndexVar> 119 ) { 120 fieldsWithIndices = 121 indices.map { (column, indexVar) -> 122 val field = mapping.matchedFields.first { it.columnName == column } 123 PropertyWithIndex( 124 property = field, 125 indexVar = indexVar, 126 alwaysExists = info != null 127 ) 128 } 129 emitRelationCollectorsReady(stmtVarName, scope) 130 } 131 132 private fun emitRelationCollectorsReady(stmtVarName: String, scope: CodeGenScope) { 133 if (relationCollectors.isNotEmpty()) { 134 relationCollectors.forEach { it.writeInitCode(scope) } 135 scope.builder.apply { 136 beginControlFlow("while (%L.step())", stmtVarName).apply { 137 relationCollectors.forEach { 138 it.writeReadParentKeyCode(stmtVarName, fieldsWithIndices, scope) 139 } 140 } 141 endControlFlow() 142 addStatement("%L.reset()", stmtVarName) 143 } 144 relationCollectors.forEach { it.writeFetchRelationCall(scope) } 145 } 146 } 147 148 override fun convert(outVarName: String, stmtVarName: String, scope: CodeGenScope) { 149 PropertyReadWriteWriter.readFromStatement( 150 outVar = outVarName, 151 outDataClass = dataClass, 152 stmtVar = stmtVarName, 153 propertiesWithIndices = fieldsWithIndices, 154 relationCollectors = relationCollectors, 155 scope = scope 156 ) 157 } 158 159 override fun getDefaultIndexAdapter() = indexAdapter 160 161 data class DataClassMapping( 162 val dataClass: DataClass, 163 val matchedFields: List<Property>, 164 val unusedColumns: List<String>, 165 val unusedFields: List<Property> 166 ) : Mapping() { 167 override val usedColumns = matchedFields.map { it.columnName } 168 } 169 } 170