1 /* 2 * Copyright 2018 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.ext.AndroidTypeNames 20 import androidx.room.ext.L 21 import androidx.room.ext.N 22 import androidx.room.ext.RoomGuavaTypeNames 23 import androidx.room.ext.T 24 import androidx.room.ext.typeName 25 import androidx.room.solver.CodeGenScope 26 import androidx.room.writer.DaoWriter 27 import com.squareup.javapoet.FieldSpec 28 import com.squareup.javapoet.MethodSpec 29 import com.squareup.javapoet.ParameterizedTypeName 30 import com.squareup.javapoet.TypeSpec 31 import javax.lang.model.element.Modifier 32 import javax.lang.model.type.TypeMirror 33 34 /** 35 * A ResultBinder that emits a ListenableFuture<T> where T is the input {@code typeArg}. 36 * 37 * <p>The Future runs on the background thread Executor. 38 */ 39 class GuavaListenableFutureQueryResultBinder( 40 val typeArg: TypeMirror, 41 adapter: QueryResultAdapter?) 42 : BaseObservableQueryResultBinder(adapter) { 43 convertAndReturnnull44 override fun convertAndReturn( 45 roomSQLiteQueryVar: String, 46 canReleaseQuery: Boolean, 47 dbField: FieldSpec, 48 inTransaction: Boolean, 49 scope: CodeGenScope) { 50 // Callable<T> 51 val callableImpl = createCallableOfT( 52 roomSQLiteQueryVar, 53 dbField, 54 inTransaction, 55 scope) 56 57 scope.builder().apply { 58 addStatement( 59 "return $T.createListenableFuture($L, $L, $L)", 60 RoomGuavaTypeNames.GUAVA_ROOM, 61 callableImpl, 62 roomSQLiteQueryVar, 63 canReleaseQuery) 64 } 65 } 66 67 /** 68 * Returns an anonymous subclass of Callable<T> that executes the database transaction and 69 * constitutes the result T. 70 * 71 * <p>Note that this method does not release the query object. 72 */ createCallableOfTnull73 private fun createCallableOfT( 74 roomSQLiteQueryVar: String, 75 dbField: FieldSpec, 76 inTransaction: Boolean, 77 scope: CodeGenScope): TypeSpec { 78 return TypeSpec.anonymousClassBuilder("").apply { 79 superclass( 80 ParameterizedTypeName.get(java.util.concurrent.Callable::class.typeName(), 81 typeArg.typeName())) 82 addMethod( 83 MethodSpec.methodBuilder("call").apply { 84 // public T call() throws Exception {} 85 returns(typeArg.typeName()) 86 addAnnotation(Override::class.typeName()) 87 addModifiers(Modifier.PUBLIC) 88 addException(Exception::class.typeName()) 89 90 // Body. 91 val transactionWrapper = if (inTransaction) { 92 transactionWrapper(dbField) 93 } else { 94 null 95 } 96 transactionWrapper?.beginTransactionWithControlFlow() 97 apply { 98 val outVar = scope.getTmpVar("_result") 99 val cursorVar = scope.getTmpVar("_cursor") 100 addStatement("final $T $L = $N.query($L)", AndroidTypeNames.CURSOR, 101 cursorVar, 102 DaoWriter.dbField, roomSQLiteQueryVar) 103 beginControlFlow("try").apply { 104 val adapterScope = scope.fork() 105 adapter?.convert(outVar, cursorVar, adapterScope) 106 addCode(adapterScope.builder().build()) 107 transactionWrapper?.commitTransaction() 108 addStatement("return $L", outVar) 109 } 110 nextControlFlow("finally").apply { 111 addStatement("$L.close()", cursorVar) 112 } 113 endControlFlow() 114 } 115 transactionWrapper?.endTransactionWithControlFlow() 116 }.build()) 117 }.build() 118 } 119 } 120