Home | History | Annotate | Download | only in writer
      1 /*
      2  * 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 android.arch.persistence.room.writer
     18 
     19 import android.arch.persistence.room.ext.AndroidTypeNames
     20 import android.arch.persistence.room.ext.L
     21 import android.arch.persistence.room.ext.N
     22 import android.arch.persistence.room.ext.S
     23 import android.arch.persistence.room.ext.T
     24 import android.arch.persistence.room.solver.CodeGenScope
     25 import android.arch.persistence.room.vo.RelationCollector
     26 import com.squareup.javapoet.ClassName
     27 import com.squareup.javapoet.MethodSpec
     28 import com.squareup.javapoet.ParameterSpec
     29 import com.squareup.javapoet.ParameterizedTypeName
     30 import com.squareup.javapoet.TypeName
     31 import stripNonJava
     32 import javax.lang.model.element.Modifier
     33 
     34 /**
     35  * Writes the method that fetches the relations of a POJO and assigns them into the given map.
     36  */
     37 class RelationCollectorMethodWriter(val collector: RelationCollector)
     38     : ClassWriter.SharedMethodSpec(
     39         "fetchRelationship${collector.relation.entity.tableName.stripNonJava()}" +
     40                 "As${collector.relation.pojo.typeName.toString().stripNonJava()}") {
     41     companion object {
     42         val KEY_SET_VARIABLE = "__mapKeySet"
     43     }
     44     override fun getUniqueKey(): String {
     45         val relation = collector.relation
     46         return "RelationCollectorMethodWriter" +
     47                 "-${collector.mapTypeName}" +
     48                 "-${relation.entity.typeName}" +
     49                 "-${relation.entityField.columnName}" +
     50                 "-${relation.pojo.typeName}" +
     51                 "-${relation.createLoadAllSql()}"
     52     }
     53 
     54     override fun prepare(writer: ClassWriter, builder: MethodSpec.Builder) {
     55         val scope = CodeGenScope(writer)
     56         val relation = collector.relation
     57 
     58         val param = ParameterSpec.builder(collector.mapTypeName, "_map")
     59                 .addModifiers(Modifier.FINAL)
     60                 .build()
     61         val sqlQueryVar = scope.getTmpVar("_sql")
     62         val keySetVar = KEY_SET_VARIABLE
     63 
     64         val cursorVar = "_cursor"
     65         val itemKeyIndexVar = "_itemKeyIndex"
     66         val stmtVar = scope.getTmpVar("_stmt")
     67         scope.builder().apply {
     68 
     69             val keySetType = ParameterizedTypeName.get(
     70                     ClassName.get(Set::class.java), collector.keyTypeName
     71             )
     72             addStatement("final $T $L = $N.keySet()", keySetType, keySetVar, param)
     73             beginControlFlow("if ($L.isEmpty())", keySetVar).apply {
     74                 addStatement("return")
     75             }
     76             endControlFlow()
     77             collector.queryWriter.prepareReadAndBind(sqlQueryVar, stmtVar, scope)
     78 
     79             addStatement("final $T $L = $N.query($L)", AndroidTypeNames.CURSOR, cursorVar,
     80                     DaoWriter.dbField, stmtVar)
     81 
     82             beginControlFlow("try").apply {
     83                 addStatement("final $T $L = $L.getColumnIndex($S)",
     84                         TypeName.INT, itemKeyIndexVar, cursorVar, relation.entityField.columnName)
     85 
     86                 beginControlFlow("if ($L == -1)", itemKeyIndexVar).apply {
     87                     addStatement("return")
     88                 }
     89                 endControlFlow()
     90 
     91                 collector.rowAdapter.onCursorReady(cursorVar, scope)
     92                 val tmpVarName = scope.getTmpVar("_item")
     93                 beginControlFlow("while($L.moveToNext())", cursorVar).apply {
     94                     // read key from the cursor
     95                     collector.readKey(
     96                             cursorVarName = cursorVar,
     97                             indexVar = itemKeyIndexVar,
     98                             scope = scope
     99                     ) { keyVar ->
    100                         val collectionVar = scope.getTmpVar("_tmpCollection")
    101                         addStatement("$T $L = $N.get($L)", collector.collectionTypeName,
    102                                 collectionVar, param, keyVar)
    103                         beginControlFlow("if ($L != null)", collectionVar).apply {
    104                             addStatement("final $T $L", relation.pojo.typeName, tmpVarName)
    105                             collector.rowAdapter.convert(tmpVarName, cursorVar, scope)
    106                             addStatement("$L.add($L)", collectionVar, tmpVarName)
    107                         }
    108                         endControlFlow()
    109                     }
    110                 }
    111                 endControlFlow()
    112                 collector.rowAdapter.onCursorFinished()?.invoke(scope)
    113             }
    114             nextControlFlow("finally").apply {
    115                 addStatement("$L.close()", cursorVar)
    116             }
    117             endControlFlow()
    118         }
    119         builder.apply {
    120             addModifiers(Modifier.PRIVATE)
    121             addParameter(param)
    122             returns(TypeName.VOID)
    123             addCode(scope.builder().build())
    124         }
    125     }
    126 }
    127