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