PojoIndexAdapter.kt

/*
 * Copyright 2022 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package androidx.room.solver.query.result

import androidx.room.ext.L
import androidx.room.ext.RoomTypeNames
import androidx.room.ext.S
import androidx.room.ext.T
import androidx.room.ext.capitalize
import androidx.room.ext.stripNonJava
import androidx.room.parser.ParsedQuery
import androidx.room.processor.ProcessorErrors
import androidx.room.solver.CodeGenScope
import androidx.room.verifier.QueryResultInfo
import androidx.room.vo.ColumnIndexVar
import com.squareup.javapoet.TypeName
import java.util.Locale

/**
 * Creates the index variables to retrieve columns from a cursor for a [PojoRowAdapter].
 */
class PojoIndexAdapter(
    private val mapping: PojoRowAdapter.PojoMapping,
    private val info: QueryResultInfo?,
    private val query: ParsedQuery?,
) : IndexAdapter {

    private lateinit var columnIndexVars: List<ColumnIndexVar>

    override fun onCursorReady(cursorVarName: String, scope: CodeGenScope) {
        columnIndexVars = mapping.matchedFields.map {
            val indexVar = scope.getTmpVar(
                "_cursorIndexOf${it.name.stripNonJava().capitalize(Locale.US)}"
            )
            if (info != null && query != null && query.hasTopStarProjection == false) {
                // When result info is available and query does not have a top-level star
                // projection we can generate column to field index since the column result order
                // is deterministic.
                val infoIndex = info.columns.indexOfFirst { columnInfo ->
                    columnInfo.name == it.columnName
                }
                check(infoIndex != -1) {
                    "Result column index not found for field '$it' with column name " +
                        "'${it.columnName}'. Query: ${query.original}. Please file a bug at " +
                        ProcessorErrors.ISSUE_TRACKER_LINK
                }
                scope.builder().addStatement(
                    "final $T $L = $L",
                    TypeName.INT, indexVar, infoIndex
                )
            } else {
                val indexMethod = if (info == null) {
                    "getColumnIndex"
                } else {
                    "getColumnIndexOrThrow"
                }
                scope.builder().addStatement(
                    "final $T $L = $T.$L($L, $S)",
                    TypeName.INT, indexVar, RoomTypeNames.CURSOR_UTIL, indexMethod, cursorVarName,
                    it.columnName
                )
            }
            ColumnIndexVar(it.columnName, indexVar)
        }
    }

    override fun getIndexVars() = columnIndexVars
}